diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 0000000000..08e1985ae7
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,18 @@
+# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
+language: "en-US"
+early_access: false
+reviews:
+ profile: "chill"
+ request_changes_workflow: true
+ high_level_summary: true
+ poem: true
+ review_status: true
+ collapse_walkthrough: false
+ auto_review:
+ enabled: true
+ drafts: false
+ base_branches:
+ - develop
+ - main
+chat:
+ auto_reply: true
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000000..de7cff92ec
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,31 @@
+# 👋 Welcome, we're glad you're setting up an installation of Talawa-admin. Copy this
+# file to .env or set the variables in your local environment manually.
+
+
+# Custom port number for the talawa-admin development server to run on. Default is 4321.
+
+PORT=4321
+
+# Run Talawa-api locally in your system, and put its url into the same.
+
+REACT_APP_TALAWA_URL=
+
+# Do you want to setup and use "I'm not a robot" Checkbox (Google Recaptcha)?
+# If no, leave blank, else write yes
+# Example: REACT_APP_USE_RECAPTCHA=yes
+
+REACT_APP_USE_RECAPTCHA=
+
+# If you are using Google Recaptcha, i.e., REACT_APP_USE_RECAPTCHA=yes, read the following steps
+# Get the google recaptcha site key from google recaptcha admin or https://www.google.com/recaptcha/admin/create
+# from here for reCAPTCHA v2 and "I'm not a robot" Checkbox, and paste the key here.
+# Note: In domains, fill localhost
+
+REACT_APP_RECAPTCHA_SITE_KEY=
+
+# has to be inserted in the env file to use plugins and other websocket based features.
+REACT_APP_BACKEND_WEBSOCKET_URL=ws://localhost:4000/graphql/
+
+# If you want to logs Compiletime and Runtime error , warning and info write YES or if u want to
+# keep the console clean leave it blank
+ALLOW_LOGS=
diff --git a/.eslintignore b/.eslintignore
index e69de29bb2..7e45de312a 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -0,0 +1,2 @@
+# Contains the PDF file of the Tag as JSON string, thus does not need to be linted
+src/components/CheckIn/tagTemplate.ts
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index a1f8002a3c..26470f7aab 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,6 +1,7 @@
{
"env": {
"browser": true,
+ "node": true,
"es6": true
},
@@ -10,7 +11,9 @@
"eslint:recommended",
"plugin:jest/recommended",
"plugin:prettier/recommended",
- "plugin:@typescript-eslint/recommended"
+ "plugin:@typescript-eslint/recommended",
+ "eslint-config-prettier",
+ "prettier"
],
"globals": {
"Atomics": "readonly",
@@ -26,12 +29,97 @@
},
// Specify the ESLint plugins tobe used
- "plugins": ["react", "@typescript-eslint", "react-hooks", "jest"],
+ "plugins": [
+ "react",
+ "@typescript-eslint",
+ "jest",
+ "import",
+ "eslint-plugin-tsdoc",
+ "prettier"
+ ],
"rules": {
- "react/destructuring-assignment": ["warn", "always"],
- "react/no-multi-comp": ["error", { "ignoreStateless": false }],
- "react/jsx-filename-extension": ["error", { "extensions": [".tsx"] }],
+ "react/destructuring-assignment": "error",
+ "@typescript-eslint/explicit-module-boundary-types": "error",
+ "react/no-multi-comp": [
+ "error",
+ {
+ "ignoreStateless": false
+ }
+ ],
+ "react/jsx-filename-extension": [
+ "error",
+ {
+ "extensions": [".tsx"]
+ }
+ ],
+ "import/no-duplicates": "error",
+ "tsdoc/syntax": "error",
+ "@typescript-eslint/ban-ts-comment": "error",
+ "@typescript-eslint/no-explicit-any": "error",
+ "@typescript-eslint/no-inferrable-types": "error",
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
+ "@typescript-eslint/no-non-null-assertion": "error",
+ "@typescript-eslint/no-var-requires": "error",
+ "@typescript-eslint/no-unsafe-function-type": "error",
+ "@typescript-eslint/no-wrapper-object-types": "error",
+ "@typescript-eslint/no-empty-object-type": "error",
+ "@typescript-eslint/no-duplicate-enum-values": "error",
+ "@typescript-eslint/array-type": "error",
+ "@typescript-eslint/consistent-type-assertions": "error",
+ "@typescript-eslint/consistent-type-imports": "error",
+ "@typescript-eslint/explicit-function-return-type": [
+ 2,
+ {
+ "allowExpressions": true,
+ "allowTypedFunctionExpressions": true
+ }
+ ],
+ "camelcase": "off",
+ "@typescript-eslint/naming-convention": [
+ "error",
+ // Interfaces must begin with Interface or TestInterface followed by a PascalCase name
+ {
+ "selector": "interface",
+ "format": ["PascalCase"],
+ "prefix": ["Interface", "TestInterface"]
+ },
+ // Type Aliases must be in PascalCase
+ {
+ "selector": ["typeAlias", "typeLike", "enum"],
+ "format": ["PascalCase"]
+ },
+ {
+ "selector": "typeParameter",
+ "format": ["PascalCase"],
+ "prefix": ["T"]
+ },
+ {
+ "selector": "variable",
+ "format": ["camelCase", "UPPER_CASE", "PascalCase"],
+ "leadingUnderscore": "allow"
+ },
+ {
+ "selector": "parameter",
+ "format": ["camelCase"],
+ "leadingUnderscore": "allow"
+ },
+ {
+ "selector": "function",
+ "format": ["camelCase", "PascalCase"]
+ },
+ {
+ "selector": "memberLike",
+ "modifiers": ["private"],
+ "format": ["camelCase"],
+ "leadingUnderscore": "require"
+ },
+ {
+ "selector": "variable",
+ "modifiers": ["exported"],
+ "format": null
+ }
+ ],
// Ensures that components are always written in PascalCase
"react/jsx-pascal-case": [
"error",
@@ -42,23 +130,16 @@
"react/jsx-equals-spacing": ["warn", "never"],
"react/no-this-in-sfc": "error",
- // Ensures that components are always indented by 2 spaces
- "react/jsx-indent": ["warn", 2],
- "react/jsx-tag-spacing": [
- "warn",
- {
- "afterOpening": "never",
- "beforeClosing": "never",
- "beforeSelfClosing": "always"
- }
- ],
+ // All tests must need not have an assertion
+ "jest/expect-expect": 0,
// Enforce Strictly functional components
"react/no-unstable-nested-components": ["error", { "allowAsProps": true }],
"react/function-component-definition": [
- "error",
+ 0,
{ "namedComponents": "function-declaration" }
- ]
+ ],
+ "prettier/prettier": "error"
},
// Let ESLint use the react version in the package.json
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug-report.md
similarity index 60%
rename from .github/ISSUE_TEMPLATE/bug_report.md
rename to .github/ISSUE_TEMPLATE/bug-report.md
index ccbb9c4d8e..d9f95c0d65 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -1,9 +1,10 @@
---
-name: Bug report
+name: Bug Report
about: Create a report to help us improve.
-title: Bug report
-labels: Bug
-assignees: ""
+title: Bug Report
+labels: bug
+assignees: ''
+
---
**Describe the bug**
@@ -27,3 +28,9 @@ A clear and concise description of how the code performed w.r.t expectations.
If applicable, add screenshots to help explain your problem.
**Additional details**
+Add any other context or screenshots about the feature request here.
+
+**Potential internship candidates**
+
+Please read this if you are planning to apply for a Palisadoes Foundation internship
+- https://github.com/PalisadoesFoundation/talawa/issues/359
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature-request.md
similarity index 69%
rename from .github/ISSUE_TEMPLATE/feature_request.md
rename to .github/ISSUE_TEMPLATE/feature-request.md
index 1c93611c44..51aea0e9d9 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -1,9 +1,10 @@
---
-name: Feature request
+name: Feature Request
about: Suggest an idea for this project
-title: Feature request
-labels: Feature
-assignees: ""
+title: Feature Request
+labels: feature request
+assignees: ''
+
---
**Is your feature request related to a problem? Please describe.**
@@ -20,3 +21,8 @@ A clear and concise description of approach to be followed.
**Additional context**
Add any other context or screenshots about the feature request here.
+
+**Potential internship candidates**
+
+Please read this if you are planning to apply for a Palisadoes Foundation internship
+- https://github.com/PalisadoesFoundation/talawa/issues/359
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
new file mode 100644
index 0000000000..81d68df4bd
--- /dev/null
+++ b/.github/dependabot.yaml
@@ -0,0 +1,18 @@
+# Configuration for automated dependency updates using Dependabot
+version: 2
+updates:
+ # Define the target package ecosystem
+ - package-ecosystem: "npm"
+ # Specify the root directory
+ directory: "/"
+ # Schedule automated updates to run weekly
+ schedule:
+ interval: "monthly"
+ # Labels to apply to Dependabot PRs
+ labels:
+ - "dependencies"
+ # Specify the target branch for PRs
+ target-branch: "develop-postgres"
+ # Customize commit message prefix
+ commit-message:
+ prefix: "chore(deps):"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index b3f679554c..9e3081d0ee 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,13 +1,40 @@
-
+
+
+
**What kind of change does this PR introduce?**
+**Issue Number:**
+
+Fixes #
+
**Did you add tests for your changes?**
+
+
+**Snapshots/Videos:**
+
+
+
**If relevant, did you update the documentation?**
+
+
**Summary**
@@ -18,3 +45,9 @@
**Other information**
+
+
+
+**Have you read the [contributing guide](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/CONTRIBUTING.md)?**
+
+
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 0000000000..1e9a81eaf8
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,47 @@
+# Talawa GitHub Workflows Guidelines
+
+Follow these guidelines when contributing to this directory.
+
+## General
+
+Any changes to files in this directory are flagged when pull requests are run. Make changes only on the advice of a contributor.
+
+## YAML Workflow Files
+
+The YAML files in this directory have very specific roles depending on the type of workflow.
+
+Whenever possible you must ensure that:
+1. The file roles below are maintained
+1. The sequence of the jobs in the workflows are maintained using [GitHub Action dependencies](https://docs.github.com/en/actions/learn-github-actions/managing-complex-workflows).
+
+### File Roles
+Follow these guidelines when creating new YAML defined GitHub actions. This is done to make troubleshooting easier.
+
+1. `Issue` Workflows:
+ 1. Place all actions related to issues in the `issues.yml` file.
+1. `Pull Request` workflows to be run by:
+ 1. Workflows to run **First Time** repo contributors:
+ 1. Place all actions related to to this in the `pull-request-target.yml` file.
+ 1. Workflows to be run by **ALL** repo contributors:
+ 1. Place all actions related to pull requests in the `pull-request.yml` file.
+1. `Push` workflows:
+ 1. Place all actions related to pushes in the `push.yml` file.
+
+#### File Role Exceptions
+
+There are some exceptions to these rules in which jobs can be placed in dedicated separate files:
+1. Jobs that require unique `cron:` schedules
+1. Jobs that require unique `paths:` statements that operate only when files in a specific path are updated.
+1. Jobs only work correctly if they have a dedicated file (eg. `CodeQL`)
+
+## Scripts
+
+Follow these guidelines when creating or modifying scripts in this directory.
+
+1. All scripts in this directory must be written in python3 for consistency.
+1. The python3 scripts must follow the following coding standards. Run these commands against your scripts before submitting PRs that modify or create python3 scripts in this directory.
+ 1. Pycodestyle
+ 1. Pydocstyle
+ 1. Pylint
+ 1. Flake8
+1. All scripts must run a main() function.
diff --git a/.github/workflows/auto-label.json5 b/.github/workflows/auto-label.json5
new file mode 100644
index 0000000000..37929ea97b
--- /dev/null
+++ b/.github/workflows/auto-label.json5
@@ -0,0 +1,8 @@
+{
+ "labelsSynonyms": {
+ "dependencies": ["dependabot", "dependency", "dependencies"],
+ "security": ["security"],
+ "ui/ux": ["layout", "screen", "design", "figma"]
+ },
+ "defaultLabels": ["unapproved"],
+}
\ No newline at end of file
diff --git a/.github/workflows/check-tsdoc.js b/.github/workflows/check-tsdoc.js
new file mode 100644
index 0000000000..d5c3b33b90
--- /dev/null
+++ b/.github/workflows/check-tsdoc.js
@@ -0,0 +1,68 @@
+import fs from 'fs/promises'; // Import fs.promises for async operations
+import path from 'path';
+
+// List of files to skip
+const filesToSkip = [
+ 'index.tsx',
+ 'EventActionItems.tsx',
+ 'OrgPostCard.tsx',
+ 'UsersTableItem.tsx',
+ 'FundCampaignPledge.tsx'
+];
+
+// Recursively find all .tsx files, excluding files listed in filesToSkip
+async function findTsxFiles(dir) {
+ let results = [];
+ try {
+ const list = await fs.readdir(dir);
+ for (const file of list) {
+ const filePath = path.join(dir, file);
+ const stat = await fs.stat(filePath);
+ if (stat.isDirectory()) {
+ results = results.concat(await findTsxFiles(filePath));
+ } else if (
+ filePath.endsWith('.tsx') &&
+ !filePath.endsWith('.test.tsx') &&
+ !filesToSkip.includes(path.relative(dir, filePath))
+ ) {
+ results.push(filePath);
+ }
+ }
+ } catch (err) {
+ console.error(`Error reading directory ${dir}: ${err.message}`);
+ }
+ return results;
+}
+
+// Check if a file contains at least one TSDoc comment
+async function containsTsDocComment(filePath) {
+ try {
+ const content = await fs.readFile(filePath, 'utf8');
+ return /\/\*\*[\s\S]*?\*\//.test(content);
+ } catch (err) {
+ console.error(`Error reading file ${filePath}: ${err.message}`);
+ return false;
+ }
+}
+
+// Main function to run the validation
+async function run() {
+ const dir = process.argv[2] || './src'; // Allow directory path as a command-line argument
+ const files = await findTsxFiles(dir);
+ const filesWithoutTsDoc = [];
+
+ for (const file of files) {
+ if (!await containsTsDocComment(file)) {
+ filesWithoutTsDoc.push(file);
+ }
+ }
+
+ if (filesWithoutTsDoc.length > 0) {
+ filesWithoutTsDoc.forEach(file => {
+ console.error(`No TSDoc comment found in file: ${file}`);
+ });
+ process.exit(1);
+ }
+}
+
+run();
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 3efc4fd51f..0000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: Continuous Integration
-on: [pull_request]
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: Install Dependencies
- run: yarn
- - name: Run tests
- run: yarn test
-
-
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index ccd6fc3705..0000000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ master ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ master ]
- schedule:
- - cron: '22 0 * * 5'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'javascript' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v2
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v1
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v1
-
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 https://git.io/JvXDl
-
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/codeql-codescan.yml b/.github/workflows/codeql-codescan.yml
new file mode 100644
index 0000000000..6fa463001f
--- /dev/null
+++ b/.github/workflows/codeql-codescan.yml
@@ -0,0 +1,44 @@
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: codeql codescan workflow
+
+on:
+ pull_request:
+ branches:
+ - '**'
+ push:
+ branches:
+ - '**'
+jobs:
+ CodeQL:
+ if: ${{ github.actor != 'dependabot[bot]' }}
+ name: Analyse Code With CodeQL
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'javascript' ]
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ debug: true
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/compare_translations.py b/.github/workflows/compare_translations.py
new file mode 100644
index 0000000000..ef65b6c52b
--- /dev/null
+++ b/.github/workflows/compare_translations.py
@@ -0,0 +1,212 @@
+"""Script to encourage more efficient coding practices.
+Methodology:
+
+ Utility for comparing translations between default and other languages.
+
+ This module defines a function to compare two translations
+ and print any missing keys in the other language's translation.
+Attributes:
+
+ FileTranslation : Named tuple to represent a combination
+ of file and missing translations.
+
+ Fields:
+ - file (str): The file name.
+ - missing_translations (list): List of missing translations.
+
+Functions:
+ compare_translations(default_translation, other_translation):
+ Compare two translations and print missing keys.
+
+ load_translation(filepath):
+ Load translation from a file.
+
+ check_translations():
+ Load the default translation and compare it with other translations.
+
+ main():
+ The main function to run the script.
+ Parses command-line arguments, checks for the
+ existence of the specified directory, and then
+ calls check_translations with the provided or default directory.
+
+
+Usage:
+ This script can be executed to check and print missing
+ translations in other languages based on the default English translation.
+
+Example:
+ python compare_translations.py
+NOTE:
+ This script complies with our python3 coding and documentation standards
+ and should be used as a reference guide. It complies with:
+
+ 1) Pylint
+ 2) Pydocstyle
+ 3) Pycodestyle
+ 4) Flake8
+
+"""
+# standard imports
+import argparse
+import json
+import os
+import sys
+from collections import namedtuple
+
+# Named tuple for file and missing
+# translations combination
+FileTranslation = namedtuple("FileTranslation",
+ ["file", "missing_translations"])
+
+
+def compare_translations(default_translation,
+ other_translation, default_file, other_file):
+ """Compare two translations and return detailed info about missing/mismatched keys.
+
+ Args:
+ default_translation (dict): The default translation (en.json).
+ other_translation (dict): The other language translation.
+ default_file (str): The name of the default translation file.
+ other_file (str): The name of the other
+ translation file.
+
+ Returns:
+ list: A list of detailed error messages for each missing/mismatched key.
+ """
+ errors = []
+
+ # Check for missing keys in other_translation
+ for key in default_translation:
+ if key not in other_translation:
+ error_msg = f"Missing Key: '{key}' - This key from '{default_file}' is missing in '{other_file}'."
+ errors.append(error_msg)
+ # Check for keys in other_translation that don't match any in default_translation
+ for key in other_translation:
+ if key not in default_translation:
+ error_msg = f"Error Key: '{key}' - This key in '{other_file}' does not match any key in '{default_file}'."
+ errors.append(error_msg)
+ return errors
+
+def flatten_json(nested_json, parent_key=""):
+ """
+ Flattens a nested JSON, concatenating keys to represent the hierarchy.
+
+ Args:
+ nested_json (dict): The JSON object to flatten.
+ parent_key (str): The base key for recursion (used to track key hierarchy).
+
+ Returns:
+ dict: A flattened dictionary with concatenated keys.
+ """
+ flat_dict = {}
+
+ for key, value in nested_json.items():
+ # Create the new key by concatenating parent and current key
+ new_key = f"{parent_key}.{key}" if parent_key else key
+
+ if isinstance(value, dict):
+ # Recursively flatten the nested dictionary
+ flat_dict.update(flatten_json(value, new_key))
+ else:
+ # Assign the value to the flattened key
+ flat_dict[new_key] = value
+
+ return flat_dict
+
+def load_translation(filepath):
+ """Load translation from a file.
+
+ Args:
+ filepath: Path to the translation file
+
+ Returns:
+ translation: Loaded translation
+ """
+ try:
+ with open(filepath, "r", encoding="utf-8") as file:
+ content = file.read()
+ if not content.strip():
+ raise ValueError(f"File {filepath} is empty.")
+ translation = json.loads(content)
+ flattened_translation = flatten_json(translation)
+ return flattened_translation
+ except json.JSONDecodeError as e:
+ raise ValueError(f"Error decoding JSON from file {filepath}: {e}")
+
+
+def check_translations(directory):
+ """Load default translation and compare with other translations.
+
+ Args:
+ directory (str): The directory containing translation files.
+
+ Returns:
+ None
+ """
+ default_language_dir = os.path.join(directory, "en")
+ default_files = ["common.json", "errors.json", "translation.json"]
+ default_translations = {}
+ for file in default_files:
+ file_path = os.path.join(default_language_dir, file)
+ default_translations[file] = load_translation(file_path)
+
+ languages = os.listdir(directory)
+ languages.remove("en") # Exclude default language directory
+
+
+ error_found = False
+
+ for language in languages:
+ language_dir = os.path.join(directory, language)
+ for file in default_files:
+ default_translation = default_translations[file]
+ other_file_path = os.path.join(language_dir, file)
+ other_translation = load_translation(other_file_path)
+
+ # Compare translations and get detailed error messages
+ errors = compare_translations(
+ default_translation, other_translation, f"en/{file}", f"{language}/{file}"
+ )
+ if errors:
+ error_found = True
+ print(f"File {language}/{file} has missing translations for:")
+ for error in errors:
+ print(f" - {error}")
+
+
+ if error_found:
+ sys.exit(1) # Exit with an error status code
+ else:
+ print("All translations are present")
+ sys.exit(0)
+
+
+def main():
+ """
+
+ Parse command-line arguments, check for the existence of the specified directory
+ and call check_translations with the provided or default directory.
+
+ """
+ parser = argparse.ArgumentParser(
+ description="Check and print missing translations for all non-default languages."
+ )
+ parser.add_argument(
+ "--directory",
+ type=str,
+ nargs="?",
+ default=os.path.join(os.getcwd(), "public/locales"),
+ help="Directory containing translation files(relative to the root directory).",
+ )
+ args = parser.parse_args()
+
+ if not os.path.exists(args.directory):
+ print(f"Error: The specified directory '{args.directory}' does not exist.")
+ sys.exit(1)
+
+ check_translations(args.directory)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.github/workflows/countline.py b/.github/workflows/countline.py
new file mode 100755
index 0000000000..d0b03c503f
--- /dev/null
+++ b/.github/workflows/countline.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+# -*- coding: UTF-8 -*-
+"""Script to encourage more efficient coding practices.
+
+Methodology:
+
+ Analyses the `lib` and `test` directories to find files that exceed a
+ pre-defined number of lines of code.
+
+ This script was created to help improve code quality by encouraging
+ contributors to create reusable code.
+
+NOTE:
+
+ This script complies with our python3 coding and documentation standards
+ and should be used as a reference guide. It complies with:
+
+ 1) Pylint
+ 2) Pydocstyle
+ 3) Pycodestyle
+ 4) Flake8
+
+ Run these commands from the CLI to ensure the code is compliant for all
+ your pull requests.
+
+"""
+
+# Standard imports
+import os
+import sys
+import argparse
+from collections import namedtuple
+
+
+def _valid_filename(filepath):
+ """Determine whether filepath has the correct filename.
+
+ Args:
+ filepath: Filepath to check
+
+ Returns:
+ result: True if valid
+
+ """
+ # Initialize key variables
+ invalid_filenames = [".test.", ".spec."]
+ result = True
+
+ # Test
+ for invalid_filename in invalid_filenames:
+ if invalid_filename.lower() not in filepath.lower():
+ continue
+ result = False
+
+ return result
+
+
+def _valid_extension(filepath):
+ """Determine whether filepath has the correct extension.
+
+ Args:
+ filepath: Filepath to check
+
+ Returns:
+ result: True if valid
+
+ """
+ # Initialize key variables
+ invalid_extensions = [".css", ".jpg", ".png", ".jpeg"]
+ result = True
+
+ # Test
+ for invalid_extension in invalid_extensions:
+ if filepath.lower().endswith(invalid_extension.lower()) is False:
+ continue
+ result = False
+
+ return result
+
+
+def _valid_exclusions(excludes):
+ """Create a list of full file paths to exclude from the analysis.
+
+ Args:
+ excludes: Excludes object
+
+ Returns:
+ result: A list of full file paths
+
+ """
+ # Initialize key variables
+ result = []
+ filenames = []
+ more_filenames = []
+
+ # Create a list of files to ignore
+ if bool(excludes.files):
+ filenames = excludes.files
+ if bool(excludes.directories):
+ more_filenames = _filepaths_in_directories(excludes.directories)
+ filenames.extend(more_filenames)
+
+ # Remove duplicates
+ filenames = list(set(filenames))
+
+ # Process files
+ for filename in filenames:
+ # Ignore files that appear to be full paths because they start
+ # with a '/' or whatever the OS uses to distinguish directories
+ if filename.startswith(os.sep):
+ continue
+
+ # Create a file path
+ filepath = "{}{}{}".format(os.getcwd(), os.sep, filename)
+ if os.path.isfile(filepath) is True:
+ result.append(filepath)
+
+ # Return
+ return result
+
+
+def _filepaths_in_directories(directories):
+ """Create a list of full file paths based on input directories.
+
+ Args:
+ directories: A list of directories
+
+ Returns:
+ result: A list of full file paths
+
+ """
+ # Initialize key variables
+ result = []
+
+ # Iterate and analyze each directory
+ for directory in directories:
+ for root, _, files in os.walk(directory, topdown=False):
+ for name in files:
+ # Read each file and count the lines found
+ result.append(os.path.join(root, name))
+ # Return
+ return result
+
+
+def _arg_parser_resolver():
+ """Resolve the CLI arguments provided by the user.
+
+ Args:
+ None
+
+ Returns:
+ result: Parsed argument object
+
+ """
+ # Initialize parser and add the CLI options we should expect
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--lines",
+ type=int,
+ required=False,
+ default=300,
+ help="The maximum number of lines of code to accept.",
+ )
+ parser.add_argument(
+ "--directory",
+ type=str,
+ required=False,
+ default=os.getcwd(),
+ help="The parent directory of files to analyze.",
+ )
+ parser.add_argument(
+ "--exclude_files",
+ type=str,
+ required=False,
+ nargs="*",
+ default=None,
+ const=None,
+ help="""An optional space separated list of \
+files to exclude from the analysis.""",
+ )
+ parser.add_argument(
+ "--exclude_directories",
+ type=str,
+ required=False,
+ nargs="*",
+ default=None,
+ const=None,
+ help="""An optional space separated list of \
+directories to exclude from the analysis.""",
+ )
+
+ # Return parser
+ result = parser.parse_args()
+ return result
+
+
+def main():
+ """Analyze dart files.
+
+ This function finds, and prints the files that exceed the CLI
+ defined defaults.
+
+ Args:
+ None
+
+ Returns:
+ None
+
+ """
+ # Initialize key variables
+ lookup = {}
+ errors_found = False
+ file_count = 0
+ Excludes = namedtuple("Excludes", "files directories")
+
+ # Get the CLI arguments
+ args = _arg_parser_resolver()
+
+ # Define the directories of interest
+ directories = [
+ os.path.expanduser(os.path.join(args.directory, "lib")),
+ os.path.expanduser(os.path.join(args.directory, "src")),
+ os.path.expanduser(os.path.join(args.directory, "test")),
+ ]
+
+ # Get a corrected list of filenames to exclude
+ exclude_list = _valid_exclusions(
+ Excludes(
+ files=args.exclude_files, directories=args.exclude_directories
+ )
+ )
+
+ # Get interesting filepaths
+ repo_filepath_list = _filepaths_in_directories(directories)
+
+ # Iterate and analyze each directory
+ for filepath in repo_filepath_list:
+ # Skip excluded files
+ if filepath in exclude_list:
+ continue
+
+ # Skip /node_modules/ sub directories
+ if "{0}node_modules{0}".format(os.sep) in filepath:
+ continue
+
+ # Ignore invalid file extensions
+ if _valid_extension(filepath) is False:
+ continue
+
+ # Ignore invalid file filenames
+ if _valid_filename(filepath) is False:
+ continue
+
+ # Process the rest
+ with open(filepath, encoding="latin-1") as code:
+ line_count = sum(
+ 1
+ for line in code
+ if line.strip()
+ and not (
+ line.strip().startswith("#")
+ or line.strip().startswith("/")
+ )
+ )
+ lookup[filepath] = line_count
+
+ # If the line rule is voilated then the value is changed to 1
+ for filepath, line_count in lookup.items():
+ if line_count > args.lines:
+ errors_found = True
+ file_count += 1
+ if file_count == 1:
+ print(
+ """
+LINE COUNT ERROR: Files with excessive lines of code have been found\n"""
+ )
+
+ print(" Line count: {:>5} File: {}".format(line_count, filepath))
+
+ # Evaluate and exit
+ if bool(errors_found) is True:
+ print(
+ """
+The {} files listed above have more than {} lines of code.
+
+Please fix this. It is a pre-requisite for pull request approval.
+""".format(
+ file_count, args.lines
+ )
+ )
+ sys.exit(1)
+ else:
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.github/workflows/eslint_disable_check.py b/.github/workflows/eslint_disable_check.py
new file mode 100644
index 0000000000..201b4462b8
--- /dev/null
+++ b/.github/workflows/eslint_disable_check.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# -*- coding: UTF-8 -*-
+"""ESLint Checker Script.
+
+Methodology:
+
+ Recursively analyzes TypeScript files in the 'src' directory and its subdirectories
+ as well as 'setup.ts' files to ensure they do not contain eslint-disable statements.
+
+ This script enforces code quality practices in the project.
+
+NOTE:
+
+ This script complies with our python3 coding and documentation standards.
+ It complies with:
+
+ 1) Pylint
+ 2) Pydocstyle
+ 3) Pycodestyle
+ 4) Flake8
+
+"""
+
+import os
+import re
+import argparse
+import sys
+
+def has_eslint_disable(file_path):
+ """
+ Check if a TypeScript file contains eslint-disable statements.
+
+ Args:
+ file_path (str): Path to the TypeScript file.
+
+ Returns:
+ bool: True if eslint-disable statement is found, False otherwise.
+ """
+ eslint_disable_pattern = re.compile(r'//\s*eslint-disable(?:-next-line|-line)?', re.IGNORECASE)
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as file:
+ content = file.read()
+ return bool(eslint_disable_pattern.search(content))
+ except Exception as e:
+ print(f"Error reading file {file_path}: {e}")
+ return False
+
+def check_eslint(directory):
+ """
+ Recursively check TypeScript files for eslint-disable statements in the 'src' directory.
+
+ Args:
+ directory (str): Path to the directory.
+
+ Returns:
+ bool: True if eslint-disable statement is found, False otherwise.
+ """
+ eslint_found = False
+
+ for root, dirs, files in os.walk(os.path.join(directory, 'src')):
+ for file_name in files:
+ if file_name.endswith('.tsx') and not file_name.endswith('.test.tsx'):
+ file_path = os.path.join(root, file_name)
+ if has_eslint_disable(file_path):
+ print(f'File {file_path} contains eslint-disable statement.')
+ eslint_found = True
+
+ setup_path = os.path.join(directory, 'setup.ts')
+ if os.path.exists(setup_path) and has_eslint_disable(setup_path):
+ print(f'Setup file {setup_path} contains eslint-disable statement.')
+ eslint_found = True
+
+ return eslint_found
+
+def arg_parser_resolver():
+ """Resolve the CLI arguments provided by the user.
+
+ Returns:
+ result: Parsed argument object
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--directory",
+ type=str,
+ default=os.getcwd(),
+ help="Path to the directory to check (default: current directory)"
+ )
+ return parser.parse_args()
+
+def main():
+ """
+ Execute the script's main functionality.
+
+ This function serves as the entry point for the script. It performs
+ the following tasks:
+ 1. Validates and retrieves the directory to check from
+ command line arguments.
+ 2. Recursively checks TypeScript files for eslint-disable statements.
+ 3. Provides informative messages based on the analysis.
+ 4. Exits with an error if eslint-disable statements are found.
+
+ Raises:
+ SystemExit: If an error occurs during execution.
+ """
+ args = arg_parser_resolver()
+
+ if not os.path.exists(args.directory):
+ print(f"Error: The specified directory '{args.directory}' does not exist.")
+ sys.exit(1)
+
+ # Check eslint in the specified directory
+ eslint_found = check_eslint(args.directory)
+
+ if eslint_found:
+ print("ESLint-disable check failed. Exiting with error.")
+ sys.exit(1)
+
+ print("ESLint-disable check completed successfully.")
+
+if __name__ == "__main__":
+ main()
diff --git a/.github/workflows/issue.yml b/.github/workflows/issue.yml
index 798c766ca6..420d50adbe 100644
--- a/.github/workflows/issue.yml
+++ b/.github/workflows/issue.yml
@@ -1,13 +1,66 @@
-name: Issue Auto label
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: Issue Workflow
on:
issues:
types: ['opened']
jobs:
- build:
+ Opened-issue-label:
+ name: Adding Issue Label
runs-on: ubuntu-latest
steps:
- - uses: Renato66/auto-label@v2.2.0
+ - uses: actions/checkout@v4
+ with:
+ sparse-checkout: |
+ .github/workflows/auto-label.json5
+ sparse-checkout-cone-mode: false
+ - uses: Renato66/auto-label@v3
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ - uses: actions/github-script@v6
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ script: |
+ const { owner, repo } = context.repo;
+ const issue_number = context.issue.number;
+ const apiParams = {
+ owner,
+ repo,
+ issue_number
+ };
+ const labels = await github.rest.issues.listLabelsOnIssue(apiParams);
+ if(labels.data.reduce((a, c)=>a||["dependencies"].includes(c.name), false))
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ labels: ["good first issue", "security"]
+ });
+ else if(labels.data.reduce((a, c)=>a||["security", "ui/ux"].includes(c.name), false))
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ labels: ["good first issue"]
+ });
+
+
+ Issue-Greeting:
+ name: Greeting Message to User
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- ignore-comments: true
- default-labels: '["unapproved"]'
+ issue-message: "Congratulations on making your first Issue! :confetti_ball: If you haven't already, check out our [Contributing Guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/develop/CONTRIBUTING.md) and [Issue Reporting Guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/develop/ISSUE_GUIDELINES.md) to ensure that you are following our guidelines for contributing and making issues."
+
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
deleted file mode 100644
index cae9570993..0000000000
--- a/.github/workflows/linter.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: Linter
-on: [pull_request]
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: Install Dependencies
- run: yarn
- - name: Run ESLint and Prettier
- run: yarn lint
\ No newline at end of file
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
deleted file mode 100644
index 861e37239f..0000000000
--- a/.github/workflows/npm-publish.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
-# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
-
-name: Node.js Package
-
-on:
- release:
- types: [created]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12
- - run: npm ci
- - run: npm test
-
- publish-npm:
- needs: build
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12
- registry-url: https://registry.npmjs.org/
- - run: npm ci
- - run: npm publish
- env:
- NODE_AUTH_TOKEN: ${{secrets.npm_token}}
-
- publish-gpr:
- needs: build
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12
- registry-url: https://npm.pkg.github.com/
- - run: npm ci
- - run: npm publish
- env:
- NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff --git a/.github/workflows/pull-request-target.yml b/.github/workflows/pull-request-target.yml
new file mode 100644
index 0000000000..af75effc13
--- /dev/null
+++ b/.github/workflows/pull-request-target.yml
@@ -0,0 +1,77 @@
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: PR Target Workflow
+on:
+ pull_request_target:
+
+jobs:
+ PR-Greeting:
+ name: Pull Request Target
+ runs-on: ubuntu-latest
+ steps:
+ - name: Add the PR Review Policy
+ uses: thollander/actions-comment-pull-request@v2
+ with:
+ comment_tag: pr_review_policy
+ message: |
+ ## Our Pull Request Approval Process
+
+ Thanks for contributing!
+
+ ### Testing Your Code
+
+ Remember, your PRs won't be reviewed until these criteria are met:
+
+ 1. We don't merge PRs with poor code quality.
+ 1. Follow coding best practices such that CodeRabbit.ai approves your PR.
+ 1. We don't merge PRs with failed tests.
+ 1. When tests fail, click on the `Details` link to learn more.
+ 1. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
+ 1. Tests may fail if you edit sensitive files. Ask to add the `ignore-sensitive-files-pr` label if the edits are necessary.
+ 1. We cannot merge PRs with conflicting files. These must be fixed.
+
+ Our policies make our code better.
+
+ ### Reviewers
+
+ Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
+ When your PR has been assigned reviewers contact them to get your code reviewed and approved via:
+
+ 1. comments in this PR or
+ 1. our slack channel
+
+ #### Reviewing Your Code
+
+ Your reviewer(s) will have the following roles:
+
+ 1. arbitrators of future discussions with other contributors about the validity of your changes
+ 2. point of contact for evaluating the validity of your work
+ 3. person who verifies matching issues by others that should be closed.
+ 4. person who gives general guidance in fixing your tests
+
+ ### CONTRIBUTING.md
+
+ Read our CONTRIBUTING.md file. Most importantly:
+
+ 1. PRs with issues not assigned to you will be closed by the reviewer
+ 1. Fix the first comment in the PR so that each issue listed automatically closes
+
+ ### Other
+
+ 1. :dart: Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
+ 2. Read the CONTRIBUTING.md file make
+
+ - name: Greeting Message to User
+ uses: actions/first-interaction@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ pr-message: "Congratulations on making your first PR! :confetti_ball: If you haven't already, check out our [Contributing Guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/-/CONTRIBUTING.md) and [PR Reporting Guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/-/PR_GUIDELINES.md) to ensure that you are following our guidelines for contributing and creating PR."
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
new file mode 100644
index 0000000000..aaeebc8345
--- /dev/null
+++ b/.github/workflows/pull-request.yml
@@ -0,0 +1,265 @@
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: PR Workflow
+
+on:
+ pull_request:
+ branches:
+ - '**'
+
+env:
+ CODECOV_UNIQUE_NAME: CODECOV_UNIQUE_NAME-${{ github.run_id }}-${{ github.run_number }}
+
+jobs:
+ Code-Quality-Checks:
+ name: Performs linting, formatting, type-checking, checking for different source and target branch
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the Repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22.x'
+
+ - name: Install Dependencies
+ run: npm install
+
+ - name: Count number of lines
+ run: |
+ chmod +x ./.github/workflows/countline.py
+ ./.github/workflows/countline.py --lines 600 --exclude_files src/screens/LoginPage/LoginPage.tsx src/GraphQl/Queries/Queries.ts src/screens/OrgList/OrgList.tsx src/GraphQl/Mutations/mutations.ts src/components/EventListCard/EventListCardModals.tsx src/components/TagActions/TagActionsMocks.ts src/utils/interfaces.ts src/screens/MemberDetail/MemberDetail.tsx
+
+ - name: Get changed TypeScript files
+ id: changed-files
+ uses: tj-actions/changed-files@v40
+ - name: Check formatting
+ if: steps.changed-files.outputs.only_changed != 'true'
+ run: npm run format:check
+
+ - name: Run formatting if check fails
+ if: failure()
+ run: npm run format
+
+ - name: Check for type errors
+ if: steps.changed-files.outputs.only_changed != 'true'
+ run: npm run typecheck
+
+ - name: Check for linting errors in modified files
+ if: steps.changed-files.outputs.only_changed != 'true'
+ env:
+ CHANGED_FILES: ${{ steps.changed_files.outputs.all_changed_files }}
+ run: npx eslint ${CHANGED_FILES} && python .github/workflows/eslint_disable_check.py
+
+ - name: Check for TSDoc comments
+ run: npm run check-tsdoc # Run the TSDoc check script
+
+ - name: Check for localStorage Usage
+ run: |
+ chmod +x scripts/githooks/check-localstorage-usage.js
+ node scripts/githooks/check-localstorage-usage.js --scan-entire-repo
+
+ - name: Compare translation files
+ run: |
+ chmod +x .github/workflows/compare_translations.py
+ python .github/workflows/compare_translations.py --directory public/locales
+
+ - name: Check if the source and target branches are different
+ if: ${{ github.event.pull_request.base.ref == github.event.pull_request.head.ref }}
+ run: |
+ echo "Source Branch ${{ github.event.pull_request.head.ref }}"
+ echo "Target Branch ${{ github.event.pull_request.base.ref }}"
+ echo "Error: Source and Target Branches are the same. Please ensure they are different."
+ exit 1
+
+ Check-Sensitive-Files:
+ if: ${{ github.actor != 'dependabot[bot]' && !contains(github.event.pull_request.labels.*.name, 'ignore-sensitive-files-pr') }}
+ name: Checks if sensitive files have been changed without authorization
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get Changed Unauthorized files
+ id: changed-unauth-files
+ uses: tj-actions/changed-files@v40
+ with:
+ files: |
+ .github/**
+ env.example
+ .node-version
+ .husky/**
+ scripts/**
+ schema.graphql
+ package.json
+ tsconfig.json
+ .gitignore
+ .eslintrc.json
+ .eslintignore
+ .prettierrc
+ .prettierignore
+ vite.config.ts
+ docker-compose.yaml
+ Dockerfile
+ CODEOWNERS
+ LICENSE
+ setup.ts
+ .coderabbit.yaml
+ CODE_OF_CONDUCT.md
+ CODE_STYLE.md
+ CONTRIBUTING.md
+ DOCUMENTATION.md
+ INSTALLATION.md
+ ISSUE_GUIDELINES.md
+ PR_GUIDELINES.md
+ README.md
+
+ - name: List all changed unauthorized files
+ if: steps.changed-unauth-files.outputs.any_changed == 'true' || steps.changed-unauth-files.outputs.any_deleted == 'true'
+ env:
+ CHANGED_UNAUTH_FILES: ${{ steps.changed-unauth-files.outputs.all_changed_files }}
+ run: |
+ for file in ${CHANGED_UNAUTH_FILES}; do
+ echo "$file is unauthorized to change/delete"
+ done
+ exit 1
+
+ Count-Changed-Files:
+ if: ${{ github.actor != 'dependabot[bot]' }}
+ name: Checks if number of files changed is acceptable
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v40
+
+ - name: Echo number of changed files
+ env:
+ CHANGED_FILES_COUNT: ${{ steps.changed-files.outputs.all_changed_files_count }}
+ run: |
+ echo "Number of files changed: $CHANGED_FILES_COUNT"
+
+ - name: Check if the number of changed files is less than 100
+ if: steps.changed-files.outputs.all_changed_files_count > 100
+ env:
+ CHANGED_FILES_COUNT: ${{ steps.changed-files.outputs.all_changed_files_count }}
+ run: |
+ echo "Error: Too many files (greater than 100) changed in the pull request."
+ echo "Possible issues:"
+ echo "- Contributor may be merging into an incorrect branch."
+ echo "- Source branch may be incorrect please use develop as source branch."
+ exit 1
+
+ Check-ESlint-Disable:
+ name: Check for eslint-disable
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.9
+
+ - name: Run Python script
+ run: |
+ python .github/workflows/eslint_disable_check.py
+
+ Test-Application:
+ name: Test Application
+ runs-on: ubuntu-latest
+ needs: [Code-Quality-Checks, Check-ESlint-Disable]
+ steps:
+ - name: Checkout the Repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22.x'
+
+ - name: Install Dependencies
+ run: npm install
+
+ - name: Get changed TypeScript files
+ id: changed-files
+ uses: tj-actions/changed-files@v40
+
+ - name: Run tests
+ if: steps.changed-files.outputs.only_changed != 'true'
+ run: npm run test -- --watchAll=false --coverage
+
+ - name: TypeScript compilation for changed files
+ run: |
+ for file in ${{ steps.changed-files.outputs.all_files }}; do
+ if [[ "$file" == *.ts || "$file" == *.tsx ]]; then
+ npx tsc --noEmit "$file"
+ fi
+ done
+
+ - name: Present and Upload coverage to Codecov as ${{env.CODECOV_UNIQUE_NAME}}
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ verbose: true
+ fail_ci_if_error: false
+ name: '${{env.CODECOV_UNIQUE_NAME}}'
+
+ - name: Test acceptable level of code coverage
+ uses: VeryGoodOpenSource/very_good_coverage@v2
+ with:
+ path: "./coverage/lcov.info"
+ min_coverage: 95.0
+
+ Graphql-Inspector:
+ if: ${{ github.actor != 'dependabot[bot]' }}
+ name: Runs Introspection on the GitHub talawa-api repo on the schema.graphql file
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the Repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22.x'
+
+ - name: resolve dependency
+ run: npm install -g @graphql-inspector/cli
+
+ - name: Clone API Repository
+ run: |
+ # Retrieve the complete branch name directly from the GitHub context
+ FULL_BRANCH_NAME=${{ github.base_ref }}
+ echo "FULL_Branch_NAME: $FULL_BRANCH_NAME"
+
+ # Clone the specified repository using the extracted branch name
+ git clone --branch $FULL_BRANCH_NAME https://github.com/PalisadoesFoundation/talawa-api && ls -a
+
+ - name: Validate Documents
+ run: graphql-inspector validate './src/GraphQl/**/*.ts' './talawa-api/schema.graphql'
+
+ Check-Target-Branch:
+ if: ${{ github.actor != 'dependabot[bot]' }}
+ name: Check Target Branch
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check if the target branch is develop
+ if: github.event.pull_request.base.ref != 'develop'
+ run: |
+ echo "Error: Pull request target branch must be 'develop'. Please refer PR_GUIDELINES.md"
+ exit 1
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
new file mode 100644
index 0000000000..950d063fac
--- /dev/null
+++ b/.github/workflows/push.yml
@@ -0,0 +1,62 @@
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: push workflow
+
+on:
+ push:
+ branches:
+ - '**'
+
+env:
+ CODECOV_UNIQUE_NAME: CODECOV_UNIQUE_NAME-${{ github.run_id }}-${{ github.run_number }}
+
+jobs:
+ Code-Coverage:
+ if: ${{ github.actor != 'dependabot[bot]' }}
+ name: Test and Calculate Code Coverage
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [22.x]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Cache node modules
+ id: cache-npm
+ uses: actions/cache@v4
+ env:
+ cache-name: cache-node-modules
+ with:
+ path: |
+ ~/.npm
+ node_modules
+ key: ${{ runner.os }}-code-coverage-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-code-coverage-${{ env.cache-name }}-
+ ${{ runner.os }}-code-coverage-
+ ${{ runner.os }}-
+
+ - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
+ name: List the state of node modules
+ run: npm install
+ - run: npm run test -- --watchAll=false --coverage
+ - name: Present and upload coverage to Codecov as ${{env.CODECOV_UNIQUE_NAME}}
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ verbose: true
+ fail_ci_if_error: false
+ name: '${{env.CODECOV_UNIQUE_NAME}}'
+
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000000..24667f8e06
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,43 @@
+##############################################################################
+##############################################################################
+#
+# NOTE!
+#
+# Please read the README.md file in this directory that defines what should
+# be placed in this file
+#
+##############################################################################
+##############################################################################
+
+name: Mark stale issues and pull requests
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+permissions:
+ issues: write
+ pull-requests: write
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/stale@v8
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-issue-message: 'This issue did not get any activity in the past 10 days and will be closed in 180 days if no update occurs. Please check if the develop branch has fixed it and report again or close the issue.'
+ stale-pr-message: 'This pull request did not get any activity in the past 10 days and will be closed in 180 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed. Mention it now if you need help or give permission to other people to finish your work.'
+ close-issue-message: 'This issue did not get any activity in the past 180 days and thus has been closed. Please check if the newest release or develop branch has it fixed. Please, create a new issue if the issue is not fixed.'
+ close-pr-message: 'This pull request did not get any activity in the past 180 days and thus has been closed.'
+ stale-issue-label: 'no-issue-activity'
+ stale-pr-label: 'no-pr-activity'
+ days-before-stale: 10
+ days-before-close: 180
+ remove-stale-when-updated: true
+ exempt-all-milestones: true
+ exempt-pr-labels: 'wip'
+ exempt-issue-labels: 'wip'
+ operations-per-run: 30
diff --git a/.github/workflows/talawa_admin_md_mdx_format_adjuster.py b/.github/workflows/talawa_admin_md_mdx_format_adjuster.py
new file mode 100644
index 0000000000..cd76a30cf6
--- /dev/null
+++ b/.github/workflows/talawa_admin_md_mdx_format_adjuster.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# -*- coding: UTF-8 -*-
+"""
+Script to make Markdown files MDX compatible.
+
+This script scans Markdown files and escapes special characters (<, >, {, })
+to make them compatible with the MDX standard used in Docusaurus v3.
+
+This script complies with:
+ 1) Pylint
+ 2) Pydocstyle
+ 3) Pycodestyle
+ 4) Flake8
+"""
+import os
+import argparse
+import re
+
+def escape_mdx_characters(text):
+ """
+ Escape special characters in a text string for MDX compatibility.
+ Avoids escaping already escaped characters.
+
+ Args:
+ text: A string containing the text to be processed.
+
+ Returns:
+ A string with special characters (<, >, {, }) escaped, avoiding
+ double escaping.
+ """
+ # Regular expressions to find unescaped special characters
+ patterns = {
+ "<": r"(?": r"(?",
+ "{": r"(?
+
+- [Our Pledge](#our-pledge)
+- [Our Standards](#our-standards)
+- [Enforcement Responsibilities](#enforcement-responsibilities)
+- [Scope](#scope)
+- [Enforcement](#enforcement)
+- [Enforcement Guidelines](#enforcement-guidelines)
+ - [1. Correction](#1-correction)
+ - [2. Warning](#2-warning)
+ - [3. Temporary Ban](#3-temporary-ban)
+ - [4. Permanent Ban](#4-permanent-ban)
+- [Attribution](#attribution)
+
+
+
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
diff --git a/CODE_STYLE.md b/CODE_STYLE.md
new file mode 100644
index 0000000000..df184b12a0
--- /dev/null
+++ b/CODE_STYLE.md
@@ -0,0 +1,253 @@
+# Talawa Admin Code Style
+
+For Talawa Admin, most of the rules for the code style have been enforced with ESLint, but this document serves to provide an overview of the Code style used in Talawa Admin and the Rationale behind it.
+
+The code style must be strictly adhered to, to ensure that there is consistency throughout the contributions made to Talawa-Admin
+
+code style should not be changed and must be followed.
+
+# Table of Contents
+
+
+
+- [Tech Stack](#tech-stack)
+- [Component Structure](#component-structure)
+- [Code Style and Naming Conventions](#code-style-and-naming-conventions)
+- [Test and Code Linting](#test-and-code-linting)
+- [Folder/Directory Structure](#folderdirectory-structure)
+ - [Sub Directories of `src`](#sub-directories-of-src)
+- [Imports](#imports)
+- [Customising Bootstrap](#customising-bootstrap)
+
+
+
+## Tech Stack
+
+- Typescript
+
+- React.js
+
+- CSS module
+
+- React bootstrap
+
+- Material UI
+
+- GraphQL
+
+- Jest & React Testing Library for testing
+
+## Component Structure
+
+- Components should be strictly functional components
+
+- Should make use of React hooks where appropriate
+
+
+## Code Style and Naming Conventions
+
+- All React components *must* be written in PascalCase, with their file names, and associated CSS modules being written in PascalCase
+
+- All other files may follow the camelCase naming convention
+
+- All the Return fragment should be closed in empty tag
+
+- Use of custom classes directly are refrained, use of modular css is encouraged along with bootstrap classes
+
+**Wrong way ❌**
+```
+
...
+...
// No using personal custom classes directly, here you should not use myCustomClass2
+.container{...} // No changing the property of already existing classes reserved by boostrap directly in css files
+```
+
+**Correct ways ✅**
+```
+...
// Use custom class defined in modular css file
+...
// Use classes already defined in Bootstrap
+...
// Use classes already defined in Bootstrap
+```
+
+- All components should be either imported from React-Bootstrap library or Material UI library, components should not be written using plain Bootstrap classes and attributes without leveraging the React-Bootstrap library.
+
+**Example: Bootstrap Dropdown**
+
+**Wrong way ❌**
+
+Using plain Bootstrap classes and attributes without leveraging the React-Bootstrap library should be refrained. While it may work for basic functionality, it doesn't fully integrate with React and may cause issues when dealing with more complex state management or component interactions.
+```
+
+
+ Dropdown button
+
+
+
+```
+
+
+**Correct way ✅**
+
+It's recommended to use the React-Bootstrap library for seamless integration of Bootstrap components in a React application.
+```
+import Dropdown from 'react-bootstrap/Dropdown';
+
+function BasicExample() {
+ return (
+
+
+ Dropdown Button
+
+
+
+ Action
+ Another action
+ Something else
+
+
+ );
+}
+
+export default BasicExample;
+```
+
+
+## Test and Code Linting
+
+Unit tests must be written for *all* code submissions to the repository,
+the code submitted must also be linted ESLint and formatted with Prettier.
+
+## Folder/Directory Structure
+
+### Sub Directories of `src`
+
+`assets` - This houses all of the static assets used in the project
+ - `css` - This houses all of the css files used in the project
+ - `images` - This houses all of the images used in the project
+ - `scss` - This houses all of the scss files used in the project
+ - `components -` All Sass files for components
+ - `content -` All Sass files for content
+ - `forms -` All Sass files for forms
+ - `_talawa.scss` - Partial Sass file for Talawa
+ - `_utilities.scss` - Partial Sass file for utilities
+ - `_variables.scss` - Partial Sass file for variables
+ - `app.scss` - Main Sass file for the app, imports all other partial Sass files
+
+`components` - The directory for base components that will be used in the various views/screens
+
+`Constant` - This houses all of the constants used in the project
+
+`GraphQl` - This houses all of the GraphQL queries and mutations used in the project
+
+`screens` - This houses all of the views/screens to be navigated through in Talawa-Admin
+
+`state` - This houses all of the state management code for the project
+
+`utils` - This holds the utility functions that do not fall into any of the other categories
+
+
+## Imports
+
+Absolute imports have been set up for the project, so imports may be done directly from `src`.
+
+An example being
+
+```
+import Navbar from 'components/Navbar/Navbar';
+```
+
+Imports should be grouped in the following order:
+
+ - React imports
+ - Third party imports
+ - Local imports
+
+
+If there is more than one import from a single library, they should be grouped together
+
+Example - If there is single import from a library, both ways will work
+
+```
+import Row from 'react-bootstrap/Row';
+// OR
+import { Row } from 'react-bootstrap';
+```
+
+If there are multiple imports from a library, they should be grouped together
+
+```
+import { Row, Col, Container } from 'react-bootstrap';
+```
+
+## Customising Bootstrap
+
+Bootstrap v5.3.0 is used in the project.
+Follow this [link](https://getbootstrap.com/docs/5.3/customize/sass/) to learn how to customise bootstrap.
+
+**File Structure**
+
+- `src/assets/scss/components/{'{partialFile}'}.scss` - where the {'{partialFile}'} are the following files
+ - **_accordion.scss**
+ - **_alert.scss**
+ - **_badge.scss**
+ - **_breadcrumb.scss**
+ - **_buttons.scss**
+ - **_card.scss**
+ - **_carousel.scss**
+ - **_close.scss**
+ - **_dropdown.scss**
+ - **_list-group.scss**
+ - **_modal.scss**
+ - **_nav.scss**
+ - **_navbar.scss**
+ - **_offcanvas.scss**
+ - **_pagination.scss**
+ - **_placeholder.scss**
+ - **_progress.scss**
+ - **_spinners.scss**
+
+- `src/assets/scss/content/{'{partialFile}'}.scss` - where the {'{partialFile}'} are the following files
+ - **_table.scss**
+ - **_typography.scss**
+
+
+- `src/assets/scss/forms/{'{partialFile}'}.scss` - where the {'{partialFile}'} are the following files
+ - **_check-radios.scss**
+ - **_floating-label.scss**
+ - **_form-control.scss**
+ - **_input-group.scss**
+ - **_range.scss**
+ - **_select.scss**
+ - **_validation.scss**
+
+- `src/assets/scss/_utilities.scss` - The utility API is a Sass-based tool to generate utility classes.
+- `src/assets/scss/_variables.scss` - This file contains all the Sass variables used in the project
+- `src/assets/scss/_talawa.scss` - This files contains all the partial Sass files imported into it
+
+**How to compile Sass file**
+
+`src/assets/scss/app.scss` is the main Sass file for the app, it imports all other partial Sass files.
+According to naming convention the file name of the partial Sass files should start with an underscore `_` and end with `.scss`, these partial Sass files are not meant to be compiled directly, they are meant to be imported into another Sass file. Only the main Sass file `src/assets/scss/app.scss` should be compiled.
+
+The compiled CSS file is `src/assets/css/app.css` and it is imported into `src/index.tsx` file.
+
+To compile the Sass file once, run the following command in the terminal
+
+```
+npx sass src/assets/scss/app.scss src/assets/css/app.css
+```
+
+To watch the Sass file for changes and compile it automatically, run the following command in the terminal
+
+```
+npx sass src/assets/scss/app.scss src/assets/css/app.css --watch
+```
+The `src/assets/css/app.css.map` file associates the generated CSS code with the original SCSS code. It allows you to see your SCSS code in the browser's developer tools for debugging.
+
+To skip generating the map file, run
+```
+npx sass --no-source-map src/assets/scss/app.scss src/assets/css/app.css
+```
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d4a02270b6..dbe448c807 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,25 +4,50 @@ Thank you for your interest in contributing to Talawa Admin. Regardless of the s
If you are new to contributing to open source, please read the Open Source Guides on [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/).
+## Table of Contents
+
+
+
+- [Code of Conduct](#code-of-conduct)
+- [Videos](#videos)
+- [Ways to Contribute](#ways-to-contribute)
+ - [Our Development Process](#our-development-process)
+ - [Issues](#issues)
+ - [Pull Requests](#pull-requests)
+ - [Branching Strategy](#branching-strategy)
+ - [Conflict Resolution](#conflict-resolution)
+ - [Contributing Code](#contributing-code)
+- [Internships](#internships)
+- [Community](#community)
+
+
+
## Code of Conduct
-A safe environment is required for everyone to contribute. Read our [Code of Conduct Guide](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/CODE_OF_CONDUCT.md) to understand what this means. Let us know immediately if you have unacceptable experiences in this area.
+A safe environment is required for everyone to contribute. Read our [Code of Conduct Guide](CODE_OF_CONDUCT.md) to understand what this means. Let us know immediately if you have unacceptable experiences in this area.
No one should fear voicing their opinion. Respones must be respectful.
+## Videos
+
+1. Visit our [YouTube Channel playlists](https://www.youtube.com/@PalisadoesOrganization/playlists) for more insights
+ 1. The "[Getting Started - Developers](https://www.youtube.com/watch?v=YpBUoHxEeyg&list=PLv50qHwThlJUIzscg9a80a9-HmAlmUdCF&index=1)" videos are extremely helpful for new open source contributors.
+
## Ways to Contribute
-If you are ready to start contributing code right away, we have a list of [good first issues](https://github.com/PalisadoesFoundation/talawa-admin/labels/good%20first%20issue) that contain issues with a limited scope.
+If you are ready to start contributing code right away, get ready!
-## Quicklinks
+1. Join our Slack and introduce yourself. See details on how to join below in the Community section.
+ 1. This repository has its own dedicated channel.
+ 1. There are many persons on the various channels who are willing to assist you in getting started.
+1. Take a look at our issues (**_after reading our guidelines below_**):
+ 1. We have a list of [good first issues](https://github.com/PalisadoesFoundation/talawa-admin/labels/good%20first%20issue) that contain challenges with a limited scope for beginners.
+ 1. There are issues for creating tests for our code base. We need to increase reliablility. Try those issues, or create your own for files that don't already have tests. This is another good strategy for beginners.
+ 1. There are [dormant issues on which nobody has worked for some time](https://github.com/PalisadoesFoundation/talawa-admin/issues?q=is%3Aopen+is%3Aissue+label%3Ano-issue-activity). These are another place to start
+ 1. There may also be [dormant PRs on which nobody has worked for some time](https://github.com/PalisadoesFoundation/talawa-admin/issues?q=is%3Aopen+is%3Aissue+label%3Ano-issue-activity+label%3Ano-pr-activity)!
+1. Create an issue based on a bug you have found or a feature you would like to add. We value meaningful sugestions and will prioritize them.
-- [Our Development Process](#Our-development-process)
- - [Issues](#issues)
- - [Pull Requests](#pull-requests)
- - [Git Flow](#git-flow)
-- [Contributing Code](#contributing-code)
-- [GSoC](#gsoc)
-- [Community](#community)
+Welcome aboard!
### Our Development Process
@@ -30,24 +55,27 @@ We utilize GitHub issues and pull requests to keep track of issues and contribut
#### Issues
-Make sure you are following [issue report guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/issue-guidelines.md) available here before creating any new issues on Talawa Admin project.
+Make sure you are following [issue report guidelines](ISSUE_GUIDELINES.md) available here before creating any new issues on Talawa Admin project.
#### Pull Requests
-[Pull Request guidelines](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/PR-guidelines.md) is best resource to follow to start working on open issues.
+[Pull Request guidelines](PR_GUIDELINES.md) is best resource to follow to start working on open issues.
-#### Git Flow
+#### Branching Strategy
-For Talawa Admin, we utilize the GitFlow branching model. GitFlow is geared towards efficiently tracking development and managing releases. The model makes parallel development efforts easy and safe by isolating new development efforts from completed work.
+For Talawa Admin, we had employed the following branching strategy to simplify the development process and to ensure that only stable code is pushed to the `main` branch:
-The different types of branches we may use are:
+- `develop`: For unstable code and bug fixing
+- `main`: Where the stable production ready code lies. This is our default branch.
-- Feature branches (feature/branch-name)
-- Release branches (release/1.XX)
-- Bug branches (bugfix/branch-name)
-- Hotfix branches (hotfix/branch-name)
+#### Conflict Resolution
-Detailed document containing how GitFlow works: https://nvie.com/posts/a-successful-git-branching-model/
+When multiple developers are working on issues there is bound to be a conflict of interest (not to be confused with git conflicts) among issues, PRs or even ideas. Usually these conflicts are resolved in a **First Come First Serve** basis however there are certain exceptions to it.
+
+- In the cases where you feel your potential issues could be an extension or in conflict with other PRs it is important to ask the author of the PR in the slack channel or in their PRs or issues themselves why he/she did not write code for something that would require minimal effort on their part.
+- Based on basic courtesy, it is good practice to let the person who created a function apply and test that function when needed.
+- Last but not the least, communication is important make sure to talk to other contributors, in these cases, in slack channel or in a issue/PR thread.
+- As a last resort the Admins would be responsible for deciding how to resolve this conflict.
### Contributing Code
@@ -57,11 +85,81 @@ Make sure you have read the [Documentation for Setting up the Project](https://g
The process of proposing a change to Talawa Admin can be summarized as:
-1. Fork the Talawa Admin repository and branch off `master`.
-1. The repository can be cloned locally using `git clone `.
+1. Fork the Talawa Admin repository and branch off `develop`.
+1. Your newly forked repository can be cloned locally using `git clone `.
+1. Make the Palisadoes Foundation's repo your `git upstream` for your local repo.
1. Make the desired changes to the Talawa Admin project.
1. Run the app and test your changes.
-1. If you've added code that should be tested, write tests.
+1. If you've added code, then test suites must be added.
+
+ 1. **_General_:**
+
+ 1. We need to get to 100% test coverage for the app. We periodically increase the desired test coverage for our pull requests to meet this goal.
+ 1. Pull requests that don't meet the minimum test coverage levels will not be accepted. This may mean that you will have to create tests for code you did not write. You can decide which part of the code base needs additional tests if this happens to you.
+
+ 2. **_Testing_:**
+
+ 1. Test using this set of commands:
+
+ ```
+ npm install
+ npm run test --watchAll=false --coverage
+ ```
+
+ 2. Debug tests in browser
+
+ You can see the output of failing tests in broswer by running `jest-preview` package before running your tests
+
+ ```
+ npm install
+ npm run jest-preview
+ npm run test --watchAll=false --coverage
+ ```
+
+ You don't need to re-run the `npm run jest-preview` command each time, simply run the `npm run test` command if the Jest Preview server is already running in the background, it'll automatically detect any failing tests and show the preview at `http://localhost:3336` as shown in this screenshot -
+
+ ![Debugging Test Demo](./public/images/jest-preview.webp)
+
+ 3. **_Test Code Coverage_:**
+
+ 1. _General Information_
+ 1. The current code coverage of the repo is: [![codecov](https://codecov.io/gh/PalisadoesFoundation/talawa-admin/branch/develop/graph/badge.svg?token=II0R0RREES)](https://codecov.io/gh/PalisadoesFoundation/talawa-admin)
+ 2. You can determine the percentage test coverage of your code by running these two commands in sequence:
+ ```
+ npm install
+ npm run test --watchAll=false --coverage
+ genhtml coverage/lcov.info -o coverage
+ ```
+ 3. The output of the `npm run test` command will give you a tablular coverage report per file
+ 4. The overall coverage rate will be visible on the penultimate line of the `genhtml` command's output.
+ 5. The `genhtml` command is part of the Linux `lcov` package. Similar packages can be found for Windows and MacOS.
+ 6. The currently acceptable coverage rate can be found in the [GitHub Pull Request file](.github/workflows/pull-requests.yml). Search for the value below the line containing `min_coverage`.
+ 2. _Testing Individual Files_
+ 1. You can test an individual file by running this command:
+ ```
+ npm run test --watchAll=false /path/to/test/file
+ ```
+ 2. You can get the test coverage report for that file by running this command. The report will list all tests in the suite. Those tests that are not run will have zero values. You will need to look for the output line relevant to your test file.
+ ```
+ npm run test --watchAll=false --coverage /path/to/test/file
+ ```
+ 3. _Creating your code coverage account_
+
+ 1. You can also see your code coverage online for your fork of the repo. This is provided by `codecov.io`
+
+ 1. Go to this link: `https://app.codecov.io/gh/XXXX/YYYY` where XXXX is your GitHub account username and YYYY is the name of the repository
+ 2. Login to `codecov.io` using your GitHub account, and add your **repo** and **branches** to the `codecov.io` dashboard.
+ ![Debugging Test Demo](/public/images/codecov/authorise-codecov-github.jpg)
+ 3. Remember to add the `Repository Upload Token` for your forked repo. This can be found under `Settings` of your `codecov.io` account.
+
+ 4. Click on Setup Repo option
+ ![Debugging Test Demo]()
+ 5. Use the value of this token to create a secret named CODE_COV for your forked repo.
+ [![Code-cov-token.jpg](/public/images/codecov/Code-cov-token.jpg)]()
+ [![addd-your-key.jpg](/public/images/codecov/addd-your-key.jpg)]()
+ 6. You will see your code coverage reports with every push to your repo after following these steps
+ [![results.jpg](/public/images/codecov/results.jpg)]()
+
1. After making changes you can add them to git locally using `git add `(to add changes only in a particular file) or `git add .` (to add all changes).
1. After adding the changes you need to commit them using `git commit -m ''`(look at the commit guidelines below for commit messages).
1. Once you have successfully commited your changes, you need to push the changes to the forked repo on github using: `git push origin `.(Here branch name must be name of the branch you want to push the changes to.)
@@ -69,18 +167,13 @@ The process of proposing a change to Talawa Admin can be summarized as:
1. Ensure the test suite passes, either locally or on CI once a PR has been created.
1. Review and address comments on your pull request if requested.
-### Internships
-
-We have internship partnerships with a number of organizations. See below for more details.
-
-#### GSoC
-
-If you are participating in the 2021 Summer of Code, please read more about us and our processes [here](https://palisadoesfoundation.github.io/talawa-docs/docs/internships/gsoc/gsoc-introduction)
+## Internships
-#### GitHub Externship
+If you are participating in any of the various internship programs we are members of, then please read the [introduction guides on our documentation website](https://docs.talawa.io/docs/).
-If you are participating in the 2021 GitHub Externship, please read more about us and our processes [here](https://palisadoesfoundation.github.io/talawa-docs/docs/internships/github/github-introduction)
+## Community
-### Community
+There are many ways to communicate with the community.
-The Palisadoes Foundation has a Slack channel where members can assist with support and clarification. Click [here](https://join.slack.com/t/thepalisadoes-dyb6419/shared_invite/zt-nk79xxlg-OxTdlrD7RLaswu8EO_Q5rg) to join our slack channel.
+1. The Palisadoes Foundation has a Slack channel where members can assist with support and clarification. Visit the [Talawa GitHub repository home page](https://github.com/PalisadoesFoundation/talawa) for the link to join our slack channel.
+1. We also have a technical email list run by [freelists.org](https://www.freelists.org/). Search for "palisadoes" and join. Members on this list are also periodically added to our marketing email list that focuses on less technical aspects of our work.
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
new file mode 100644
index 0000000000..7691b5d452
--- /dev/null
+++ b/DOCUMENTATION.md
@@ -0,0 +1,32 @@
+# Documentation
+Welcome to our documentation guide. Here are some useful tips you need to know!
+
+# Table of Contents
+
+
+
+- [Where to find our documentation](#where-to-find-our-documentation)
+- [How to use Docusaurus](#how-to-use-docusaurus)
+- [Other information](#other-information)
+
+
+
+## Where to find our documentation
+
+Our documentation can be found in ONLY TWO PLACES:
+
+1. ***Inline within the repository's code files***: We have automated processes to extract this information and place it in our Talawa documentation site [docs.talawa.io](https://docs.talawa.io/).
+1. ***In our `talawa-docs` repository***: Our [Talawa-Docs](https://github.com/PalisadoesFoundation/talawa-docs) repository contains user edited markdown files that are automatically integrated into our Talawa documentation site [docs.talawa.io](https://docs.talawa.io/) using the [Docusaurus](https://docusaurus.io/) package.
+
+## How to use Docusaurus
+The process in easy:
+1. Install `talawa-docs` on your system
+1. Launch docusaurus on your system according to the `talawa-docs`documentation.
+ - A local version of `docs.talawa.io` should automatically launched in your browser at http://localhost:3000/
+1. Add/modify the markdown documents to the `docs/` directory of the `talawa-docs` repository
+1. If adding a file, then you will also need to edit the `sidebars.js` which is used to generate the [docs.talawa.io](https://docs.talawa.io/) menus.
+1. Always monitor the local website in your brower to make sure the changes are acceptable.
+ - You'll be able to see errors that you can use for troubleshooting in the CLI window you used to launch the local website.
+
+## Other information
+***PLEASE*** do not add markdown files in this repository. Add them to `talawa-docs`!
diff --git a/Docker_Container/.dockerignore b/Docker_Container/.dockerignore
deleted file mode 100644
index 56e8146402..0000000000
--- a/Docker_Container/.dockerignore
+++ /dev/null
@@ -1,5 +0,0 @@
-node_modules
-build
-.dockerignore
-Dockerfile
-Dockerfile.prod
\ No newline at end of file
diff --git a/Docker_Container/README.md b/Docker_Container/README.md
deleted file mode 100644
index b4bc5bf290..0000000000
--- a/Docker_Container/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-## Steps to start the build with Docker
-
-### Project Setup
-
-- Install [Docker](https://www.docker.com/)
-
-- Build and Tag The Docker Image
-
-`$ docker build -t sample:dev`
-
-- Then Spin up the container once build is done
-
-```
-$ docker run \
- -it \
- --rm \
- -v ${PWD}:/app \
- -v /app/node_modules \
- -p 3001:3000 \
- -e CHOKIDAR_USEPOLLING=true \
- sample:dev
-```
-
-- Whats Happening here
-
-1. The `docker run` command creates and runs a new conatiner instance from the image we just created
-
-2. `-it` starts the container in interactive mode
-
-3. `--rm` removes the container and volumes after the container exists.
-
-4. `-v ${PWD}:/app` mounts the code into the container at "/app".
-
-5. Since we want to use the container version of the “node_modules” folder, we configured another volume: `-v /app/node_modules` . You should now be able to remove the local “node_modules” flavor.
-
-6. `-p 3001:3000` exposes port 3000 to other Docker containers on the same network (for inter-container communication) and port 3001 to the host.
-
-7. Finally , `-e CHOKIDAR_USEPOLLING=true` enables a polling mechanism via chokidar (which wraps `fs.watch`, `fs.watchFile`, and `fsevents`) so that hot-reloading will work.
-
-### For using compose file
-
-- Build the image and fire up the container
-
-`$ docker-compose up -d --build`
-
-- Ensure the app is running in the browser and test hot - reloading again. Bring down the container before moving on
-
-`$ docker-compose stop`
-
-- Now your container is ready to run
diff --git a/Docker_Container/docker-compose.yml b/Docker_Container/docker-compose.yml
deleted file mode 100644
index ad7cad7f48..0000000000
--- a/Docker_Container/docker-compose.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-version: '3.7'
-
-services:
-
- sample:
- container_name: sample
- build:
- context: .
- dockerfile: Dockerfile
- volumes:
- - '.:/app'
- - '/app/node_modules'
- ports:
- - 3001:3000
- environment:
- - CHOKIDAR_USEPOLLING=true
\ No newline at end of file
diff --git a/Docker_Container/dockerfile b/Docker_Container/dockerfile
deleted file mode 100644
index 34eca8c102..0000000000
--- a/Docker_Container/dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-# pull official base image
-FROM node:13.12.0-alpine
-
-# set working directory
-WORKDIR /app
-
-# add `/app/node_modules/.bin` to $PATH
-ENV PATH /app/node_modules/.bin:$PATH
-
-# install app dependencies
-COPY package.json ./
-COPY yarn.lock ./
-COPY package-lock.json ./
-RUN yarn install --silent
-RUN yarn install react-scripts@3.4.1 -g --silent
-
-# add app
-COPY . ./
-
-# start app
-CMD ["yarn", "start"]
diff --git a/INSTALLATION.md b/INSTALLATION.md
new file mode 100644
index 0000000000..5813b7d1cb
--- /dev/null
+++ b/INSTALLATION.md
@@ -0,0 +1,337 @@
+# Talawa-Admin Installation
+
+This document provides instructions on how to set up and start a running instance of `talawa-admin` on your local system. The instructions are written to be followed in sequence so make sure to go through each of them step by step without skipping any sections.
+
+# Table of Contents
+
+
+
+- [Installation Steps Summary](#installation-steps-summary)
+- [Prerequisites](#prerequisites)
+ - [Install git](#install-git)
+ - [Setting up this repository](#setting-up-this-repository)
+ - [Install node.js](#install-nodejs)
+ - [Install TypeScript](#install-typescript)
+ - [Install Required Packages](#install-required-packages)
+- [Configuration](#configuration)
+ - [Creating .env file](#creating-env-file)
+ - [Setting up PORT in .env file](#setting-up-port-in-env-file)
+ - [Setting up REACT_APP_TALAWA_URL in .env file](#setting-up-react_app_talawa_url-in-env-file)
+ - [Setting up REACT_APP_BACKEND_WEBSOCKET_URL in .env file](#setting-up-react_app_backend_websocket_url-in-env-file)
+ - [Setting up REACT_APP_RECAPTCHA_SITE_KEY in .env file](#setting-up-react_app_recaptcha_site_key-in-env-file)
+ - [Setting up Compiletime and Runtime logs](#setting-up-compiletime-and-runtime-logs)
+- [Post Configuration Steps](#post-configuration-steps)
+ - [Running Talawa-Admin](#running-talawa-admin)
+ - [Accessing Talawa-Admin](#accessing-talawa-admin)
+ - [Talawa-Admin Registration](#talawa-admin-registration)
+ - [Talawa-Admin Login](#talawa-admin-login)
+- [Testing](#testing)
+ - [Running tests](#running-tests)
+ - [Debugging tests](#debugging-tests)
+ - [Linting code files](#linting-code-files)
+ - [Husky for Git Hooks](#husky-for-git-hooks)
+ - [pre-commit hook](#pre-commit-hook)
+ - [post-merge hook](#post-merge-hook)
+
+
+
+# Installation Steps Summary
+
+Installation is not difficult, but there are many steps. This is a brief explanation of what needs to be done:
+
+1. Install `git`
+2. Download the code from GitHub using `git`
+3. Install `node.js` (Node), the runtime environment the application will need to work.
+4. Configure the Node Package Manager (`npm`) to automatically use the correct version of Node for our application.
+5. Use `npm` to install TypeScript, the language the application is written in.
+6. Install other supporting software such as the database.
+7. Configure the application
+8. Start the application
+
+These steps are explained in more detail in the sections that follow.
+
+# Prerequisites
+
+In this section we'll explain how to set up all the prerequisite software packages to get you up and running.
+
+## Install git
+
+The easiest way to get the latest copies of our code is to install the `git` package on your computer.
+
+Follow the setup guide for `git` on official [git docs](https://git-scm.com/downloads). Basic `git` knowledge is required for open source contribution so make sure you're comfortable with it. [Here's](https://youtu.be/apGV9Kg7ics) a good tutorial to get started with `git` and `github`.
+
+## Setting up this repository
+
+First you need a local copy of `talawa-admin`. Run the following command in the directory of choice on your local system.
+
+1. On your computer, navigate to the folder where you want to setup the repository.
+2. Open a `cmd` (Windows) or `terminal` (Linux or MacOS) session in this folder.
+ 1. An easy way to do this is to right-click and choose appropriate option based on your OS.
+3. **For Our Open Source Contributor Software Developers:**
+
+ 1. Next, we'll fork and clone the `talawa-admin` repository.
+ 1. In your web browser, navigate to [https://github.com/PalisadoesFoundation/talawa-admin/](https://github.com/PalisadoesFoundation/talawa-admin/) and click on the `fork` button. It is placed on the right corner opposite the repository name `PalisadoesFoundation/talawa-admin`.
+
+ ![Image with fork](public/markdown/images/install1.png)
+
+ 1. You should now see `talawa-admin` under your repositories. It will be marked as forked from `PalisadoesFoundation/talawa-admin`
+
+ ![Image of user's clone](public/markdown/images/install2.png)
+
+ 1. Clone the repository to your local computer (replacing the values in `{{}}`):
+ ```bash
+ $ git clone https://github.com/{{YOUR GITHUB USERNAME}}/talawa-admin.git
+ cd talawa-admin
+ git checkout develop
+ ```
+ - **Note:** Make sure to check out the `develop` branch
+ 1. You now have a local copy of the code files. For more detailed instructions on contributing code, and managing the versions of this repository with `git`, checkout our [CONTRIBUTING.md](./CONTRIBUTING.md) file.
+
+4. **Talawa Administrators:**
+
+ 1. Clone the repository to your local computer using this command:
+
+ ```bash
+ $ git clone https://github.com/PalisadoesFoundation/talawa-admin.git
+ ```
+
+## Install node.js
+
+Best way to install and manage `node.js` is making use of node version managers. We recommend using `fnm`, which will be described in more detail later.
+
+Follow these steps to install the `node.js` packages in Windows, Linux and MacOS.
+
+1. For Windows:
+ 1. first install `node.js` from their website at https://nodejs.org
+ 1. When installing, don't click the option to install the `necessary tools`. These are not needed in our case.
+ 2. then install [fnm](https://github.com/Schniz/fnm). Please read all the steps in this section first.
+ 1. All the commands listed on this page will need to be run in a Windows terminal session in the `talawa-admin` directory.
+ 2. Install `fnm` using the `winget` option listed on the page.
+ 3. Setup `fnm` to automatically set the version of `node.js` to the version required for the repository using these steps:
+ 1. First, refer to the `fnm` web page's section on `Shell Setup` recommendations.
+ 2. Open a `Windows PowerShell` terminal window
+ 3. Run the recommended `Windows PowerShell` command to open `notepad`.
+ 4. Paste the recommended string into `notepad`
+ 5. Save the document.
+ 6. Exit `notepad`
+ 7. Exit PowerShell
+ 8. This will ensure that you are always using the correct version of `node.js`
+2. For Linux and MacOS, use the terminal window.
+ 1. install `node.js`
+ 2. then install `fnm`
+ 1. Refer to the installation page's section on the `Shell Setup` recommendations.
+ 2. Run the respective recommended commands to setup your node environment
+ 3. This will ensure that you are always using the correct version of `node.js`
+
+## Install TypeScript
+
+TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It adds optional types, classes, and modules to JavaScript, and supports tools for large-scale JavaScript applications.
+
+To install TypeScript, you can use the `npm` command which comes with `node.js`:
+
+```bash
+npm install -g typescript
+```
+
+This command installs TypeScript globally on your system so that it can be accessed from any project.
+
+## Install Required Packages
+
+Run the following command to install the packages and dependencies required by the app:
+
+```
+npm install
+```
+
+The prerequisites are now installed. The next step will be to get the app up and running.
+
+# Configuration
+
+It's important to configure Talawa-Admin. Here's how to do it.
+
+You can use our interactive setup script for the configuration. Use the following command for the same.
+
+```
+npm run setup
+```
+
+All the options in "setup" can be done manually as well and here's how to do it. - [Creating .env file](#creating-env-file)
+
+## Creating .env file
+
+A file named .env is required in the root directory of talawa-admin for storing environment variables used at runtime. It is not a part of the repo and you will have to create it. For a sample of `.env` file there is a file named `.env.example` in the root directory. Create a new `.env` file by copying the contents of the `.env.example` into `.env` file. Use this command:
+
+```
+cp .env.example .env
+```
+
+This `.env` file must be populated with the following environment variables for `talawa-admin` to work:
+
+| Variable | Description |
+| ------------------------------- | ------------------------------------------------- |
+| PORT | Custom port for Talawa-Admin development purposes |
+| REACT_APP_TALAWA_URL | URL endpoint for talawa-api graphql service |
+| REACT_APP_BACKEND_WEBSOCKET_URL | URL endpoint for websocket end point |
+| REACT_APP_USE_RECAPTCHA | Whether you want to use reCAPTCHA or not |
+| REACT_APP_RECAPTCHA_SITE_KEY | Site key for authentication using reCAPTCHA |
+
+Follow the instructions from the sections [Setting up PORT in .env file](#setting-up-port-in-env-file), [Setting up REACT_APP_TALAWA_URL in .env file](#setting-up-REACT_APP_TALAWA_URL-in-env-file), [Setting up REACT_APP_BACKEND_WEBSOCKET_URL in .env file](#setting-up-react_app_backend_websocket_url-in-env-file), [Setting up REACT_APP_RECAPTCHA_SITE_KEY in .env file](#setting-up-REACT_APP_RECAPTCHA_SITE_KEY-in-env-file) and [Setting up Compiletime and Runtime logs](#setting-up-compiletime-and-runtime-logs) to set up these environment variables.
+
+## Setting up PORT in .env file
+
+Add a custom port number for Talawa-Admin development purposes to the variable named `PORT` in the `.env` file.
+
+## Setting up REACT_APP_TALAWA_URL in .env file
+
+Add the endpoint for accessing talawa-api graphql service to the variable named `REACT_APP_TALAWA_URL` in the `.env` file.
+
+```
+REACT_APP_TALAWA_URL="http://API-IP-ADRESS:4000/graphql/"
+```
+
+If you are a software developer working on your local system, then the URL would be:
+
+```
+REACT_APP_TALAWA_URL="http://localhost:4000/graphql/"
+```
+
+If you are trying to access Talawa Admin from a remote host with the API URL containing "localhost", You will have to change the API URL to
+
+```
+REACT_APP_TALAWA_URL="http://YOUR-REMOTE-ADDRESS:4000/graphql/"
+```
+
+## Setting up REACT_APP_BACKEND_WEBSOCKET_URL in .env file
+
+The endpoint for accessing talawa-api WebSocket graphql service for handling subscriptions is automatically added to the variable named `REACT_APP_BACKEND_WEBSOCKET_URL` in the `.env` file.
+
+```
+REACT_APP_BACKEND_WEBSOCKET_URL="ws://API-IP-ADRESS:4000/graphql/"
+```
+
+If you are a software developer working on your local system, then the URL would be:
+
+```
+REACT_APP_BACKEND_WEBSOCKET_URL="ws://localhost:4000/graphql/"
+```
+
+If you are trying to access Talawa Admin from a remote host with the API URL containing "localhost", You will have to change the API URL to
+
+```
+REACT_APP_BACKEND_WEBSOCKET_URL="ws://YOUR-REMOTE-ADDRESS:4000/graphql/"
+```
+
+For additional details, please refer the `How to Access the Talawa-API URL` section in the INSTALLATION.md file found in the [Talawa-API repo](https://github.com/PalisadoesFoundation/talawa-api).
+
+## Setting up REACT_APP_RECAPTCHA_SITE_KEY in .env file
+
+You may not want to setup reCAPTCHA since the project will still work. Moreover, it is recommended to not set it up in development environment.
+
+Just skip to the [Post Configuration Steps](#post-configuration-steps) if you don't want to set it up. Else, read the following steps.
+
+If you want to setup Google reCAPTCHA now, you may refer to the `RECAPTCHA` section in the INSTALLATION.md file found in [Talawa-API repo](https://github.com/PalisadoesFoundation/talawa-api).
+
+`Talawa-admin` needs the `reCAPTCHA site key` for the `reCAPTCHA` service you set up during `talawa-api` installation as shown in this screenshot:
+
+![reCAPTCHA site key](./public/images/REACT_SITE_KEY.webp)
+
+Copy/paste this `reCAPTCHA site key` to the variable named `REACT_APP_RECAPTCHA_SITE_KEY` in `.env` file.
+
+```
+REACT_APP_RECAPTCHA_SITE_KEY="this_is_the_recaptcha_key"
+```
+
+## Setting up Compiletime and Runtime logs
+
+Set the `ALLOW_LOGS` to "YES" if you want warnings , info and error messages in your console or leave it blank if you dont need them or want to keep the console clean
+
+# Post Configuration Steps
+
+It's now time to start Talawa-Admin and get it running
+
+## Running Talawa-Admin
+
+Run the following command to start `talawa-admin` development server:
+
+```
+npm run serve
+```
+
+## Accessing Talawa-Admin
+
+By default `talawa-admin` runs on port `4321` on your system's localhost. It is available on the following endpoint:
+
+```
+http://localhost:4321/
+```
+
+If you have specified a custom port number in your `.env` file, Talawa-Admin will run on the following endpoint:
+
+```
+http://localhost:${{customPort}}/
+```
+
+Replace `${{customPort}}` with the actual custom port number you have configured in your `.env` file.
+
+## Talawa-Admin Registration
+
+The first time you navigate to the running talawa-admin's website you'll land at talawa-admin registration page. Sign up using whatever credentials you want and create the account. Make sure to remember the email and password you entered because they'll be used to sign you in later on.
+
+## Talawa-Admin Login
+
+Now sign in to talawa-admin using the `email` and `password` you used to sign up.
+
+# Testing
+
+It is important to test our code. If you are a contributor, please follow these steps.
+
+## Running tests
+
+You can run the tests for `talawa-admin` using this command:
+
+```
+npm run test
+```
+
+## Debugging tests
+
+You can see the output of failing tests in broswer by running `jest-preview` package before running your tests
+
+```
+npm run jest-preview
+npm run test
+```
+
+You don't need to re-run the `npm run jest-preview` command each time, simply run the `npm run test` command if the Jest Preview server is already running in the background, it'll automatically detect any failing tests and show the preview at `http://localhost:3336` as shown in this screenshot -
+
+![Debugging Test Demo](./public/images/jest-preview.webp)
+
+## Linting code files
+
+You can lint your code files using this command:
+
+```
+npm run lint:fix
+```
+
+## Husky for Git Hooks
+
+We are using the package `Husky` to run git hooks that run according to different git workflows.
+
+#### pre-commit hook
+
+We run a pre-commit hook which automatically runs code quality checks each time you make a commit and also fixes some of the issues. This way you don't have to run them manually each time.
+
+If you don't want these pre-commit checks running on each commit, you can manually opt out of it using the `--no-verify` flag with your commit message as shown:-
+
+ git commit -m "commit message" --no-verify
+
+#### post-merge hook
+
+We are also running a post-merge(post-pull) hook which will automatically run "npm install" only if there is any change made to pakage.json file so that the developer has all the required dependencies when pulling files from remote.
+
+If you don't want this hook to run, you can manually opt out of this using the `no verify` flag while using the merge command(git pull):
+
+ git pull --no-verify
+
+
diff --git a/ISSUE_GUIDELINES.md b/ISSUE_GUIDELINES.md
new file mode 100644
index 0000000000..5170de5839
--- /dev/null
+++ b/ISSUE_GUIDELINES.md
@@ -0,0 +1,59 @@
+# Issue Report Guidelines
+
+:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
+
+In order to give everyone a chance to submit a issues reports and contribute to the Talawa project, we have put restrictions in place. This section outlines the guidelines that should be imposed upon issue reports in the Talawa project.
+
+___
+## Table of Contents
+
+
+
+- [Issue Management](#issue-management)
+ - [New Issues](#new-issues)
+ - [Existing Issues](#existing-issues)
+ - [Feature Request Issues](#feature-request-issues)
+ - [Monitoring the Creation of New Issues](#monitoring-the-creation-of-new-issues)
+- [General Guidelines](#general-guidelines)
+
+
+
+___
+## Issue Management
+
+In all cases please use the [GitHub open issue search](https://github.com/PalisadoesFoundation/talawa-admin/issues) to check whether the issue has already been reported.
+
+### New Issues
+To create new issues follow these steps:
+
+1. Your issue may have already been created. Search for duplicate open issues before submitting yours.for similar deficiencies in the code.duplicate issues are created.
+1. Verify whether the issue has been fixed by trying to reproduce it using the latest master or development branch in the repository.
+1. Click on the [`New Issue`](https://github.com/PalisadoesFoundation/talawa-admin/issues/new/choose) button
+1. Use the templates to create a standardized report of what needs to be done and why.
+1. If you want to be assigned the issue that you have created, then add a comment immediately after submitting it.
+
+We welcome contributors who find new ways to make the code better.
+
+### Existing Issues
+
+You can also be a valuable contributor by searching for dormant issues. Here's how you can do that:
+
+1. **Previously Assigned Issues**: We regularly review issues and add a [`no-issue-activity`](https://github.com/PalisadoesFoundation/talawa-admin/issues?q=is%3Aissue+is%3Aopen+label%3Ano-issue-activity) label to them. Use the issue comments to ask whether the assignee is still working on the issue, and if not, ask for the issue to be assigned to you.
+1. **Unassigned Issues**: If the issue is already reported and [not assigned to anyone](https://github.com/PalisadoesFoundation/talawa-admin/issues?q=is%3Aissue+is%3Aopen+no%3Aassignee) and you are interested in working on the issue then:
+ 1. Ask for the issue to be assigned to you in the issue comments
+ 2. Ask our contributors to assign it to you in `#talawa` slack channel.
+
+Working on these types of existing issues is a good way of getting started with the community.
+
+### Feature Request Issues
+
+Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the mentors of the merits of this feature. Please provide as much detail and context as possible.
+
+### Monitoring the Creation of New Issues
+1. Join our `#talawa-github` slack channel for automatic issue and pull request updates.
+
+## General Guidelines
+
+1. Discuss issues in our various slack channels when necessary
+2. Please do not derail or troll issues.
+3. Keep the discussion on topic and respect the opinions of others.
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
deleted file mode 100644
index c59e24c1ee..0000000000
--- a/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-## Expected Behavior
-
-
-
-## Current Behavior
-
-
-
-## Possible Solution
-
-
-
-## Steps to Reproduce
-
-
-
-
-1.
-2.
-3.
-4.
-
-## Context (Environment)
-
-
-
-
-
-
-## Detailed Description
-
-
-
-## Possible Implementation
-
-
diff --git a/PR-guidelines.md b/PR-guidelines.md
deleted file mode 100644
index c469028b0b..0000000000
--- a/PR-guidelines.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Pull Request Guidelines
-
-:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
-
-In order to give everyone a chance to submit a pull request and contribute to the Talawa API project, we have put restrictions in place. This section outlines the guidelines that should be imposed upon pull requests in the Talawa API project.
-
-1. Do not start working on any open issue and raise a PR unless it is assigned to you.
-2. Pull requests must be based on [open issues](https://github.com/PalisadoesFoundation/talawa-admin/issues) available.
-3. [Use this method to automatically close the issue when the PR is completed.](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue)
-4. Each contributor may only create one pull request at a time. We have this rule in place due to our limited resources - if everyone was allowed to post multiple pull requests we will not be able to review them properly. It is also better for contributors because you can focus on creating one quality PR - so spend time making sure it is as good as it can be.
-5. If the pull request's code quality is not up to par, or it would break the app, it will more likely be closed. So please be careful when creating a PR.
-6. Please follow the [PR template](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/templates/pr-template.md). Ensure the PR title clearly describes the problem it is solving. In the description, include the relevant issue number, snapshots and videos after changes added.
-7. If you are borrowing code, please disclose it. It is fine and sometimes even recommended to borrow code, but we need to know about it to assess your work. If we find out that your pull request contains a lot of code copied from elsewhere, we will close the pull request.
-8. All pull request must have test units. If for some reason it is not possible to add tests, please let us know and explain why. In that case, you'll need to tell us what steps you followed to manually test your changes.
-9. No Work In Progress. ONLY completed and working pull requests, and with test units, will be accepted. A WIP would fall under rule 4 and be closed immediately.
-10. Please do not @mention contributors and mentors. Sometimes it takes time before we can review your pull request or answer your questions but we'll get to it sooner or later. @mentioning someone just adds to the pile of notifications we get and it won't make us look at your issue faster.
-11. Do not force push. If you make changes to your pull request, please simply add a new commit as that makes it easy for us to review your new changes. If you force push, we'll have to review everything from the beginning.
-12. PR should be small, easy to review and should follow standard coding styles.
-13. If PR has conflicts because of recently added changes to the same file, resolve issues, test new changes and submit PR again for review.
-14. PRs should be atomic. That is, they should address one item (issue or feature)
-15. After submitting PR, if you are not replying within 48 hours then in that case we may need to assign issue to other contributors based on priority of the issue.
diff --git a/PR_GUIDELINES.md b/PR_GUIDELINES.md
new file mode 100644
index 0000000000..4c904c782d
--- /dev/null
+++ b/PR_GUIDELINES.md
@@ -0,0 +1,69 @@
+# Pull Request Guidelines
+
+:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
+
+In order to give everyone a chance to submit a pull request and contribute to the Talawa project, we have put restrictions in place. This section outlines the guidelines that should be imposed upon pull requests in the Talawa project.
+
+# Table of Contents
+
+
+
+- [Pull Requests and Issues](#pull-requests-and-issues)
+- [Linting and Formatting](#linting-and-formatting)
+- [Testing](#testing)
+- [Pull Request Processing](#pull-request-processing)
+ - [Only submit PRs against our `develop` branch, not the default `main` branch](#only-submit-prs-against-our-develop-branch-not-the-default-main-branch)
+
+
+
+## Pull Requests and Issues
+
+1. Do not start working on any open issue and raise a PR unless the issue is assigned to you. PRs that don't meet these guidelines will be closed.
+1. Pull requests must be based on [open issues](https://github.com/PalisadoesFoundation/talawa-admin/issues) available.
+1. [Use this method to automatically close the issue when the PR is completed.](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue)
+
+## Linting and Formatting
+
+All the pull requests must have code that is properly linted and formatted, so that uniformity across the repository can be ensured.
+
+Before opening a PR, you can run the following scripts to automatically lint and format the code properly:
+
+```
+npm run lint:fix
+npm run format:fix
+```
+
+Both of these scripts also have a `check` counterpart, which would be used by the GitHub CI to ensure that the code is properly formatted.
+You can run the following scripts yourself to ensure that your pull request doesn't fail due to linting and formatting errors:
+
+```
+npm run lint:check
+npm run format:check
+```
+
+## Testing
+
+1. All pull requests must have test units. If, for some reason, it is not possible to add tests, please let us know and explain why. In that case, you'll need to tell us what steps you followed to manually test your changes.
+1. Please read our [CONTRIBUTING.md](CONTRIBUTING.md) document for details on our testing policy.
+
+## Pull Request Processing
+These are key guidelines for the procedure:
+
+### Only submit PRs against our `develop` branch, not the default `main` branch
+
+1. Only submit PRs against our `develop` branch. The default is `main`, so you will have to modify this before submitting your PR for review. PRs made against `main` will be closed.
+1. We do not accept draft Pull Requests. They will be closed if submitted. We focus on work that is ready for immediate review.
+1. Removing assigned reviewers from your Pull Request will cause it to be closed. The quality of our code is very important to us. Therefore we make experienced maintainers of our code base review your code. Removing these assigned persons is not in the best interest of this goal.
+1. If you have not done so already, please read the `Pull Requests and Issues` and `Testing` sections above.
+1. Each contributor may only create one pull request at a time. We have this rule in place due to our limited resources - if everyone was allowed to post multiple pull requests, we would not be able to review them properly. It is also better for contributors because you can focus on creating one quality PR - so spend time making sure it is as good as it can be.
+1. Upon successful push to the fork, check if all tests are passing; if not, fix the issues and then create a pull request.
+1. If the pull request's code quality is not up to par, or it would break the app, it will more likely be closed. So please be careful when creating a PR.
+1. Please follow the PR template provided. Ensure the PR title clearly describes the problem it is solving. In the description, include the relevant issue number, snapshots, and videos after changes are added.
+1. If you are borrowing a code, please disclose it. It is fine and sometimes even recommended to borrow code, but we need to know about it to assess your work. If we find out that your pull request contains a lot of code copied from elsewhere, we will close the pull request.
+1. No Work In Progress. ONLY completed and working pull requests and with respective test units will be accepted. A WIP would fall under rule 4 and be closed immediately.
+1. Please do not @mention contributors and mentors. Sometimes it takes time before we can review your pull request or answer your questions, but we'll get to it sooner or later. @mentioning someone just adds to the pile of notifications we get and it won't make us look at your issue faster.
+1. Do not force push. If you make changes to your pull request, please simply add a new commit, as that makes it easy for us to review your new changes. If you force push, we'll have to review everything from the beginning.
+1. PR should be small, easy to review and should follow standard coding styles.
+1. If PR has conflicts because of recently added changes to the same file, resolve issues, test new changes, and submit PR again for review.
+1. PRs should be atomic. That is, they should address one item (issue or feature)
+1. After submitting PR, if you are not replying within 48 hours, then in that case, we may need to assign the issue to other contributors based on the priority of the issue.
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index 435ecf4ada..0000000000
--- a/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,15 +0,0 @@
-- **Please check if the PR fulfills these requirements**
-
-* [ ] The commit message follows our guidelines
-* [ ] Tests for the changes have been added (for bug fixes / features)
-* [ ] Docs have been added / updated (for bug fixes / features)
-
-- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
-
-- **What is the current behavior?** (You can also link to an open issue here)
-
-- **What is the new behavior (if this is a feature change)?**
-
-- **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
-
-- **Other information**:
diff --git a/README.md b/README.md
index 66d98dd25c..cbade9e407 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,13 @@
# Talawa Admin
+[💬 Join the community on Slack](https://github.com/PalisadoesFoundation/)
+
+![talawa-logo-lite-200x200](https://github.com/PalisadoesFoundation/talawa-admin/assets/16875803/26291ec5-d3c1-4135-8bc7-80885dff613d)
+
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![GitHub stars](https://img.shields.io/github/stars/PalisadoesFoundation/talawa-admin.svg?style=social&label=Star&maxAge=2592000)](https://github.com/PalisadoesFoundation/talawa-admin)
[![GitHub forks](https://img.shields.io/github/forks/PalisadoesFoundation/talawa-admin.svg?style=social&label=Fork&maxAge=2592000)](https://github.com/PalisadoesFoundation/talawa-admin)
-
-[![N|Solid](src/assets/talawa-logo-lite-200x200.png)](https://github.com/PalisadoesFoundation/talawa-admin)
+[![codecov](https://codecov.io/gh/PalisadoesFoundation/talawa-admin/branch/develop/graph/badge.svg?token=II0R0RREES)](https://codecov.io/gh/PalisadoesFoundation/talawa-admin)
Talawa is a modular open source project to manage group activities of both non-profit organizations and businesses.
@@ -18,6 +21,16 @@ Core features include:
`talawa` is based on the original `quito` code created by the [Palisadoes Foundation][pfd] as part of its annual Calico Challenge program. Calico provides paid summer internships for Jamaican university students to work on selected open source projects. They are mentored by software professionals and receive stipends based on the completion of predefined milestones. Calico was started in 2015. Visit [The Palisadoes Foundation's website](http://www.palisadoes.org/) for more details on its origin and activities.
+# Table of Contents
+
+
+
+- [Talawa Components](#talawa-components)
+- [Documentation](#documentation)
+- [Videos](#videos)
+
+
+
# Talawa Components
`talawa` has these major software components:
@@ -29,44 +42,15 @@ Core features include:
# Documentation
-- The `talawa` documentation can be found [here](https://palisadoesfoundation.github.io/talawa-docs/).
-- Want to contribute? Look at [CONTRIBUTING.md](CONTRIBUTING.md) to get started.
-- Visit the [Talawa-Docs GitHub](https://github.com/PalisadoesFoundation/talawa-docs) to see the code.
-
-# Project Setup
-
-```
-yarn install
-```
-
-## Compiles and hot-reloads for development
-
-```
-yarn serve
-```
-
-## Compiles and minifies for production
-
-```
-yarn build
-```
-
-## Run your end-to-end tests
-
-```
-yarn test:e2e
-```
-
-## Lints and fixes files
-
-```
-yarn lint
-```
-
-## Customize configuration
-
-See [Configuration Reference](https://cli.vuejs.org/config/).
+1. You can install the software for this repository using the steps in our [INSTALLATION.md](INSTALLATION.md) file.
+1. Do you want to contribute to our code base? Look at our [CONTRIBUTING.md](CONTRIBUTING.md) file to get started. There you'll also find links to:
+ 1. Our code of conduct documentation in the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) file.
+ 1. How we handle the processing of new and existing issues in our [ISSUE_GUIDELINES.md](ISSUE_GUIDELINES.md) file.
+ 1. The methodologies we use to manage our pull requests in our [PR_GUIDELINES.md](PR_GUIDELINES.md) file.
+1. The `talawa` documentation can be found at our [docs.talawa.io](https://docs.talawa.io) site.
+ 1. It is automatically generated from the markdown files stored in our [Talawa-Docs GitHub repository](https://github.com/PalisadoesFoundation/talawa-docs). This makes it easy for you to update our documenation.
-## Project setup using docker
+# Videos
-See [Docker Container](Docker_Container/README.md)
+1. Visit our [YouTube Channel playlists](https://www.youtube.com/@PalisadoesOrganization/playlists) for more insights
+ 1. The "[Getting Started - Developers](https://www.youtube.com/watch?v=YpBUoHxEeyg&list=PLv50qHwThlJUIzscg9a80a9-HmAlmUdCF&index=1)" videos are extremely helpful for new open source contributors.
diff --git a/config/babel.config.cjs b/config/babel.config.cjs
new file mode 100644
index 0000000000..10cfe1914a
--- /dev/null
+++ b/config/babel.config.cjs
@@ -0,0 +1,8 @@
+module.exports = {
+ presets: [
+ '@babel/preset-env', // Transforms modern JavaScript
+ '@babel/preset-typescript', // Transforms TypeScript
+ '@babel/preset-react', // Transforms JSX
+ ],
+ plugins: ['babel-plugin-transform-import-meta'],
+};
diff --git a/config/vite.config.ts b/config/vite.config.ts
new file mode 100644
index 0000000000..71ce6c6f47
--- /dev/null
+++ b/config/vite.config.ts
@@ -0,0 +1,30 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import viteTsconfigPaths from 'vite-tsconfig-paths';
+import svgrPlugin from 'vite-plugin-svgr';
+import EnvironmentPlugin from 'vite-plugin-environment';
+
+export default defineConfig({
+ // depending on your application, base can also be "/"
+ build: {
+ outDir: 'build',
+ },
+ base: '',
+ plugins: [
+ react(),
+ viteTsconfigPaths(),
+ EnvironmentPlugin('all'),
+ svgrPlugin({
+ svgrOptions: {
+ icon: true,
+ // ...svgr options (https://react-svgr.com/docs/options/)
+ },
+ }),
+ ],
+ server: {
+ // this ensures that the browser opens upon server start
+ open: true,
+ // this sets a default port to 3000
+ port: 4321,
+ },
+});
diff --git a/dump.rdb b/dump.rdb
new file mode 100644
index 0000000000..6d49ca7f96
Binary files /dev/null and b/dump.rdb differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000000..4375f88592
--- /dev/null
+++ b/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Talawa Admin
+
+
+
+
+
+
diff --git a/issue-guidelines.md b/issue-guidelines.md
deleted file mode 100644
index 5d626c332d..0000000000
--- a/issue-guidelines.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Issue Report Guidelines
-
-:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
-
-In order to give everyone a chance to submit a issues reports and contribute to the Talawa API project, we have put restrictions in place. This section outlines the guidelines that should be imposed upon issue reports in the Talawa API project.
-
-1. Use the [GitHub open issue search](https://github.com/PalisadoesFoundation/talawa-admin/issues) — check if the issue has already been reported.
-2. If the issue is already reported and not assigned to anyone, if you are interested to work on the issue then ask mentors to assign issign to you in #talawa-api slack channel.
-3. Check if the issue has been fixed — try to reproduce it using the latest master or development branch in the repository.
-4. For newly found unfixed issues or features, start discussing it in #gsoc-newissues channel with mentors. Please do not derail or troll issues. Keep the discussion on topic and respect the opinions of others.
-5. After mentor approval you can create a new issue by following [issue template](https://github.com/PalisadoesFoundation/talawa-admin/blob/master/templates/issue-template.md) available here.
-6. Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the mentors of the merits of this feature. Please provide as much detail and context as possible.
diff --git a/jest-preview.config.ts b/jest-preview.config.ts
new file mode 100644
index 0000000000..0bcc13d4c7
--- /dev/null
+++ b/jest-preview.config.ts
@@ -0,0 +1,5 @@
+export default {
+ moduleNameMapper: {
+ '^@mui/(.*)$': '/node_modules/@mui/$1',
+ },
+};
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000000..bd17983c75
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,71 @@
+export default {
+ roots: ['/src'],
+ collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/index.tsx'],
+ // setupFiles: ['react-app-polyfill/jsdom'],
+ setupFiles: ['whatwg-fetch'],
+ setupFilesAfterEnv: ['/src/setupTests.ts'],
+ testMatch: [
+ '/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
+ '/src/**/*.{spec,test}.{js,jsx,ts,tsx}',
+ ],
+ testEnvironment: 'jsdom',
+ transform: {
+ '^.+\\.(js|jsx|ts|tsx)$': [
+ 'babel-jest',
+ { configFile: './config/babel.config.cjs' },
+ ], // Use babel-jest for JavaScript and TypeScript files
+ '^.+\\.(css|scss|sass|less)$': 'jest-preview/transforms/css', // CSS transformations
+ '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': 'jest-preview/transforms/file', // File transformations
+ },
+ transformIgnorePatterns: [
+ '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
+ ],
+ modulePaths: [
+ '/Users/prathamesh/Desktop/Open-Source/palisadoes/talawa-admin/src',
+ '/src',
+ ],
+ moduleNameMapper: {
+ '^react-native$': 'react-native-web',
+ '^@dicebear/core$': '/scripts/__mocks__/@dicebear/core.ts',
+ '^@dicebear/collection$':
+ '/scripts/__mocks__/@dicebear/collection.ts',
+ '\\.svg\\?react$': '/scripts/__mocks__/fileMock.js',
+ '\\.svg$': '/scripts/__mocks__/fileMock.js',
+ '^@pdfme/generator$': '/scripts/__mocks__/@pdfme/generator.ts'
+ },
+ moduleFileExtensions: [
+ 'web.js',
+ 'js',
+ 'web.ts',
+ 'ts',
+ 'web.tsx',
+ 'tsx',
+ 'json',
+ 'web.jsx',
+ 'jsx',
+ 'node',
+ ],
+ // watchPlugins: [
+ // 'jest-watch-typeahead/filename',
+ // 'jest-watch-typeahead/testname',
+ // ],
+ resetMocks: false,
+ coveragePathIgnorePatterns: [
+ 'src/state/index.ts',
+ 'src/components/plugins/index.ts',
+ 'src/components/AddOn/support/services/Render.helper.ts',
+ 'src/components/SecuredRoute/SecuredRoute.tsx',
+ 'src/reportWebVitals.ts',
+ ],
+ coverageThreshold: {
+ global: {
+ lines: 20,
+ statements: 20,
+ },
+ },
+ testPathIgnorePatterns: [
+ '/node_modules/',
+ '/build/',
+ '/public/',
+ ],
+};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000..0add51da52
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,19051 @@
+{
+ "name": "talawa-admin",
+ "version": "3.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "talawa-admin",
+ "version": "3.0.0",
+ "dependencies": {
+ "@apollo/client": "^3.11.8",
+ "@apollo/link-error": "^2.0.0-beta.3",
+ "@apollo/react-testing": "^4.0.0",
+ "@dicebear/collection": "^9.2.2",
+ "@dicebear/core": "^9.2.2",
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@mui/base": "^5.0.0-beta.61",
+ "@mui/icons-material": "^6.1.6",
+ "@mui/material": "^6.1.6",
+ "@mui/private-theming": "^6.1.6",
+ "@mui/system": "^6.1.6",
+ "@mui/x-charts": "^7.22.1",
+ "@mui/x-data-grid": "^7.22.1",
+ "@mui/x-date-pickers": "^7.22.1",
+ "@pdfme/generator": "^5.1.7",
+ "@pdfme/schemas": "^5.1.6",
+ "@reduxjs/toolkit": "^2.3.0",
+ "@vitejs/plugin-react": "^4.3.2",
+ "babel-plugin-transform-import-meta": "^2.2.1",
+ "bootstrap": "^5.3.3",
+ "chart.js": "^4.4.6",
+ "customize-cra": "^1.0.0",
+ "dayjs": "^1.11.13",
+ "dotenv": "^16.4.5",
+ "flag-icons": "^7.2.3",
+ "graphql": "^16.9.0",
+ "graphql-tag": "^2.12.6",
+ "graphql-ws": "^5.16.0",
+ "history": "^5.3.0",
+ "i18next": "^23.15.1",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-http-backend": "^2.6.1",
+ "inquirer": "^8.0.0",
+ "js-cookie": "^3.0.1",
+ "markdown-toc": "^1.2.0",
+ "prettier": "^3.3.3",
+ "prop-types": "^15.8.1",
+ "react": "^18.3.1",
+ "react-beautiful-dnd": "^13.1.1",
+ "react-bootstrap": "^2.10.5",
+ "react-chartjs-2": "^5.2.0",
+ "react-datepicker": "^7.5.0",
+ "react-dom": "^18.3.1",
+ "react-google-recaptcha": "^3.1.0",
+ "react-i18next": "^15.0.2",
+ "react-icons": "^5.2.1",
+ "react-infinite-scroll-component": "^6.1.0",
+ "react-multi-carousel": "^2.8.5",
+ "react-redux": "^9.1.2",
+ "react-router-dom": "^6.27.0",
+ "react-toastify": "^10.0.6",
+ "react-tooltip": "^5.28.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "sanitize-html": "^2.13.0",
+ "typedoc": "^0.26.10",
+ "typedoc-plugin-markdown": "^4.2.10",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vite-plugin-environment": "^1.1.3",
+ "vite-tsconfig-paths": "^5.1.2",
+ "web-vitals": "^4.2.4"
+ },
+ "devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
+ "@babel/preset-env": "^7.25.4",
+ "@babel/preset-react": "^7.25.7",
+ "@babel/preset-typescript": "^7.26.0",
+ "@testing-library/jest-dom": "^6.5.0",
+ "@testing-library/react": "^16.0.1",
+ "@testing-library/user-event": "^12.1.10",
+ "@types/inquirer": "^9.0.7",
+ "@types/jest": "^26.0.24",
+ "@types/js-cookie": "^3.0.6",
+ "@types/node": "^22.5.4",
+ "@types/node-fetch": "^2.6.10",
+ "@types/react": "^18.3.3",
+ "@types/react-beautiful-dnd": "^13.1.8",
+ "@types/react-bootstrap": "^0.32.37",
+ "@types/react-chartjs-2": "^2.5.7",
+ "@types/react-datepicker": "^7.0.0",
+ "@types/react-dom": "^18.3.1",
+ "@types/react-google-recaptcha": "^2.1.9",
+ "@types/react-router-dom": "^5.1.8",
+ "@types/sanitize-html": "^2.13.0",
+ "@typescript-eslint/eslint-plugin": "^8.11.0",
+ "@typescript-eslint/parser": "^8.5.0",
+ "babel-jest": "^29.7.0",
+ "cross-env": "^7.0.3",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-import": "^2.30.0",
+ "eslint-plugin-jest": "^28.8.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-react": "^7.37.1",
+ "eslint-plugin-tsdoc": "^0.3.0",
+ "husky": "^9.1.6",
+ "identity-obj-proxy": "^3.0.0",
+ "jest": "^27.4.5",
+ "jest-localstorage-mock": "^2.4.19",
+ "jest-location-mock": "^2.0.0",
+ "jest-preview": "^0.3.1",
+ "lint-staged": "^15.2.8",
+ "postcss-modules": "^6.0.0",
+ "sass": "^1.80.6",
+ "tsx": "^4.19.1",
+ "vite-plugin-svgr": "^4.2.0",
+ "whatwg-fetch": "^3.6.20"
+ },
+ "engines": {
+ "node": ">=20.x"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@adobe/css-tools": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz",
+ "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@apollo/client": {
+ "version": "3.11.8",
+ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.11.8.tgz",
+ "integrity": "sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==",
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@wry/caches": "^1.0.0",
+ "@wry/equality": "^0.5.6",
+ "@wry/trie": "^0.5.0",
+ "graphql-tag": "^2.12.6",
+ "hoist-non-react-statics": "^3.3.2",
+ "optimism": "^0.18.0",
+ "prop-types": "^15.7.2",
+ "rehackt": "^0.1.0",
+ "response-iterator": "^0.2.6",
+ "symbol-observable": "^4.0.0",
+ "ts-invariant": "^0.10.3",
+ "tslib": "^2.3.0",
+ "zen-observable-ts": "^1.2.5"
+ },
+ "peerDependencies": {
+ "graphql": "^15.0.0 || ^16.0.0",
+ "graphql-ws": "^5.5.5",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0",
+ "subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
+ },
+ "peerDependenciesMeta": {
+ "graphql-ws": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "subscriptions-transport-ws": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@apollo/link-error": {
+ "version": "2.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/@apollo/link-error/-/link-error-2.0.0-beta.3.tgz",
+ "integrity": "sha512-blNBBi9+4SEfb4Bhn8cYqGFhb0C7MjqLiRwNdUqwGefl1w+G8Ze8pCLHAyPxXLcslirtht9LY0i6ZOpCzSXHCg==",
+ "dependencies": {
+ "@apollo/client": "^3.0.0-beta.23",
+ "tslib": "^1.9.3"
+ }
+ },
+ "node_modules/@apollo/link-error/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "node_modules/@apollo/react-testing": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/react-testing/-/react-testing-4.0.0.tgz",
+ "integrity": "sha512-P7Z/flUHpRRZYc3FkIqxZH9XD3FuP2Sgks1IXqGq2Zb7qI0aaTfVeRsLYmZNUcFOh2pTHxs0NXgPnH1VfYOpig==",
+ "dependencies": {
+ "@apollo/client": "latest"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
+ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
+ "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.0",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-module-transforms": "^7.25.2",
+ "@babel/helpers": "^7.25.0",
+ "@babel/parser": "^7.25.0",
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.2",
+ "@babel/types": "^7.25.2",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
+ "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
+ "dependencies": {
+ "@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"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
+ "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz",
+ "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
+ "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
+ "dependencies": {
+ "@babel/compat-data": "^7.25.2",
+ "@babel/helper-validator-option": "^7.24.8",
+ "browserslist": "^4.23.1",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
+ "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/helper-replace-supers": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz",
+ "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "regexpu-core": "^5.3.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz",
+ "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.22.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
+ "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+ "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
+ "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
+ "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz",
+ "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-wrap-function": "^7.25.0",
+ "@babel/traverse": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
+ "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz",
+ "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
+ "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+ "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz",
+ "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.0",
+ "@babel/types": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz",
+ "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==",
+ "dependencies": {
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+ "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
+ "dependencies": {
+ "@babel/types": "^7.26.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.25.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz",
+ "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz",
+ "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz",
+ "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
+ "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
+ "@babel/plugin-transform-optional-chaining": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz",
+ "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.21.0",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz",
+ "integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz",
+ "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
+ "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz",
+ "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz",
+ "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
+ "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-remap-async-to-generator": "^7.25.0",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/traverse": "^7.25.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz",
+ "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-remap-async-to-generator": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz",
+ "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz",
+ "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
+ "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz",
+ "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz",
+ "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-replace-supers": "^7.25.0",
+ "@babel/traverse": "^7.25.4",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz",
+ "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/template": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz",
+ "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz",
+ "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz",
+ "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz",
+ "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.25.0",
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz",
+ "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz",
+ "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz",
+ "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz",
+ "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.25.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz",
+ "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.24.8",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz",
+ "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-json-strings": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz",
+ "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz",
+ "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz",
+ "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz",
+ "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz",
+ "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-simple-access": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz",
+ "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.25.0",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/traverse": "^7.25.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz",
+ "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz",
+ "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz",
+ "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz",
+ "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz",
+ "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz",
+ "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz",
+ "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-replace-supers": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz",
+ "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz",
+ "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz",
+ "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz",
+ "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz",
+ "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz",
+ "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-display-name": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz",
+ "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz",
+ "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.7",
+ "@babel/helper-module-imports": "^7.25.7",
+ "@babel/helper-plugin-utils": "^7.25.7",
+ "@babel/plugin-syntax-jsx": "^7.25.7",
+ "@babel/types": "^7.25.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-development": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz",
+ "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.25.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz",
+ "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz",
+ "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-pure-annotations": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz",
+ "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.7",
+ "@babel/helper-plugin-utils": "^7.25.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz",
+ "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "regenerator-transform": "^0.15.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz",
+ "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz",
+ "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz",
+ "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz",
+ "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz",
+ "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz",
+ "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz",
+ "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/plugin-syntax-typescript": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
+ "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz",
+ "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz",
+ "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
+ "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
+ "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.25.4",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-validator-option": "^7.24.8",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-import-assertions": "^7.24.7",
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
+ "@babel/plugin-syntax-import-meta": "^7.10.4",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.24.7",
+ "@babel/plugin-transform-async-generator-functions": "^7.25.4",
+ "@babel/plugin-transform-async-to-generator": "^7.24.7",
+ "@babel/plugin-transform-block-scoped-functions": "^7.24.7",
+ "@babel/plugin-transform-block-scoping": "^7.25.0",
+ "@babel/plugin-transform-class-properties": "^7.25.4",
+ "@babel/plugin-transform-class-static-block": "^7.24.7",
+ "@babel/plugin-transform-classes": "^7.25.4",
+ "@babel/plugin-transform-computed-properties": "^7.24.7",
+ "@babel/plugin-transform-destructuring": "^7.24.8",
+ "@babel/plugin-transform-dotall-regex": "^7.24.7",
+ "@babel/plugin-transform-duplicate-keys": "^7.24.7",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0",
+ "@babel/plugin-transform-dynamic-import": "^7.24.7",
+ "@babel/plugin-transform-exponentiation-operator": "^7.24.7",
+ "@babel/plugin-transform-export-namespace-from": "^7.24.7",
+ "@babel/plugin-transform-for-of": "^7.24.7",
+ "@babel/plugin-transform-function-name": "^7.25.1",
+ "@babel/plugin-transform-json-strings": "^7.24.7",
+ "@babel/plugin-transform-literals": "^7.25.2",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.24.7",
+ "@babel/plugin-transform-member-expression-literals": "^7.24.7",
+ "@babel/plugin-transform-modules-amd": "^7.24.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.24.8",
+ "@babel/plugin-transform-modules-systemjs": "^7.25.0",
+ "@babel/plugin-transform-modules-umd": "^7.24.7",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
+ "@babel/plugin-transform-new-target": "^7.24.7",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
+ "@babel/plugin-transform-numeric-separator": "^7.24.7",
+ "@babel/plugin-transform-object-rest-spread": "^7.24.7",
+ "@babel/plugin-transform-object-super": "^7.24.7",
+ "@babel/plugin-transform-optional-catch-binding": "^7.24.7",
+ "@babel/plugin-transform-optional-chaining": "^7.24.8",
+ "@babel/plugin-transform-parameters": "^7.24.7",
+ "@babel/plugin-transform-private-methods": "^7.25.4",
+ "@babel/plugin-transform-private-property-in-object": "^7.24.7",
+ "@babel/plugin-transform-property-literals": "^7.24.7",
+ "@babel/plugin-transform-regenerator": "^7.24.7",
+ "@babel/plugin-transform-reserved-words": "^7.24.7",
+ "@babel/plugin-transform-shorthand-properties": "^7.24.7",
+ "@babel/plugin-transform-spread": "^7.24.7",
+ "@babel/plugin-transform-sticky-regex": "^7.24.7",
+ "@babel/plugin-transform-template-literals": "^7.24.7",
+ "@babel/plugin-transform-typeof-symbol": "^7.24.8",
+ "@babel/plugin-transform-unicode-escapes": "^7.24.7",
+ "@babel/plugin-transform-unicode-property-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.25.4",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.10",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
+ "babel-plugin-polyfill-regenerator": "^0.6.1",
+ "core-js-compat": "^3.37.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/preset-react": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz",
+ "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.7",
+ "@babel/helper-validator-option": "^7.25.7",
+ "@babel/plugin-transform-react-display-name": "^7.25.7",
+ "@babel/plugin-transform-react-jsx": "^7.25.7",
+ "@babel/plugin-transform-react-jsx-development": "^7.25.7",
+ "@babel/plugin-transform-react-pure-annotations": "^7.25.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-typescript": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz",
+ "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-validator-option": "^7.25.9",
+ "@babel/plugin-syntax-jsx": "^7.25.9",
+ "@babel/plugin-transform-modules-commonjs": "^7.25.9",
+ "@babel/plugin-transform-typescript": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==",
+ "dev": true
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
+ "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime/node_modules/regenerator-runtime": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+ },
+ "node_modules/@babel/template": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
+ "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.25.9",
+ "@babel/parser": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz",
+ "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==",
+ "dependencies": {
+ "@babel/code-frame": "^7.25.9",
+ "@babel/generator": "^7.25.9",
+ "@babel/parser": "^7.25.9",
+ "@babel/template": "^7.25.9",
+ "@babel/types": "^7.25.9",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
+ "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true
+ },
+ "node_modules/@dicebear/adventurer": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/adventurer/-/adventurer-9.2.2.tgz",
+ "integrity": "sha512-WjBXCP9EXbUul2zC3BS2/R3/4diw1uh/lU4jTEnujK1mhqwIwanFboIMzQsasNNL/xf+m3OHN7MUNJfHZ1fLZA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/adventurer-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-9.2.2.tgz",
+ "integrity": "sha512-XVAjhUWjav6luTZ7txz8zVJU/H0DiUy4uU1Z7IO5MDO6kWvum+If1+0OUgEWYZwM+RDI7rt2CgVP910DyZGd1w==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/avataaars": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/avataaars/-/avataaars-9.2.2.tgz",
+ "integrity": "sha512-WqJPQEt0OhBybTpI0TqU1uD1pSk9M2+VPIwvBye/dXo46b+0jHGpftmxjQwk6tX8z0+mRko8pwV5n+cWht1/+w==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/avataaars-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/avataaars-neutral/-/avataaars-neutral-9.2.2.tgz",
+ "integrity": "sha512-pRj16P27dFDBI3LtdiHUDwIXIGndHAbZf5AxaMkn6/+0X93mVQ/btVJDXyW0G96WCsyC88wKAWr6/KJotPxU6Q==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/big-ears": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/big-ears/-/big-ears-9.2.2.tgz",
+ "integrity": "sha512-hz4UXdPq4qqZpu0YVvlqM4RDFhk5i0WgPcuwj/MOLlgTjuj63uHUhCQSk6ZiW1DQOs12qpwUBMGWVHxBRBas9g==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/big-ears-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/big-ears-neutral/-/big-ears-neutral-9.2.2.tgz",
+ "integrity": "sha512-IPHt8fi3dv9cyfBJBZ4s8T+PhFCrQvOCf91iRHBT3iOLNPdyZpI5GNLmGiV0XMAvIDP5NvA5+f6wdoBLhYhbDA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/big-smile": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/big-smile/-/big-smile-9.2.2.tgz",
+ "integrity": "sha512-D4td0GL8or1nTNnXvZqkEXlzyqzGPWs3znOnm1HIohtFTeIwXm72Ob2lNDsaQJSJvXmVlwaQQ0CCTvyCl8Stjw==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/bottts": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/bottts/-/bottts-9.2.2.tgz",
+ "integrity": "sha512-wugFkzw8JNWV1nftq/Wp/vmQsLAXDxrMtRK3AoMODuUpSVoP3EHRUfKS043xggOsQFvoj0HZ7kadmhn0AMLf5A==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/bottts-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-9.2.2.tgz",
+ "integrity": "sha512-lSgpqmSJtlnyxVuUgNdBwyzuA0O9xa5zRJtz7x2KyWbicXir5iYdX0MVMCkp1EDvlcxm9rGJsclktugOyakTlw==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/collection": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/collection/-/collection-9.2.2.tgz",
+ "integrity": "sha512-vZAmXhPWCK3sf8Fj9/QflFC6XOLroJOT5K1HdnzHaPboEvffUQideGCrrEamnJtlH0iF0ZDXh8gqmwy2fu+yHA==",
+ "dependencies": {
+ "@dicebear/adventurer": "9.2.2",
+ "@dicebear/adventurer-neutral": "9.2.2",
+ "@dicebear/avataaars": "9.2.2",
+ "@dicebear/avataaars-neutral": "9.2.2",
+ "@dicebear/big-ears": "9.2.2",
+ "@dicebear/big-ears-neutral": "9.2.2",
+ "@dicebear/big-smile": "9.2.2",
+ "@dicebear/bottts": "9.2.2",
+ "@dicebear/bottts-neutral": "9.2.2",
+ "@dicebear/croodles": "9.2.2",
+ "@dicebear/croodles-neutral": "9.2.2",
+ "@dicebear/dylan": "9.2.2",
+ "@dicebear/fun-emoji": "9.2.2",
+ "@dicebear/glass": "9.2.2",
+ "@dicebear/icons": "9.2.2",
+ "@dicebear/identicon": "9.2.2",
+ "@dicebear/initials": "9.2.2",
+ "@dicebear/lorelei": "9.2.2",
+ "@dicebear/lorelei-neutral": "9.2.2",
+ "@dicebear/micah": "9.2.2",
+ "@dicebear/miniavs": "9.2.2",
+ "@dicebear/notionists": "9.2.2",
+ "@dicebear/notionists-neutral": "9.2.2",
+ "@dicebear/open-peeps": "9.2.2",
+ "@dicebear/personas": "9.2.2",
+ "@dicebear/pixel-art": "9.2.2",
+ "@dicebear/pixel-art-neutral": "9.2.2",
+ "@dicebear/rings": "9.2.2",
+ "@dicebear/shapes": "9.2.2",
+ "@dicebear/thumbs": "9.2.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/core": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-9.2.2.tgz",
+ "integrity": "sha512-ROhgHG249dPtcXgBHcqPEsDeAPRPRD/9d+tZCjLYyueO+cXDlIA8dUlxpwIVcOuZFvCyW6RJtqo8BhNAi16pIQ==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.11"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@dicebear/croodles": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/croodles/-/croodles-9.2.2.tgz",
+ "integrity": "sha512-OzvAXQWsOgMwL3Sl+lBxCubqSOWoBJpC78c4TKnNTS21rR63TtXUyVdLLzgKVN4YHRnvMgtPf8F/W9YAgIDK4w==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/croodles-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/croodles-neutral/-/croodles-neutral-9.2.2.tgz",
+ "integrity": "sha512-/4mNirxoQ+z1kHXnpDRbJ1JV1ZgXogeTeNp0MaFYxocCgHfJ7ckNM23EE1I7akoo9pqPxrKlaeNzGAjKHdS9vA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/dylan": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/dylan/-/dylan-9.2.2.tgz",
+ "integrity": "sha512-s7e3XliC1YXP+Wykj+j5kwdOWFRXFzYHYk/PB4oZ1F3sJandXiG0HS4chaNu4EoP0yZgKyFMUVTGZx+o6tMaYg==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/fun-emoji": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/fun-emoji/-/fun-emoji-9.2.2.tgz",
+ "integrity": "sha512-M+rYTpB3lfwz18f+/i+ggNwNWUoEj58SJqXJ1wr7Jh/4E5uL+NmJg9JGwYNaVtGbCFrKAjSaILNUWGQSFgMfog==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/glass": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/glass/-/glass-9.2.2.tgz",
+ "integrity": "sha512-imCMxcg+XScHYtQq2MUv1lCzhQSCUglMlPSezKEpXhTxgbgUpmGlSGVkOfmX5EEc7SQowKkF1W/1gNk6CXvBaQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/icons": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/icons/-/icons-9.2.2.tgz",
+ "integrity": "sha512-Tqq2OVCdS7J02DNw58xwlgLGl40sWEckbqXT3qRvIF63FfVq+wQZBGuhuiyAURcSgvsc3h2oQeYFi9iXh7HTOA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/identicon": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/identicon/-/identicon-9.2.2.tgz",
+ "integrity": "sha512-POVKFulIrcuZf3rdAgxYaSm2XUg/TJg3tg9zq9150reEGPpzWR7ijyJ03dzAADPzS3DExfdYVT9+z3JKwwJnTQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/initials": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/initials/-/initials-9.2.2.tgz",
+ "integrity": "sha512-/xNnsEmsstWjmF77htAOuwOMhFlP6eBVXgcgFlTl/CCH/Oc6H7t0vwX1he8KLQBBzjGpvJcvIAn4Wh9rE4D5/A==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/lorelei": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/lorelei/-/lorelei-9.2.2.tgz",
+ "integrity": "sha512-koXqVr/vcWUPo00VP5H6Czsit+uF1tmwd2NK7Q/e34/9Sd1f4QLLxHjjBNm/iNjCI1+UNTOvZ2Qqu0N5eo7Flw==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/lorelei-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/lorelei-neutral/-/lorelei-neutral-9.2.2.tgz",
+ "integrity": "sha512-Eys9Os6nt2Xll7Mvu66CfRR2YggTopWcmFcRZ9pPdohS96kT0MsLI2iTcfZXQ51K8hvT3IbwoGc86W8n0cDxAQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/micah": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/micah/-/micah-9.2.2.tgz",
+ "integrity": "sha512-NCajcJV5yw8uMKiACp694w1T/UyYme2CUEzyTzWHgWnQ+drAuCcH8gpAoLWd67viNdQB/MTpNlaelUgTjmI4AQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/miniavs": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/miniavs/-/miniavs-9.2.2.tgz",
+ "integrity": "sha512-vvkWXttdw+KHF3j+9qcUFzK+P0nbNnImGjvN48wwkPIh2h08WWFq0MnoOls4IHwUJC4GXBjWtiyVoCxz6hhtOA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/notionists": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/notionists/-/notionists-9.2.2.tgz",
+ "integrity": "sha512-Z9orRaHoj7Y9Ap4wEu8XOrFACsG1KbbBQUPV1R50uh6AHwsyNrm4cS84ICoGLvxgLNHHOae3YCjd8aMu2z19zg==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/notionists-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/notionists-neutral/-/notionists-neutral-9.2.2.tgz",
+ "integrity": "sha512-AhOzk+lz6kB4uxGun8AJhV+W1nttnMlxmxd+5KbQ/txCIziYIaeD3il44wsAGegEpGFvAZyMYtR/jjfHcem3TA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/open-peeps": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/open-peeps/-/open-peeps-9.2.2.tgz",
+ "integrity": "sha512-6PeQDHYyjvKrGSl/gP+RE5dSYAQGKpcGnM65HorgyTIugZK7STo0W4hvEycedupZ3MCCEH8x/XyiChKM2sHXog==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/personas": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/personas/-/personas-9.2.2.tgz",
+ "integrity": "sha512-705+ObNLC0w1fcgE/Utav+8bqO+Esu53TXegpX5j7trGEoIMf2bThqJGHuhknZ3+T2az3Wr89cGyOGlI0KLzLA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/pixel-art": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/pixel-art/-/pixel-art-9.2.2.tgz",
+ "integrity": "sha512-BvbFdrpzQl04+Y9UsWP63YGug+ENGC7GMG88qbEFWxb/IqRavGa4H3D0T4Zl2PSLiw7f2Ctv98bsCQZ1PtCznQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/pixel-art-neutral": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/pixel-art-neutral/-/pixel-art-neutral-9.2.2.tgz",
+ "integrity": "sha512-CdUY77H6Aj7dKLW3hdkv7tu0XQJArUjaWoXihQxlhl3oVYplWaoyu9omYy5pl8HTqs8YgVTGljjMXYoFuK0JUw==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/rings": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/rings/-/rings-9.2.2.tgz",
+ "integrity": "sha512-eD1J1k364Arny+UlvGrk12HP/XGG6WxPSm4BarFqdJGSV45XOZlwqoi7FlcMr9r9yvE/nGL8OizbwMYusEEdjw==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/shapes": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/shapes/-/shapes-9.2.2.tgz",
+ "integrity": "sha512-e741NNWBa7fg0BjomxXa0fFPME2XCIR0FA+VHdq9AD2taTGHEPsg5x1QJhCRdK6ww85yeu3V3ucpZXdSrHVw5Q==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@dicebear/thumbs": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@dicebear/thumbs/-/thumbs-9.2.2.tgz",
+ "integrity": "sha512-FkPLDNu7n5kThLSk7lR/0cz/NkUqgGdZGfLZv6fLkGNGtv6W+e2vZaO7HCXVwIgJ+II+kImN41zVIZ6Jlll7pQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@dicebear/core": "^9.0.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
+ "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.2.0",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.13.1",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
+ "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz",
+ "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.13.3",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz",
+ "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/cache": "^11.13.0",
+ "@emotion/serialize": "^1.3.1",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz",
+ "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.1",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.13.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
+ "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.12.0",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.0",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+ "@emotion/utils": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz",
+ "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz",
+ "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA=="
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0",
+ "peer": true
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@eslint/eslintrc/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
+ "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.4.tgz",
+ "integrity": "sha512-0G8R+zOvQsAG1pg2Q99P21jiqxqGBW1iRe/iXHsBRBxnpXKFI8QwbB4x5KmYLggNO5m34IQgOIu9SCRfR/WWiQ==",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.26.25",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.25.tgz",
+ "integrity": "sha512-hZOmgN0NTOzOuZxI1oIrDu3Gcl8WViIkvPMpB4xdd4QD6xAMtwgwr3VPoiyH/bLtRcS1cDnhxLSD1NsMJmwh/A==",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.2",
+ "@floating-ui/utils": "^0.2.8",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
+ "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
+ "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
+ },
+ "node_modules/@graphql-typed-document-node/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "peer": true
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jedmao/location": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@jedmao/location/-/location-3.0.0.tgz",
+ "integrity": "sha512-p7mzNlgJbCioUYLUEKds3cQG4CHONVFJNYqMe6ocEtENCL/jYmMo1Q3ApwsMmU+L0ZkaDJEyv4HokaByLoPwlQ==",
+ "dev": true
+ },
+ "node_modules/@jest/console": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz",
+ "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/console/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jest/core": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz",
+ "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.5.1",
+ "@jest/reporters": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-changed-files": "^27.5.1",
+ "jest-config": "^27.5.1",
+ "jest-haste-map": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-regex-util": "^27.5.1",
+ "jest-resolve": "^27.5.1",
+ "jest-resolve-dependencies": "^27.5.1",
+ "jest-runner": "^27.5.1",
+ "jest-runtime": "^27.5.1",
+ "jest-snapshot": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-validate": "^27.5.1",
+ "jest-watcher": "^27.5.1",
+ "micromatch": "^4.0.4",
+ "rimraf": "^3.0.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/core/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jest/environment": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz",
+ "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/fake-timers": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "jest-mock": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/fake-timers": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz",
+ "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@sinonjs/fake-timers": "^8.0.1",
+ "@types/node": "*",
+ "jest-message-util": "^27.5.1",
+ "jest-mock": "^27.5.1",
+ "jest-util": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/globals": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz",
+ "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "expect": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/reporters": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz",
+ "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==",
+ "dev": true,
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.2.9",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^5.1.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.1.3",
+ "jest-haste-map": "^27.5.1",
+ "jest-resolve": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-worker": "^27.5.1",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.0",
+ "string-length": "^4.0.1",
+ "terminal-link": "^2.0.0",
+ "v8-to-istanbul": "^8.1.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/reporters/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jest/reporters/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@jest/source-map": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz",
+ "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.9",
+ "source-map": "^0.6.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/source-map/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@jest/test-result": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz",
+ "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/test-sequencer": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz",
+ "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^27.5.1",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^27.5.1",
+ "jest-runtime": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/transform": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz",
+ "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.1.0",
+ "@jest/types": "^27.5.1",
+ "babel-plugin-istanbul": "^6.1.1",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^1.4.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^27.5.1",
+ "jest-regex-util": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.4",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.1",
+ "write-file-atomic": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/transform/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jest/transform/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
+ "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/types/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==",
+ "license": "MIT"
+ },
+ "node_modules/@microsoft/tsdoc": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz",
+ "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==",
+ "dev": true
+ },
+ "node_modules/@microsoft/tsdoc-config": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz",
+ "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==",
+ "dev": true,
+ "dependencies": {
+ "@microsoft/tsdoc": "0.15.0",
+ "ajv": "~8.12.0",
+ "jju": "~1.4.0",
+ "resolve": "~1.22.2"
+ }
+ },
+ "node_modules/@mui/base": {
+ "version": "5.0.0-beta.61",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.61.tgz",
+ "integrity": "sha512-YaMOTXS3ecDNGsPKa6UdlJ8loFLvcL9+VbpCK3hfk71OaNauZRp4Yf7KeXDYr7Ms3M/XBD3SaiR6JMr6vYtfDg==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@floating-ui/react-dom": "^2.1.1",
+ "@mui/types": "^7.2.19",
+ "@mui/utils": "^6.1.6",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.6.tgz",
+ "integrity": "sha512-nz1SlR9TdBYYPz4qKoNasMPRiGb4PaIHFkzLzhju0YVYS5QSuFF2+n7CsiHMIDcHv3piPu/xDWI53ruhOqvZwQ==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/icons-material": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.6.tgz",
+ "integrity": "sha512-5r9urIL2lxXb/sPN3LFfFYEibsXJUb986HhhIeu1gOcte460pwdSiEhBSxkAuyT8Dj7jvu9MjqSBmSumQELo8A==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^6.1.6",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.6.tgz",
+ "integrity": "sha512-1yvejiQ/601l5AK3uIdUlAVElyCxoqKnl7QA+2oFB/2qYPWfRwDgavW/MoywS5Y2gZEslcJKhe0s2F3IthgFgw==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/core-downloads-tracker": "^6.1.6",
+ "@mui/system": "^6.1.6",
+ "@mui/types": "^7.2.19",
+ "@mui/utils": "^6.1.6",
+ "@popperjs/core": "^2.11.8",
+ "@types/react-transition-group": "^4.4.11",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.3.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@mui/material-pigment-css": "^6.1.6",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@mui/material-pigment-css": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.6.tgz",
+ "integrity": "sha512-ioAiFckaD/fJSnTrUMWgjl9HYBWt7ixCh7zZw7gDZ+Tae7NuprNV6QJK95EidDT7K0GetR2rU3kAeIR61Myttw==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/utils": "^6.1.6",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.6.tgz",
+ "integrity": "sha512-I+yS1cSuSvHnZDBO7e7VHxTWpj+R7XlSZvTC4lS/OIbUNJOMMSd3UDP6V2sfwzAdmdDNBi7NGCRv2SZ6O9hGDA==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@emotion/cache": "^11.13.1",
+ "@emotion/serialize": "^1.3.2",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.6.tgz",
+ "integrity": "sha512-qOf1VUE9wK8syiB0BBCp82oNBAVPYdj4Trh+G1s+L+ImYiKlubWhhqlnvWt3xqMevR+D2h1CXzA1vhX2FvA+VQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/private-theming": "^6.1.6",
+ "@mui/styled-engine": "^6.1.6",
+ "@mui/types": "^7.2.19",
+ "@mui/utils": "^6.1.6",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.2.19",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz",
+ "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==",
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.6.tgz",
+ "integrity": "sha512-sBS6D9mJECtELASLM+18WUcXF6RH3zNxBRFeyCRg8wad6NbyNrdxLuwK+Ikvc38sTZwBzAz691HmSofLqHd9sQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/types": "^7.2.19",
+ "@types/prop-types": "^15.7.13",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-charts": {
+ "version": "7.22.1",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.22.1.tgz",
+ "integrity": "sha512-zgr8CN4yLen5puqaX7Haj5+AoVG7E13HHsIiDoEAuQvuFDF0gKTxTTdLSKXqhd1qJUIIzJaztZtrr3YCVrENqw==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "@mui/utils": "^5.16.6 || ^6.0.0",
+ "@mui/x-charts-vendor": "7.20.0",
+ "@mui/x-internals": "7.21.0",
+ "@react-spring/rafz": "^9.7.5",
+ "@react-spring/web": "^9.7.5",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-charts-vendor": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.20.0.tgz",
+ "integrity": "sha512-pzlh7z/7KKs5o0Kk0oPcB+sY0+Dg7Q7RzqQowDQjpy5Slz6qqGsgOB5YUzn0L+2yRmvASc4Pe0914Ao3tMBogg==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "@types/d3-color": "^3.1.3",
+ "@types/d3-delaunay": "^6.0.4",
+ "@types/d3-interpolate": "^3.0.4",
+ "@types/d3-scale": "^4.0.8",
+ "@types/d3-shape": "^3.1.6",
+ "@types/d3-time": "^3.0.3",
+ "d3-color": "^3.1.0",
+ "d3-delaunay": "^6.0.4",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.2.0",
+ "d3-time": "^3.1.0",
+ "delaunator": "^5.0.1",
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/@mui/x-data-grid": {
+ "version": "7.22.1",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.22.1.tgz",
+ "integrity": "sha512-YHF96MEv7ACG/VuiycZjEAPH7cZLNuV2+bi/MyR1t/e6E6LTolYFykvjSFq+Imz1mYbW4+9mEvrHZsIKL5KKIQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "@mui/utils": "^5.16.6 || ^6.0.0",
+ "@mui/x-internals": "7.21.0",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "reselect": "^5.1.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-date-pickers": {
+ "version": "7.22.1",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.22.1.tgz",
+ "integrity": "sha512-VBgicE+7PvJrdHSL6HyieHT6a/0dENH8RaMIM2VwUFrGoZzvik50WNwY5U+Hip1BwZLIEvlqtNRQIIj6kgBR6Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "@mui/utils": "^5.16.6 || ^6.0.0",
+ "@mui/x-internals": "7.21.0",
+ "@types/react-transition-group": "^4.4.11",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.15.14 || ^6.0.0",
+ "@mui/system": "^5.15.14 || ^6.0.0",
+ "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
+ "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0",
+ "dayjs": "^1.10.7",
+ "luxon": "^3.0.2",
+ "moment": "^2.29.4",
+ "moment-hijri": "^2.1.2",
+ "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ },
+ "date-fns-jalali": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ },
+ "moment-hijri": {
+ "optional": true
+ },
+ "moment-jalaali": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-internals": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.21.0.tgz",
+ "integrity": "sha512-94YNyZ0BhK5Z+Tkr90RKf47IVCW8R/1MvdUhh6MCQg6sZa74jsX+x+gEZ4kzuCqOsuyTyxikeQ8vVuCIQiP7UQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "@mui/utils": "^5.16.6 || ^6.0.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
+ "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^1.0.3",
+ "is-glob": "^4.0.3",
+ "micromatch": "^4.0.5",
+ "node-addon-api": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.0",
+ "@parcel/watcher-darwin-arm64": "2.5.0",
+ "@parcel/watcher-darwin-x64": "2.5.0",
+ "@parcel/watcher-freebsd-x64": "2.5.0",
+ "@parcel/watcher-linux-arm-glibc": "2.5.0",
+ "@parcel/watcher-linux-arm-musl": "2.5.0",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.0",
+ "@parcel/watcher-linux-arm64-musl": "2.5.0",
+ "@parcel/watcher-linux-x64-glibc": "2.5.0",
+ "@parcel/watcher-linux-x64-musl": "2.5.0",
+ "@parcel/watcher-win32-arm64": "2.5.0",
+ "@parcel/watcher-win32-ia32": "2.5.0",
+ "@parcel/watcher-win32-x64": "2.5.0"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz",
+ "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz",
+ "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz",
+ "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz",
+ "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz",
+ "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz",
+ "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz",
+ "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz",
+ "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz",
+ "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz",
+ "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz",
+ "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz",
+ "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz",
+ "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@pdf-lib/standard-fonts": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
+ "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "^1.0.6"
+ }
+ },
+ "node_modules/@pdf-lib/upng": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
+ "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "^1.0.10"
+ }
+ },
+ "node_modules/@pdfme/common": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@pdfme/common/-/common-1.2.6.tgz",
+ "integrity": "sha512-ROmQ/iMUdmFS2QXD/kKDdcU5T6H3azDs2b1hE/OXs8531BPZ9ABbu9+1NRZQoNK4U/zP2F+Osb/B8ckr9lAmGg==",
+ "peer": true,
+ "dependencies": {
+ "buffer": "^6.0.3",
+ "fontkit": "^2.0.2",
+ "zod": "^3.20.2"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@pdfme/generator": {
+ "version": "5.1.7",
+ "resolved": "https://registry.npmjs.org/@pdfme/generator/-/generator-5.1.7.tgz",
+ "integrity": "sha512-dVbVWzuiTL6c0HcGmKiuTp77ClZIQOneKXc6ysYpY518kbR6YlxPstdqigFJfFL02P09kIStLrugrS18B3aVuA==",
+ "dependencies": {
+ "@pdfme/pdf-lib": "^1.18.3",
+ "atob": "^2.1.2",
+ "fontkit": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@pdfme/common": "latest",
+ "@pdfme/schemas": "latest"
+ }
+ },
+ "node_modules/@pdfme/pdf-lib": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/@pdfme/pdf-lib/-/pdf-lib-1.18.3.tgz",
+ "integrity": "sha512-Zy4lRrxhDo7pbexNCvn+nzO5gLRc1XOXQyFOHC1FJoaHzrZ3GDd4OZwR5AaGIlCwV7ocPP2+iiLFA5wQcx3s9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@pdf-lib/standard-fonts": "^1.0.0",
+ "@pdf-lib/upng": "^1.0.1",
+ "color": "^4.2.3",
+ "node-html-better-parser": "^1.4.0",
+ "pako": "^1.0.11"
+ }
+ },
+ "node_modules/@pdfme/schemas": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/@pdfme/schemas/-/schemas-5.1.6.tgz",
+ "integrity": "sha512-QAsWZEfe8tB3xmaj35xsGf5kSjFzMJc4F90kBbudEq8/Pkcx+0yznD7+zQK0OEj7uLD3frmXyR1OVX6dBWHBpQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@pdfme/pdf-lib": "^1.18.3",
+ "air-datepicker": "^3.5.3",
+ "bwip-js": "^4.1.1",
+ "date-fns": "^4.1.0",
+ "fast-xml-parser": "^4.3.2",
+ "fontkit": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@pdfme/common": "latest"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.21",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz",
+ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
+ "dev": true
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.5.tgz",
+ "integrity": "sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/ssr/node_modules/@swc/helpers": {
+ "version": "0.5.12",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz",
+ "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@react-spring/animated": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz",
+ "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==",
+ "dependencies": {
+ "@react-spring/shared": "~9.7.5",
+ "@react-spring/types": "~9.7.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/core": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz",
+ "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==",
+ "dependencies": {
+ "@react-spring/animated": "~9.7.5",
+ "@react-spring/shared": "~9.7.5",
+ "@react-spring/types": "~9.7.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-spring/donate"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/rafz": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz",
+ "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw=="
+ },
+ "node_modules/@react-spring/shared": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz",
+ "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==",
+ "dependencies": {
+ "@react-spring/rafz": "~9.7.5",
+ "@react-spring/types": "~9.7.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/types": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz",
+ "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g=="
+ },
+ "node_modules/@react-spring/web": {
+ "version": "9.7.5",
+ "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz",
+ "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==",
+ "dependencies": {
+ "@react-spring/animated": "~9.7.5",
+ "@react-spring/core": "~9.7.5",
+ "@react-spring/shared": "~9.7.5",
+ "@react-spring/types": "~9.7.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz",
+ "integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
+ "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@restart/hooks": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
+ "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@restart/ui": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.8.0.tgz",
+ "integrity": "sha512-xJEOXUOTmT4FngTmhdjKFRrVVF0hwCLNPdatLCHkyS4dkiSK12cEu1Y0fjxktjJrdst9jJIc5J6ihMJCoWEN/g==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "@popperjs/core": "^2.11.6",
+ "@react-aria/ssr": "^3.5.0",
+ "@restart/hooks": "^0.4.9",
+ "@types/warning": "^3.0.0",
+ "dequal": "^2.0.3",
+ "dom-helpers": "^5.2.0",
+ "uncontrollable": "^8.0.1",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.14.0",
+ "react-dom": ">=16.14.0"
+ }
+ },
+ "node_modules/@restart/ui/node_modules/uncontrollable": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz",
+ "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==",
+ "peerDependencies": {
+ "react": ">=16.14.0"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
+ "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
+ "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
+ "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
+ "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
+ "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
+ "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
+ "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
+ "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
+ "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
+ "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
+ "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
+ "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
+ "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
+ "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
+ "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true
+ },
+ "node_modules/@shikijs/core": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.17.6.tgz",
+ "integrity": "sha512-9ztslig6/YmCg/XwESAXbKjAjOhaq6HVced9NY6qcbDz1X5g/S90Wco2vMjBNX/6V71ASkzri76JewSGPa7kiQ==",
+ "dependencies": {
+ "@shikijs/engine-javascript": "1.17.6",
+ "@shikijs/engine-oniguruma": "1.17.6",
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.2"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.17.6.tgz",
+ "integrity": "sha512-5EEZj8tVcierNxm4V0UMS2PVoflb0UJPalWWV8l9rRg+oOfnr5VivqBJbkyq5grltVPvByIXvVbY8GSM/356jQ==",
+ "dependencies": {
+ "@shikijs/types": "1.17.6",
+ "oniguruma-to-js": "0.4.3"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.17.6.tgz",
+ "integrity": "sha512-NLfWDMXFYe0nDHFbEoyZdz89aIIey3bTfF3zLYSUNTXks5s4uinZVmuPOFf1HfTeGqIn8uErJSBc3VnpJO7Alw==",
+ "dependencies": {
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.17.6.tgz",
+ "integrity": "sha512-ndTFa2TJi2w51ddKQDn3Jy8f6K4E5Q2x3dA3Hmsd3+YmxDQ10UWHjcw7VbVbKzv3VcUvYPLy+z9neqytSzUMUg==",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz",
+ "integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg=="
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "1.8.6",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
+ "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz",
+ "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==",
+ "dev": true,
+ "dependencies": {
+ "@sinonjs/commons": "^1.7.0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz",
+ "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
+ "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz",
+ "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz",
+ "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz",
+ "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz",
+ "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz",
+ "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-preset": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz",
+ "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==",
+ "dev": true,
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
+ "@svgr/babel-plugin-remove-jsx-attribute": "*",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "*",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1",
+ "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1",
+ "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1",
+ "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1",
+ "@svgr/babel-plugin-transform-svg-component": "^6.5.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/core": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz",
+ "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/plugin-jsx": "^6.5.1",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/core/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz",
+ "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.0",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/plugin-jsx": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz",
+ "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/hast-util-to-babel-ast": "^6.5.1",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "^6.0.0"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz",
+ "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+ "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+ "dev": true,
+ "dependencies": {
+ "defer-to-connect": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@testing-library/dom": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
+ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.3.0",
+ "chalk": "^4.1.0",
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "pretty-format": "^27.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@testing-library/jest-dom": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz",
+ "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==",
+ "dev": true,
+ "dependencies": {
+ "@adobe/css-tools": "^4.4.0",
+ "aria-query": "^5.0.0",
+ "chalk": "^3.0.0",
+ "css.escape": "^1.5.1",
+ "dom-accessibility-api": "^0.6.3",
+ "lodash": "^4.17.21",
+ "redent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
+ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@testing-library/react": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz",
+ "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": "^10.0.0",
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@testing-library/user-event": {
+ "version": "12.8.3",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz",
+ "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": ">=7.21.4"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/aria-query": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
+ "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
+ "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+ "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.1",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz",
+ "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==",
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+ "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
+ "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+ "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz",
+ "integrity": "sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+ },
+ "node_modules/@types/graceful-fs": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
+ "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/history": {
+ "version": "4.7.11",
+ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
+ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
+ "dev": true
+ },
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+ "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
+ "node_modules/@types/inquirer": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz",
+ "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==",
+ "dev": true,
+ "dependencies": {
+ "@types/through": "*",
+ "rxjs": "^7.2.0"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+ "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+ "dev": true
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+ "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/jest": {
+ "version": "26.0.24",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz",
+ "integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==",
+ "dev": true,
+ "dependencies": {
+ "jest-diff": "^26.0.0",
+ "pretty-format": "^26.0.0"
+ }
+ },
+ "node_modules/@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
+ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "22.5.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
+ "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
+ "devOptional": true,
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
+ "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/@types/node-fetch/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
+ },
+ "node_modules/@types/prettier": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
+ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==",
+ "dev": true
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.13",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
+ "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA=="
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
+ "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-beautiful-dnd": {
+ "version": "13.1.8",
+ "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz",
+ "integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-bootstrap": {
+ "version": "0.32.37",
+ "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-0.32.37.tgz",
+ "integrity": "sha512-CVHj++uxsj1pRnM3RQ/NAXcWj+JwJZ3MqQ28sS1OQUD1sI2gRlbeAjRT+ak2nuwL+CY+gtnIsMaIDq0RNfN0PA==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-chartjs-2": {
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@types/react-chartjs-2/-/react-chartjs-2-2.5.7.tgz",
+ "integrity": "sha512-waqYqiNULIVUqaKO7MGUpFmWrVtH7gVPOzqwV4y4zgUyu/JiDwC005PpveO442HKnby9kLgp3t1SB2sld+ACLw==",
+ "deprecated": "This is a stub types definition for react-chartjs-2 (https://github.com/gor181/react-chartjs-2). react-chartjs-2 provides its own type definitions, so you don't need @types/react-chartjs-2 installed!",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "react-chartjs-2": "*"
+ }
+ },
+ "node_modules/@types/react-datepicker": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-7.0.0.tgz",
+ "integrity": "sha512-4tWwOUq589tozyQPBVEqGNng5DaZkomx5IVNuur868yYdgjH6RaL373/HKiVt1IDoNNXYiTGspm1F7kjrarM8Q==",
+ "deprecated": "This is a stub types definition. react-datepicker provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "dependencies": {
+ "react-datepicker": "*"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-google-recaptcha": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz",
+ "integrity": "sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-redux": {
+ "version": "7.1.34",
+ "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz",
+ "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
+ "dependencies": {
+ "@types/hoist-non-react-statics": "^3.3.0",
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0",
+ "redux": "^4.0.0"
+ }
+ },
+ "node_modules/@types/react-redux/node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/@types/react-router": {
+ "version": "5.1.20",
+ "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
+ "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-router-dom": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
+ "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
+ "dev": true,
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router": "*"
+ }
+ },
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.11",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz",
+ "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/sanitize-html": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz",
+ "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==",
+ "dev": true,
+ "dependencies": {
+ "htmlparser2": "^8.0.0"
+ }
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "dev": true
+ },
+ "node_modules/@types/through": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz",
+ "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
+ },
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
+ "node_modules/@types/warning": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
+ "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
+ },
+ "node_modules/@types/yargs": {
+ "version": "16.0.5",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz",
+ "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.0",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
+ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz",
+ "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.11.0",
+ "@typescript-eslint/type-utils": "8.11.0",
+ "@typescript-eslint/utils": "8.11.0",
+ "@typescript-eslint/visitor-keys": "8.11.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz",
+ "integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.5.0",
+ "@typescript-eslint/types": "8.5.0",
+ "@typescript-eslint/typescript-estree": "8.5.0",
+ "@typescript-eslint/visitor-keys": "8.5.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz",
+ "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.5.0",
+ "@typescript-eslint/visitor-keys": "8.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz",
+ "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz",
+ "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.5.0",
+ "@typescript-eslint/visitor-keys": "8.5.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz",
+ "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.5.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz",
+ "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.11.0",
+ "@typescript-eslint/visitor-keys": "8.11.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
+ "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.11.0",
+ "@typescript-eslint/utils": "8.11.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz",
+ "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz",
+ "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.11.0",
+ "@typescript-eslint/visitor-keys": "8.11.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz",
+ "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.11.0",
+ "@typescript-eslint/types": "8.11.0",
+ "@typescript-eslint/typescript-estree": "8.11.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz",
+ "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "8.11.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "license": "ISC"
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz",
+ "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==",
+ "dependencies": {
+ "@babel/core": "^7.25.2",
+ "@babel/plugin-transform-react-jsx-self": "^7.24.7",
+ "@babel/plugin-transform-react-jsx-source": "^7.24.7",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react/node_modules/react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@wry/caches": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
+ "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/context": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
+ "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/equality": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
+ "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/trie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
+ "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "devOptional": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
+ "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^7.1.1",
+ "acorn-walk": "^7.1.1"
+ }
+ },
+ "node_modules/acorn-globals/node_modules/acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/air-datepicker": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.5.3.tgz",
+ "integrity": "sha512-Elf9gLhv/jidN1+TfeRJYMQRUfYx5apXw2dY5DuAMPRnNtQ4Iw9fTTJK772osmXSUB9xQ2Y8Q1Pt6pgBOQLPQw==",
+ "license": "MIT"
+ },
+ "node_modules/ajv": {
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
+ "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-red": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
+ "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==",
+ "dependencies": {
+ "ansi-wrap": "0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
+ "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "is-shared-array-buffer": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/autolinker": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz",
+ "integrity": "sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ==",
+ "dependencies": {
+ "gulp-header": "^1.7.1"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/transform": "^29.7.0",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^29.6.3",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/@jest/transform": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "babel-plugin-istanbul": "^6.1.1",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^2.0.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.4",
+ "slash": "^3.0.0",
+ "write-file-atomic": "^4.0.2"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
+ "node_modules/babel-jest/node_modules/@types/yargs": {
+ "version": "17.0.33",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
+ "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/babel-jest/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/babel-jest/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/babel-jest/node_modules/jest-haste-map": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/graceful-fs": "^4.1.3",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/babel-jest/node_modules/jest-regex-util": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-jest/node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/babel-jest/node_modules/write-file-atomic": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.7"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.1.14",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz",
+ "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.22.6",
+ "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
+ "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "core-js-compat": "^3.38.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz",
+ "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-import-meta": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz",
+ "integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==",
+ "dependencies": {
+ "@babel/template": "^7.4.4",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.10.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+ "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.8.3",
+ "@babel/plugin-syntax-import-meta": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-top-level-await": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/bootstrap": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "peerDependencies": {
+ "@popperjs/core": "^2.11.8"
+ }
+ },
+ "node_modules/boxen": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
+ "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-align": "^3.0.0",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.1.0",
+ "cli-boxes": "^2.2.1",
+ "string-width": "^4.2.2",
+ "type-fest": "^0.20.2",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "devOptional": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brotli": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
+ "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
+ "dependencies": {
+ "base64-js": "^1.1.2"
+ }
+ },
+ "node_modules/browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/bwip-js": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.5.1.tgz",
+ "integrity": "sha512-83yQCKiIftz5YonnsTh6wIkFoHHWl+B/XaGWD1UdRw7aB6XP9JtyYP9n8sRy3m5rzL+Ch/RUPnu28UW0RrPZUA==",
+ "license": "MIT",
+ "bin": {
+ "bwip-js": "bin/bwip-js.js"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+ "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+ "dev": true,
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^3.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^4.1.0",
+ "responselike": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cacheable-request/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cacheable-request/node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cacheable-request/node_modules/normalize-url": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+ "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001660",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
+ "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
+ },
+ "node_modules/chart.js": {
+ "version": "4.4.6",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz",
+ "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz",
+ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
+ "dev": true
+ },
+ "node_modules/classnames": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
+ "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ },
+ "node_modules/cli-boxes": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
+ "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-response": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+ "dev": true,
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/coffee-script": {
+ "version": "1.12.7",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
+ "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
+ "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)",
+ "bin": {
+ "cake": "bin/cake",
+ "coffee": "bin/coffee"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
+ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+ "dev": true
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "engines": [
+ "node >= 0.8"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/concat-stream/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/concat-stream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/concat-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/concat-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/concat-with-sourcemaps": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
+ "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
+ "dependencies": {
+ "source-map": "^0.6.1"
+ }
+ },
+ "node_modules/concat-with-sourcemaps/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/configstore": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+ "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+ "dev": true,
+ "dependencies": {
+ "dot-prop": "^5.2.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^3.0.0",
+ "unique-string": "^2.0.0",
+ "write-file-atomic": "^3.0.0",
+ "xdg-basedir": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/connect/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/connect/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.38.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
+ "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-random-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/css-box-model": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+ "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+ "dependencies": {
+ "tiny-invariant": "^1.0.6"
+ }
+ },
+ "node_modules/css.escape": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
+ "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
+ "dev": true
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "node_modules/customize-cra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/customize-cra/-/customize-cra-1.0.0.tgz",
+ "integrity": "sha512-DbtaLuy59224U+xCiukkxSq8clq++MOtJ1Et7LED1fLszWe88EoblEYFBJ895sB1mC6B4uu3xPT/IjClELhMbA==",
+ "dependencies": {
+ "lodash.flow": "^3.5.0"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/data-urls": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+ "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.3",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
+ },
+ "node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+ "dev": true
+ },
+ "node_modules/decompress-response": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+ "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
+ "dev": true,
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/dedent": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+ "dev": true
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defaults/node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/defer-to-connect": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delaunator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+ "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+ "optional": true,
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dfa": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
+ "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="
+ },
+ "node_modules/diacritics-map": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz",
+ "integrity": "sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
+ "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-accessibility-api": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
+ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domexception": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
+ "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
+ "dev": true,
+ "dependencies": {
+ "webidl-conversions": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/domexception/node_modules/webidl-conversions": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
+ "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/duplexer3": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
+ "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==",
+ "dev": true
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.19",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz",
+ "integrity": "sha512-kpLJJi3zxTR1U828P+LIUDZ5ohixyo68/IcYOHLqnbTPr/wdgn4i1ECvmALN9E16JPA6cvCG5UG79gVwVdEK5w=="
+ },
+ "node_modules/emittery": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
+ "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "data-view-buffer": "^1.0.1",
+ "data-view-byte-length": "^1.0.1",
+ "data-view-byte-offset": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.1",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.2",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.9",
+ "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.6",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz",
+ "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "iterator.prototype": "^1.1.2",
+ "safe-array-concat": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-goat": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
+ "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
+ "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz",
+ "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==",
+ "dev": true,
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.8",
+ "array.prototype.findlastindex": "^1.2.5",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.9.0",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.15.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.0",
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jest": {
+ "version": "28.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz",
+ "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "engines": {
+ "node": "^16.10.0 || ^18.12.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0",
+ "jest": "*"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ },
+ "jest": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
+ "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz",
+ "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.2",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.0.19",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.8",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.0",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.11",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-tsdoc": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz",
+ "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==",
+ "dev": true,
+ "dependencies": {
+ "@microsoft/tsdoc": "0.15.0",
+ "@microsoft/tsdoc-config": "0.17.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+ "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expand-range": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
+ "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==",
+ "dependencies": {
+ "fill-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/fill-range": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
+ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
+ "dependencies": {
+ "is-number": "^2.1.0",
+ "isobject": "^2.0.0",
+ "randomatic": "^3.0.0",
+ "repeat-element": "^1.1.2",
+ "repeat-string": "^1.5.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/is-number": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+ "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/expand-range/node_modules/isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-range/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expect": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz",
+ "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "jest-matcher-utils": "^27.5.1",
+ "jest-message-util": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dependencies": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/external-editor/node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz",
+ "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/naturalintelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^1.0.5"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fb-watchman": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
+ "node_modules/figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/figures/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "devOptional": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/find-node-modules": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz",
+ "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==",
+ "dev": true,
+ "dependencies": {
+ "findup-sync": "^4.0.0",
+ "merge": "^2.1.1"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/findup-sync": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz",
+ "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==",
+ "dev": true,
+ "dependencies": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^4.0.2",
+ "resolve-dir": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/flag-icons": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.2.3.tgz",
+ "integrity": "sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg=="
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fontkit": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.2.tgz",
+ "integrity": "sha512-jc4k5Yr8iov8QfS6u8w2CnHWVmbOGtdBtOXMze5Y+QD966Rx6PEVWXSEGwXlsDlKtu1G12cJjcsybnqhSk/+LA==",
+ "dependencies": {
+ "@swc/helpers": "^0.4.2",
+ "brotli": "^1.3.2",
+ "clone": "^2.1.2",
+ "dfa": "^1.2.0",
+ "fast-deep-equal": "^3.1.3",
+ "restructure": "^3.0.0",
+ "tiny-inflate": "^1.0.3",
+ "unicode-properties": "^1.4.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generic-names": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz",
+ "integrity": "sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^3.2.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
+ "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.7.5",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz",
+ "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/global-dirs": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+ "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+ "dev": true,
+ "dependencies": {
+ "ini": "2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/global-dirs/node_modules/ini": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+ "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+ "dev": true,
+ "dependencies": {
+ "@sindresorhus/is": "^0.14.0",
+ "@szmarczak/http-timer": "^1.1.2",
+ "cacheable-request": "^6.0.0",
+ "decompress-response": "^3.3.0",
+ "duplexer3": "^0.1.4",
+ "get-stream": "^4.1.0",
+ "lowercase-keys": "^1.0.1",
+ "mimic-response": "^1.0.1",
+ "p-cancelable": "^1.0.0",
+ "to-readable-stream": "^1.0.0",
+ "url-parse-lax": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/got/node_modules/get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/graphql": {
+ "version": "16.9.0",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz",
+ "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==",
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+ },
+ "node_modules/graphql-tag": {
+ "version": "2.12.6",
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+ "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+ }
+ },
+ "node_modules/graphql-ws": {
+ "version": "5.16.0",
+ "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz",
+ "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": ">=0.11 <=16"
+ }
+ },
+ "node_modules/gray-matter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz",
+ "integrity": "sha512-vbmvP1Fe/fxuT2QuLVcqb2BfK7upGhhbLIt9/owWEvPYrZZEkelLcq2HqzxosV+PQ67dUFLaAeNpH7C4hhICAA==",
+ "dependencies": {
+ "ansi-red": "^0.1.1",
+ "coffee-script": "^1.12.4",
+ "extend-shallow": "^2.0.1",
+ "js-yaml": "^3.8.1",
+ "toml": "^2.3.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gray-matter/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gray-matter/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gulp-header": {
+ "version": "1.8.12",
+ "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz",
+ "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==",
+ "deprecated": "Removed event-stream from gulp-header",
+ "dependencies": {
+ "concat-with-sourcemaps": "*",
+ "lodash.template": "^4.4.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "node_modules/harmony-reflect": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
+ "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
+ "dev": true
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-yarn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
+ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.2.tgz",
+ "integrity": "sha512-RP5wNpj5nm1Z8cloDv4Sl4RS8jH5HYa0v93YB6Wb4poEzgMo/dAAL0KcT4974dCjcNG5pkLqTImeFHHCwwfY3g==",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/history": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
+ "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.7.6"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "dependencies": {
+ "parse-passwd": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
+ "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-encoding": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/html-entities": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
+ "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ]
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "node_modules/html-parse-stringify": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
+ "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "dependencies": {
+ "void-elements": "3.1.0"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+ "dev": true
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.1.6",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz",
+ "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==",
+ "dev": true,
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/i18next": {
+ "version": "23.15.1",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz",
+ "integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-browser-languagedetector": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
+ "integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-http-backend": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.6.1.tgz",
+ "integrity": "sha512-rCilMAnlEQNeKOZY1+x8wLM5IpYOj10guGvEpeC59tNjj6MMreLIjIW8D1RclhD3ifLwn6d/Y9HEM1RUE6DSog==",
+ "dependencies": {
+ "cross-fetch": "4.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/identity-obj-proxy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
+ "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
+ "dev": true,
+ "dependencies": {
+ "harmony-reflect": "^1.4.6"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
+ "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
+ "devOptional": true
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-lazy": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+ "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/inquirer": {
+ "version": "8.2.6",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
+ "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.5.5",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6",
+ "wrap-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/inquirer/node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inquirer/node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inquirer/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "node_modules/is-async-function": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
+ "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "dependencies": {
+ "ci-info": "^2.0.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-ci/node_modules/ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dev": true,
+ "dependencies": {
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz",
+ "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "devOptional": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-installed-globally": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+ "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+ "dev": true,
+ "dependencies": {
+ "global-dirs": "^3.0.0",
+ "is-path-inside": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-npm": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz",
+ "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-yarn-global": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
+ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
+ "dev": true
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+ "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz",
+ "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "get-intrinsic": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "reflect.getprototypeof": "^1.0.4",
+ "set-function-name": "^2.0.1"
+ }
+ },
+ "node_modules/jest": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
+ "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^27.5.1",
+ "import-local": "^3.0.2",
+ "jest-cli": "^27.5.1"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-changed-files": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz",
+ "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "execa": "^5.0.0",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-circus": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz",
+ "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^0.7.0",
+ "expect": "^27.5.1",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.5.1",
+ "jest-matcher-utils": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-runtime": "^27.5.1",
+ "jest-snapshot": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "pretty-format": "^27.5.1",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-circus/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-circus/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-circus/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-cli": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz",
+ "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "import-local": "^3.0.2",
+ "jest-config": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-validate": "^27.5.1",
+ "prompts": "^2.0.1",
+ "yargs": "^16.2.0"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-cli/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-config": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz",
+ "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.8.0",
+ "@jest/test-sequencer": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "babel-jest": "^27.5.1",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.2.9",
+ "jest-circus": "^27.5.1",
+ "jest-environment-jsdom": "^27.5.1",
+ "jest-environment-node": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "jest-jasmine2": "^27.5.1",
+ "jest-regex-util": "^27.5.1",
+ "jest-resolve": "^27.5.1",
+ "jest-runner": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-validate": "^27.5.1",
+ "micromatch": "^4.0.4",
+ "parse-json": "^5.2.0",
+ "pretty-format": "^27.5.1",
+ "slash": "^3.0.0",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-config/node_modules/babel-jest": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz",
+ "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^27.5.1",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/jest-config/node_modules/babel-plugin-jest-hoist": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz",
+ "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.0.0",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-config/node_modules/babel-preset-jest": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz",
+ "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^27.5.1",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/jest-config/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-config/node_modules/jest-environment-node": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz",
+ "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/fake-timers": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "jest-mock": "^27.5.1",
+ "jest-util": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-config/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-config/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-diff": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
+ "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^26.6.2",
+ "jest-get-type": "^26.3.0",
+ "pretty-format": "^26.6.2"
+ },
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
+ "node_modules/jest-diff/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-diff/node_modules/jest-get-type": {
+ "version": "26.3.0",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
+ "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
+ "node_modules/jest-docblock": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz",
+ "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==",
+ "dev": true,
+ "dependencies": {
+ "detect-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-docblock/node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-each": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz",
+ "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-each/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-each/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-each/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-environment-jsdom": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz",
+ "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/fake-timers": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "jest-mock": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jsdom": "^16.6.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz",
+ "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-haste-map": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz",
+ "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@types/graceful-fs": "^4.1.2",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-regex-util": "^27.5.1",
+ "jest-serializer": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-worker": "^27.5.1",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.7"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/jest-jasmine2": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz",
+ "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/source-map": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "expect": "^27.5.1",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.5.1",
+ "jest-matcher-utils": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-runtime": "^27.5.1",
+ "jest-snapshot": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "pretty-format": "^27.5.1",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-jasmine2/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-jasmine2/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-jasmine2/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-jasmine2/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-leak-detector": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz",
+ "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==",
+ "dev": true,
+ "dependencies": {
+ "jest-get-type": "^27.5.1",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-leak-detector/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-leak-detector/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-leak-detector/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-localstorage-mock": {
+ "version": "2.4.26",
+ "resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.4.26.tgz",
+ "integrity": "sha512-owAJrYnjulVlMIXOYQIPRCCn3MmqI3GzgfZCXdD3/pmwrIvFMXcKVWZ+aMc44IzaASapg0Z4SEFxR+v5qxDA2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.16.0"
+ }
+ },
+ "node_modules/jest-location-mock": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jest-location-mock/-/jest-location-mock-2.0.0.tgz",
+ "integrity": "sha512-loakfclgY/y65/2i4s0fcdlZY3hRPfwNnmzRsGFQYQryiaow2DEIGTLXIPI8cAO1Is36xsVLVkIzgvhQ+FXHdw==",
+ "dev": true,
+ "dependencies": {
+ "@jedmao/location": "^3.0.0",
+ "jest-diff": "^29.6.4"
+ },
+ "engines": {
+ "node": "^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
+ "node_modules/jest-location-mock/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/jest-diff": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/jest-get-type": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-location-mock/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz",
+ "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/diff-sequences": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
+ "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/jest-diff": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz",
+ "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-matcher-utils/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-message-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz",
+ "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^27.5.1",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^27.5.1",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-message-util/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-message-util/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-message-util/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-mock": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz",
+ "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@types/node": "*"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-preview": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/jest-preview/-/jest-preview-0.3.1.tgz",
+ "integrity": "sha512-gRR4shnXFSh8tdNaIncJC98d1zXD7w7LA52HQC0bu0DsPb+FXVEg+NQh9GTbO+n6/SCgcZNQAVt4MeCfsIkBPA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@svgr/core": "^6.2.1",
+ "camelcase": "^6.3.0",
+ "chalk": "^4.1.2",
+ "chokidar": "^3.5.3",
+ "commander": "^9.2.0",
+ "connect": "^3.7.0",
+ "find-node-modules": "^2.1.3",
+ "open": "^8.4.0",
+ "postcss-import": "^14.1.0",
+ "postcss-load-config": "^4.0.1",
+ "sirv": "^2.0.2",
+ "slash": "^3.0.0",
+ "string-hash": "^1.1.3",
+ "update-notifier": "^5.1.0",
+ "ws": "^8.5.0"
+ },
+ "bin": {
+ "jest-preview": "cli/index.js"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/jest-preview"
+ }
+ },
+ "node_modules/jest-preview/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-preview/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-regex-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz",
+ "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-resolve": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz",
+ "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^27.5.1",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^27.5.1",
+ "jest-validate": "^27.5.1",
+ "resolve": "^1.20.0",
+ "resolve.exports": "^1.1.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-resolve-dependencies": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz",
+ "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "jest-regex-util": "^27.5.1",
+ "jest-snapshot": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-resolve/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-runner": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz",
+ "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.5.1",
+ "@jest/environment": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "graceful-fs": "^4.2.9",
+ "jest-docblock": "^27.5.1",
+ "jest-environment-jsdom": "^27.5.1",
+ "jest-environment-node": "^27.5.1",
+ "jest-haste-map": "^27.5.1",
+ "jest-leak-detector": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-resolve": "^27.5.1",
+ "jest-runtime": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "jest-worker": "^27.5.1",
+ "source-map-support": "^0.5.6",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-runner/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-runner/node_modules/jest-environment-node": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz",
+ "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/fake-timers": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "jest-mock": "^27.5.1",
+ "jest-util": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-runtime": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz",
+ "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.5.1",
+ "@jest/fake-timers": "^27.5.1",
+ "@jest/globals": "^27.5.1",
+ "@jest/source-map": "^27.5.1",
+ "@jest/test-result": "^27.5.1",
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "execa": "^5.0.0",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-mock": "^27.5.1",
+ "jest-regex-util": "^27.5.1",
+ "jest-resolve": "^27.5.1",
+ "jest-snapshot": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-runtime/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-serializer": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz",
+ "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "graceful-fs": "^4.2.9"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz",
+ "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.7.2",
+ "@babel/generator": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/traverse": "^7.7.2",
+ "@babel/types": "^7.0.0",
+ "@jest/transform": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/babel__traverse": "^7.0.4",
+ "@types/prettier": "^2.1.5",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^27.5.1",
+ "graceful-fs": "^4.2.9",
+ "jest-diff": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "jest-haste-map": "^27.5.1",
+ "jest-matcher-utils": "^27.5.1",
+ "jest-message-util": "^27.5.1",
+ "jest-util": "^27.5.1",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^27.5.1",
+ "semver": "^7.3.2"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/diff-sequences": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
+ "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/jest-diff": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz",
+ "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^27.5.1",
+ "jest-get-type": "^27.5.1",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
+ "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-util/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-validate": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz",
+ "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.5.1",
+ "leven": "^3.1.0",
+ "pretty-format": "^27.5.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-validate/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-validate/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-validate/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/jest-watcher": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz",
+ "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^27.5.1",
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "jest-util": "^27.5.1",
+ "string-length": "^4.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-watcher/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/jju": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
+ "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
+ "dev": true
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "16.7.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
+ "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.5",
+ "acorn": "^8.2.4",
+ "acorn-globals": "^6.0.0",
+ "cssom": "^0.4.4",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^2.0.0",
+ "decimal.js": "^10.2.1",
+ "domexception": "^2.0.1",
+ "escodegen": "^2.0.0",
+ "form-data": "^3.0.0",
+ "html-encoding-sniffer": "^2.0.1",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.0",
+ "parse5": "6.0.1",
+ "saxes": "^5.0.1",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.0.0",
+ "w3c-hr-time": "^1.0.2",
+ "w3c-xmlserializer": "^2.0.0",
+ "webidl-conversions": "^6.1.0",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.5.0",
+ "ws": "^7.4.6",
+ "xml-name-validator": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsdom/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/jsdom/node_modules/ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
+ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+ "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+ "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.0"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/latest-version": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
+ "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
+ "dev": true,
+ "dependencies": {
+ "package-json": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lazy-cache": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
+ "integrity": "sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==",
+ "dependencies": {
+ "set-getter": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/lint-staged": {
+ "version": "15.2.8",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.8.tgz",
+ "integrity": "sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "~5.3.0",
+ "commander": "~12.1.0",
+ "debug": "~4.3.6",
+ "execa": "~8.0.1",
+ "lilconfig": "~3.1.2",
+ "listr2": "~8.2.4",
+ "micromatch": "~4.0.7",
+ "pidtree": "~0.6.0",
+ "string-argv": "~0.3.2",
+ "yaml": "~2.5.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/lint-staged/node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/lint-staged/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/lint-staged/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/lilconfig": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
+ "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lint-staged/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/npm-run-path": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
+ "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/lint-staged/node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lint-staged/node_modules/yaml": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
+ "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/list-item": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz",
+ "integrity": "sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw==",
+ "dependencies": {
+ "expand-range": "^1.8.1",
+ "extend-shallow": "^2.0.1",
+ "is-number": "^2.1.0",
+ "repeat-string": "^1.5.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/list-item/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/list-item/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/list-item/node_modules/is-number": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+ "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/list-item/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz",
+ "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==",
+ "dev": true,
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "node_modules/listr2/node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "node_modules/listr2/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/listr2/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/loader-utils": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
+ "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA=="
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "node_modules/lodash.flow": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz",
+ "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw=="
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/lodash.template": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
+ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "dependencies": {
+ "lodash._reinterpolate": "^3.0.0",
+ "lodash.templatesettings": "^4.0.0"
+ }
+ },
+ "node_modules/lodash.templatesettings": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
+ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
+ "dependencies": {
+ "lodash._reinterpolate": "^3.0.0"
+ }
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
+ },
+ "node_modules/lz-string": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "lz-string": "bin/bin.js"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
+ "dependencies": {
+ "tmpl": "1.0.5"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/markdown-link": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz",
+ "integrity": "sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/markdown-toc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz",
+ "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==",
+ "dependencies": {
+ "concat-stream": "^1.5.2",
+ "diacritics-map": "^0.1.0",
+ "gray-matter": "^2.1.0",
+ "lazy-cache": "^2.0.2",
+ "list-item": "^1.1.1",
+ "markdown-link": "^0.1.1",
+ "minimist": "^1.2.0",
+ "mixin-deep": "^1.1.3",
+ "object.pick": "^1.2.0",
+ "remarkable": "^1.7.1",
+ "repeat-string": "^1.6.1",
+ "strip-color": "^0.1.0"
+ },
+ "bin": {
+ "markdown-toc": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/math-random": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
+ "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
+ "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
+ },
+ "node_modules/memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
+ },
+ "node_modules/merge": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz",
+ "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==",
+ "dev": true
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
+ "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
+ "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
+ "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "devOptional": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
+ "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "optional": true
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-fetch/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/node-fetch/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/node-fetch/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/node-html-better-parser": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.4.4.tgz",
+ "integrity": "sha512-uvlqL1uMU7m/aIY9WsGM0jDW7gVFIuFSWS6f2rlJeL7K1ZzKnA3B8cNbUGw9ywwYm9W7W2ooi0iQ7aI29aQmPw==",
+ "license": "MIT",
+ "dependencies": {
+ "html-entities": "^2.3.2"
+ }
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
+ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
+ "dev": true
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
+ "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
+ "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/oniguruma-to-js": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz",
+ "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==",
+ "dependencies": {
+ "regex": "^4.3.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/open": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+ "dev": true,
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optimism": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz",
+ "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==",
+ "dependencies": {
+ "@wry/caches": "^1.0.0",
+ "@wry/context": "^0.7.0",
+ "@wry/trie": "^0.4.3",
+ "tslib": "^2.3.0"
+ }
+ },
+ "node_modules/optimism/node_modules/@wry/trie": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz",
+ "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+ "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-json": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
+ "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
+ "dev": true,
+ "dependencies": {
+ "got": "^9.6.0",
+ "registry-auth-token": "^4.0.0",
+ "registry-url": "^5.0.0",
+ "semver": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-srcset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
+ "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.45",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
+ "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.1",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
+ "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
+ "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
+ "dev": true,
+ "dependencies": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^2.1.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/yaml": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
+ "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/postcss-modules": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-6.0.0.tgz",
+ "integrity": "sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==",
+ "dev": true,
+ "dependencies": {
+ "generic-names": "^4.0.0",
+ "icss-utils": "^5.1.0",
+ "lodash.camelcase": "^4.3.0",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "string-hash": "^1.1.1"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+ "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz",
+ "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz",
+ "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prepend-http": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+ "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
+ "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^26.6.2",
+ "ansi-regex": "^5.0.0",
+ "ansi-styles": "^4.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/pretty-format/node_modules/@jest/types": {
+ "version": "26.6.2",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+ "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^15.0.0",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 10.14.2"
+ }
+ },
+ "node_modules/pretty-format/node_modules/@types/yargs": {
+ "version": "15.0.15",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz",
+ "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/pretty-format/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/pretty-format/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types-extra": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
+ "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
+ "dependencies": {
+ "react-is": "^16.3.2",
+ "warning": "^4.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=0.14.0"
+ }
+ },
+ "node_modules/prop-types-extra/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/property-information": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+ "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pupa": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz",
+ "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==",
+ "dev": true,
+ "dependencies": {
+ "escape-goat": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/raf-schd": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
+ },
+ "node_modules/randomatic": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
+ "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
+ "dependencies": {
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "math-random": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/randomatic/node_modules/is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-async-script": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
+ "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
+ "dependencies": {
+ "hoist-non-react-statics": "^3.3.0",
+ "prop-types": "^15.5.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.4.1"
+ }
+ },
+ "node_modules/react-beautiful-dnd": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
+ "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2",
+ "css-box-model": "^1.2.0",
+ "memoize-one": "^5.1.1",
+ "raf-schd": "^4.0.2",
+ "react-redux": "^7.2.0",
+ "redux": "^4.0.4",
+ "use-memo-one": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.5 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-beautiful-dnd/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
+ "node_modules/react-beautiful-dnd/node_modules/react-redux": {
+ "version": "7.2.9",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
+ "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.15.4",
+ "@types/react-redux": "^7.1.20",
+ "hoist-non-react-statics": "^3.3.2",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2"
+ },
+ "peerDependencies": {
+ "react": "^16.8.3 || ^17 || ^18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-beautiful-dnd/node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/react-bootstrap": {
+ "version": "2.10.5",
+ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.5.tgz",
+ "integrity": "sha512-XueAOEn64RRkZ0s6yzUTdpFtdUXs5L5491QU//8ZcODKJNDLt/r01tNyriZccjgRImH1REynUc9pqjiRMpDLWQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7",
+ "@restart/hooks": "^0.4.9",
+ "@restart/ui": "^1.6.9",
+ "@types/react-transition-group": "^4.4.6",
+ "classnames": "^2.3.2",
+ "dom-helpers": "^5.2.1",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.8.1",
+ "prop-types-extra": "^1.1.0",
+ "react-transition-group": "^4.4.5",
+ "uncontrollable": "^7.2.1",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.14.8",
+ "react": ">=16.14.0",
+ "react-dom": ">=16.14.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-datepicker": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.5.0.tgz",
+ "integrity": "sha512-6MzeamV8cWSOcduwePHfGqY40acuGlS1cG//ePHT6bVbLxWyqngaStenfH03n1wbzOibFggF66kWaBTb1SbTtQ==",
+ "dependencies": {
+ "@floating-ui/react": "^0.26.23",
+ "clsx": "^2.1.1",
+ "date-fns": "^3.6.0",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17 || ^18",
+ "react-dom": "^16.9.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/react-datepicker/node_modules/date-fns": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+ "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-google-recaptcha": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
+ "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
+ "dependencies": {
+ "prop-types": "^15.5.0",
+ "react-async-script": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.4.1"
+ }
+ },
+ "node_modules/react-i18next": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.2.tgz",
+ "integrity": "sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.25.0",
+ "html-parse-stringify": "^3.0.1"
+ },
+ "peerDependencies": {
+ "i18next": ">= 23.2.3",
+ "react": ">= 16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
+ "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-infinite-scroll-component": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz",
+ "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==",
+ "dependencies": {
+ "throttle-debounce": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-multi-carousel": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.5.tgz",
+ "integrity": "sha512-C5DAvJkfzR2JK9YixZ3oyF9x6R4LW6nzTpIXrl9Oujxi4uqP9SzVVCjl+JLM3tSdqdjAx/oWZK3dTVBSR73Q+w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/react-redux": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
+ "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
+ "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
+ "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0",
+ "react-router": "6.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/react-toastify": {
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz",
+ "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-tooltip": {
+ "version": "5.28.0",
+ "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz",
+ "integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.1",
+ "classnames": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.14.0",
+ "react-dom": ">=16.14.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/redent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+ "dev": true,
+ "dependencies": {
+ "indent-string": "^4.0.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
+ "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.1",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "which-builtin-type": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
+ "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regenerator-transform": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
+ "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "node_modules/regex": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz",
+ "integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw=="
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
+ "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/regjsgen": "^0.8.0",
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.1.0",
+ "regjsparser": "^0.9.1",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/registry-auth-token": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz",
+ "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==",
+ "dev": true,
+ "dependencies": {
+ "rc": "1.2.8"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
+ "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
+ "dev": true,
+ "dependencies": {
+ "rc": "^1.2.8"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/regjsparser": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
+ "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+ "dev": true,
+ "dependencies": {
+ "jsesc": "~0.5.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ }
+ },
+ "node_modules/rehackt": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
+ "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/remarkable": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
+ "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==",
+ "dependencies": {
+ "argparse": "^1.0.10",
+ "autolinker": "~0.28.0"
+ },
+ "bin": {
+ "remarkable": "bin/remarkable.js"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-cwd/node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-dir/node_modules/global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "dependencies": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-dir/node_modules/global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-dir/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/resolve.exports": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz",
+ "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/response-iterator": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz",
+ "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/responselike": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+ "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
+ "dev": true,
+ "dependencies": {
+ "lowercase-keys": "^1.0.0"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/restructure": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.0.tgz",
+ "integrity": "sha512-Xj8/MEIhhfj9X2rmD9iJ4Gga9EFqVlpMj3vfLnV2r/Mh5jRMryNV+6lWh9GdJtDBcBSPIqzRdfBQ3wDtNFv/uw=="
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
+ },
+ "node_modules/rollup": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
+ "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.21.3",
+ "@rollup/rollup-android-arm64": "4.21.3",
+ "@rollup/rollup-darwin-arm64": "4.21.3",
+ "@rollup/rollup-darwin-x64": "4.21.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.21.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.21.3",
+ "@rollup/rollup-linux-arm64-musl": "4.21.3",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.21.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.21.3",
+ "@rollup/rollup-linux-x64-gnu": "4.21.3",
+ "@rollup/rollup-linux-x64-musl": "4.21.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.21.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.21.3",
+ "@rollup/rollup-win32-x64-msvc": "4.21.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.21.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
+ "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/sanitize-html": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
+ "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
+ "dependencies": {
+ "deepmerge": "^4.2.2",
+ "escape-string-regexp": "^4.0.0",
+ "htmlparser2": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "parse-srcset": "^1.0.2",
+ "postcss": "^8.3.11"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sass": {
+ "version": "1.80.6",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz",
+ "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==",
+ "devOptional": true,
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
+ }
+ },
+ "node_modules/sass/node_modules/chokidar": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
+ "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
+ "devOptional": true,
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/sass/node_modules/readdirp": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
+ "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/saxes": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+ "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "dev": true,
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver-diff": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
+ "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semver-diff/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-getter": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz",
+ "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==",
+ "dependencies": {
+ "to-object-path": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shiki": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.17.6.tgz",
+ "integrity": "sha512-RejGugKpDM75vh6YtF9R771acxHRDikC/01kxsUGW+Pnaz3pTY+c8aZB5CnD7p0vuFPs1HaoAIU/4E+NCfS+mQ==",
+ "dependencies": {
+ "@shikijs/core": "1.17.6",
+ "@shikijs/engine-javascript": "1.17.6",
+ "@shikijs/engine-oniguruma": "1.17.6",
+ "@shikijs/types": "1.17.6",
+ "@shikijs/vscode-textmate": "^9.2.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT"
+ },
+ "node_modules/sirv": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz",
+ "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==",
+ "dev": true,
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.20",
+ "mrmime": "^1.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/snake-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
+ "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
+ "dev": true,
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "devOptional": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/stack-utils/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-hash": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",
+ "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==",
+ "dev": true
+ },
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
+ "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "regexp.prototype.flags": "^1.5.2",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-color": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz",
+ "integrity": "sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
+ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==",
+ "license": "MIT"
+ },
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
+ "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/svg-parser": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
+ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
+ "dev": true
+ },
+ "node_modules/symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "node_modules/synckit": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
+ "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+ },
+ "node_modules/terminal-link": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
+ "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "supports-hyperlinks": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.32.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz",
+ "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/throat": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz",
+ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==",
+ "dev": true
+ },
+ "node_modules/throttle-debounce": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz",
+ "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+ },
+ "node_modules/through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/through2/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/through2/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/through2/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/through2/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
+ },
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/tmpl": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-readable-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
+ "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "devOptional": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toml": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz",
+ "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ=="
+ },
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
+ "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+ "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/ts-invariant": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
+ "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
+ "node_modules/tsx": {
+ "version": "4.19.1",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz",
+ "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "~0.23.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+ "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+ "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+ "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+ "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+ "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+ "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+ "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+ "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+ "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+ "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+ "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+ "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+ "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+ "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+ "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+ "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+ "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+ "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+ "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+ "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+ "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.23.1",
+ "@esbuild/android-arm": "0.23.1",
+ "@esbuild/android-arm64": "0.23.1",
+ "@esbuild/android-x64": "0.23.1",
+ "@esbuild/darwin-arm64": "0.23.1",
+ "@esbuild/darwin-x64": "0.23.1",
+ "@esbuild/freebsd-arm64": "0.23.1",
+ "@esbuild/freebsd-x64": "0.23.1",
+ "@esbuild/linux-arm": "0.23.1",
+ "@esbuild/linux-arm64": "0.23.1",
+ "@esbuild/linux-ia32": "0.23.1",
+ "@esbuild/linux-loong64": "0.23.1",
+ "@esbuild/linux-mips64el": "0.23.1",
+ "@esbuild/linux-ppc64": "0.23.1",
+ "@esbuild/linux-riscv64": "0.23.1",
+ "@esbuild/linux-s390x": "0.23.1",
+ "@esbuild/linux-x64": "0.23.1",
+ "@esbuild/netbsd-x64": "0.23.1",
+ "@esbuild/openbsd-arm64": "0.23.1",
+ "@esbuild/openbsd-x64": "0.23.1",
+ "@esbuild/sunos-x64": "0.23.1",
+ "@esbuild/win32-arm64": "0.23.1",
+ "@esbuild/win32-ia32": "0.23.1",
+ "@esbuild/win32-x64": "0.23.1"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+ },
+ "node_modules/typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "node_modules/typedoc": {
+ "version": "0.26.10",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.10.tgz",
+ "integrity": "sha512-xLmVKJ8S21t+JeuQLNueebEuTVphx6IrP06CdV7+0WVflUSW3SPmR+h1fnWVdAR/FQePEgsSWCUHXqKKjzuUAw==",
+ "dependencies": {
+ "lunr": "^2.3.9",
+ "markdown-it": "^14.1.0",
+ "minimatch": "^9.0.5",
+ "shiki": "^1.16.2",
+ "yaml": "^2.5.1"
+ },
+ "bin": {
+ "typedoc": "bin/typedoc"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x"
+ }
+ },
+ "node_modules/typedoc-plugin-markdown": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.10.tgz",
+ "integrity": "sha512-PLX3pc1/7z13UJm4TDE9vo9jWGcClFUErXXtd5LdnoLjV6mynPpqZLU992DwMGFSRqJFZeKbVyqlNNeNHnk2tQ==",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "typedoc": "0.26.x"
+ }
+ },
+ "node_modules/typedoc/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/typedoc/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typedoc/node_modules/yaml": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.6.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
+ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/uncontrollable": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
+ "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.6.3",
+ "@types/react": ">=16.9.11",
+ "invariant": "^2.2.4",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "devOptional": true
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-properties": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
+ "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-trie": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
+ "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
+ "dependencies": {
+ "pako": "^0.2.5",
+ "tiny-inflate": "^1.0.0"
+ }
+ },
+ "node_modules/unicode-trie/node_modules/pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
+ },
+ "node_modules/unique-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+ "dev": true,
+ "dependencies": {
+ "crypto-random-string": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/update-notifier": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz",
+ "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==",
+ "dev": true,
+ "dependencies": {
+ "boxen": "^5.0.0",
+ "chalk": "^4.1.0",
+ "configstore": "^5.0.1",
+ "has-yarn": "^2.1.0",
+ "import-lazy": "^2.1.0",
+ "is-ci": "^2.0.0",
+ "is-installed-globally": "^0.4.0",
+ "is-npm": "^5.0.0",
+ "is-yarn-global": "^0.3.0",
+ "latest-version": "^5.1.0",
+ "pupa": "^2.1.1",
+ "semver": "^7.3.4",
+ "semver-diff": "^3.1.1",
+ "xdg-basedir": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/yeoman/update-notifier?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/url-parse-lax": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
+ "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
+ "dev": true,
+ "dependencies": {
+ "prepend-http": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/use-memo-one": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz",
+ "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^1.6.0",
+ "source-map": "^0.7.3"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/v8-to-istanbul/node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.8",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
+ "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-environment": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-environment/-/vite-plugin-environment-1.1.3.tgz",
+ "integrity": "sha512-9LBhB0lx+2lXVBEWxFZC+WO7PKEyE/ykJ7EPWCq95NEcCpblxamTbs5Dm3DLBGzwODpJMEnzQywJU8fw6XGGGA==",
+ "peerDependencies": {
+ "vite": ">= 2.7"
+ }
+ },
+ "node_modules/vite-plugin-svgr": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz",
+ "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.5",
+ "@svgr/core": "^8.1.0",
+ "@svgr/plugin-jsx": "^8.1.0"
+ },
+ "peerDependencies": {
+ "vite": "^2.6.0 || 3 || 4 || 5"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@rollup/pluginutils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
+ "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
+ "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
+ "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
+ "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
+ "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
+ "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-preset": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
+ "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
+ "dev": true,
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
+ "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
+ "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
+ "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
+ "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/core": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
+ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^8.1.3",
+ "snake-case": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
+ "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.21.3",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/@svgr/plugin-jsx": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
+ "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "@svgr/hast-util-to-babel-ast": "8.0.0",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "*"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/vite-plugin-svgr/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "dependencies": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-svgr/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/vite-plugin-svgr/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/vite-tsconfig-paths": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.2.tgz",
+ "integrity": "sha512-gEIbKfJzSEv0yR3XS2QEocKetONoWkbROj6hGx0FHM18qKUojhvcokQsxQx5nMkelZq2n37zbSGCJn+FSODSjA==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^3.0.3"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-tsconfig-paths/node_modules/tsconfck": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.3.tgz",
+ "integrity": "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.",
+ "dev": true,
+ "dependencies": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
+ "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
+ "dev": true,
+ "dependencies": {
+ "xml-name-validator": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/walker": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
+ "dependencies": {
+ "makeerror": "1.0.12"
+ }
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/web-vitals": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
+ "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="
+ },
+ "node_modules/webidl-conversions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.4"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "dependencies": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "node_modules/whatwg-encoding/node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/whatwg-fetch": {
+ "version": "3.6.20",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
+ "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
+ "dev": true
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "node_modules/whatwg-url": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+ "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.7.0",
+ "tr46": "^2.1.0",
+ "webidl-conversions": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz",
+ "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==",
+ "dev": true,
+ "dependencies": {
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.0.5",
+ "is-finalizationregistry": "^1.0.2",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.1.4",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xdg-basedir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zen-observable": {
+ "version": "0.8.15",
+ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
+ "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="
+ },
+ "node_modules/zen-observable-ts": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
+ "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
+ "dependencies": {
+ "zen-observable": "0.8.15"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.22.4",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
+ "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 6526d77a59..892a1331e4 100644
--- a/package.json
+++ b/package.json
@@ -1,34 +1,92 @@
{
- "name": "my-app",
- "version": "0.1.0",
+ "name": "talawa-admin",
+ "version": "3.0.0",
"private": true,
+ "type": "module",
+ "config-overrides-path": "scripts/config-overrides",
"dependencies": {
- "@testing-library/jest-dom": "^5.11.4",
- "@testing-library/react": "^11.1.0",
- "@testing-library/user-event": "^12.1.10",
- "@types/jest": "^26.0.15",
- "@types/node": "^12.0.0",
- "@types/react": "^17.0.0",
- "@types/react-dom": "^17.0.0",
- "eslint-config-prettier": "^8.3.0",
- "eslint-plugin-prettier": "^3.4.0",
- "prettier": "^2.3.0",
- "react": "^17.0.2",
- "react-bootstrap": "^1.5.2",
- "react-dom": "^17.0.2",
- "react-router-dom": "^5.2.0",
- "react-scripts": "4.0.3",
- "typescript": "^4.2.4",
- "web-vitals": "^1.0.1"
+ "@apollo/client": "^3.11.8",
+ "@apollo/link-error": "^2.0.0-beta.3",
+ "@apollo/react-testing": "^4.0.0",
+ "@dicebear/collection": "^9.2.2",
+ "@dicebear/core": "^9.2.2",
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@mui/base": "^5.0.0-beta.61",
+ "@mui/icons-material": "^6.1.6",
+ "@mui/material": "^6.1.6",
+ "@mui/private-theming": "^6.1.6",
+ "@mui/system": "^6.1.6",
+ "@mui/x-charts": "^7.22.1",
+ "@mui/x-data-grid": "^7.22.1",
+ "@mui/x-date-pickers": "^7.22.1",
+ "@pdfme/schemas": "^5.1.6",
+ "chart.js": "^4.4.6",
+ "@pdfme/generator": "^5.1.7",
+ "@reduxjs/toolkit": "^2.3.0",
+ "@vitejs/plugin-react": "^4.3.2",
+ "babel-plugin-transform-import-meta": "^2.2.1",
+ "bootstrap": "^5.3.3",
+ "customize-cra": "^1.0.0",
+ "dayjs": "^1.11.13",
+ "dotenv": "^16.4.5",
+ "flag-icons": "^7.2.3",
+ "graphql": "^16.9.0",
+ "graphql-tag": "^2.12.6",
+ "graphql-ws": "^5.16.0",
+ "history": "^5.3.0",
+ "i18next": "^23.15.1",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-http-backend": "^2.6.1",
+ "inquirer": "^8.0.0",
+ "js-cookie": "^3.0.1",
+ "markdown-toc": "^1.2.0",
+ "prettier": "^3.3.3",
+ "prop-types": "^15.8.1",
+ "react": "^18.3.1",
+ "react-beautiful-dnd": "^13.1.1",
+ "react-bootstrap": "^2.10.5",
+ "react-chartjs-2": "^5.2.0",
+ "react-datepicker": "^7.5.0",
+ "react-dom": "^18.3.1",
+ "react-google-recaptcha": "^3.1.0",
+ "react-i18next": "^15.0.2",
+ "react-icons": "^5.2.1",
+ "react-infinite-scroll-component": "^6.1.0",
+ "react-multi-carousel": "^2.8.5",
+ "react-redux": "^9.1.2",
+ "react-router-dom": "^6.27.0",
+ "react-toastify": "^10.0.6",
+ "react-tooltip": "^5.28.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "sanitize-html": "^2.13.0",
+ "typedoc": "^0.26.10",
+ "typedoc-plugin-markdown": "^4.2.10",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vite-plugin-environment": "^1.1.3",
+ "vite-tsconfig-paths": "^5.1.2",
+ "web-vitals": "^4.2.4"
},
"scripts": {
- "serve": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
+ "serve": "cross-env ESLINT_NO_DEV_ERRORS=true vite --config config/vite.config.ts",
+ "build": "tsc && vite build --config config/vite.config.ts",
+ "preview": "vite preview --config config/vite.config.ts",
+ "test": "cross-env NODE_ENV=test jest --env=./scripts/custom-test-env.js --watchAll --coverage",
"eject": "react-scripts eject",
- "lint": "eslint 'src/*.{ts,tsx}'",
- "lint-fix": "eslint 'src/*.{ts,tsx}' --fix",
- "format": "prettier --write \"**/*.{ts,tsx,json,scss,css}\""
+ "lint:check": "eslint \"**/*.{ts,tsx}\" --max-warnings=0 && python .github/workflows/eslint_disable_check.py",
+ "lint:fix": "eslint --fix \"**/*.{ts,tsx}\"",
+ "format:fix": "prettier --write \"**/*.{ts,tsx,json,scss,css}\"",
+ "format:check": "prettier --check \"**/*.{ts,tsx,json,scss,css}\"",
+ "check-tsdoc": "node .github/workflows/check-tsdoc.js",
+ "typecheck": "tsc --project tsconfig.json --noEmit",
+ "prepare": "husky install",
+ "jest-preview": "jest-preview",
+ "update:toc": "node scripts/githooks/update-toc.js",
+ "lint-staged": "lint-staged --concurrent false",
+ "setup": "tsx setup.ts",
+ "check-localstorage": "node scripts/githooks/check-localstorage-usage.js"
},
"eslintConfig": {
"extends": [
@@ -49,8 +107,63 @@
]
},
"devDependencies": {
- "@typescript-eslint/eslint-plugin": "^4.24.0",
- "@typescript-eslint/parser": "^4.24.0",
- "eslint-plugin-react-hooks": "^4.2.0"
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
+ "@babel/preset-env": "^7.25.4",
+ "@babel/preset-react": "^7.25.7",
+ "@babel/preset-typescript": "^7.26.0",
+ "@testing-library/jest-dom": "^6.5.0",
+ "@testing-library/react": "^16.0.1",
+ "@testing-library/user-event": "^12.1.10",
+ "@types/inquirer": "^9.0.7",
+ "@types/jest": "^26.0.24",
+ "@types/js-cookie": "^3.0.6",
+ "@types/node": "^22.5.4",
+ "@types/node-fetch": "^2.6.10",
+ "@types/react": "^18.3.3",
+ "@types/react-beautiful-dnd": "^13.1.8",
+ "@types/react-chartjs-2": "^2.5.7",
+ "@types/react-bootstrap": "^0.32.37",
+ "@types/react-datepicker": "^7.0.0",
+ "@types/react-dom": "^18.3.1",
+ "@types/react-google-recaptcha": "^2.1.9",
+ "@types/react-router-dom": "^5.1.8",
+ "@types/sanitize-html": "^2.13.0",
+ "@typescript-eslint/eslint-plugin": "^8.11.0",
+ "@typescript-eslint/parser": "^8.5.0",
+ "babel-jest": "^29.7.0",
+ "cross-env": "^7.0.3",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-import": "^2.30.0",
+ "eslint-plugin-jest": "^28.8.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-react": "^7.37.1",
+ "eslint-plugin-tsdoc": "^0.3.0",
+ "husky": "^9.1.6",
+ "identity-obj-proxy": "^3.0.0",
+ "jest": "^27.4.5",
+ "jest-localstorage-mock": "^2.4.19",
+ "jest-location-mock": "^2.0.0",
+ "jest-preview": "^0.3.1",
+ "lint-staged": "^15.2.8",
+ "postcss-modules": "^6.0.0",
+ "sass": "^1.80.6",
+ "tsx": "^4.19.1",
+ "vite-plugin-svgr": "^4.2.0",
+ "whatwg-fetch": "^3.6.20"
+ },
+ "resolutions": {
+ "@apollo/client": "^3.4.0-beta.19",
+ "@types/react": "17.0.2",
+ "@types/react-dom": "17.0.2",
+ "graphql": "^16.5.0"
+ },
+ "engines": {
+ "node": ">=20.x"
+ },
+ "lint-staged": {
+ "**/*.{ts, tsx, json, scss, css}": [
+ "prettier --write"
+ ],
+ "**/*.{ts, tsx, json}": "eslint --fix"
}
}
diff --git a/public/favicon.ico b/public/favicon.ico
index a11777cc47..0675af2934 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/favicon_palisadoes.ico b/public/favicon_palisadoes.ico
new file mode 100644
index 0000000000..0675af2934
Binary files /dev/null and b/public/favicon_palisadoes.ico differ
diff --git a/public/images/REACT_SITE_KEY.webp b/public/images/REACT_SITE_KEY.webp
new file mode 100644
index 0000000000..9afe77c4d5
Binary files /dev/null and b/public/images/REACT_SITE_KEY.webp differ
diff --git a/public/images/jest-preview.webp b/public/images/jest-preview.webp
new file mode 100644
index 0000000000..a5810192af
Binary files /dev/null and b/public/images/jest-preview.webp differ
diff --git a/public/logo512.png b/public/images/logo512.png
similarity index 100%
rename from public/logo512.png
rename to public/images/logo512.png
diff --git a/public/images/svg/angleDown.svg b/public/images/svg/angleDown.svg
new file mode 100644
index 0000000000..0dfea5e56c
--- /dev/null
+++ b/public/images/svg/angleDown.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/images/svg/arrow-left.svg b/public/images/svg/arrow-left.svg
new file mode 100644
index 0000000000..395c38fc25
--- /dev/null
+++ b/public/images/svg/arrow-left.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/svg/arrow-right.svg b/public/images/svg/arrow-right.svg
new file mode 100644
index 0000000000..bf754cec86
--- /dev/null
+++ b/public/images/svg/arrow-right.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/svg/attendees.svg b/public/images/svg/attendees.svg
new file mode 100644
index 0000000000..3864a85b7e
--- /dev/null
+++ b/public/images/svg/attendees.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/images/svg/feedback.svg b/public/images/svg/feedback.svg
new file mode 100644
index 0000000000..649e84a1c9
--- /dev/null
+++ b/public/images/svg/feedback.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/public/images/svg/profiledefault.svg b/public/images/svg/profiledefault.svg
new file mode 100644
index 0000000000..a321caaeab
--- /dev/null
+++ b/public/images/svg/profiledefault.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/images/svg/up-down.svg b/public/images/svg/up-down.svg
new file mode 100644
index 0000000000..1dd7fdab1d
--- /dev/null
+++ b/public/images/svg/up-down.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index 72527d8e95..0000000000
--- a/public/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
- Talawa Admin
-
-
-
-
-
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
new file mode 100644
index 0000000000..6c8882bcfd
--- /dev/null
+++ b/public/locales/en/common.json
@@ -0,0 +1,98 @@
+{
+ "firstName": "First Name",
+ "lastName": "Last Name",
+ "searchByName": "Search By Name",
+ "loading": "Loading...",
+ "endOfResults": "End of results",
+ "noResultsFoundFor": "No results found for ",
+ "edit": "Edit",
+ "admins": "Admins",
+ "admin": "ADMIN",
+ "user": "USER",
+ "superAdmin": "SUPERADMIN",
+ "members": "Members",
+ "logout": "Logout",
+ "login": "Login",
+ "register": "Register",
+ "menu": "Menu",
+ "settings": "Settings",
+ "users": "Users",
+ "requests": "Requests",
+ "OR": "OR",
+ "cancel": "Cancel",
+ "close": "Close",
+ "create": "Create",
+ "delete": "Delete",
+ "done": "Done",
+ "yes": "Yes",
+ "no": "No",
+ "filter": "Filter",
+ "search": "Search",
+ "description": "Description",
+ "saveChanges": "Save Changes",
+ "gender": "Gender",
+ "resetChanges": "Reset Changes",
+ "displayImage": "Display Image",
+ "enterEmail": "Enter Email",
+ "emailAddress": "Email Address",
+ "email": "Email",
+ "name": "Name",
+ "desc": "Description",
+ "enterPassword": "Enter Password",
+ "password": "Password",
+ "confirmPassword": "Confirm Password",
+ "forgotPassword": "Forgot Password ?",
+ "talawaAdminPortal": "Talawa Admin Portal",
+ "address": "Address",
+ "location": "Location",
+ "enterLocation": "Enter Location",
+ "joined": "Joined",
+ "startDate": "Start Date",
+ "endDate": "End Date",
+ "startTime": "Start Time",
+ "endTime": "End Time",
+ "My Organizations": "My Organizations",
+ "Dashboard": "Dashboard",
+ "People": "People",
+ "Events": "Events",
+ "Venues": "Venues",
+ "Action Items": "Action Items",
+ "Posts": "Posts",
+ "Block/Unblock": "Block/Unblock",
+ "Advertisement": "Advertisement",
+ "Funds": "Funds",
+ "Membership Requests": "Membership Requests",
+ "Plugins": "Plugins",
+ "Plugin Store": "Plugin Store",
+ "Settings": "Settings",
+ "createdOn": "Created On",
+ "createdBy": "Created By",
+ "usersRole": "User's Role",
+ "changeRole": "Change Role",
+ "action": "Action",
+ "removeUser": "Remove User",
+ "remove": "Remove",
+ "viewProfile": "View Profile",
+ "profile": "Profile",
+ "noFiltersApplied": "No filters applied",
+ "manage": "Manage",
+ "searchResultsFor": "Search results for {{text}}",
+ "none": "None",
+ "Donate": "Donate",
+ "addedSuccessfully": "{{item}} added Successfully",
+ "updatedSuccessfully": "{{item}} updated Successfully",
+ "removedSuccessfully": "{{item}} removed Successfully",
+ "successfullyUpdated": "Successfully Updated",
+ "sessionWarning": "Your session will expire soon due to inactivity. Please interact with the page to extend your session.",
+ "sessionLogOut": "Your session has expired due to inactivity. Please log in again to continue.",
+ "sort": "Sort",
+ "all": "All",
+ "active": "Active",
+ "disabled": "Disabled",
+ "pending": "Pending",
+ "completed": "Completed",
+ "late": "Late",
+ "createdLatest": "Created Latest",
+ "createdEarliest": "Created Earliest",
+ "searchBy": "Search by {{item}}"
+}
diff --git a/public/locales/en/errors.json b/public/locales/en/errors.json
new file mode 100644
index 0000000000..752c0db750
--- /dev/null
+++ b/public/locales/en/errors.json
@@ -0,0 +1,11 @@
+{
+ "talawaApiUnavailable": "Talawa-API service is unavailable!. Is it running? Check your network connectivity too.",
+ "notFound": "Not found",
+ "unknownError": "An unknown error occurred. Please try again later. {{msg}}",
+ "notAuthorised": "Sorry! you are not Authorised!",
+ "errorSendingMail": "Error sending mail",
+ "emailNotRegistered": "Email not registered",
+ "notFoundMsg": "Oops! The Page you requested was not found!",
+ "errorOccurredCouldntCreate": "An error occurred. Couldn't create {{entity}}",
+ "errorLoading": "Error occured while loading {{entity}} data"
+}
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
new file mode 100644
index 0000000000..5da5ef49ee
--- /dev/null
+++ b/public/locales/en/translation.json
@@ -0,0 +1,1483 @@
+{
+ "leaderboard": {
+ "title": "Leaderboard",
+ "searchByVolunteer": "Search By Volunteer",
+ "mostHours": "Most Hours",
+ "leastHours": "Least Hours",
+ "timeFrame": "Time Frame",
+ "allTime": "All Time",
+ "weekly": "This Week",
+ "monthly": "This Month",
+ "yearly": "This Year",
+ "noVolunteers": "No Volunteers Found!"
+ },
+ "loginPage": {
+ "title": "Talawa Admin",
+ "fromPalisadoes": "An open source application by Palisadoes Foundation volunteers",
+ "userLogin": "User Login",
+ "atleast_8_char_long": "Atleast 8 Character long",
+ "atleast_6_char_long": "Atleast 6 Character long",
+ "firstName_invalid": "First name should contain only lower and upper case letters",
+ "lastName_invalid": "Last name should contain only lower and upper case letters",
+ "password_invalid": "Password should contain atleast one lowercase letter, one uppercase letter, one numeric value and one special character",
+ "email_invalid": "Email should have atleast 8 characters",
+ "Password_and_Confirm_password_mismatches.": "Password and Confirm password mismatches.",
+ "doNotOwnAnAccount": "Do not own an account?",
+ "captchaError": "Captcha Error!",
+ "Please_check_the_captcha": "Please, check the captcha.",
+ "Something_went_wrong": "Something went wrong, Please try after sometime.",
+ "passwordMismatches": "Password and Confirm password mismatches.",
+ "fillCorrectly": "Fill all the Details Correctly.",
+ "successfullyRegistered": "Successfully Registered. Please wait until you will be approved.",
+ "lowercase_check": "Atleast one lowercase letter",
+ "uppercase_check": "Atleast one uppercase letter",
+ "numeric_value_check": "Atleaset one numeric value",
+ "special_char_check": "Atleast one special character",
+ "selectOrg": "Select an organization",
+ "afterRegister": "Successfully registered. Please wait for admin to approve your request.",
+ "talawa_portal": "talawa_portal",
+ "login": "login",
+ "register": "register",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "email": "email",
+ "password": "password",
+ "confirmPassword": "confirmPassword",
+ "forgotPassword": "forgotPassword",
+ "enterEmail": "enterEmail",
+ "enterPassword": "enterPassword",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "notAuthorised": "notAuthorised",
+ "notFound": "notFound",
+ "OR": "OR",
+ "admin": "admin",
+ "user": "user",
+ "loading": "loading"
+ },
+ "userLoginPage": {
+ "title": "Talawa Admin",
+ "fromPalisadoes": "An open source application by Palisadoes Foundation volunteers",
+ "atleast_8_char_long": "Atleast 8 Character long",
+ "Password_and_Confirm_password_mismatches.": "Password and Confirm password mismatches.",
+ "doNotOwnAnAccount": "Do not own an account?",
+ "captchaError": "Captcha Error!",
+ "Please_check_the_captcha": "Please, check the captcha.",
+ "Something_went_wrong": "Something went wrong, Please try after sometime.",
+ "passwordMismatches": "Password and Confirm password mismatches.",
+ "fillCorrectly": "Fill all the Details Correctly.",
+ "successfullyRegistered": "Successfully Registered. Please wait until you will be approved.",
+ "userLogin": "User Login",
+ "afterRegister": "Successfully registered. Please wait for admin to approve your request.",
+ "selectOrg": "Select an organization",
+ "talawa_portal": "talawa_portal",
+ "login": "login",
+ "register": "register",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "email": "email",
+ "password": "password",
+ "confirmPassword": "confirmPassword",
+ "forgotPassword": "forgotPassword",
+ "enterEmail": "enterEmail",
+ "enterPassword": "enterPassword",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "notAuthorised": "notAuthorised",
+ "notFound": "notFound",
+ "OR": "OR",
+ "loading": "loading"
+ },
+ "latestEvents": {
+ "eventCardTitle": "Upcoming Events",
+ "eventCardSeeAll": "See All",
+ "noEvents": "No Upcoming Events"
+ },
+ "latestPosts": {
+ "latestPostsTitle": "Latest Posts",
+ "seeAllLink": "See All",
+ "noPostsCreated": "No Posts Created"
+ },
+ "listNavbar": {
+ "roles": "Roles",
+ "talawa_portal": "talawa_portal",
+ "requests": "requests",
+ "logout": "logout"
+ },
+ "leftDrawer": {
+ "my organizations": "My Organizations",
+ "requests": "Membership Requests",
+ "communityProfile": "Community Profile",
+ "talawaAdminPortal": "talawaAdminPortal",
+ "menu": "menu",
+ "users": "users",
+ "logout": "logout"
+ },
+ "leftDrawerOrg": {
+ "Dashboard": "Dashboard",
+ "People": "People",
+ "Events": "Events",
+ "Contributions": "Contributions",
+ "Posts": "Posts",
+ "Block/Unblock": "Block/Unblock",
+ "Plugins": "Plugins",
+ "Plugin Store": "Plugin Store",
+ "Advertisement": "Advertisements",
+ "allOrganizations": "All Organizations",
+ "yourOrganization": "Your Organization",
+ "notification": "Notification",
+ "language": "Language",
+ "notifications": "Notifications",
+ "spamsThe": "spams the",
+ "group": "group",
+ "noNotifications": "No Notifications",
+ "talawaAdminPortal": "talawaAdminPortal",
+ "menu": "menu",
+ "talawa_portal": "talawa_portal",
+ "settings": "settings",
+ "logout": "logout",
+ "close": "close"
+ },
+ "orgList": {
+ "title": "Talawa Organizations",
+ "you": "You",
+ "designation": "Designation",
+ "my organizations": "My Organizations",
+ "createOrganization": "Create Organization",
+ "createSampleOrganization": "Create Sample Organization",
+ "city": "City",
+ "countryCode": "Country Code",
+ "dependentLocality": "Dependent Locality",
+ "line1": "Line 1",
+ "line2": "Line 2",
+ "postalCode": "Postal Code",
+ "sortingCode": "Sorting code",
+ "state": "State / Province",
+ "userRegistrationRequired": "User Registration Required",
+ "visibleInSearch": "Visible In Search",
+ "enterName": "Enter Name",
+ "sort": "Sort",
+ "Latest": "Latest",
+ "Earliest": "Earliest",
+ "noOrgErrorTitle": "Organizations Not Found",
+ "sampleOrgDuplicate": "Only one sample organization allowed",
+ "noOrgErrorDescription": "Please create an organization through dashboard",
+ "manageFeatures": "Manage Features",
+ "manageFeaturesInfo": "Creation Successful ! Please select features that you want to enale for this organization from the plugin store.",
+ "goToStore": "Go to Plugin Store",
+ "enableEverything": "Enable Everything",
+ "sampleOrgSuccess": "Sample Organization Successfully Created",
+ "name": "name",
+ "email": "email",
+ "searchByName": "searchByName",
+ "description": "description",
+ "location": "location",
+ "address": "address",
+ "displayImage": "displayImage",
+ "filter": "filter",
+ "cancel": "cancel",
+ "endOfResults": "endOfResults",
+ "noResultsFoundFor": "noResultsFoundFor",
+ "OR": "OR"
+ },
+ "orgListCard": {
+ "manage": "Manage",
+ "sampleOrganization": "Sample Organization",
+ "admins": "admins",
+ "members": "members"
+ },
+ "paginationList": {
+ "rowsPerPage": "rows per page",
+ "all": "All"
+ },
+ "requests": {
+ "title": "Membership Requests",
+ "sl_no": "Sl. No.",
+ "accept": "Accept",
+ "reject": "Reject",
+ "searchRequests": "Search membership requests",
+ "noOrgError": "Organizations not found, please create an organization through dashboard",
+ "noRequestsFound": "No Membership Requests Found",
+ "acceptedSuccessfully": "Request accepted successfully",
+ "rejectedSuccessfully": "Request rejected successfully",
+ "noOrgErrorTitle": "Organizations Not Found",
+ "noOrgErrorDescription": "Please create an organization through dashboard",
+ "name": "name",
+ "email": "email",
+ "endOfResults": "endOfResults",
+ "noResultsFoundFor": "noResultsFoundFor"
+ },
+ "users": {
+ "title": "Talawa Roles",
+ "joined_organizations": "Joined Organizations",
+ "blocked_organizations": "Blocked Organizations",
+ "orgJoinedBy": "Organizations Joined By",
+ "orgThatBlocked": "Organizations That Blocked",
+ "hasNotJoinedAnyOrg": "has not joined any organization",
+ "isNotBlockedByAnyOrg": "is not blocked by any organization",
+ "searchByOrgName": "Search By Organization Name",
+ "view": "View",
+ "enterName": "Enter Name",
+ "loadingUsers": "Loading Users...",
+ "noUserFound": "No User Found",
+ "sort": "Sort",
+ "Newest": "Newest First",
+ "Oldest": "Oldest First",
+ "noOrgError": "Organizations not found, please create an organization through dashboard",
+ "roleUpdated": "Role Updated.",
+ "joinNow": "Join Now",
+ "visit": "Visit",
+ "withdraw": "Widthdraw",
+ "removeUserFrom": "Remove User from {{org}}",
+ "removeConfirmation": "Are you sure you want to remove '{{name}}' from organization '{{org}}'?",
+ "searchByName": "searchByName",
+ "users": "users",
+ "name": "name",
+ "email": "email",
+ "endOfResults": "endOfResults",
+ "admin": "admin",
+ "superAdmin": "superAdmin",
+ "user": "user",
+ "filter": "filter",
+ "noResultsFoundFor": "noResultsFoundFor",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "cancel": "cancel",
+ "admins": "admins",
+ "members": "members",
+ "orgJoined": "orgJoined",
+ "MembershipRequestSent": "MembershipRequestSent",
+ "AlreadyJoined": "AlreadyJoined",
+ "errorOccured": "errorOccured"
+ },
+ "communityProfile": {
+ "title": "Community Profile",
+ "editProfile": "Edit Profile",
+ "communityProfileInfo": "These details will appear on the login/signup screen for you and your community members",
+ "communityName": "Community Name",
+ "wesiteLink": "Website Link",
+ "logo": "Logo",
+ "social": "Social Media Links",
+ "url": "Enter url",
+ "profileChangedMsg": "Successfully updated the Profile Details.",
+ "resetData": "Successfully reset the Profile Details."
+ },
+ "dashboard": {
+ "title": "Dashboard",
+ "about": "About",
+ "deleteThisOrganization": "Delete This Organization",
+ "statistics": "Statistics",
+ "posts": "Posts",
+ "events": "Events",
+ "blockedUsers": "Blocked Users",
+ "viewAll": "View All",
+ "upcomingEvents": "Upcoming Events",
+ "noUpcomingEvents": "No Upcoming Events",
+ "latestPosts": "Latest Posts",
+ "noPostsPresent": "No Posts Present",
+ "membershipRequests": "Membership requests",
+ "noMembershipRequests": "No Membership requests present",
+ "location": "location",
+ "members": "members",
+ "admins": "admins",
+ "requests": "requests",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "volunteerRankings": "Volunteer Rankings",
+ "noVolunteers": "No Volunteers Found!"
+ },
+ "organizationPeople": {
+ "title": "Talawa Members",
+ "filterByName": "Filter by Name",
+ "filterByLocation": "Filter by Location",
+ "filterByEvent": "Filter by Event",
+ "searchName": "Enter Name",
+ "searchevent": "Enter Event",
+ "searchFullName": "Enter Full Name",
+ "people": "People",
+ "sort": "Search by Role",
+ "actions": "Actions",
+ "addMembers": "Add Members",
+ "existingUser": "Existing User",
+ "newUser": "New User",
+ "enterFirstName": "Enter your first name",
+ "enterLastName": "Enter your last name",
+ "enterConfirmPassword": "Enter Password to confirm",
+ "organization": "Organization",
+ "invalidDetailsMessage": "Please enter valid details.",
+ "members": "members",
+ "admins": "admins",
+ "users": "users",
+ "searchFirstName": "searchFirstName",
+ "searchLastName": "searchLastName",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "emailAddress": "emailAddress",
+ "enterEmail": "enterEmail",
+ "password": "password",
+ "enterPassword": "enterPassword",
+ "confirmPassword": "confirmPassword",
+ "create": "create",
+ "cancel": "cancel",
+ "user": "user",
+ "profile": "profile"
+ },
+ "organizationTags": {
+ "title": "Organization Tags",
+ "createTag": "Create a new tag",
+ "manageTag": "Manage",
+ "editTag": "Edit",
+ "removeTag": "Remove",
+ "tagDetails": "Tag Details",
+ "tagName": "Name",
+ "tagType": "Type",
+ "tagNamePlaceholder": "Write the name of the tag",
+ "tagCreationSuccess": "New tag created successfully",
+ "tagUpdationSuccess": "Tag updated successfully",
+ "tagRemovalSuccess": "Tag deleted successfully",
+ "noTagsFound": "No tags found",
+ "removeUserTag": "Delete Tag",
+ "removeUserTagMessage": "Do you want to delete this tag?",
+ "addChildTag": "Add a Sub Tag",
+ "enterTagName": "Enter Tag Name"
+ },
+ "manageTag": {
+ "title": "Tag Details",
+ "addPeopleToTag": "Add People to tag",
+ "viewProfile": "View",
+ "noAssignedMembersFound": "No one assigned",
+ "unassignUserTag": "Unassign Tag",
+ "unassignUserTagMessage": "Do you want to remove the tag from this user?",
+ "successfullyUnassigned": "Tag unassigned from user",
+ "addPeople": "Add People",
+ "add": "Add",
+ "subTags": "Sub Tags",
+ "successfullyAssignedToPeople": "Tag assigned successfully",
+ "errorOccurredWhileLoadingMembers": "Error occured while loading members",
+ "userName": "User Name",
+ "actions": "Actions",
+ "noOneSelected": "No One Selected",
+ "assignToTags": "Assign to Tags",
+ "removeFromTags": "Remove from Tags",
+ "assign": "Assign",
+ "remove": "Remove",
+ "successfullyAssignedToTags": "Successfully Assigned to Tags",
+ "successfullyRemovedFromTags": "Successfully Removed from Tags",
+ "errorOccurredWhileLoadingOrganizationUserTags": "Error occurred while loading organization tags",
+ "errorOccurredWhileLoadingSubTags": "Error occurred while loading subTags tags",
+ "removeUserTag": "Delete Tag",
+ "removeUserTagMessage": "Do you want to delete this tag? It delete all the sub tags and all the associations.",
+ "tagDetails": "Tag Details",
+ "tagName": "Name",
+ "tagUpdationSuccess": "Tag updated successfully",
+ "tagRemovalSuccess": "Tag deleted successfully",
+ "noTagSelected": "No Tag Selected",
+ "changeNameToEdit": "Change the name to make an update",
+ "selectTag": "Select Tag",
+ "collapse": "Collapse",
+ "expand": "Expand",
+ "tagNamePlaceholder": "Write the name of the tag",
+ "allTags": "All Tags",
+ "noMoreMembersFound": "No more members found"
+ },
+ "userListCard": {
+ "addAdmin": "Add Admin",
+ "addedAsAdmin": "User is added as admin.",
+ "joined": "joined",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "orgAdminListCard": {
+ "remove": "Remove",
+ "removeAdmin": "Remove Admin",
+ "removeAdminMsg": "Do you want to remove this admin?",
+ "adminRemoved": "The admin is removed.",
+ "joined": "joined",
+ "no": "no",
+ "yes": "yes",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "orgPeopleListCard": {
+ "remove": "Remove",
+ "removeMember": "Remove Member",
+ "removeMemberMsg": "Do you want to remove this member?",
+ "memberRemoved": "The Member is removed",
+ "joined": "joined",
+ "no": "no",
+ "yes": "yes",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "organizationEvents": {
+ "title": "Events",
+ "filterByTitle": "Filter by Title",
+ "filterByLocation": "Filter by Location",
+ "filterByDescription": "Filter by Description",
+ "addEvent": "Add Event",
+ "eventDetails": "Event Details",
+ "eventTitle": "Title",
+ "allDay": "All Day",
+ "recurringEvent": "Recurring Event",
+ "isPublic": "Is Public",
+ "isRegistrable": "Is Registrable",
+ "createEvent": "Create Event",
+ "enterFilter": "Enter Filter",
+ "enterTitle": "Enter Title",
+ "enterDescrip": "Enter Description",
+ "searchEventName": "Search Event Name",
+ "searchMemberName": "Search Member Name",
+ "eventType": "Event Type",
+ "eventCreated": "Congratulations! The Event is created.",
+ "customRecurrence": "Custom Recurrence",
+ "repeatsEvery": "Repeats Every",
+ "repeatsOn": "Repeats On",
+ "ends": "Ends",
+ "never": "Never",
+ "on": "On",
+ "after": "After",
+ "occurences": "occurences",
+ "startTime": "startTime",
+ "endTime": "endTime",
+ "eventLocation": "eventLocation",
+ "events": "events",
+ "description": "description",
+ "location": "location",
+ "startDate": "startDate",
+ "endDate": "endDate",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "done": "done"
+ },
+ "organizationActionItems": {
+ "actionItemCategory": "Action Item Category",
+ "actionItemDetails": "Action Item Details",
+ "actionItemCompleted": "Action Item Completed",
+ "assignee": "Assignee",
+ "assigner": "Assigner",
+ "assignmentDate": "Assignment Date",
+ "active": "Active",
+ "clearFilters": "Clear Filters",
+ "completionDate": "Completion Date",
+ "createActionItem": "Create Action Item",
+ "deleteActionItem": "Delete Action Item",
+ "deleteActionItemMsg": "Do you want to remove this action item?",
+ "details": "Details",
+ "dueDate": "Due Date",
+ "earliest": "Earliest",
+ "editActionItem": "Edit Action Item",
+ "isCompleted": "Completed",
+ "latest": "Latest",
+ "makeActive": "Active",
+ "noActionItems": "No Action Items",
+ "options": "Options",
+ "preCompletionNotes": "Notes",
+ "actionItemActive": "Active",
+ "markCompletion": "Mark Completion",
+ "actionItemStatus": "Action Item Status",
+ "postCompletionNotes": "Completion Notes",
+ "selectActionItemCategory": "Select an action item category",
+ "selectAssignee": "Select an assignee",
+ "status": "Status",
+ "successfulCreation": "Action Item created successfully",
+ "successfulUpdation": "Action Item updated successfully",
+ "successfulDeletion": "Action Item deleted successfully",
+ "title": "Action Items",
+ "category": "Category",
+ "allottedHours": "Allotted Hours",
+ "latestDueDate": "Latest Due Date",
+ "earliestDueDate": "Earliest Due Date",
+ "updateActionItem": "Update Action Item",
+ "noneUpdated": "None of the fields were updated",
+ "updateStatusMsg": "Are you sure you want to mark this action item as pending?",
+ "close": "close",
+ "eventActionItems": "eventActionItems",
+ "no": "no",
+ "yes": "yes",
+ "individuals": "Individuals",
+ "groups": "Groups",
+ "assignTo": "Assign To",
+ "volunteers": "Volunteers",
+ "volunteerGroups": "Volunteer Groups"
+ },
+ "organizationAgendaCategory": {
+ "agendaCategoryDetails": "Agenda Category Details",
+ "updateAgendaCategory": "Update Agenda Category",
+ "title": "Agenda Categories",
+ "name": "Category",
+ "description": "Description",
+ "createdBy": "Created By",
+ "options": "Options",
+ "createAgendaCategory": "Create Agenda Category",
+ "noAgendaCategories": "No Agenda Categories",
+ "update": "Update",
+ "agendaCategoryCreated": "Agenda Category created successfully",
+ "agendaCategoryUpdated": "Agenda Category updated successfully",
+ "agendaCategoryDeleted": "Agenda Category deleted successfully",
+ "deleteAgendaCategory": "Delete Agenda Category",
+ "deleteAgendaCategoryMsg": "Do you want to remove this agenda category?"
+ },
+ "agendaItems": {
+ "agendaItemDetails": "Agenda Item Details",
+ "updateAgendaItem": "Update Agenda Item",
+ "title": "Title",
+ "enterTitle": "Enter Title",
+ "sequence": "Sequence",
+ "description": "Description",
+ "enterDescription": "Enter Description",
+ "category": "Agenda Category",
+ "attachments": "Attachments",
+ "attachmentLimit": "Add any image file or video file upto 10MB",
+ "fileSizeExceedsLimit": "File size exceeds the limit which is 10MB",
+ "urls": "URLs",
+ "url": "add link to URL",
+ "enterUrl": "https://example.com",
+ "invalidUrl": "Please enter a valid URL",
+ "link": "Link",
+ "createdBy": "Created By",
+ "regular": "Regular",
+ "note": "Note",
+ "duration": "Duration",
+ "enterDuration": "mm:ss",
+ "options": "Options",
+ "createAgendaItem": "Create Agenda Item",
+ "noAgendaItems": "No Agenda Items",
+ "selectAgendaItemCategory": "Select an agenda item category",
+ "update": "Update",
+ "delete": "Delete",
+ "agendaItemCreated": "Agenda Item created successfully",
+ "agendaItemUpdated": "Agenda Item updated successfully",
+ "agendaItemDeleted": "Agenda Item deleted successfully",
+ "deleteAgendaItem": "Delete Agenda Item",
+ "deleteAgendaItemMsg": "Do you want to remove this agenda item?"
+ },
+ "eventListCard": {
+ "deleteEvent": "Delete Event",
+ "deleteEventMsg": "Do you want to remove this event?",
+ "editEvent": "Edit Event",
+ "eventTitle": "Title",
+ "alreadyRegistered": "Already registered",
+ "allDay": "All Day",
+ "recurringEvent": "Recurring Event",
+ "isPublic": "Is Public",
+ "isRegistrable": "Is Registrable",
+ "updatePost": "Update Post",
+ "eventDetails": "Event Details",
+ "eventDeleted": "Event deleted successfully.",
+ "eventUpdated": "Event updated successfully.",
+ "thisInstance": "This Instance",
+ "thisAndFollowingInstances": "This & Following Instances",
+ "allInstances": "All Instances",
+ "customRecurrence": "Custom Recurrence",
+ "repeatsEvery": "Repeats Every",
+ "repeatsOn": "Repeats On",
+ "ends": "Ends",
+ "never": "Never",
+ "on": "On",
+ "after": "After",
+ "occurences": "occurences",
+ "startTime": "startTime",
+ "endTime": "endTime",
+ "location": "location",
+ "no": "no",
+ "yes": "yes",
+ "description": "description",
+ "startDate": "startDate",
+ "endDate": "endDate",
+ "registerEvent": "registerEvent",
+ "close": "close",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "done": "done"
+ },
+ "funds": {
+ "title": "Funds",
+ "createFund": "Create Fund",
+ "fundName": "Fund Name",
+ "fundId": "Fund (Reference) ID",
+ "taxDeductible": "Tax Deductible",
+ "default": "Default",
+ "archived": "Archived",
+ "fundCreate": "Create Fund",
+ "fundUpdate": "Update Fund",
+ "fundDelete": "Delete Fund",
+ "noFundsFound": "No Funds Found",
+ "createdBy": "Created By",
+ "createdOn": "Created On",
+ "status": "Status",
+ "fundCreated": "Fund created successfully",
+ "fundUpdated": "Fund updated successfully",
+ "fundDeleted": "Fund deleted successfully",
+ "deleteFundMsg": "Are you sure you want to delete this fund?",
+ "createdLatest": "Created Latest",
+ "createdEarliest": "Created Earliest",
+ "viewCampaigns": "View Campaigns",
+ "searchByName": "searchByName"
+ },
+ "fundCampaign": {
+ "title": "Fundraising Campaigns",
+ "campaignName": "Campaign Name",
+ "campaignOptions": "Options",
+ "fundingGoal": "Funding Goal",
+ "addCampaign": "Add Campaign",
+ "createdCampaign": "Campaign created successfully",
+ "updatedCampaign": "Campaign updated successfully",
+ "deletedCampaign": "Campaign deleted successfully",
+ "deleteCampaignMsg": "Are you sure you want to delete this campaign?",
+ "noCampaigns": "No Campaigns Found",
+ "createCampaign": "Create Campaign",
+ "updateCampaign": "Update Campaign",
+ "deleteCampaign": "Delete Campaign",
+ "currency": "Currency",
+ "selectCurrency": "Select Currency",
+ "searchFullName": "Search By Name",
+ "viewPledges": "View Pledges",
+ "noCampaignsFound": "No Campaigns Found",
+ "latestEndDate": "Latest End Date",
+ "earliestEndDate": "Earliest End Date",
+ "lowestGoal": "Lowest Goal",
+ "highestGoal": "Highest Goal"
+ },
+ "pledges": {
+ "title": "Fund Campaign Pledges",
+ "pledgeAmount": "Pledge Amount",
+ "pledgeOptions": "Options",
+ "pledgeCreated": "Pledge created successfully",
+ "pledgeUpdated": "Pledge updated successfully",
+ "pledgeDeleted": "Pledge deleted successfully",
+ "addPledge": "Add Pledge",
+ "createPledge": "Create Pledge",
+ "currency": "Currency",
+ "selectCurrency": "Select Currency",
+ "updatePledge": "Update Pledge",
+ "deletePledge": "Delete Pledge",
+ "amount": "Amount",
+ "editPledge": "Edit Pledge",
+ "deletePledgeMsg": "Are you sure you want to delete this pledge?",
+ "noPledges": "No Pledges Found",
+ "searchPledger": "Search By Pledgers",
+ "highestAmount": "Highest Amount",
+ "lowestAmount": "Lowest Amount",
+ "latestEndDate": "Latest End Date",
+ "earliestEndDate": "Earliest End Date",
+ "campaigns": "Campaigns",
+ "pledges": "Pledges",
+ "endsOn": "Ends on",
+ "raisedAmount": "Raised amount ",
+ "pledgedAmount": "Pledged amount",
+ "startDate": "startDate",
+ "endDate": "endDate"
+ },
+ "orgPost": {
+ "title": "Posts",
+ "searchPost": "Search Post",
+ "posts": "Posts",
+ "createPost": "Create Post",
+ "postDetails": "Post Details",
+ "postTitle1": "Write title of the post",
+ "postTitle": "Title",
+ "addMedia": "Upload Media",
+ "information": "Information",
+ "information1": "Write information of the post",
+ "addPost": "Add Post",
+ "searchTitle": "Search By Title",
+ "searchText": "Search By Text",
+ "ptitle": "Post Title",
+ "postDes": "What do you to talk about?",
+ "Title": "Title",
+ "Text": "Text",
+ "searchBy": "Search By",
+ "Oldest": "Oldest First",
+ "Latest": "Latest First",
+ "sortPost": "Sort Post",
+ "tag": " Your browser does not support the video tag",
+ "postCreatedSuccess": "Congratulations! You have Posted Something.",
+ "pinPost": "Pin post",
+ "Next": "Next Page",
+ "Previous": "Previous Page",
+ "cancel": "cancel"
+ },
+ "postNotFound": {
+ "post": "Post",
+ "not found!": "Not Found!",
+ "organization": "Organization",
+ "post not found!": "Post Not Found!",
+ "organization not found!": "Organization Not Found!"
+ },
+ "userNotFound": {
+ "not found!": "Not Found!",
+ "roles": "Roles",
+ "user not found!": "User Not Found!",
+ "member not found!": "Member Not Found!",
+ "admin not found!": "Admin Not Found!",
+ "roles not found!": "Roles Not Found!",
+ "user": "user"
+ },
+ "orgPostCard": {
+ "author": "Author",
+ "imageURL": "Image URL",
+ "videoURL": "Video URL",
+ "deletePost": "Delete Post",
+ "deletePostMsg": "Do you want to remove this post?",
+ "editPost": "Edit Post",
+ "postTitle": "Title",
+ "postTitle1": "Edit title of the post",
+ "information1": "Edit information of the post",
+ "information": "Information",
+ "image": "Image",
+ "video": "Video",
+ "updatePost": "Update Post",
+ "postDeleted": "Post deleted successfully.",
+ "postUpdated": "Post Updated successfully.",
+ "tag": " Your browser does not support the video tag",
+ "pin": "Pin Post",
+ "edit": "edit",
+ "no": "no",
+ "yes": "yes",
+ "close": "close",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "blockUnblockUser": {
+ "title": "Block/Unblock User",
+ "pageName": "Block/Unblock",
+ "listOfUsers": "List of Users who spammed",
+ "block_unblock": "Block/Unblock",
+ "unblock": "UnBlock",
+ "block": "Block",
+ "orgName": "Enter Name",
+ "blockedSuccessfully": "User blocked successfully",
+ "Un-BlockedSuccessfully": "User Un-Blocked successfully",
+ "allMembers": "All Members",
+ "blockedUsers": "Blocked Users",
+ "searchByFirstName": "Search By First Name",
+ "searchByLastName": "Search By Last Name",
+ "noSpammerFound": "No spammer found",
+ "searchByName": "searchByName",
+ "name": "name",
+ "email": "email",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "noResultsFoundFor": "noResultsFoundFor"
+ },
+ "eventManagement": {
+ "title": "Event Management Dashboard",
+ "dashboard": "Dashboard",
+ "registrants": "Registrants",
+ "attendance": "Attendance",
+ "actions": "Actions",
+ "agendas": "Agendas",
+ "statistics": "Statistics",
+ "to": "TO",
+ "volunteers": "Volunteers"
+ },
+ "eventAttendance": {
+ "historical_statistics": "Historical Statistics",
+ "Search member": "Search member",
+ "Member Name": "Member Name",
+ "Status": "Status",
+ "Events Attended": "Events Attended",
+ "Task Assigned": "Task Assigned",
+ "Member": "Member",
+ "Admin": "Admin",
+ "loading": "Loading...",
+ "noAttendees": "Attendees not Found"
+ },
+ "onSpotAttendee": {
+ "title": "On-spot Attendee",
+ "enterFirstName": "Enter First Name",
+ "enterLastName": "Enter Last Name",
+ "enterEmail": "Enter Email",
+ "enterPhoneNo": "Enter Phone Number",
+ "selectGender": "Select Gender",
+ "invalidDetailsMessage": "Please fill in all required fields",
+ "orgIdMissing": "Organization ID is missing. Please try again.",
+ "attendeeAddedSuccess": "Attendee added successfully!",
+ "addAttendee": "Add",
+ "phoneNumber": "Phone No.",
+ "addingAttendee": "Adding...",
+ "male": "Male",
+ "female": "Female",
+ "other": "Other"
+ },
+ "forgotPassword": {
+ "title": "Talawa Forgot Password",
+ "registeredEmail": "Registered Email",
+ "getOtp": "Get OTP",
+ "enterOtp": "Enter OTP",
+ "enterNewPassword": "Enter New Password",
+ "cofirmNewPassword": "Confirm New Password",
+ "changePassword": "Change Password",
+ "backToLogin": "Back to Login",
+ "userOtp": "e.g. 12345",
+ "emailNotRegistered": "Email is not registered.",
+ "errorSendingMail": "Error in sending mail.",
+ "passwordMismatches": "Password and Confirm password mismatches.",
+ "passwordChanges": "Password changes successfully.",
+ "OTPsent": "OTP is sent to your registered email.",
+ "forgotPassword": "forgotPassword",
+ "password": "password",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "pageNotFound": {
+ "title": "404 Not Found",
+ "talawaAdmin": "Talawa Admin",
+ "talawaUser": "Talawa User",
+ "404": "404",
+ "notFoundMsg": "Oops! The Page you requested was not found!",
+ "backToHome": "Back to Home"
+ },
+ "orgContribution": {
+ "title": "Talawa Contributions",
+ "filterByName": "Filter by Name",
+ "filterByTransId": "Filter by Trans. ID",
+ "recentStats": "Recent Stats",
+ "contribution": "Contribution",
+ "orgname": "Enter Name",
+ "searchtransaction": "Enter Transaction ID"
+ },
+ "contriStats": {
+ "recentContribution": "Recent Contribution",
+ "highestContribution": "Highest Contribution",
+ "totalContribution": "Total Contribution"
+ },
+ "orgContriCards": {
+ "date": "Date",
+ "transactionId": "Transaction ID",
+ "amount": "Amount"
+ },
+ "orgSettings": {
+ "title": "Settings",
+ "general": "General",
+ "actionItemCategories": "Action Item Categories",
+ "updateOrganization": "Update Organization",
+ "seeRequest": "See Request",
+ "noData": "No data",
+ "otherSettings": "Other Settings",
+ "changeLanguage": "Change Language",
+ "manageCustomFields": "Manage Custom Fields",
+ "agendaItemCategories": "Agenda Item Categories"
+ },
+ "deleteOrg": {
+ "deleteOrganization": "Delete Organization",
+ "deleteSampleOrganization": "Delete Sample Organization",
+ "deleteMsg": "Do you want to delete this organization?",
+ "confirmDelete": "Confirm Delete",
+ "longDelOrgMsg": "By clicking on Delete Organization button the organization will be permanently deleted along with its events, tags and all related data.",
+ "successfullyDeletedSampleOrganization": "Successfully deleted sample Organization",
+ "cancel": "cancel"
+ },
+ "userUpdate": {
+ "appLanguageCode": "Default Language",
+ "userType": "User Type",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "email": "email",
+ "password": "password",
+ "admin": "admin",
+ "superAdmin": "superAdmin",
+ "displayImage": "displayImage",
+ "saveChanges": "saveChanges",
+ "cancel": "cancel"
+ },
+ "userPasswordUpdate": {
+ "previousPassword": "Previous Password",
+ "newPassword": "New Password",
+ "confirmNewPassword": "Confirm New Password",
+ "passCantBeEmpty": "Password can't be empty",
+ "passNoMatch": "New and Confirm password do not match.",
+ "saveChanges": "saveChanges",
+ "cancel": "cancel"
+ },
+ "orgDelete": {
+ "deleteOrg": "Delete Org"
+ },
+ "membershipRequest": {
+ "accept": "Accept",
+ "reject": "Reject",
+ "memberAdded": "it is accepted",
+ "joined": "joined",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "orgUpdate": {
+ "city": "City",
+ "countryCode": "Country Code",
+ "line1": "Line 1",
+ "line2": "Line 2",
+ "postalCode": "Postal Code",
+ "dependentLocality": "Dependent Locality",
+ "sortingCode": "Sorting code",
+ "state": "State / Province",
+ "userRegistrationRequired": "User Registration Required",
+ "isVisibleInSearch": "Visible in Search",
+ "enterNameOrganization": "Enter Organization Name",
+ "successfulUpdated": "Organization updated successfully",
+ "name": "name",
+ "description": "description",
+ "location": "location",
+ "address": "address",
+ "displayImage": "displayImage",
+ "saveChanges": "saveChanges",
+ "cancel": "cancel",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "addOnRegister": {
+ "addNew": "Add New",
+ "addPlugin": "Add Plugin",
+ "pluginName": "Plugin Name",
+ "creatorName": "Creator Name",
+ "pluginDesc": "Plugin Description",
+ "pName": "Ex: Donations",
+ "cName": "Ex: john Doe",
+ "pDesc": "This Plugin enables UI for",
+ "close": "close",
+ "register": "register"
+ },
+ "addOnStore": {
+ "title": "Add On Store",
+ "searchName": "Ex: Donations",
+ "search": "Search",
+ "enable": "Enabled",
+ "disable": "Disabled",
+ "pHeading": "Plugins",
+ "install": "Installed",
+ "available": "Available",
+ "pMessage": "Plugin does not exists",
+ "filter": "Filters"
+ },
+ "addOnEntry": {
+ "enable": "Enabled",
+ "install": "Install",
+ "uninstall": "Uninstall",
+ "uninstallMsg": "This feature is now removed from your organization",
+ "installMsg": "This feature is now enabled in your organization"
+ },
+ "memberDetail": {
+ "title": "User Details",
+ "addAdmin": "Add Admin",
+ "noeventsAttended": "No Events Attended",
+ "alreadyIsAdmin": "Member is already an Admin",
+ "organizations": "Organizations",
+ "events": "Events",
+ "role": "Role",
+ "createdOn": "Created on",
+ "main": "Main",
+ "firstName": "First name",
+ "lastName": "Last name",
+ "language": "Language",
+ "gender": "Gender",
+ "birthDate": "Birth Date",
+ "educationGrade": "Educational Grade",
+ "employmentStatus": "Employment Status",
+ "maritalStatus": "Marital Status",
+ "phone": "Phone",
+ "countryCode": "Country Code",
+ "state": "State",
+ "city": "City",
+ "personalInfoHeading": "Personal Information",
+ "viewAll": "View All",
+ "eventsAttended": "Events Attended",
+ "contactInfoHeading": "Contact Information",
+ "actionsHeading": "Actions",
+ "personalDetailsHeading": "Profile Details",
+ "appLanguageCode": "Choose Language",
+ "deleteUser": "Delete User",
+ "pluginCreationAllowed": "Plugin creation allowed",
+ "created": "Created",
+ "adminForOrganizations": "Admin for organizations",
+ "membershipRequests": "Membership requests",
+ "adminForEvents": "Admin for events",
+ "addedAsAdmin": "User is added as admin.",
+ "userType": "User Type",
+ "email": "email",
+ "displayImage": "displayImage",
+ "address": "address",
+ "delete": "delete",
+ "saveChanges": "saveChanges",
+ "joined": "joined",
+ "talawaApiUnavailable": "talawaApiUnavailable",
+ "unassignUserTag": "Unassign Tag",
+ "unassignUserTagMessage": "Do you want to remove the tag from this user?",
+ "successfullyUnassigned": "Tag unassigned from user",
+ "tagsAssigned": "Tags Assigned",
+ "noTagsAssigned": "No Tags Assigned"
+ },
+ "people": {
+ "title": "People",
+ "searchUsers": "Search users"
+ },
+ "userLogin": {
+ "login": "Login",
+ "loginIntoYourAccount": "Login into your account",
+ "invalidDetailsMessage": "Please enter a valid email and password.",
+ "notAuthorised": "Sorry! you are not Authorised!",
+ "invalidCredentials": "Entered credentials are incorrect. Please enter valid credentials.",
+ "forgotPassword": "forgotPassword",
+ "emailAddress": "emailAddress",
+ "enterEmail": "enterEmail",
+ "password": "password",
+ "enterPassword": "enterPassword",
+ "register": "register",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "userRegister": {
+ "enterFirstName": "Enter your first name",
+ "enterLastName": "Enter your last name",
+ "enterConfirmPassword": "Enter Password to confirm",
+ "alreadyhaveAnAccount": "Already have an account?",
+ "login": "Login",
+ "afterRegister": "Successfully registered. Please wait for admin to approve your request.",
+ "passwordNotMatch": "Password doesn't match. Confirm Password and try again.",
+ "invalidDetailsMessage": "Please enter valid details.",
+ "register": "register",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "emailAddress": "emailAddress",
+ "enterEmail": "enterEmail",
+ "password": "password",
+ "enterPassword": "enterPassword",
+ "confirmPassword": "confirmPassword",
+ "talawaApiUnavailable": "talawaApiUnavailable"
+ },
+ "userNavbar": {
+ "talawa": "Talawa",
+ "home": "Home",
+ "people": "People",
+ "events": "Events",
+ "chat": "Chat",
+ "donate": "Donate",
+ "language": "Language",
+ "settings": "settings",
+ "logout": "logout",
+ "close": "close"
+ },
+ "userOrganizations": {
+ "allOrganizations": "All Organizations",
+ "joinedOrganizations": "Joined Organizations",
+ "createdOrganizations": "Created Organizations",
+ "selectOrganization": "Select an organization",
+ "searchUsers": "Search users",
+ "nothingToShow": "Nothing to show here.",
+ "organizations": "Organizations",
+ "search": "search",
+ "filter": "filter",
+ "searchByName": "searchByName",
+ "searchOrganizations": "Search Organization"
+ },
+ "userSidebarOrg": {
+ "yourOrganizations": "Your Organizations",
+ "noOrganizations": "You haven't joined any organization yet.",
+ "viewAll": "View all",
+ "talawaUserPortal": "Talawa User Portal",
+ "my organizations": "My Organizations",
+ "users": "Users",
+ "requests": "Requests",
+ "communityProfile": "Community Profile",
+ "logout": "Logout",
+ "settings": "Settings",
+ "chat": "Chat",
+ "menu": "menu"
+ },
+ "organizationSidebar": {
+ "viewAll": "View all",
+ "events": "Events",
+ "noEvents": "No Events to show",
+ "noMembers": "No Members to show",
+ "members": "members"
+ },
+ "postCard": {
+ "likes": "Likes",
+ "comments": "Comments",
+ "viewPost": "View Post",
+ "editPost": "Edit Post",
+ "postedOn": "Posted on {{date}}"
+ },
+ "home": {
+ "posts": "Posts",
+ "post": "Post",
+ "title": "Posts",
+ "textArea": "Something on your mind?",
+ "feed": "Feed",
+ "loading": "Loading",
+ "pinnedPosts": "Pinned Posts",
+ "yourFeed": "Your Feed",
+ "nothingToShowHere": "Nothing to show here",
+ "somethingOnYourMind": "Something on your mind?",
+ "addPost": "Add Post",
+ "startPost": "Start a post",
+ "media": "Media",
+ "event": "Event",
+ "article": "Article",
+ "postNowVisibleInFeed": "Post now visible in feed"
+ },
+ "settings": {
+ "eventAttended": "Events Attended",
+ "noeventsAttended": "No Events Attended",
+ "profileSettings": "Profile Settings",
+ "gender": "Gender",
+ "phoneNumber": "Phone Number",
+ "chooseFile": "Choose File",
+ "birthDate": "Birth Date",
+ "grade": "Educational Grade",
+ "empStatus": "Employment Status",
+ "maritalStatus": "Marital Status",
+ "state": "City/State",
+ "country": "Country",
+ "resetChanges": "Reset Changes",
+ "profileDetails": "Profile Details",
+ "deleteUserMessage": "By clicking on Delete User button your user will be permanently deleted along with its events, tags and all related data.",
+ "copyLink": "Copy Profile Link",
+ "deleteUser": "Delete User",
+ "otherSettings": "Other Settings",
+ "changeLanguage": "Change Language",
+ "sgender": "Select gender",
+ "gradePlaceholder": "Enter Grade",
+ "sEmpStatus": "Select employement status",
+ "female": "Female",
+ "male": "Male",
+ "employed": "Employed",
+ "other": "Other",
+ "sMaritalStatus": "Select marital status",
+ "unemployed": "Unemployed",
+ "married": "Married",
+ "single": "Single",
+ "widowed": "Widowed",
+ "divorced": "Divorced",
+ "engaged": "Engaged",
+ "separated": "Separated",
+ "grade1": "Grade 1",
+ "grade2": "Grade 2",
+ "grade3": "Grade 3",
+ "grade4": "Grade 4",
+ "grade5": "Grade 5",
+ "grade6": "Grade 6",
+ "grade7": "Grade 7",
+ "grade8": "Grade 8",
+ "grade9": "Grade 9",
+ "grade10": "Grade 10",
+ "grade11": "Grade 11",
+ "grade12": "Grade 12",
+ "graduate": "Graduate",
+ "kg": "KG",
+ "preKg": "Pre-KG",
+ "noGrade": "No Grade",
+ "fullTime": "Full Time",
+ "partTime": "Part Time",
+ "selectCountry": "Select a country",
+ "enterState": "Enter City or State",
+ "settings": "settings",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "emailAddress": "emailAddress",
+ "displayImage": "displayImage",
+ "address": "address",
+ "saveChanges": "saveChanges",
+ "joined": "joined"
+ },
+ "donate": {
+ "title": "Donations",
+ "donations": "Donations",
+ "searchDonations": "Search donations",
+ "donateForThe": "Donate for the",
+ "amount": "Amount",
+ "yourPreviousDonations": "Your Previous Donations",
+ "donate": "Donate",
+ "nothingToShow": "Nothing to show here.",
+ "success": "Donation Successful",
+ "invalidAmount": "Please enter a numerical value for the donation amount.",
+ "donationAmountDescription": "Please enter the numerical value for the donation amount.",
+ "donationOutOfRange": "Donation amount must be between {{min}} and {{max}}.",
+ "donateTo": "donateTo"
+ },
+ "userEvents": {
+ "title": "Events",
+ "nothingToShow": "Nothing to show here.",
+ "createEvent": "Create Event",
+ "recurring": "Recurring Event",
+ "listView": "List View",
+ "calendarView": "Calendar View",
+ "allDay": "All Day",
+ "eventCreated": "Event created and posted successfully.",
+ "eventDetails": "Event Details",
+ "eventTitle": "Title",
+ "enterTitle": "Enter Title",
+ "enterDescription": "Enter Description",
+ "publicEvent": "Is Public",
+ "registerable": "Is Registerable",
+ "monthlyCalendarView": "Monthly Calendar",
+ "yearlyCalendarView": "Yearly Calender",
+ "startTime": "startTime",
+ "endTime": "endTime",
+ "enterLocation": "enterLocation",
+ "search": "search",
+ "cancel": "cancel",
+ "create": "create",
+ "eventDescription": "eventDescription",
+ "eventLocation": "eventLocation",
+ "startDate": "startDate",
+ "endDate": "endDate"
+ },
+ "userEventCard": {
+ "starts": "Starts",
+ "ends": "Ends",
+ "creator": "Creator",
+ "alreadyRegistered": "Already registered",
+ "location": "location",
+ "register": "register"
+ },
+ "advertisement": {
+ "title": "Advertisements",
+ "activeAds": "Active Campaigns",
+ "archievedAds": "Completed Campaigns",
+ "pMessage": "Ads not present for this campaign.",
+ "validLink": "Link is valid",
+ "invalidLink": "Link is invalid",
+ "Rname": "Enter name of Advertisement",
+ "Rtype": "Select type of Advertisement",
+ "Rmedia": "Provide media content to be displayed",
+ "RstartDate": "Select Start Date",
+ "RendDate": "Select End Date",
+ "RClose": "Close the window",
+ "addNew": "Create new advertisement",
+ "EXname": "Ex. Cookie Shop",
+ "EXlink": "Ex. http://yourwebsite.com/photo",
+ "createAdvertisement": "Create Advertisement",
+ "deleteAdvertisement": "Delete Advertisement",
+ "deleteAdvertisementMsg": "Do you want to remove this advertisement?",
+ "view": "View",
+ "editAdvertisement": "Edit Advertisement",
+ "advertisementDeleted": "Advertisement deleted successfully.",
+ "endDateGreaterOrEqual": "End Date should be greater than or equal to Start Date",
+ "advertisementCreated": "Advertisement created successfully.",
+ "pHeading": "pHeading",
+ "delete": "delete",
+ "close": "close",
+ "no": "no",
+ "yes": "yes",
+ "edit": "edit",
+ "saveChanges": "saveChanges",
+ "endOfResults": "endOfResults"
+ },
+ "userChat": {
+ "chat": "Chat",
+ "search": "Search",
+ "messages": "Messages",
+ "contacts": "Contacts"
+ },
+ "userChatRoom": {
+ "selectContact": "Select a contact to start conversation",
+ "sendMessage": "Send Message"
+ },
+ "orgProfileField": {
+ "loading": "Loading...",
+ "noCustomField": "No custom fields available",
+ "customFieldName": "Field Name",
+ "enterCustomFieldName": "Enter Field name",
+ "customFieldType": "Field Type",
+ "Remove Custom Field": "Remove Custom Field",
+ "fieldSuccessMessage": "Field added successfully",
+ "fieldRemovalSuccess": "Field removed successfully",
+ "String": "String",
+ "Boolean": "Boolean",
+ "Date": "Date",
+ "Number": "Number",
+ "saveChanges": "saveChanges"
+ },
+ "orgActionItemCategories": {
+ "enableButton": "Enable",
+ "disableButton": "Disable",
+ "updateActionItemCategory": "Update",
+ "actionItemCategoryName": "Name",
+ "categoryDetails": "Category Details",
+ "enterName": "Enter Name",
+ "successfulCreation": "Action Item Category created successfully",
+ "successfulUpdation": "Action Item Category updated successfully",
+ "sameNameConflict": "Please change the name to make an update",
+ "categoryEnabled": "Action Item Category Enabled",
+ "categoryDisabled": "Action Item Category Disabled",
+ "noActionItemCategories": "No Action Item Categories",
+ "status": "Status",
+ "categoryDeleted": "Action Item Category deleted successfully",
+ "deleteCategory": "Delete Category",
+ "deleteCategoryMsg": "Are you sure you want to delete this Action Item Category?",
+ "createButton": "createButton",
+ "editButton": "editButton"
+ },
+ "organizationVenues": {
+ "title": "Venues",
+ "addVenue": "Add Venue",
+ "venueDetails": "Venue Details",
+ "venueName": "Name of the Venue",
+ "enterVenueName": "Enter Venue Name",
+ "enterVenueDesc": "Enter Venue Description",
+ "capacity": "Capacity",
+ "enterVenueCapacity": "Enter Venue Capacity",
+ "image": "Venue Image",
+ "uploadVenueImage": "Upload Venue Image",
+ "createVenue": "Create Venue",
+ "venueAdded": "Venue added Successfully",
+ "editVenue": "Update Venue",
+ "venueUpdated": "Venue details updated successfully",
+ "sort": "Sort",
+ "highestCapacity": "Highest Capacity",
+ "lowestCapacity": "Lowest Capacity",
+ "noVenues": "No Venues Found!",
+ "view": "View",
+ "venueTitleError": "Venue title cannot be empty!",
+ "venueCapacityError": "Capacity must be a positive number!",
+ "searchBy": "Search By",
+ "description": "description",
+ "edit": "edit",
+ "delete": "delete",
+ "name": "name",
+ "desc": "desc"
+ },
+ "addMember": {
+ "title": "Add Member",
+ "addMembers": "Add Members",
+ "existingUser": "Existing User",
+ "newUser": "New User",
+ "searchFullName": "Search by Full Name",
+ "enterFirstName": "Enter First Name",
+ "enterLastName": "Enter Last Name",
+ "enterConfirmPassword": "Enter Confirm Password",
+ "organization": "Organization",
+ "invalidDetailsMessage": "Please provide all required details.",
+ "passwordNotMatch": "Passwords do not match.",
+ "addMember": "Add Member",
+ "firstName": "firstName",
+ "lastName": "lastName",
+ "emailAddress": "emailAddress",
+ "enterEmail": "enterEmail",
+ "password": "password",
+ "enterPassword": "enterPassword",
+ "confirmPassword": "confirmPassword",
+ "cancel": "cancel",
+ "create": "create",
+ "user": "user",
+ "profile": "profile"
+ },
+ "eventActionItems": {
+ "title": "Action Items",
+ "createActionItem": "Create Action Item",
+ "actionItemCategory": "Action Item Category",
+ "selectActionItemCategory": "Select an action item category",
+ "selectAssignee": "Select an assignee",
+ "assignee": "Assignee",
+ "assigner": "Assigner",
+ "preCompletionNotes": "Notes",
+ "postCompletionNotes": "Completion Notes",
+ "assignmentDate": "Assignment Date",
+ "status": "Status",
+ "actionItemActive": "Active",
+ "actionItemStatus": "Action Item Status",
+ "actionItemCompleted": "Action Item Completed",
+ "markCompletion": "Mark Completion",
+ "actionItemDetails": "Action Item Details",
+ "dueDate": "Due Date",
+ "completionDate": "Completion Date",
+ "editActionItem": "Edit Action Item",
+ "deleteActionItem": "Delete Action Item",
+ "deleteActionItemMsg": "Do you want to remove this action item?",
+ "successfulDeletion": "Action Item deleted successfully",
+ "successfulCreation": "Action Item created successfully",
+ "successfulUpdation": "Action Item updated successfully",
+ "notes": "Notes",
+ "save": "Save",
+ "yes": "yes",
+ "no": "no"
+ },
+ "checkIn": {
+ "errorCheckingIn": "Error checking in",
+ "checkedInSuccessfully": "Checked in successfully"
+ },
+ "eventRegistrantsModal": {
+ "errorAddingAttendee": "Error adding attendee",
+ "errorRemovingAttendee": "Error removing attendee"
+ },
+ "userCampaigns": {
+ "title": "Fundraising Campaigns",
+ "searchByName": "Search by Name...",
+ "searchBy": "Search by",
+ "pledgers": "Pledgers",
+ "campaigns": "Campaigns",
+ "myPledges": "My Pledges",
+ "lowestAmount": "Lowest Amount",
+ "highestAmount": "Highest Amount",
+ "lowestGoal": "Lowest Goal",
+ "highestGoal": "Highest Goal",
+ "latestEndDate": "Latest End Date",
+ "earliestEndDate": "Earliest End Date",
+ "addPledge": "Add Pledge",
+ "viewPledges": "View Pledges",
+ "noPledges": "No Pledges Found",
+ "noCampaigns": "No Campaigns Found"
+ },
+ "userPledges": {
+ "title": "My Pledges"
+ },
+ "eventVolunteers": {
+ "volunteers": "Volunteers",
+ "volunteer": "Volunteer",
+ "volunteerGroups": "Volunteer Groups",
+ "individuals": "Individuals",
+ "groups": "Groups",
+ "status": "Status",
+ "noVolunteers": "No Volunteers",
+ "noVolunteerGroups": "No Volunteer Groups",
+ "add": "Add",
+ "mostHoursVolunteered": "Most Hours Volunteered",
+ "leastHoursVolunteered": "Least Hours Volunteered",
+ "accepted": "Accepted",
+ "addVolunteer": "Add Volunteer",
+ "removeVolunteer": "Remove Volunteer",
+ "volunteerAdded": "Volunteer added successfully",
+ "volunteerRemoved": "Volunteer removed successfully",
+ "volunteerGroupCreated": "Volunteer group created successfully",
+ "volunteerGroupUpdated": "Volunteer group updated successfully",
+ "volunteerGroupDeleted": "Volunteer group deleted successfully",
+ "removeVolunteerMsg": "Are you sure you want to remove this Volunteer?",
+ "deleteVolunteerGroupMsg": "Are you sure you want to delete this Volunteer Group?",
+ "leader": "Leader",
+ "group": "Group",
+ "createGroup": "Create Group",
+ "updateGroup": "Update Group",
+ "deleteGroup": "Delete Group",
+ "volunteersRequired": "Volunteers Required",
+ "volunteerDetails": "Volunteer Details",
+ "hoursVolunteered": "Hours Volunteered",
+ "groupDetails": "Group Details",
+ "creator": "Creator",
+ "requests": "Requests",
+ "noRequests": "No Requests",
+ "latest": "Latest",
+ "earliest": "Earliest",
+ "requestAccepted": "Request accepted successfully",
+ "requestRejected": "Request rejected successfully",
+ "details": "Details",
+ "manageGroup": "Manage Group",
+ "mostVolunteers": "Most Volunteers",
+ "leastVolunteers": "Least Volunteers"
+ },
+ "userVolunteer": {
+ "title": "Volunteership",
+ "name": "Title",
+ "upcomingEvents": "Upcoming Events",
+ "requests": "Requests",
+ "invitations": "Invitations",
+ "groups": "Volunteer Groups",
+ "actions": "Actions",
+ "searchByName": "Search by Name",
+ "latestEndDate": "Latest End Date",
+ "earliestEndDate": "Earliest End Date",
+ "noEvents": "No Upcoming Events",
+ "volunteer": "Volunteer",
+ "volunteered": "Volunteered",
+ "join": "Join",
+ "joined": "Joined",
+ "searchByEventName": "Search by Event title",
+ "filter": "Filter",
+ "groupInvite": "Group Invite",
+ "individualInvite": "Individual Invite",
+ "noInvitations": "No Invitations",
+ "accept": "Accept",
+ "reject": "Reject",
+ "receivedLatest": "Received Latest",
+ "receivedEarliest": "Received Earliest",
+ "invitationAccepted": "Invitation accepted successfully",
+ "invitationRejected": "Invitation rejected successfully",
+ "volunteerSuccess": "Requested to volunteer successfully",
+ "recurring": "Recurring",
+ "groupInvitationSubject": "Invitation to join volunteer group",
+ "eventInvitationSubject": "Invitation to volunteer for event"
+ }
+}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
new file mode 100644
index 0000000000..132a913c7d
--- /dev/null
+++ b/public/locales/fr/common.json
@@ -0,0 +1,98 @@
+{
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "searchByName": "Rechercher par nom",
+ "loading": "Chargement...",
+ "endOfResults": "Fin des résultats",
+ "noResultsFoundFor": "Aucun résultat trouvé pour ",
+ "edit": "Modifier",
+ "admins": "Administrateurs",
+ "admin": "ADMINISTRATEUR",
+ "user": "UTILISATEUR",
+ "superAdmin": "SUPERADMIN",
+ "members": "Membres",
+ "logout": "Se déconnecter",
+ "login": "Se connecter",
+ "register": "Registre",
+ "menu": "Menu",
+ "settings": "Paramètres",
+ "users": "Utilisateurs",
+ "requests": "Demandes",
+ "OR": "OU",
+ "cancel": "Annuler",
+ "close": "Fermer",
+ "create": "Créer",
+ "delete": "Supprimer",
+ "done": "Fait",
+ "yes": "Oui",
+ "no": "Non",
+ "filter": "Filtre",
+ "search": "Recherche",
+ "description": "Description",
+ "saveChanges": "Sauvegarder les modifications",
+ "resetChanges": "Réinitialiser les modifications",
+ "displayImage": "Afficher l'image",
+ "enterEmail": "Entrez l'e-mail",
+ "emailAddress": "Adresse e-mail",
+ "email": "E-mail",
+ "name": "Nom",
+ "gender": "Genre",
+ "desc": "Description",
+ "enterPassword": "Entrer le mot de passe",
+ "password": "Mot de passe",
+ "confirmPassword": "Confirmez le mot de passe",
+ "forgotPassword": "Mot de passe oublié ?",
+ "talawaAdminPortal": "Portail d'administration Talawa",
+ "address": "Adresse",
+ "location": "Emplacement",
+ "enterLocation": "Entrez l'emplacement",
+ "joined": "Rejoint",
+ "startDate": "Date de début",
+ "endDate": "Date de fin",
+ "startTime": "Heure de début",
+ "endTime": "Heure de fin",
+ "My Organizations": "Mes Organisations",
+ "Dashboard": "Tableau de Bord",
+ "People": "Personnes",
+ "Events": "Événements",
+ "Venues": "Lieux",
+ "Action Items": "Éléments d'Action",
+ "Posts": "Publications",
+ "Block/Unblock": "Bloquer/Débloquer",
+ "Advertisement": "Publicité",
+ "Funds": "Fonds",
+ "Membership Requests": "Demandes d'Adhésion",
+ "Plugins": "Plugins",
+ "Plugin Store": "Magasin de Plugins",
+ "Settings": "Paramètres",
+ "createdOn": "Créé Le",
+ "createdBy": "Créé Par",
+ "usersRole": "Rôle de l'Utilisateur",
+ "changeRole": "Changer de Rôle",
+ "action": "Action",
+ "removeUser": "Supprimer l'Utilisateur",
+ "remove": "Supprimer",
+ "viewProfile": "Voir le Profil",
+ "profile": "Profil",
+ "noFiltersApplied": "Aucun filtre appliqué",
+ "manage": "Gérer",
+ "searchResultsFor": "Résultats de recherche pour {{text}}",
+ "none": "Aucun",
+ "sort": "Trier",
+ "Donate": "Faire un don",
+ "addedSuccessfully": "{{item}} ajouté avec succès",
+ "updatedSuccessfully": "{{item}} mis à jour avec succès",
+ "removedSuccessfully": "{{item}} supprimé avec succès",
+ "successfullyUpdated": "Mis à jour avec succès",
+ "sessionWarning": "Votre session expirera bientôt en raison de l'inactivité. Veuillez interagir avec la page pour prolonger votre session.",
+ "sessionLogOut": "Votre session a expiré en raison de l'inactivité. Veuillez vous reconnecter pour continuer.",
+ "all": "Tous",
+ "active": "Actif",
+ "disabled": "Désactivé",
+ "pending": "En attente",
+ "completed": "Complété",
+ "late": "En retard",
+ "createdLatest": "Créé le plus récemment",
+ "createdEarliest": "Créé le plus tôt",
+ "searchBy": "Rechercher par {{item}}"
+}
diff --git a/public/locales/fr/errors.json b/public/locales/fr/errors.json
new file mode 100644
index 0000000000..e9a7cf4fd9
--- /dev/null
+++ b/public/locales/fr/errors.json
@@ -0,0 +1,11 @@
+{
+ "talawaApiUnavailable": "Le service Talawa-API n'est pas disponible !. ",
+ "notFound": "Pas trouvé",
+ "unknownError": "Une erreur inconnue est survenue. {{msg}}",
+ "notAuthorised": "Désolé! ",
+ "errorSendingMail": "Erreur lors de l'envoi du courrier",
+ "emailNotRegistered": "Email non enregistré",
+ "notFoundMsg": "Oops! ",
+ "errorOccurredCouldntCreate": "Une erreur s'est produite. Impossible de créer {{entity}}",
+ "errorLoading": "Une erreur s'est produite lors du chargement des données {{entity}}"
+}
diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json
new file mode 100644
index 0000000000..d089aefeb5
--- /dev/null
+++ b/public/locales/fr/translation.json
@@ -0,0 +1,1483 @@
+{
+ "leaderboard": {
+ "title": "Tableau des Leaders",
+ "searchByVolunteer": "Recherche par Bénévole",
+ "mostHours": "Le Plus d'Heures",
+ "leastHours": "Le Moins d'Heures",
+ "timeFrame": "Période",
+ "allTime": "Tout le Temps",
+ "weekly": "Cette Semaine",
+ "monthly": "Ce Mois",
+ "yearly": "Cette Année",
+ "noVolunteers": "Aucun Bénévole Trouvé!"
+ },
+ "loginPage": {
+ "title": "Administrateur Talawa",
+ "fromPalisadoes": "Une application open source réalisée par les bénévoles de la Fondation Palisadoes",
+ "userLogin": "Utilisateur en ligne",
+ "atleast_8_char_long": "Au moins 8 caractères",
+ "atleast_6_char_long": "Au moins 6 caractères",
+ "firstName_invalid": "Le prénom ne doit contenir que des lettres minuscules et majuscules",
+ "lastName_invalid": "Le nom de famille ne doit contenir que des lettres minuscules et majuscules",
+ "password_invalid": "Le mot de passe doit contenir au moins une lettre minuscule, une lettre majuscule, une valeur numérique et un caractère spécial",
+ "email_invalid": "L'e-mail doit contenir au moins 8 caractères",
+ "Password_and_Confirm_password_mismatches.": "Mot de passe et Confirmer les incompatibilités de mot de passe.",
+ "doNotOwnAnAccount": "Vous ne possédez pas de compte ?",
+ "captchaError": "Erreur CAPTCHA!",
+ "Please_check_the_captcha": "S'il vous plaît, vérifiez le captcha.",
+ "Something_went_wrong": "Quelque chose s'est mal passé. Veuillez réessayer plus tard.",
+ "passwordMismatches": "Mot de passe et Confirmer les incompatibilités de mot de passe.",
+ "fillCorrectly": "Remplissez correctement tous les détails.",
+ "successfullyRegistered": "Enregistré avec succès. ",
+ "lowercase_check": "Au moins une lettre minuscule",
+ "uppercase_check": "Au moins une lettre majuscule",
+ "numeric_value_check": "Au moins une valeur numérique",
+ "special_char_check": "Au moins un caractère spécial",
+ "selectOrg": "Sélectionnez une organisation",
+ "afterRegister": "Enregistré avec succès. ",
+ "talawa_portal": "Portail Talawa",
+ "login": "Connexion",
+ "register": "S'inscrire",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "email": "E-mail",
+ "password": "Mot de passe",
+ "confirmPassword": "Confirmer le mot de passe",
+ "forgotPassword": "Mot de passe oublié",
+ "enterEmail": "Entrer l'e-mail",
+ "enterPassword": "Entrer le mot de passe",
+ "talawaApiUnavailable": "API Talawa indisponible",
+ "notAuthorised": "Non autorisé",
+ "notFound": "Non trouvé",
+ "OR": "OU",
+ "admin": "Administrateur",
+ "user": "Utilisateur",
+ "loading": "Chargement"
+ },
+ "userLoginPage": {
+ "title": "Administrateur Talawa",
+ "fromPalisadoes": "Une application open source réalisée par les bénévoles de la Fondation Palisadoes",
+ "atleast_8_char_long": "Au moins 8 caractères",
+ "Password_and_Confirm_password_mismatches.": "Mot de passe et Confirmer les incompatibilités de mot de passe.",
+ "doNotOwnAnAccount": "Vous ne possédez pas de compte ?",
+ "captchaError": "Erreur CAPTCHA!",
+ "Please_check_the_captcha": "S'il vous plaît, vérifiez le captcha.",
+ "Something_went_wrong": "Quelque chose s'est mal passé. Veuillez réessayer plus tard.",
+ "passwordMismatches": "Mot de passe et Confirmer les incompatibilités de mot de passe.",
+ "fillCorrectly": "Remplissez correctement tous les détails.",
+ "successfullyRegistered": "Enregistré avec succès. ",
+ "userLogin": "Utilisateur en ligne",
+ "afterRegister": "Enregistré avec succès. ",
+ "selectOrg": "Sélectionnez une organisation",
+ "talawa_portal": "Portail Talawa",
+ "login": "Connexion",
+ "register": "S'inscrire",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "email": "E-mail",
+ "password": "Mot de passe",
+ "confirmPassword": "Confirmer le mot de passe",
+ "forgotPassword": "Mot de passe oublié",
+ "enterEmail": "Entrer l'e-mail",
+ "enterPassword": "Entrer le mot de passe",
+ "talawaApiUnavailable": "API Talawa indisponible",
+ "notAuthorised": "Non autorisé",
+ "notFound": "Non trouvé",
+ "OR": "OU",
+ "loading": "Chargement"
+ },
+ "latestEvents": {
+ "eventCardTitle": "évènements à venir",
+ "eventCardSeeAll": "Voir tout",
+ "noEvents": "Aucun événement à venir"
+ },
+ "latestPosts": {
+ "latestPostsTitle": "Derniers messages",
+ "seeAllLink": "Voir tout",
+ "noPostsCreated": "Aucun message créé"
+ },
+ "listNavbar": {
+ "roles": "Les rôles",
+ "talawa_portal": "Portail Talawa",
+ "requests": "Demandes",
+ "logout": "Déconnexion"
+ },
+ "leftDrawer": {
+ "my organizations": "Mes organisations",
+ "requests": "Demandes d'adhésion",
+ "communityProfile": "Profil de la communauté",
+ "talawaAdminPortal": "Portail Administrateur Talawa",
+ "menu": "Menu",
+ "users": "Utilisateurs",
+ "logout": "Déconnexion"
+ },
+ "leftDrawerOrg": {
+ "Dashboard": "Tableau de bord",
+ "People": "Personnes",
+ "Events": "Événements",
+ "Contributions": "Contributions",
+ "Posts": "Des postes",
+ "Block/Unblock": "Bloquer/Débloquer",
+ "Plugins": "Plugins",
+ "Plugin Store": "Magasin de plugins",
+ "Advertisement": "Annonces",
+ "allOrganizations": "Toutes les organisations",
+ "yourOrganization": "Ton organisation",
+ "notification": "Notification",
+ "language": "Langue",
+ "notifications": "Notifications",
+ "spamsThe": "spamme le",
+ "group": "groupe",
+ "noNotifications": "Aucune notification",
+ "talawaAdminPortal": "Portail Administrateur Talawa",
+ "menu": "Menu",
+ "talawa_portal": "Portail Talawa",
+ "settings": "Paramètres",
+ "logout": "Déconnexion",
+ "close": "Fermer"
+ },
+ "orgList": {
+ "title": "Organisations Talawa",
+ "you": "Toi",
+ "designation": "Désignation",
+ "my organizations": "Mes organisations",
+ "createOrganization": "Créer une organisation",
+ "createSampleOrganization": "Créer un exemple d'organisation",
+ "city": "Ville",
+ "countryCode": "Code postal",
+ "dependentLocality": "Localité dépendante",
+ "line1": "Ligne 1",
+ "line2": "Ligne 2",
+ "postalCode": "Code Postal",
+ "sortingCode": "Code de tri",
+ "state": "État/Province",
+ "userRegistrationRequired": "Inscription de l'utilisateur requise",
+ "visibleInSearch": "Visible dans la recherche",
+ "enterName": "Entrez le nom",
+ "sort": "Trier",
+ "Latest": "Dernier",
+ "Earliest": "Le plus tôt",
+ "noOrgErrorTitle": "Organisations introuvables",
+ "sampleOrgDuplicate": "Un seul échantillon d’organisation autorisé",
+ "noOrgErrorDescription": "Veuillez créer une organisation via le tableau de bord",
+ "manageFeatures": "Gérer les fonctionnalités",
+ "manageFeaturesInfo": "Création réussie ! ",
+ "goToStore": "Accédez à la boutique de plugins",
+ "enableEverything": "Activer tout",
+ "sampleOrgSuccess": "Exemple d'organisation créée avec succès",
+ "name": "Nom",
+ "email": "E-mail",
+ "searchByName": "Rechercher par nom",
+ "description": "Description",
+ "location": "Emplacement",
+ "address": "Adresse",
+ "displayImage": "Image d'affichage",
+ "filter": "Filtrer",
+ "cancel": "Annuler",
+ "endOfResults": "Fin des résultats",
+ "noResultsFoundFor": "Aucun résultat trouvé pour",
+ "OR": "OU"
+ },
+ "orgListCard": {
+ "manage": "Gérer",
+ "sampleOrganization": "Exemple d'organisation",
+ "admins": "Administrateurs",
+ "members": "Membres"
+ },
+ "paginationList": {
+ "rowsPerPage": "lignes par page",
+ "all": "Tous"
+ },
+ "requests": {
+ "title": "Demandes d'adhésion",
+ "sl_no": "Sl. ",
+ "accept": "Accepter",
+ "reject": "Rejeter",
+ "searchRequests": "Rechercher des demandes d'adhésion",
+ "noOrgError": "Organisations introuvables, veuillez créer une organisation via le tableau de bord",
+ "noRequestsFound": "Aucune demande d'adhésion trouvée",
+ "acceptedSuccessfully": "Demande acceptée avec succès",
+ "rejectedSuccessfully": "Demande rejetée avec succès",
+ "noOrgErrorTitle": "Organisations introuvables",
+ "noOrgErrorDescription": "Veuillez créer une organisation via le tableau de bord",
+ "name": "Nom",
+ "email": "E-mail",
+ "endOfResults": "Fin des résultats",
+ "noResultsFoundFor": "Aucun résultat trouvé pour"
+ },
+ "users": {
+ "title": "Rôles Talawa",
+ "joined_organizations": "Organisations rejointes",
+ "blocked_organizations": "Organisations bloquées",
+ "orgJoinedBy": "Organisations rejointes par",
+ "orgThatBlocked": "Organisations qui ont bloqué",
+ "hasNotJoinedAnyOrg": "n'a rejoint aucune organisation",
+ "isNotBlockedByAnyOrg": "n'est bloqué par aucune organisation",
+ "searchByOrgName": "Rechercher par nom d'organisation",
+ "view": "Voir",
+ "enterName": "Entrez le nom",
+ "loadingUsers": "Chargement des utilisateurs...",
+ "noUserFound": "Aucun utilisateur trouvé",
+ "sort": "Trier",
+ "Newest": "Le plus récent d'abord",
+ "Oldest": "Le plus âgé en premier",
+ "noOrgError": "Organisations introuvables, veuillez créer une organisation via le tableau de bord",
+ "roleUpdated": "Rôle mis à jour.",
+ "joinNow": "Adhérer maintenant",
+ "visit": "Visite",
+ "withdraw": "Largeur de tirage",
+ "removeUserFrom": "Supprimer l'Utilisateur de {{org}}",
+ "removeConfirmation": "Êtes-vous sûr de vouloir supprimer '{{name}}' de l'organisation '{{org}}' ?",
+ "searchByName": "Rechercher par nom",
+ "users": "Utilisateurs",
+ "name": "Nom",
+ "email": "E-mail",
+ "endOfResults": "Fin des résultats",
+ "admin": "Administrateur",
+ "superAdmin": "Super Administrateur",
+ "user": "Utilisateur",
+ "filter": "Filtrer",
+ "noResultsFoundFor": "Aucun résultat trouvé pour",
+ "talawaApiUnavailable": "API Talawa indisponible",
+ "cancel": "Annuler",
+ "admins": "Administrateurs",
+ "members": "Membres",
+ "orgJoined": "Organisation Rejointe",
+ "MembershipRequestSent": "Demande d'adhésion envoyée",
+ "AlreadyJoined": "Déjà rejoint",
+ "errorOccured": "Une erreur s'est produite"
+ },
+ "communityProfile": {
+ "title": "Profil de la communauté",
+ "editProfile": "Editer le profil",
+ "communityProfileInfo": "Ces détails apparaîtront sur l'écran de connexion/inscription pour vous et les membres de votre communauté.",
+ "communityName": "Nom de la communauté",
+ "wesiteLink": "Lien de site Web",
+ "logo": "Logo",
+ "social": "Liens vers les réseaux sociaux",
+ "url": "Entrer l'URL",
+ "profileChangedMsg": "Les détails du profil ont été mis à jour avec succès.",
+ "resetData": "Réinitialisez avec succès les détails du profil."
+ },
+ "dashboard": {
+ "title": "Tableau de bord",
+ "about": "À propos",
+ "deleteThisOrganization": "Supprimer cette organisation",
+ "statistics": "Statistiques",
+ "posts": "Des postes",
+ "events": "Événements",
+ "blockedUsers": "Utilisateurs bloqués",
+ "viewAll": "Voir tout",
+ "upcomingEvents": "évènements à venir",
+ "noUpcomingEvents": "Aucun événement à venir",
+ "latestPosts": "Derniers messages",
+ "noPostsPresent": "Aucun message présent",
+ "membershipRequests": "Demandes d'adhésion",
+ "noMembershipRequests": "Aucune demande d'adhésion présente",
+ "location": "Emplacement",
+ "members": "Membres",
+ "admins": "Administrateurs",
+ "requests": "Demandes",
+ "talawaApiUnavailable": "API Talawa indisponible",
+ "volunteerRankings": "Classement des Bénévoles",
+ "noVolunteers": "Aucun Bénévole Trouvé!"
+ },
+ "organizationPeople": {
+ "title": "Membres Talawa",
+ "filterByName": "Filtrer par nom",
+ "filterByLocation": "Filtrer par emplacement",
+ "filterByEvent": "Filtrer par événement",
+ "searchName": "Entrez le nom",
+ "searchevent": "Entrer l'événement",
+ "searchFullName": "Entrez le nom complet",
+ "people": "Personnes",
+ "sort": "Recherche par rôle",
+ "actions": "Actions",
+ "addMembers": "Ajouter des membres",
+ "existingUser": "Utilisateur existant",
+ "newUser": "Nouvel utilisateur",
+ "enterFirstName": "Entrez votre prénom",
+ "enterLastName": "Entrez votre nom de famille",
+ "enterConfirmPassword": "Entrez votre mot de passe pour confirmer",
+ "organization": "Organisation",
+ "user": "Utilisateur",
+ "profile": "Profil",
+ "invalidDetailsMessage": "Veuillez saisir des informations valides.",
+ "members": "Membres",
+ "admins": "Administrateurs",
+ "users": "Utilisateurs",
+ "searchFirstName": "Rechercher par prénom",
+ "searchLastName": "Rechercher par nom de famille",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "emailAddress": "Adresse e-mail",
+ "enterEmail": "Entrer l'e-mail",
+ "password": "Mot de passe",
+ "enterPassword": "Entrer le mot de passe",
+ "confirmPassword": "Confirmer le mot de passe",
+ "create": "Créer",
+ "cancel": "Annuler"
+ },
+ "organizationTags": {
+ "title": "Étiquettes d'Organisation",
+ "createTag": "Créer une nouvelle étiquette",
+ "manageTag": "Gérer",
+ "editTag": "Modifier",
+ "removeTag": "Supprimer",
+ "tagDetails": "Détails de l'Étiquette",
+ "tagName": "Nom",
+ "tagType": "Type",
+ "tagNamePlaceholder": "Écrire le nom de l'étiquette",
+ "tagCreationSuccess": "Nouvelle étiquette créée avec succès",
+ "tagUpdationSuccess": "Étiquette mise à jour avec succès",
+ "tagRemovalSuccess": "Étiquette supprimée avec succès",
+ "noTagsFound": "Aucune étiquette trouvée",
+ "removeUserTag": "Supprimer l'Étiquette",
+ "removeUserTagMessage": "Voulez-vous supprimer cette étiquette ?",
+ "addChildTag": "Ajouter une Sous-Étiquette",
+ "enterTagName": "Entrez le nom de l'étiquette"
+ },
+ "manageTag": {
+ "title": "Détails de l'étiquette",
+ "addPeopleToTag": "Ajouter des personnes à l'étiquette",
+ "viewProfile": "Voir",
+ "noAssignedMembersFound": "Aucun membre assigné",
+ "unassignUserTag": "Désassigner l'étiquette",
+ "unassignUserTagMessage": "Voulez-vous retirer l'étiquette de cet utilisateur?",
+ "successfullyUnassigned": "Étiquette retirée de l'utilisateur",
+ "addPeople": "Ajouter des personnes",
+ "add": "Ajouter",
+ "subTags": "Sous-étiquettes",
+ "successfullyAssignedToPeople": "Étiquette attribuée avec succès",
+ "errorOccurredWhileLoadingMembers": "Erreur survenue lors du chargement des membres",
+ "userName": "Nom d'utilisateur",
+ "actions": "Actions",
+ "noOneSelected": "Personne sélectionnée",
+ "assignToTags": "Attribuer aux étiquettes",
+ "removeFromTags": "Retirer des étiquettes",
+ "assign": "Attribuer",
+ "remove": "Retirer",
+ "successfullyAssignedToTags": "Attribué aux étiquettes avec succès",
+ "successfullyRemovedFromTags": "Retiré des étiquettes avec succès",
+ "errorOccurredWhileLoadingOrganizationUserTags": "Erreur lors du chargement des étiquettes de l'organisation",
+ "errorOccurredWhileLoadingSubTags": "Une erreur s'est produite lors du chargement des sous-étiquettes",
+ "removeUserTag": "Supprimer l'étiquette",
+ "removeUserTagMessage": "Voulez-vous supprimer cette étiquette ? Cela supprimera toutes les sous-étiquettes et toutes les associations.",
+ "tagDetails": "Détails de l'étiquette",
+ "tagName": "Nom de l'étiquette",
+ "tagUpdationSuccess": "Étiquette mise à jour avec succès",
+ "tagRemovalSuccess": "Étiquette supprimée avec succès",
+ "noTagSelected": "Aucune étiquette sélectionnée",
+ "changeNameToEdit": "Modifiez le nom pour faire une mise à jour",
+ "selectTag": "Sélectionner l'étiquette",
+ "collapse": "Réduire",
+ "expand": "Développer",
+ "tagNamePlaceholder": "Écrire le nom de l'étiquette",
+ "allTags": "Toutes les étiquettes",
+ "noMoreMembersFound": "Aucun autre membre trouvé"
+ },
+ "userListCard": {
+ "addAdmin": "Ajouter un administrateur",
+ "addedAsAdmin": "L'utilisateur est ajouté en tant qu'administrateur.",
+ "joined": "Rejoint",
+ "talawaApiUnavailable": "API Talawa indisponible"
+ },
+ "orgAdminListCard": {
+ "remove": "Retirer",
+ "removeAdmin": "Supprimer l'administrateur",
+ "removeAdminMsg": "Voulez-vous supprimer cet administrateur ?",
+ "adminRemoved": "L'administrateur est supprimé.",
+ "joined": "Rejoint",
+ "no": "Non",
+ "yes": "Oui",
+ "talawaApiUnavailable": "API Talawa indisponible"
+ },
+ "orgPeopleListCard": {
+ "remove": "Retirer",
+ "removeMember": "Supprimer un membre",
+ "removeMemberMsg": "Voulez-vous supprimer ce membre ?",
+ "memberRemoved": "Le membre est supprimé",
+ "joined": "Rejoint",
+ "no": "Non",
+ "yes": "Oui",
+ "talawaApiUnavailable": "API Talawa indisponible"
+ },
+ "organizationEvents": {
+ "title": "Événements",
+ "filterByTitle": "Filtrer par titre",
+ "filterByLocation": "Filtrer par emplacement",
+ "filterByDescription": "Filtrer par description",
+ "addEvent": "Ajouter un évènement",
+ "eventDetails": "Détails de l'évènement",
+ "eventTitle": "Titre",
+ "startTime": "Heure de début",
+ "endTime": "Heure de fin",
+ "allDay": "Toute la journée",
+ "recurringEvent": "Événement récurrent",
+ "searchMemberName": "Rechercher le nom du membre",
+ "isPublic": "est public",
+ "isRegistrable": "Est enregistrable",
+ "createEvent": "Créer un évènement",
+ "enterFilter": "Entrer le filtre",
+ "enterTitle": "Entrez le titre",
+ "enterDescrip": "Entrez la description",
+ "eventLocation": "Entrez l'emplacement",
+ "searchEventName": "Rechercher le nom de l'événement",
+ "eventType": "Type d'événement",
+ "eventCreated": "Toutes nos félicitations! ",
+ "customRecurrence": "Récurrence personnalisée",
+ "repeatsEvery": "Se répète tous les",
+ "repeatsOn": "Répétition activée",
+ "ends": "Prend fin",
+ "never": "Jamais",
+ "on": "Sur",
+ "after": "Après",
+ "occurences": "événements",
+ "events": "Événements",
+ "description": "Description",
+ "location": "Emplacement",
+ "startDate": "Date de début",
+ "endDate": "Date de fin",
+ "talawaApiUnavailable": "API Talawa indisponible",
+ "done": "Fait"
+ },
+ "organizationActionItems": {
+ "actionItemCategory": "Catégorie de l'Action",
+ "actionItemDetails": "Détails de l'Action",
+ "actionItemCompleted": "Action Terminée",
+ "assignee": "Attribué à",
+ "assigner": "Assignateur",
+ "assignmentDate": "Date d'Attribution",
+ "active": "Actif",
+ "clearFilters": "Effacer les Filtres",
+ "completionDate": "Date de Complétion",
+ "createActionItem": "Créer une Action",
+ "deleteActionItem": "Supprimer l'Action",
+ "deleteActionItemMsg": "Voulez-vous supprimer cette action?",
+ "details": "Détails",
+ "dueDate": "Date d'Échéance",
+ "earliest": "Le Plus Ancien",
+ "editActionItem": "Modifier l'Action",
+ "isCompleted": "Terminé",
+ "latest": "Le Plus Récent",
+ "makeActive": "Rendre Actif",
+ "noActionItems": "Aucune Action",
+ "options": "Options",
+ "preCompletionNotes": "Notes Pré-Complétion",
+ "actionItemActive": "Action Active",
+ "markCompletion": "Marquer comme Terminé",
+ "actionItemStatus": "État de l'Action",
+ "postCompletionNotes": "Notes Post-Complétion",
+ "selectActionItemCategory": "Sélectionnez une Catégorie d'Action",
+ "selectAssignee": "Sélectionner un Attribué",
+ "status": "Statut",
+ "successfulCreation": "Action créée avec succès",
+ "successfulUpdation": "Action mise à jour avec succès",
+ "successfulDeletion": "Action supprimée avec succès",
+ "title": "Actions",
+ "category": "Catégorie",
+ "allottedHours": "Heures Attribuées",
+ "latestDueDate": "Date d'Échéance la Plus Récente",
+ "earliestDueDate": "Date d'Échéance la Plus Ancienne",
+ "updateActionItem": "Mettre à Jour l'Action",
+ "noneUpdated": "Aucun champ n'a été mis à jour",
+ "updateStatusMsg": "Voulez-vous vraiment marquer cette action comme en attente?",
+ "close": "Fermer",
+ "eventActionItems": "Actions de l'Événement",
+ "no": "Non",
+ "yes": "Oui",
+ "individuals": "Individus",
+ "groups": "Groupes",
+ "assignTo": "Attribuer à",
+ "volunteers": "Bénévoles",
+ "volunteerGroups": "Groupes de Bénévoles"
+ },
+ "organizationAgendaCategory": {
+ "agendaCategoryDetails": "Détails de la catégorie d'ordre du jour",
+ "updateAgendaCategory": "Mettre à jour la catégorie d'ordre du jour",
+ "title": "Catégories d'ordre du jour",
+ "name": "Catégorie",
+ "description": "Description",
+ "createdBy": "Créé par",
+ "options": "Options",
+ "createAgendaCategory": "Créer une catégorie d'ordre du jour",
+ "noAgendaCategories": "Aucune catégorie d'ordre du jour",
+ "update": "Mettre à jour",
+ "agendaCategoryCreated": "Catégorie d'ordre du jour créée avec succès",
+ "agendaCategoryUpdated": "Catégorie d'ordre du jour mise à jour avec succès",
+ "agendaCategoryDeleted": "Catégorie d'ordre du jour supprimée avec succès",
+ "deleteAgendaCategory": "Supprimer la catégorie d'ordre du jour",
+ "deleteAgendaCategoryMsg": "Souhaitez-vous supprimer cette catégorie d'ordre du jour ?"
+ },
+ "agendaItems": {
+ "agendaItemDetails": "Détails du point de l'ordre du jour",
+ "updateAgendaItem": "Mettre à jour le point de l'ordre du jour",
+ "title": "Titre",
+ "enterTitle": "Entrer le titre",
+ "sequence": "Ordre",
+ "description": "Description",
+ "enterDescription": "Entrer la description",
+ "category": "Catégorie de l'ordre du jour",
+ "attachments": "Pièces jointes",
+ "attachmentLimit": "Ajouter un fichier image ou vidéo jusqu'à 10 Mo",
+ "fileSizeExceedsLimit": "La taille du fichier dépasse la limite de 10 Mo",
+ "urls": "URL",
+ "url": "Ajouter un lien vers l'URL",
+ "enterUrl": "https://example.com",
+ "invalidUrl": "Veuillez saisir une URL valide",
+ "link": "Lien",
+ "createdBy": "Créé par",
+ "regular": "Régulier",
+ "note": "Note",
+ "duration": "Durée",
+ "enterDuration": "mm:ss",
+ "options": "Options",
+ "createAgendaItem": "Créer un point à l'ordre du jour",
+ "noAgendaItems": "Aucun point à l'ordre du jour",
+ "selectAgendaItemCategory": "Sélectionner une catégorie de point de l'ordre du jour",
+ "update": "Mettre à jour",
+ "delete": "Supprimer",
+ "agendaItemCreated": "Point de l'ordre du jour créé avec succès",
+ "agendaItemUpdated": "Point de l'ordre du jour mis à jour avec succès",
+ "agendaItemDeleted": "Point de l'ordre du jour supprimé avec succès",
+ "deleteAgendaItem": "Supprimer le point de l'ordre du jour",
+ "deleteAgendaItemMsg": "Voulez-vous supprimer ce point de l'ordre du jour ?"
+ },
+ "eventListCard": {
+ "deleteEvent": "Supprimer l'événement",
+ "deleteEventMsg": "Voulez-vous supprimer cet événement ?",
+ "editEvent": "Modifier l'événement",
+ "eventTitle": "Titre",
+ "alreadyRegistered": "Déjà enregistré",
+ "startTime": "Heure de début",
+ "endTime": "Heure de fin",
+ "allDay": "Toute la journée",
+ "recurringEvent": "Événement récurrent",
+ "isPublic": "est public",
+ "isRegistrable": "Est enregistrable",
+ "updatePost": "Mettre à jour le message",
+ "eventDetails": "Détails de l'évènement",
+ "eventDeleted": "Événement supprimé avec succès.",
+ "eventUpdated": "Événement mis à jour avec succès.",
+ "thisInstance": "Cette instance",
+ "thisAndFollowingInstances": "Instances présentes et suivantes",
+ "allInstances": "Toutes les instances",
+ "customRecurrence": "Récurrence personnalisée",
+ "repeatsEvery": "Se répète tous les",
+ "repeatsOn": "Répétition activée",
+ "ends": "Prend fin",
+ "never": "Jamais",
+ "on": "Sur",
+ "after": "Après",
+ "occurences": "événements",
+ "location": "Lieu",
+ "no": "Non",
+ "yes": "Oui",
+ "description": "Description",
+ "startDate": "Date de début",
+ "endDate": "Date de fin",
+ "registerEvent": "Inscrire à l'événement",
+ "close": "Fermer",
+ "talawaApiUnavailable": "API Talawa non disponible",
+ "done": "Terminé"
+ },
+ "funds": {
+ "title": "Fonds",
+ "createFund": "Créer un fonds",
+ "fundName": "Nom du fonds",
+ "fundId": "ID de référence du fonds",
+ "taxDeductible": "Déductible d'impôt",
+ "default": "Par défaut",
+ "archived": "Archivé",
+ "fundCreate": "Créer un fonds",
+ "fundUpdate": "Mettre à jour le fonds",
+ "fundDelete": "Supprimer le fonds",
+ "searchByName": "Rechercher par nom",
+ "noFundsFound": "Aucun fonds trouvé",
+ "createdBy": "Créé par",
+ "createdOn": "Créé le",
+ "status": "Statut",
+ "fundCreated": "Fonds créé avec succès",
+ "fundUpdated": "Fonds mis à jour avec succès",
+ "fundDeleted": "Fonds supprimé avec succès",
+ "deleteFundMsg": "Êtes-vous sûr de vouloir supprimer ce fonds ?",
+ "createdLatest": "Créé le plus récemment",
+ "createdEarliest": "Créé le plus tôt",
+ "viewCampaigns": "Voir les campagnes"
+ },
+ "fundCampaign": {
+ "title": "Campagnes de collecte de fonds",
+ "campaignName": "Nom de la campagne",
+ "campaignOptions": "Options",
+ "fundingGoal": "Objectif de financement",
+ "addCampaign": "Ajouter une campagne",
+ "createdCampaign": "Campagne créée avec succès",
+ "updatedCampaign": "Campagne mise à jour avec succès",
+ "deletedCampaign": "Campagne supprimée avec succès",
+ "deleteCampaignMsg": "Êtes-vous sûr de vouloir supprimer cette campagne ?",
+ "noCampaigns": "Aucune campagne trouvée",
+ "createCampaign": "Créer une campagne",
+ "updateCampaign": "Mettre à jour la campagne",
+ "deleteCampaign": "Supprimer la campagne",
+ "currency": "Devise",
+ "selectCurrency": "Sélectionner la devise",
+ "searchFullName": "Rechercher par nom",
+ "viewPledges": "Voir les promesses de dons",
+ "noCampaignsFound": "Aucune campagne trouvée",
+ "latestEndDate": "Dernière date de fin",
+ "earliestEndDate": "Date de fin la plus ancienne",
+ "lowestGoal": "Objectif le plus bas",
+ "highestGoal": "Objectif le plus élevé"
+ },
+ "pledges": {
+ "title": "Engagements de campagne de financement",
+ "pledgeAmount": "Montant de la promesse de don",
+ "pledgeOptions": "Possibilités",
+ "pledgeCreated": "Engagement créé avec succès",
+ "pledgeUpdated": "Engagement mis à jour avec succès",
+ "pledgeDeleted": "Engagement supprimé avec succès",
+ "addPledge": "Ajouter un engagement",
+ "createPledge": "Créer un engagement",
+ "currency": "Devise",
+ "selectCurrency": "Sélectionnez la devise",
+ "updatePledge": "Engagement de mise à jour",
+ "deletePledge": "Supprimer l'engagement",
+ "amount": "Montant",
+ "editPledge": "Modifier l'engagement",
+ "deletePledgeMsg": "Etes-vous sûr de vouloir supprimer cet engagement ?",
+ "noPledges": "Aucun engagement trouvé",
+ "searchPledger": "Rechercher par les bailleurs de fonds",
+ "highestAmount": "Montant le plus élevé",
+ "lowestAmount": "Montant le plus bas",
+ "latestEndDate": "Date de fin la plus récente",
+ "earliestEndDate": "Date de fin la plus proche",
+ "campaigns": "Campagnes",
+ "pledges": "Promesses de dons",
+ "endsOn": "Se termine le",
+ "raisedAmount": "Montant collecté",
+ "pledgedAmount": "Montant promis",
+ "startDate": "Date de début",
+ "endDate": "Date de fin"
+ },
+ "orgPost": {
+ "title": "Des postes",
+ "searchPost": "Rechercher un article",
+ "posts": "Des postes",
+ "createPost": "Créer un message",
+ "postDetails": "Détails du message",
+ "postTitle1": "Écrivez le titre du message",
+ "postTitle": "Titre",
+ "addMedia": "Télécharger des médias",
+ "information": "Information",
+ "information1": "Écrire les informations du message",
+ "addPost": "Ajouter un message",
+ "searchTitle": "Recherche par titre",
+ "searchText": "Recherche par texte",
+ "ptitle": "Titre de l'article",
+ "postDes": "De quoi veux-tu parler ?",
+ "Title": "Titre",
+ "Text": "Texte",
+ "searchBy": "Recherché par",
+ "Oldest": "Le plus âgé en premier",
+ "Latest": "Dernier premier",
+ "sortPost": "Trier le message",
+ "tag": " Votre navigateur ne prend pas en charge la balise vidéo",
+ "postCreatedSuccess": "Toutes nos félicitations! ",
+ "pinPost": "Épingler le message",
+ "Next": "Page suivante",
+ "Previous": "Page précédente",
+ "cancel": "Annuler"
+ },
+ "postNotFound": {
+ "post": "Poste",
+ "not found!": "Pas trouvé!",
+ "organization": "Organisation",
+ "post not found!": "Message introuvable !",
+ "organization not found!": "Organisation introuvable !"
+ },
+ "userNotFound": {
+ "not found!": "Pas trouvé!",
+ "roles": "Les rôles",
+ "user not found!": "Utilisateur non trouvé!",
+ "member not found!": "Membre introuvable!",
+ "admin not found!": "Administrateur introuvable !",
+ "roles not found!": "Rôles introuvables !",
+ "user": "Utilisateur"
+ },
+ "orgPostCard": {
+ "author": "Auteur",
+ "imageURL": "URL de l'image",
+ "videoURL": "URL de la vidéo",
+ "deletePost": "Supprimer le message",
+ "deletePostMsg": "Voulez-vous supprimer ce message?",
+ "editPost": "Modifier le message",
+ "postTitle": "Titre",
+ "postTitle1": "Modifier le titre du message",
+ "information1": "Modifier les informations du message",
+ "information": "Information",
+ "image": "Image",
+ "video": "Vidéo",
+ "updatePost": "Mettre à jour le message",
+ "postDeleted": "Message supprimé avec succès.",
+ "postUpdated": "Post mis à jour avec succès.",
+ "tag": " Votre navigateur ne prend pas en charge la balise vidéo",
+ "pin": "Épingler le message",
+ "edit": "Modifier",
+ "no": "Non",
+ "yes": "Oui",
+ "close": "Fermer",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "blockUnblockUser": {
+ "title": "Bloquer/débloquer un utilisateur",
+ "pageName": "Bloquer/Débloquer",
+ "listOfUsers": "Liste des utilisateurs qui ont spammé",
+ "block_unblock": "Bloquer/Débloquer",
+ "unblock": "Débloquer",
+ "block": "Bloc",
+ "orgName": "Entrez le nom",
+ "blockedSuccessfully": "Utilisateur bloqué avec succès",
+ "Un-BlockedSuccessfully": "Utilisateur débloqué avec succès",
+ "allMembers": "Tous les membres",
+ "blockedUsers": "Utilisateurs bloqués",
+ "searchByFirstName": "Recherche par prénom",
+ "searchByLastName": "Rechercher par nom de famille",
+ "noSpammerFound": "Aucun spammeur trouvé",
+ "searchByName": "Rechercher par nom",
+ "name": "Nom",
+ "email": "Email",
+ "talawaApiUnavailable": "API Talawa non disponible",
+ "noResultsFoundFor": "Aucun résultat trouvé pour"
+ },
+ "eventManagement": {
+ "title": "Gestion d'événements",
+ "dashboard": "Tableau de bord",
+ "registrants": "Inscrits",
+ "attendance": "Présence",
+ "actions": "Actions",
+ "agendas": "Ordres du jour",
+ "statistics": "Statistiques",
+ "to": "À",
+ "volunteers": "Bénévoles"
+ },
+ "forgotPassword": {
+ "title": "Talawa Mot de passe oublié",
+ "registeredEmail": "Email enregistré",
+ "getOtp": "Obtenir OTP",
+ "enterOtp": "Entrez OTP",
+ "enterNewPassword": "Entrez un nouveau mot de passe",
+ "cofirmNewPassword": "Confirmer le nouveau mot de passe",
+ "changePassword": "Changer le mot de passe",
+ "backToLogin": "Retour connexion",
+ "userOtp": "par exemple. ",
+ "emailNotRegistered": "L'e-mail n'est pas enregistré.",
+ "errorSendingMail": "Erreur lors de l'envoi du courrier.",
+ "passwordMismatches": "Mot de passe et Confirmer les incompatibilités de mot de passe.",
+ "passwordChanges": "Le mot de passe a été modifié avec succès.",
+ "OTPsent": "OTP est envoyé à votre adresse e-mail enregistrée.",
+ "forgotPassword": "Mot de passe oublié",
+ "password": "Mot de passe",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "pageNotFound": {
+ "404": "404",
+ "title": "404 introuvable",
+ "talawaAdmin": "Administrateur Talawa",
+ "talawaUser": "Utilisateur Talawa",
+ "notFoundMsg": "Oops! ",
+ "backToHome": "De retour à la maison"
+ },
+ "orgContribution": {
+ "title": "Contributions de Talawa",
+ "filterByName": "Filtrer par nom",
+ "filterByTransId": "Filtrer par Trans. ",
+ "recentStats": "Statistiques récentes",
+ "contribution": "Contribution",
+ "orgname": "Entrez le nom",
+ "searchtransaction": "Entrez l'ID de la transaction"
+ },
+ "contriStats": {
+ "recentContribution": "Contribution récente",
+ "highestContribution": "Contribution la plus élevée",
+ "totalContribution": "Contribution totale"
+ },
+ "orgContriCards": {
+ "date": "Date",
+ "transactionId": "identifiant de transaction",
+ "amount": "Montant"
+ },
+ "orgSettings": {
+ "title": "Paramètres",
+ "general": "Général",
+ "actionItemCategories": "Catégories d'éléments d'action",
+ "updateOrganization": "Mettre à jour l'organisation",
+ "seeRequest": "Voir la demande",
+ "noData": "Aucune donnée",
+ "otherSettings": "Autres paramètres",
+ "changeLanguage": "Changer de langue",
+ "manageCustomFields": "Gérer les champs personnalisés",
+ "agendaItemCategories": "Catégories d'éléments d'agenda"
+ },
+ "deleteOrg": {
+ "deleteOrganization": "Supprimer l'organisation",
+ "deleteSampleOrganization": "Supprimer un exemple d'organisation",
+ "deleteMsg": "Voulez-vous supprimer cette organisation ?",
+ "confirmDelete": "Confirmation de la suppression",
+ "longDelOrgMsg": "En cliquant sur le bouton Supprimer l'organisation, l'organisation sera définitivement supprimée ainsi que ses événements, balises et toutes les données associées.",
+ "successfullyDeletedSampleOrganization": "Exemple d'organisation supprimé avec succès",
+ "cancel": "Annuler"
+ },
+ "userUpdate": {
+ "appLanguageCode": "Langage par défaut",
+ "userType": "Type d'utilisateur",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "email": "Email",
+ "password": "Mot de passe",
+ "admin": "Admin",
+ "superAdmin": "Super Admin",
+ "displayImage": "Image d'affichage",
+ "saveChanges": "Enregistrer les modifications",
+ "cancel": "Annuler"
+ },
+ "userPasswordUpdate": {
+ "previousPassword": "Mot de passe précédent",
+ "newPassword": "nouveau mot de passe",
+ "confirmNewPassword": "Confirmer le nouveau mot de passe",
+ "passCantBeEmpty": "Le mot de passe ne peut pas être vide",
+ "passNoMatch": "Le nouveau mot de passe et la confirmation du mot de passe ne correspondent pas.",
+ "saveChanges": "Enregistrer les modifications",
+ "cancel": "Annuler"
+ },
+ "orgDelete": {
+ "deleteOrg": "Supprimer l'organisation"
+ },
+ "membershipRequest": {
+ "accept": "Accepter",
+ "reject": "Rejeter",
+ "memberAdded": "c'est accepté",
+ "joined": "Rejoint",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "orgUpdate": {
+ "city": "Ville",
+ "countryCode": "Code postal",
+ "line1": "Ligne 1",
+ "line2": "Ligne 2",
+ "postalCode": "Code Postal",
+ "dependentLocality": "Localité dépendante",
+ "sortingCode": "Code de tri",
+ "state": "État/Province",
+ "userRegistrationRequired": "Inscription de l'utilisateur requise",
+ "isVisibleInSearch": "Visible dans la recherche",
+ "enterNameOrganization": "Entrez le nom de l'organisation",
+ "successfulUpdated": "Organisation mise à jour avec succès",
+ "name": "Nom",
+ "description": "Description",
+ "location": "Lieu",
+ "address": "Adresse",
+ "displayImage": "Image d'affichage",
+ "saveChanges": "Enregistrer les modifications",
+ "cancel": "Annuler",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "addOnRegister": {
+ "addNew": "Ajouter un nouveau",
+ "addPlugin": "Ajouter un plugin",
+ "pluginName": "Nom du plugin",
+ "creatorName": "Nom du créateur",
+ "pluginDesc": "Description du plugin",
+ "pName": "Ex : Dons",
+ "cName": "Ex : John Doe",
+ "pDesc": "Ce plugin active l'interface utilisateur pour",
+ "close": "Fermer",
+ "register": "Inscription"
+ },
+ "addOnStore": {
+ "title": "Ajouter sur la boutique",
+ "searchName": "Ex : Dons",
+ "search": "Rechercher",
+ "enable": "Activé",
+ "disable": "Désactivé",
+ "pHeading": "Plugins",
+ "install": "installée",
+ "available": "Disponible",
+ "pMessage": "Le plugin n'existe pas",
+ "filter": "filtrer"
+ },
+ "addOnEntry": {
+ "enable": "Activé",
+ "install": "Installer",
+ "uninstall": "Désinstaller",
+ "uninstallMsg": "Cette fonctionnalité est désormais supprimée de votre organisation",
+ "installMsg": "Cette fonctionnalité est désormais activée dans votre organisation"
+ },
+ "memberDetail": {
+ "title": "Détails de l'utilisateur",
+ "addAdmin": "Ajouter un administrateur",
+ "noeventsAttended": "Aucun événement assisté",
+ "alreadyIsAdmin": "Le membre est déjà un administrateur",
+ "organizations": "Organisations",
+ "events": "Événements",
+ "role": "Rôle",
+ "createdOn": "Créé sur",
+ "main": "Principal",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "language": "Langue",
+ "gender": "Genre",
+ "birthDate": "Date de naissance",
+ "educationGrade": "Niveau d'éducation",
+ "employmentStatus": "Statut d'emploi",
+ "maritalStatus": "État civil",
+ "phone": "Téléphone",
+ "countryCode": "Code postal",
+ "state": "État",
+ "city": "Ville",
+ "personalInfoHeading": "Informations personnelles",
+ "eventsAttended": "Événements attenus",
+ "viewAll": "Voir tout",
+ "contactInfoHeading": "Coordonnées",
+ "actionsHeading": "Actions",
+ "personalDetailsHeading": "Détails du profil",
+ "appLanguageCode": "Choisissez la langue",
+ "deleteUser": "Supprimer l'utilisateur",
+ "pluginCreationAllowed": "Création de plugin autorisée",
+ "created": "Créé",
+ "adminForOrganizations": "Administrateur pour les organisations",
+ "membershipRequests": "Demandes d'adhésion",
+ "adminForEvents": "Administrateur pour les événements",
+ "addedAsAdmin": "L'utilisateur est ajouté en tant qu'administrateur.",
+ "userType": "Type d'utilisateur",
+ "email": "Email",
+ "displayImage": "Image d'affichage",
+ "address": "Adresse",
+ "delete": "Supprimer",
+ "saveChanges": "Enregistrer les modifications",
+ "joined": "Rejoint",
+ "talawaApiUnavailable": "API Talawa non disponible",
+ "unassignUserTag": "Désassigner l'étiquette",
+ "unassignUserTagMessage": "Voulez-vous retirer l'étiquette de cet utilisateur?",
+ "successfullyUnassigned": "Étiquette retirée de l'utilisateur",
+ "tagsAssigned": "étiquettes assignées",
+ "noTagsAssigned": "Aucune étiquette assignée"
+ },
+ "people": {
+ "title": "Personnes",
+ "searchUsers": "Rechercher des utilisateurs"
+ },
+ "userLogin": {
+ "login": "Se connecter",
+ "loginIntoYourAccount": "Connectez-vous à votre compte",
+ "invalidDetailsMessage": "Veuillez entrer un email et un mot de passe valides.",
+ "notAuthorised": "Désolé! ",
+ "invalidCredentials": "Les informations d'identification saisies sont incorrectes. ",
+ "forgotPassword": "Mot de passe oublié",
+ "emailAddress": "Adresse email",
+ "enterEmail": "Entrer l'email",
+ "password": "Mot de passe",
+ "enterPassword": "Entrer le mot de passe",
+ "register": "Inscription",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "userRegister": {
+ "enterFirstName": "Entrez votre prénom",
+ "enterLastName": "Entrez votre nom de famille",
+ "enterConfirmPassword": "Entrez votre mot de passe pour confirmer",
+ "alreadyhaveAnAccount": "Vous avez déjà un compte?",
+ "login": "Se connecter",
+ "afterRegister": "Enregistré avec succès. ",
+ "passwordNotMatch": "Le mot de passe ne correspond pas. ",
+ "invalidDetailsMessage": "Veuillez saisir des informations valides.",
+ "register": "Inscription",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "emailAddress": "Adresse email",
+ "enterEmail": "Entrer l'email",
+ "password": "Mot de passe",
+ "enterPassword": "Entrer le mot de passe",
+ "confirmPassword": "Confirmer le mot de passe",
+ "talawaApiUnavailable": "API Talawa non disponible"
+ },
+ "userNavbar": {
+ "talawa": "Talawa",
+ "home": "Maison",
+ "people": "Personnes",
+ "events": "Événements",
+ "chat": "Chat",
+ "donate": "Faire un don",
+ "language": "Langue",
+ "settings": "Paramètres",
+ "logout": "Déconnexion",
+ "close": "Fermer"
+ },
+ "userOrganizations": {
+ "allOrganizations": "Toutes les organisations",
+ "joinedOrganizations": "Organisations rejointes",
+ "createdOrganizations": "Organisations créées",
+ "selectOrganization": "Sélectionnez une organisation",
+ "searchUsers": "Rechercher des utilisateurs",
+ "nothingToShow": "Rien à montrer ici.",
+ "organizations": "Organisations",
+ "search": "Rechercher",
+ "filter": "Filtrer",
+ "searchByName": "Rechercher par nom",
+ "searchOrganizations": "Rechercher Organisations"
+ },
+ "userSidebarOrg": {
+ "yourOrganizations": "Vos organisations",
+ "noOrganizations": "Vous n'avez encore rejoint aucune organisation.",
+ "viewAll": "Voir tout",
+ "talawaUserPortal": "Portail utilisateur Talawa",
+ "my organizations": "Mes organisations",
+ "communityProfile": "Profil de la communauté",
+ "users": "utilisateurs",
+ "requests": "demandes",
+ "logout": "se déconnecter",
+ "settings": "paramètres",
+ "chat": "discussion",
+ "menu": "Menu"
+ },
+ "organizationSidebar": {
+ "viewAll": "Voir tout",
+ "events": "Événements",
+ "noEvents": "Aucun événement à afficher",
+ "noMembers": "Aucun membre à afficher",
+ "members": "Membres"
+ },
+ "postCard": {
+ "likes": "Aime",
+ "comments": "commentaires",
+ "viewPost": "Voir le message",
+ "editPost": "Modifier le message",
+ "postedOn": "Publié le {{date}}"
+ },
+ "home": {
+ "posts": "Des postes",
+ "post": "Poste",
+ "title": "Titre",
+ "textArea": "Quelque chose vous préoccupe ?",
+ "feed": "Alimentation",
+ "loading": "Chargement",
+ "pinnedPosts": "Messages épinglés",
+ "yourFeed": "Votre flux",
+ "nothingToShowHere": "Rien à montrer ici",
+ "somethingOnYourMind": "Quelque chose vous préoccupe ?",
+ "addPost": "Ajouter un message",
+ "startPost": "Démarrer un message",
+ "media": "Médias",
+ "event": "Événement",
+ "article": "Article",
+ "postNowVisibleInFeed": "Le post est maintenant visible dans le fil d'actualité"
+ },
+ "eventAttendance": {
+ "historical_statistics": "Statistiques historiques",
+ "Search member": "Rechercher un membre",
+ "Member Name": "Nom du membre",
+ "Status": "Statut",
+ "Events Attended": "Événements assistés",
+ "Task Assigned": "Tâche assignée",
+ "Member": "Membre",
+ "Admin": "Administrateur",
+ "loading": "Chargement...",
+ "noAttendees": "Aucun participant trouvé"
+ },
+ "onSpotAttendee": {
+ "title": "Participant sur place",
+ "enterFirstName": "Entrez le prénom",
+ "enterLastName": "Entrez le nom de famille",
+ "enterEmail": "Entrez l'e-mail",
+ "enterPhoneNo": "Entrez le numéro de téléphone",
+ "selectGender": "Sélectionnez le sexe",
+ "invalidDetailsMessage": "Veuillez remplir tous les champs obligatoires",
+ "orgIdMissing": "L'ID de l'organisation est manquant. Veuillez réessayer.",
+ "attendeeAddedSuccess": "Participant ajouté avec succès!",
+ "addAttendee": "Ajouter",
+ "phoneNumber": "Numéro de téléphone",
+ "addingAttendee": "Ajout en cours...",
+ "male": "Homme",
+ "female": "Femme",
+ "other": "Autre"
+ },
+ "settings": {
+ "eventAttended": "Événements Assistés",
+ "noeventsAttended": "Aucun événement assisté",
+ "profileSettings": "Paramètres de profil",
+ "gender": "Genre",
+ "phoneNumber": "Numéro de téléphone",
+ "chooseFile": "Choisir le fichier",
+ "birthDate": "Date de naissance",
+ "grade": "Niveau d'éducation",
+ "empStatus": "Statut d'emploi",
+ "maritalStatus": "État civil",
+ "state": "Ville/État",
+ "country": "Pays",
+ "resetChanges": "Réinitialiser les modifications",
+ "profileDetails": "Détails du profil",
+ "deleteUserMessage": "En cliquant sur le bouton Supprimer l'utilisateur, votre utilisateur sera définitivement supprimé ainsi que ses événements, balises et toutes les données associées.",
+ "copyLink": "Copier le lien du profil",
+ "deleteUser": "Supprimer l'utilisateur",
+ "otherSettings": "Autres réglages",
+ "changeLanguage": "Changer de langue",
+ "sgender": "Sélectionnez le sexe",
+ "gradePlaceholder": "Entrez la note",
+ "sEmpStatus": "Sélectionnez le statut d'emploi",
+ "female": "Femelle",
+ "male": "Mâle",
+ "employed": "Employé",
+ "other": "Autre",
+ "sMaritalStatus": "Sélectionnez l'état civil",
+ "unemployed": "Sans emploi",
+ "married": "Marié",
+ "single": "Célibataire",
+ "widowed": "Veuf",
+ "divorced": "Divorcé",
+ "engaged": "Engagé",
+ "separated": "Séparé",
+ "grade1": "1re année",
+ "grade2": "2e année",
+ "grade3": "3e année",
+ "grade4": "Niveau 4",
+ "grade5": "Niveau 5",
+ "grade6": "6ème année",
+ "grade7": "7e année",
+ "grade8": "8e année",
+ "grade9": "9e année",
+ "grade10": "10 e année",
+ "grade11": "11e année",
+ "grade12": "12 e année",
+ "graduate": "Diplômé",
+ "kg": "KG",
+ "preKg": "Pré-KG",
+ "noGrade": "Aucune note",
+ "fullTime": "À temps plein",
+ "partTime": "À temps partiel",
+ "selectCountry": "Choisissez un pays",
+ "enterState": "Entrez la ville ou l'état",
+ "settings": "Paramètres",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "emailAddress": "Adresse email",
+ "displayImage": "Image d'affichage",
+ "address": "Adresse",
+ "saveChanges": "Enregistrer les modifications",
+ "joined": "Rejoint"
+ },
+ "donate": {
+ "title": "Des dons",
+ "donations": "Des dons",
+ "searchDonations": "Rechercher des dons",
+ "donateForThe": "Faites un don pour le",
+ "amount": "Montant",
+ "yourPreviousDonations": "Vos dons précédents",
+ "donate": "Faire un don",
+ "nothingToShow": "Rien à montrer ici.",
+ "success": "Don réussi",
+ "invalidAmount": "Veuillez saisir une valeur numérique pour le montant du don.",
+ "donationAmountDescription": "Veuillez saisir la valeur numérique du montant du don.",
+ "donationOutOfRange": "Le montant du don doit être compris entre {{min}} et {{max}}.",
+ "donateTo": "Faire un don à"
+ },
+ "userEvents": {
+ "title": "Événements",
+ "nothingToShow": "Rien à montrer ici.",
+ "createEvent": "Créer un évènement",
+ "recurring": "Événement récurrent",
+ "startTime": "Heure de début",
+ "endTime": "Heure de fin",
+ "listView": "Vue en liste",
+ "calendarView": "Vue du calendrier",
+ "allDay": "Toute la journée",
+ "eventCreated": "Événement créé et publié avec succès.",
+ "eventDetails": "Détails de l'évènement",
+ "eventTitle": "Titre",
+ "enterTitle": "Entrez le titre",
+ "enterDescription": "Entrez la description",
+ "enterLocation": "Entrez l'emplacement",
+ "publicEvent": "est public",
+ "registerable": "Est enregistrable",
+ "monthlyCalendarView": "Calendrier mensuel",
+ "yearlyCalendarView": "Calendrier annuel",
+ "search": "Rechercher",
+ "cancel": "Annuler",
+ "create": "Créer",
+ "eventDescription": "Description de l'événement",
+ "eventLocation": "Lieu de l'événement",
+ "startDate": "Date de début",
+ "endDate": "Date de fin"
+ },
+ "userEventCard": {
+ "starts": "Départs",
+ "ends": "Prend fin",
+ "creator": "Créateur",
+ "alreadyRegistered": "Déjà enregistré",
+ "location": "Lieu",
+ "register": "Inscription"
+ },
+ "advertisement": {
+ "title": "Annonces",
+ "activeAds": "Campagnes actives",
+ "archievedAds": "Campagnes terminées",
+ "pMessage": "Annonces non présentes pour cette campagne.",
+ "validLink": "Le lien est valide",
+ "invalidLink": "Le lien n'est pas valide",
+ "Rname": "Entrez le nom de l'annonce",
+ "Rtype": "Sélectionnez le type de publicité",
+ "Rmedia": "Fournir du contenu multimédia à afficher",
+ "RstartDate": "Sélectionnez la date de début",
+ "RendDate": "Sélectionnez la date de fin",
+ "RClose": "Ferme la fenêtre",
+ "addNew": "Créer une nouvelle annonce",
+ "EXname": "Ex. ",
+ "EXlink": "Ex. ",
+ "createAdvertisement": "Créer une publicité",
+ "deleteAdvertisement": "Supprimer la publicité",
+ "deleteAdvertisementMsg": "Voulez-vous supprimer cette publicité ?",
+ "view": "Voir",
+ "editAdvertisement": "Modifier l'annonce",
+ "advertisementDeleted": "Publicité supprimée avec succès.",
+ "endDateGreaterOrEqual": "La date de fin doit être supérieure ou égale à la date de début",
+ "advertisementCreated": "Publicité créée avec succès.",
+ "pHeading": "Titre principal",
+ "delete": "Supprimer",
+ "close": "Fermer",
+ "no": "Non",
+ "yes": "Oui",
+ "edit": "Modifier",
+ "saveChanges": "Enregistrer les modifications",
+ "endOfResults": "Fin des résultats"
+ },
+ "userChat": {
+ "chat": "Chat",
+ "contacts": "Contacts",
+ "search": "rechercher",
+ "messages": "messages"
+ },
+ "userChatRoom": {
+ "selectContact": "Sélectionnez un contact pour démarrer la conversation",
+ "sendMessage": "Envoyer le message"
+ },
+ "orgProfileField": {
+ "loading": "Chargement...",
+ "noCustomField": "Aucun champ personnalisé disponible",
+ "customFieldName": "Nom de domaine",
+ "enterCustomFieldName": "Entrez le nom du champ",
+ "customFieldType": "Type de champ",
+ "Remove Custom Field": "Supprimer le champ personnalisé",
+ "fieldSuccessMessage": "Champ ajouté avec succès",
+ "fieldRemovalSuccess": "Champ supprimé avec succès",
+ "String": "Chaîne",
+ "Boolean": "Booléen",
+ "Date": "Date",
+ "Number": "Nombre",
+ "saveChanges": "Enregistrer les modifications"
+ },
+ "orgActionItemCategories": {
+ "enableButton": "Activer",
+ "disableButton": "Désactiver",
+ "updateActionItemCategory": "Mise à jour",
+ "actionItemCategoryName": "Nom",
+ "categoryDetails": "Détails de la catégorie",
+ "enterName": "Entrez le nom",
+ "successfulCreation": "Catégorie d'élément d'action créée avec succès",
+ "successfulUpdation": "Catégorie d'élément d'action mise à jour avec succès",
+ "sameNameConflict": "Veuillez changer le nom pour effectuer une mise à jour",
+ "categoryEnabled": "Catégorie d'élément d'action activée",
+ "categoryDisabled": "Catégorie d'élément d'action désactivée",
+ "noActionItemCategories": "Aucune catégorie d'élément d'action",
+ "status": "Statut",
+ "categoryDeleted": "Catégorie d'élément d'action supprimée avec succès",
+ "deleteCategory": "Supprimer la catégorie",
+ "deleteCategoryMsg": "Êtes-vous sûr de vouloir supprimer cette catégorie d'élément d'action ?",
+ "createButton": "Créer",
+ "editButton": "Modifier"
+ },
+ "organizationVenues": {
+ "title": "Lieux",
+ "addVenue": "Ajouter un lieu",
+ "venueDetails": "Détails du lieu",
+ "venueName": "Nom du lieu",
+ "enterVenueName": "Entrez le nom du lieu",
+ "enterVenueDesc": "Entrez la description du lieu",
+ "capacity": "Capacité",
+ "enterVenueCapacity": "Entrez la capacité du site",
+ "image": "Image du lieu",
+ "uploadVenueImage": "Télécharger l'image du lieu",
+ "createVenue": "Créer un lieu",
+ "venueAdded": "Lieu ajouté avec succès",
+ "editVenue": "Mettre à jour le lieu",
+ "venueUpdated": "Les détails du lieu ont été mis à jour avec succès",
+ "sort": "Trier",
+ "highestCapacity": "Capacité la plus élevée",
+ "lowestCapacity": "Capacité la plus basse",
+ "noVenues": "Aucun lieu trouvé !",
+ "view": "Voir",
+ "venueTitleError": "Le titre du lieu ne peut pas être vide !",
+ "venueCapacityError": "La capacité doit être un nombre positif !",
+ "searchBy": "Recherché par",
+ "description": "Description",
+ "edit": "Modifier",
+ "delete": "Supprimer",
+ "name": "Nom",
+ "desc": "Description"
+ },
+ "addMember": {
+ "title": "Ajouter un membre",
+ "addMembers": "Ajouter des membres",
+ "existingUser": "Utilisateur existant",
+ "newUser": "Nouvel utilisateur",
+ "searchFullName": "Rechercher par nom complet",
+ "enterFirstName": "Entrez votre prénom",
+ "enterLastName": "Entrer le nom de famille",
+ "enterConfirmPassword": "Entrez Confirmer le mot de passe",
+ "organization": "Organisation",
+ "invalidDetailsMessage": "Veuillez fournir tous les détails requis.",
+ "passwordNotMatch": "Les mots de passe ne correspondent pas.",
+ "addMember": "Ajouter un membre",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "emailAddress": "Adresse email",
+ "enterEmail": "Entrer l'email",
+ "password": "Mot de passe",
+ "enterPassword": "Entrer le mot de passe",
+ "confirmPassword": "Confirmer le mot de passe",
+ "cancel": "Annuler",
+ "create": "Créer",
+ "user": "Utilisateur",
+ "profile": "Profil"
+ },
+ "eventActionItems": {
+ "title": "Éléments d'action",
+ "createActionItem": "Créer des éléments d'action",
+ "actionItemCategory": "Catégorie d'élément d'action",
+ "selectActionItemCategory": "Sélectionnez une catégorie d'élément d'action",
+ "selectAssignee": "Sélectionnez un responsable",
+ "preCompletionNotes": "Remarques",
+ "postCompletionNotes": "Notes d'achèvement",
+ "actionItemDetails": "Détails de l'action",
+ "dueDate": "Date d'échéance",
+ "completionDate": "Date d'achèvement",
+ "editActionItem": "Modifier l'élément d'action",
+ "deleteActionItem": "Supprimer l'élément d'action",
+ "deleteActionItemMsg": "Voulez-vous supprimer cette action ?",
+ "successfulDeletion": "Élément d'action supprimé avec succès",
+ "successfulCreation": "Élément d'action créé avec succès",
+ "successfulUpdation": "Élément d'action mis à jour avec succès",
+ "notes": "Remarques",
+ "assignee": "Cessionnaire",
+ "assigner": "Assigner",
+ "assignmentDate": "Date d'affectation",
+ "status": "Statut",
+ "actionItemActive": "Actif",
+ "actionItemStatus": "Statut de l'action",
+ "actionItemCompleted": "Élément d'action terminé",
+ "markCompletion": "Marquer l'achèvement",
+ "save": "Sauvegarder",
+ "yes": "Oui",
+ "no": "Non"
+ },
+ "checkIn": {
+ "errorCheckingIn": "Erreur lors de l'enregistrement",
+ "checkedInSuccessfully": "Enregistrement réussi"
+ },
+ "eventRegistrantsModal": {
+ "errorAddingAttendee": "Erreur lors de l'ajout du participant",
+ "errorRemovingAttendee": "Erreur lors de la suppression du participant"
+ },
+ "userCampaigns": {
+ "title": "Campagnes de financement",
+ "searchByName": "Rechercher par nom...",
+ "searchBy": "Rechercher par",
+ "pledgers": "Contributeurs",
+ "campaigns": "Campagnes",
+ "myPledges": "Mes Promesses",
+ "lowestAmount": "Montant le plus bas",
+ "highestAmount": "Montant le plus élevé",
+ "lowestGoal": "Objectif le plus bas",
+ "highestGoal": "Objectif le plus élevé",
+ "latestEndDate": "Date de fin la plus tardive",
+ "earliestEndDate": "Date de fin la plus proche",
+ "addPledge": "Ajouter une promesse",
+ "viewPledges": "Voir les promesses",
+ "noPledges": "Aucune promesse trouvée",
+ "noCampaigns": "Aucune campagne trouvée"
+ },
+ "userPledges": {
+ "title": "Mes Promesses"
+ },
+ "eventVolunteers": {
+ "volunteers": "Bénévoles",
+ "volunteer": "Bénévole",
+ "volunteerGroups": "Groupes de Bénévoles",
+ "individuals": "Individus",
+ "groups": "Groupes",
+ "status": "Statut",
+ "noVolunteers": "Aucun Bénévole",
+ "noVolunteerGroups": "Aucun Groupe de Bénévoles",
+ "add": "Ajouter",
+ "mostHoursVolunteered": "Le Plus d'Heures de Bénévolat",
+ "leastHoursVolunteered": "Le Moins d'Heures de Bénévolat",
+ "accepted": "Accepté",
+ "addVolunteer": "Ajouter un Bénévole",
+ "removeVolunteer": "Supprimer le Bénévole",
+ "volunteerAdded": "Bénévole ajouté avec succès",
+ "volunteerRemoved": "Bénévole supprimé avec succès",
+ "volunteerGroupCreated": "Groupe de bénévoles créé avec succès",
+ "volunteerGroupUpdated": "Groupe de bénévoles mis à jour avec succès",
+ "volunteerGroupDeleted": "Groupe de bénévoles supprimé avec succès",
+ "removeVolunteerMsg": "Êtes-vous sûr de vouloir supprimer ce bénévole?",
+ "deleteVolunteerGroupMsg": "Êtes-vous sûr de vouloir supprimer ce groupe de bénévoles?",
+ "leader": "Chef",
+ "group": "Groupe",
+ "createGroup": "Créer un Groupe",
+ "updateGroup": "Mettre à Jour le Groupe",
+ "deleteGroup": "Supprimer le Groupe",
+ "volunteersRequired": "Bénévoles Requis",
+ "volunteerDetails": "Détails du Bénévole",
+ "hoursVolunteered": "Heures de Bénévolat",
+ "groupDetails": "Détails du Groupe",
+ "creator": "Créateur",
+ "requests": "Demandes",
+ "noRequests": "Aucune Demande",
+ "latest": "Le Plus Récent",
+ "earliest": "Le Plus Ancien",
+ "requestAccepted": "Demande acceptée avec succès",
+ "requestRejected": "Demande rejetée avec succès",
+ "details": "Détails",
+ "manageGroup": "Gérer le Groupe",
+ "mostVolunteers": "Le plus de bénévoles",
+ "leastVolunteers": "Le moins de bénévoles"
+ },
+ "userVolunteer": {
+ "title": "Volontariat",
+ "name": "Titre",
+ "upcomingEvents": "Événements à Venir",
+ "requests": "Demandes",
+ "invitations": "Invitations",
+ "groups": "Groupes de Bénévoles",
+ "actions": "Actions",
+ "searchByName": "Rechercher par Nom",
+ "latestEndDate": "Date de Fin la Plus Récente",
+ "earliestEndDate": "Date de Fin la Plus Ancienne",
+ "noEvents": "Aucun Événement à Venir",
+ "volunteer": "Bénévole",
+ "volunteered": "A Bénévolé",
+ "join": "Rejoindre",
+ "joined": "Rejoint",
+ "searchByEventName": "Rechercher par Titre d'Événement",
+ "filter": "Filtrer",
+ "groupInvite": "Invitation de Groupe",
+ "individualInvite": "Invitation Individuelle",
+ "noInvitations": "Aucune Invitation",
+ "accept": "Accepter",
+ "reject": "Rejeter",
+ "receivedLatest": "Reçu le Plus Récemment",
+ "receivedEarliest": "Reçu en Premier",
+ "invitationAccepted": "Invitation acceptée avec succès",
+ "invitationRejected": "Invitation rejetée avec succès",
+ "volunteerSuccess": "Demande de bénévolat envoyée avec succès",
+ "recurring": "Récurrent",
+ "groupInvitationSubject": "Invitation à rejoindre le groupe de bénévoles",
+ "eventInvitationSubject": "Invitation à faire du bénévolat pour l'événement"
+ }
+}
diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json
new file mode 100644
index 0000000000..f312424f1d
--- /dev/null
+++ b/public/locales/hi/common.json
@@ -0,0 +1,98 @@
+{
+ "firstName": "पहला नाम",
+ "lastName": "उपनाम",
+ "searchByName": "नाम से खोजें",
+ "loading": "लोड हो रहा है...",
+ "endOfResults": "परिणाम का अंत",
+ "noResultsFoundFor": "का कोई परिणाम नहीं मिला ",
+ "edit": "संपादन करना",
+ "admins": "व्यवस्थापक",
+ "admin": "व्यवस्थापक",
+ "user": "उपयोगकर्ता",
+ "superAdmin": "सुपरएडमिन",
+ "members": "सदस्यों",
+ "logout": "लॉग आउट",
+ "login": "लॉग इन करें",
+ "register": "पंजीकरण करवाना",
+ "menu": "मेन्यू",
+ "settings": "समायोजन",
+ "users": "उपयोगकर्ताओं",
+ "requests": "अनुरोध",
+ "OR": "या",
+ "cancel": "रद्द करना",
+ "close": "बंद करना",
+ "create": "बनाएं",
+ "delete": "मिटाना",
+ "done": "हो गया",
+ "yes": "हाँ",
+ "no": "नहीं",
+ "filter": "फ़िल्टर",
+ "gender": "लिंग",
+ "search": "खोज",
+ "description": "विवरण",
+ "saveChanges": "परिवर्तनों को सुरक्षित करें",
+ "resetChanges": "परिवर्तनों को रीसेट करें",
+ "displayImage": "प्रदर्शन छवि",
+ "enterEmail": "ईमेल दर्ज करें",
+ "emailAddress": "मेल पता",
+ "email": "ईमेल",
+ "name": "नाम",
+ "desc": "विवरण",
+ "enterPassword": "पास वर्ड दर्ज करें",
+ "password": "पासवर्ड",
+ "confirmPassword": "पासवर्ड की पुष्टि कीजिये",
+ "forgotPassword": "पासवर्ड भूल गए ?",
+ "talawaAdminPortal": "तलावा एडमिन पोर्टल",
+ "address": "पता",
+ "location": "जगह",
+ "enterLocation": "स्थान दर्ज करें",
+ "joined": "में शामिल हो गए",
+ "startDate": "आरंभ करने की तिथि",
+ "endDate": "अंतिम तिथि",
+ "startTime": "समय शुरू",
+ "endTime": "अंत समय",
+ "My Organizations": "मेरे संगठन",
+ "Dashboard": "डैशबोर्ड",
+ "People": "लोग",
+ "Events": "कार्यक्रम",
+ "Venues": "स्थल",
+ "Action Items": "कार्य आइटम",
+ "Posts": "पोस्ट",
+ "Block/Unblock": "ब्लॉक/अनब्लॉक",
+ "Advertisement": "विज्ञापन",
+ "Funds": "निधि",
+ "Membership Requests": "सदस्यता अनुरोध",
+ "Plugins": "प्लगइन्स",
+ "Plugin Store": "प्लगइन स्टोर",
+ "Settings": "सेटिंग्स",
+ "createdOn": "बनाया गया",
+ "createdBy": "के द्वारा बनाया गया",
+ "usersRole": "उपयोगकर्ता की भूमिका",
+ "changeRole": "भूमिका बदलें",
+ "action": "क्रिया",
+ "removeUser": "उपयोगकर्ता हटाएं",
+ "remove": "हटाएं",
+ "viewProfile": "प्रोफ़ाइल देखें",
+ "profile": "प्रोफ़ाइल",
+ "noFiltersApplied": "कोई फ़िल्टर लागू नहीं हैं",
+ "manage": "प्रबंधित करें",
+ "searchResultsFor": "{{text}} के लिए खोज परिणाम",
+ "none": "कोई नहीं",
+ "sort": "क्रम से लगाना",
+ "Donate": "दान करें",
+ "addedSuccessfully": "{{item}} सफलतापूर्वक जोड़ा गया",
+ "updatedSuccessfully": "{{item}} सफलतापूर्वक अपडेट किया गया",
+ "removedSuccessfully": "{{item}} सफलतापूर्वक हटाया गया",
+ "sessionWarning": "आपका सत्र निष्क्रियता के कारण जल्द ही समाप्त हो जाएगा। कृपया अपने सत्र को बढ़ाने के लिए पृष्ठ के साथ बातचीत करें।",
+ "sessionLogOut": "निष्क्रियता के कारण आपका सत्र समाप्त हो गया है। कृपया जारी रखने के लिए पुनः लॉगिन करें।",
+ "successfullyUpdated": "सफलतापूर्वक अपडेट किया गया",
+ "all": "सभी",
+ "active": "सक्रिय",
+ "disabled": "अक्षम",
+ "pending": "लंबित",
+ "completed": "पूरा हुआ",
+ "late": "देर से",
+ "createdLatest": "नवीनतम बनाया गया",
+ "createdEarliest": "सबसे पहले बनाया गया",
+ "searchBy": "के द्वारा खोजें {{item}}"
+}
diff --git a/public/locales/hi/errors.json b/public/locales/hi/errors.json
new file mode 100644
index 0000000000..63b6c3f5d3
--- /dev/null
+++ b/public/locales/hi/errors.json
@@ -0,0 +1,11 @@
+{
+ "talawaApiUnavailable": "तलवा-एपीआई सेवा उपलब्ध नहीं है! ",
+ "notFound": "नहीं मिला",
+ "unknownError": "एक अज्ञात त्रुटि हुई। {{msg}}",
+ "notAuthorised": "क्षमा मांगना! ",
+ "errorSendingMail": "मेल भेजने में त्रुटि",
+ "emailNotRegistered": "ईमेल पंजीकृत नहीं है",
+ "notFoundMsg": "उफ़! ",
+ "errorOccurredCouldntCreate": "एक त्रुटि हुई। {{entity}} नहीं बना सके",
+ "errorLoading": "{{entity}} डेटा लोड करते समय त्रुटि हुई"
+}
diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json
new file mode 100644
index 0000000000..2645340b23
--- /dev/null
+++ b/public/locales/hi/translation.json
@@ -0,0 +1,1483 @@
+{
+ "leaderboard": {
+ "title": "लीडरबोर्ड",
+ "searchByVolunteer": "स्वयंसेवक द्वारा खोजें",
+ "mostHours": "सबसे अधिक घंटे",
+ "leastHours": "सबसे कम घंटे",
+ "timeFrame": "समय सीमा",
+ "allTime": "सभी समय",
+ "weekly": "इस सप्ताह",
+ "monthly": "इस माह",
+ "yearly": "इस वर्ष",
+ "noVolunteers": "कोई स्वयंसेवक नहीं मिला!"
+ },
+ "loginPage": {
+ "title": "तालावा व्यवस्थापक",
+ "fromPalisadoes": "Palisadoes फाउंडेशन स्वयंसेवकों द्वारा विकसित एक ओपन-सोर्स एप्लिकेशन",
+ "userLogin": "उपयोगकर्ता लॉगिन",
+ "atleast_8_char_long": "कम से कम 8 अक्षर लंबे",
+ "atleast_6_char_long": "कम से कम 6 अक्षर लंबे",
+ "firstName_invalid": "पहला नाम केवल छोटे और बड़े अक्षरों को शामिल कर सकता है",
+ "lastName_invalid": "अंतिम नाम केवल छोटे और बड़े अक्षरों को शामिल कर सकता है",
+ "password_invalid": "पासवर्ड में कम से कम 1 छोटा अक्षर, 1 बड़ा अक्षर, 1 संख्या और 1 विशेष अक्षर होना चाहिए",
+ "email_invalid": "ईमेल में कम से कम 8 अक्षर होने चाहिए",
+ "Password_and_Confirm_password_mismatches.": "पासवर्ड और पुष्टि पासवर्ड मेल नहीं खाते।",
+ "doNotOwnAnAccount": "कोई खाता नहीं है?",
+ "captchaError": "कैप्चा त्रुटि!",
+ "Please_check_the_captcha": "कृपया कैप्चा जांचें।",
+ "Something_went_wrong": "कुछ गलत हो गया, कृपया बाद में पुनः प्रयास करें।",
+ "passwordMismatches": "पासवर्ड और पुष्टि पासवर्ड मेल नहीं खाते।",
+ "fillCorrectly": "सभी विवरण सही ढंग से भरें।",
+ "successfullyRegistered": "सफलतापूर्वक पंजीकृत।",
+ "lowercase_check": "कम से कम एक छोटा अक्षर",
+ "uppercase_check": "कम से कम एक बड़ा अक्षर",
+ "numeric_value_check": "कम से कम एक संख्या",
+ "special_char_check": "कम से कम एक विशेष अक्षर",
+ "selectOrg": "एक संगठन चुनें",
+ "afterRegister": "सफलतापूर्वक पंजीकृत।",
+ "talawa_portal": "तालावा पोर्टल",
+ "login": "लॉगिन",
+ "register": "पंजीकरण",
+ "firstName": "पहला नाम",
+ "lastName": "अंतिम नाम",
+ "email": "ईमेल",
+ "password": "पासवर्ड",
+ "confirmPassword": "पुष्टि पासवर्ड",
+ "forgotPassword": "पासवर्ड भूल गए",
+ "enterEmail": "ईमेल दर्ज करें",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "talawaApiUnavailable": "तालावा एपीआई अनुपलब्ध",
+ "notAuthorised": "अनधिकृत",
+ "notFound": "नहीं मिला",
+ "OR": "या",
+ "admin": "व्यवस्थापक",
+ "user": "उपयोगकर्ता",
+ "loading": "लोड हो रहा है"
+ },
+ "userLoginPage": {
+ "title": "तालावा व्यवस्थापक",
+ "fromPalisadoes": "Palisadoes फाउंडेशन स्वयंसेवकों द्वारा विकसित एक ओपन-सोर्स एप्लिकेशन",
+ "atleast_8_char_long": "कम से कम 8 अक्षर लंबे",
+ "Password_and_Confirm_password_mismatches.": "पासवर्ड और पुष्टि पासवर्ड मेल नहीं खाते।",
+ "doNotOwnAnAccount": "कोई खाता नहीं है?",
+ "captchaError": "कैप्चा त्रुटि!",
+ "Please_check_the_captcha": "कृपया कैप्चा जांचें।",
+ "Something_went_wrong": "कुछ गलत हो गया, कृपया बाद में पुनः प्रयास करें।",
+ "passwordMismatches": "पासवर्ड और पुष्टि पासवर्ड मेल नहीं खाते।",
+ "fillCorrectly": "सभी विवरण सही ढंग से भरें।",
+ "successfullyRegistered": "सफलतापूर्वक पंजीकृत।",
+ "userLogin": "उपयोगकर्ता लॉगिन",
+ "afterRegister": "सफलतापूर्वक पंजीकृत।",
+ "selectOrg": "एक संगठन चुनें",
+ "talawa_portal": "तालावा पोर्टल",
+ "login": "लॉगिन",
+ "register": "पंजीकरण",
+ "firstName": "पहला नाम",
+ "lastName": "अंतिम नाम",
+ "email": "ईमेल",
+ "password": "पासवर्ड",
+ "confirmPassword": "पुष्टि पासवर्ड",
+ "forgotPassword": "पासवर्ड भूल गए",
+ "enterEmail": "ईमेल दर्ज करें",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "talawaApiUnavailable": "तालावा एपीआई अनुपलब्ध",
+ "notAuthorised": "अनधिकृत",
+ "notFound": "नहीं मिला",
+ "OR": "या",
+ "loading": "लोड हो रहा है"
+ },
+ "latestEvents": {
+ "eventCardTitle": "आगामी घटनाएँ",
+ "eventCardSeeAll": "सभी देखें",
+ "noEvents": "कोई आगामी घटनाएँ नहीं हैं"
+ },
+ "latestPosts": {
+ "latestPostsTitle": "नवीनतम पोस्ट",
+ "seeAllLink": "सभी देखें",
+ "noPostsCreated": "कोई पोस्ट नहीं बनाई गई"
+ },
+ "listNavbar": {
+ "roles": "भूमिकाएँ",
+ "talawa_portal": "तालावा पोर्टल",
+ "requests": "अनुरोध",
+ "logout": "लॉगआउट"
+ },
+ "leftDrawer": {
+ "my organizations": "मेरे संगठन",
+ "requests": "सदस्यता अनुरोध",
+ "communityProfile": "समुदाय प्रोफ़ाइल",
+ "talawaAdminPortal": "तालावा व्यवस्थापक पोर्टल",
+ "menu": "मेनू",
+ "users": "उपयोगकर्ता",
+ "logout": "लॉगआउट"
+ },
+ "leftDrawerOrg": {
+ "Dashboard": "डैशबोर्ड",
+ "People": "लोग",
+ "Events": "घटनाएँ",
+ "Contributions": "योगदान",
+ "Posts": "पोस्ट",
+ "Block/Unblock": "ब्लॉक/अनब्लॉक",
+ "Plugins": "प्लगइन्स",
+ "Plugin Store": "प्लगइन स्टोर",
+ "Advertisement": "विज्ञापन",
+ "allOrganizations": "सभी संगठन",
+ "yourOrganization": "आपका संगठन",
+ "notification": "सूचना",
+ "language": "भाषा",
+ "notifications": "सूचनाएँ",
+ "spamsThe": "स्पैम",
+ "group": "समूह",
+ "noNotifications": "कोई सूचनाएँ नहीं",
+ "talawaAdminPortal": "तालावा व्यवस्थापक पोर्टल",
+ "menu": "मेनू",
+ "talawa_portal": "तालावा पोर्टल",
+ "settings": "सेटिंग्स",
+ "logout": "लॉगआउट",
+ "close": "बंद करें"
+ },
+ "orgList": {
+ "title": "तालावा संगठन",
+ "you": "आप",
+ "designation": "पदनाम",
+ "my organizations": "मेरे संगठन",
+ "createOrganization": "संगठन बनाएं",
+ "createSampleOrganization": "नमूना संगठन बनाएं",
+ "city": "शहर",
+ "countryCode": "देश कोड",
+ "dependentLocality": "निर्भर स्थान",
+ "line1": "लाइन 1",
+ "line2": "लाइन 2",
+ "postalCode": "पोस्टल कोड",
+ "sortingCode": "सॉर्टिंग कोड",
+ "state": "राज्य",
+ "userRegistrationRequired": "उपयोगकर्ता पंजीकरण आवश्यक",
+ "visibleInSearch": "खोज में दिखाई दे",
+ "enterName": "नाम दर्ज करें",
+ "sort": "प्रकार",
+ "Latest": "नवीनतम",
+ "Earliest": "सबसे पहले",
+ "noOrgErrorTitle": "संगठन नहीं मिला",
+ "sampleOrgDuplicate": "केवल एक नमूना संगठन की अनुमति है",
+ "noOrgErrorDescription": "कृपया डैशबोर्ड के माध्यम से संगठन बनाएं",
+ "manageFeatures": "विशेषताएं प्रबंधित करें",
+ "manageFeaturesInfo": "सफलतापूर्वक बनाया गया!",
+ "goToStore": "प्लगइन स्टोर पर जाएं",
+ "enableEverything": "सब कुछ सक्षम करें",
+ "sampleOrgSuccess": "नमूना संगठन सफलतापूर्वक बनाया गया",
+ "name": "नाम",
+ "email": "ईमेल",
+ "searchByName": "नाम से खोजें",
+ "description": "विवरण",
+ "location": "स्थान",
+ "address": "पता",
+ "displayImage": "छवि प्रदर्शित करें",
+ "filter": "फ़िल्टर",
+ "cancel": "रद्द करें",
+ "endOfResults": "परिणाम समाप्त",
+ "noResultsFoundFor": "कोई परिणाम नहीं मिला",
+ "OR": "या"
+ },
+ "orgListCard": {
+ "manage": "प्रबंधित करें",
+ "sampleOrganization": "संगठन नमूना",
+ "admins": "प्रशासक",
+ "members": "सदस्य"
+ },
+ "paginationList": {
+ "rowsPerPage": "प्रति पृष्ठ पंक्तियाँ",
+ "all": "सभी"
+ },
+ "requests": {
+ "title": "सदस्यता अनुरोध",
+ "sl_no": "क्रम संख्या",
+ "accept": "स्वीकार करें",
+ "reject": "अस्वीकार करें",
+ "searchRequests": "सदस्यता अनुरोध खोजें",
+ "noOrgError": "संगठन नहीं मिला, कृपया डैशबोर्ड के माध्यम से संगठन बनाएं",
+ "noRequestsFound": "कोई सदस्यता अनुरोध नहीं मिला",
+ "acceptedSuccessfully": "अनुरोध सफलतापूर्वक स्वीकार किया गया",
+ "rejectedSuccessfully": "अनुरोध सफलतापूर्वक अस्वीकार किया गया",
+ "noOrgErrorTitle": "संगठन नहीं मिला",
+ "noOrgErrorDescription": "कृपया डैशबोर्ड के माध्यम से संगठन बनाएं",
+ "name": "नाम",
+ "email": "ईमेल",
+ "endOfResults": "परिणाम समाप्त",
+ "noResultsFoundFor": "कोई परिणाम नहीं मिला"
+ },
+ "users": {
+ "title": "तालावा भूमिकाएँ",
+ "joined_organizations": "संगठनों में शामिल हुए",
+ "blocked_organizations": "अवरुद्ध संगठन",
+ "orgJoinedBy": "द्वारा शामिल संगठन",
+ "orgThatBlocked": "अवरुद्ध संगठन",
+ "hasNotJoinedAnyOrg": "किसी भी संगठन में शामिल नहीं हुआ",
+ "isNotBlockedByAnyOrg": "किसी भी संगठन द्वारा अवरुद्ध नहीं किया गया",
+ "searchByOrgName": "संगठन के नाम से खोजें",
+ "view": "दृश्य",
+ "enterName": "नाम दर्ज करें",
+ "loadingUsers": "उपयोगकर्ताओं को लोड कर रहा है...",
+ "noUserFound": "कोई उपयोगकर्ता नहीं मिला",
+ "sort": "प्रकार",
+ "Newest": "नवीनतम",
+ "Oldest": "सबसे पुराना",
+ "noOrgError": "संगठन नहीं मिला, कृपया डैशबोर्ड के माध्यम से संगठन बनाएं",
+ "roleUpdated": "भूमिका अपडेट की गई।",
+ "joinNow": "अभी शामिल हों",
+ "visit": "यात्रा",
+ "withdraw": "वापस लें",
+ "removeUserFrom": "{{org}} से उपयोगकर्ता को हटाएं",
+ "removeConfirmation": "क्या आप वाकई '{{name}}' को संगठन '{{org}}' से हटाना चाहते हैं?",
+ "searchByName": "नाम से खोजें",
+ "users": "उपयोगकर्ता",
+ "name": "नाम",
+ "email": "ईमेल",
+ "endOfResults": "परिणाम समाप्त",
+ "admin": "प्रशासक",
+ "superAdmin": "सुपर प्रशासक",
+ "user": "उपयोगकर्ता",
+ "filter": "फ़िल्टर",
+ "noResultsFoundFor": "कोई परिणाम नहीं मिला",
+ "talawaApiUnavailable": "तालावा एपीआई अनुपलब्ध",
+ "cancel": "रद्द करें",
+ "admins": "प्रशासक",
+ "members": "सदस्य",
+ "orgJoined": "संगठन में शामिल",
+ "MembershipRequestSent": "सदस्यता अनुरोध भेजा गया",
+ "AlreadyJoined": "पहले से शामिल",
+ "errorOccured": "त्रुटि हुई"
+ },
+ "communityProfile": {
+ "title": "समुदाय प्रोफ़ाइल",
+ "editProfile": "प्रोफ़ाइल संपादित करें",
+ "communityProfileInfo": "ये विवरण आपके और आपके समुदाय के सदस्यों के लॉगिन/पंजीकरण स्क्रीन पर दिखाई देंगे",
+ "communityName": "समुदाय का नाम",
+ "wesiteLink": "वेबसाइट लिंक",
+ "logo": "लोगो",
+ "social": "सोशल मीडिया लिंक",
+ "url": "यूआरएल दर्ज करें",
+ "profileChangedMsg": "प्रोफ़ाइल विवरण सफलतापूर्वक अपडेट किया गया।",
+ "resetData": "प्रोफ़ाइल विवरण सफलतापूर्वक रीसेट किया गया।"
+ },
+ "dashboard": {
+ "title": "डैशबोर्ड",
+ "about": "के बारे में",
+ "deleteThisOrganization": "इस संगठन को हटाएं",
+ "statistics": "आंकड़े",
+ "posts": "पोस्ट",
+ "events": "घटनाएँ",
+ "blockedUsers": "अवरुद्ध उपयोगकर्ता",
+ "viewAll": "सभी देखें",
+ "upcomingEvents": "आगामी घटनाएँ",
+ "noUpcomingEvents": "कोई आगामी घटनाएँ नहीं हैं",
+ "latestPosts": "नवीनतम पोस्ट",
+ "noPostsPresent": "कोई पोस्ट नहीं हैं",
+ "membershipRequests": "सदस्यता अनुरोध",
+ "noMembershipRequests": "कोई सदस्यता अनुरोध नहीं हैं",
+ "location": "स्थान",
+ "members": "सदस्य",
+ "admins": "प्रशासक",
+ "requests": "अनुरोध",
+ "talawaApiUnavailable": "तालावा एपीआई अनुपलब्ध",
+ "volunteerRankings": "स्वयंसेवक रैंकिंग",
+ "noVolunteers": "कोई स्वयंसेवक नहीं मिला!"
+ },
+ "organizationPeople": {
+ "title": "तालावा सदस्य",
+ "filterByName": "नाम से फ़िल्टर करें",
+ "filterByLocation": "स्थान से फ़िल्टर करें",
+ "filterByEvent": "घटना से फ़िल्टर करें",
+ "searchName": "नाम दर्ज करें",
+ "searchevent": "घटना दर्ज करें",
+ "searchFullName": "पूरा नाम दर्ज करें",
+ "people": "लोग",
+ "sort": "भूमिका से खोजें",
+ "actions": "क्रियाएँ",
+ "addMembers": "सदस्य जोड़ें",
+ "existingUser": "मौजूदा उपयोगकर्ता",
+ "newUser": "नया उपयोगकर्ता",
+ "enterFirstName": "अपना पहला नाम दर्ज करें",
+ "enterLastName": "अपना अंतिम नाम दर्ज करें",
+ "enterConfirmPassword": "अपना पासवर्ड पुष्टि के लिए दर्ज करें",
+ "organization": "संगठन",
+ "invalidDetailsMessage": "कृपया मान्य विवरण दर्ज करें।",
+ "members": "सदस्य",
+ "admins": "प्रशासक",
+ "users": "उपयोगकर्ता",
+ "searchFirstName": "प्रथम नाम खोजें",
+ "searchLastName": "अंतिम नाम खोजें",
+ "firstName": "प्रथम नाम",
+ "lastName": "अंतिम नाम",
+ "emailAddress": "ईमेल पता",
+ "enterEmail": "ईमेल दर्ज करें",
+ "password": "पासवर्ड",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "confirmPassword": "पासवर्ड की पुष्टि करें",
+ "user": "उपयोगकर्ता",
+ "profile": "प्रोफ़ाइल",
+ "create": "बनाएं",
+ "cancel": "रद्द करें"
+ },
+ "organizationTags": {
+ "title": "संस्थान टैग",
+ "createTag": "नया टैग बनाएँ",
+ "manageTag": "प्रबंधित करें",
+ "editTag": "संपादित करें",
+ "removeTag": "हटाएँ",
+ "tagDetails": "टैग विवरण",
+ "tagName": "नाम",
+ "tagType": "प्रकार",
+ "tagNamePlaceholder": "टैग का नाम लिखें",
+ "tagCreationSuccess": "नई टैग सफलतापूर्वक बनाई गई",
+ "tagUpdationSuccess": "टैग सफलतापूर्वक अपडेट की गई",
+ "tagRemovalSuccess": "टैग सफलतापूर्वक हटाई गई",
+ "noTagsFound": "कोई टैग नहीं मिला",
+ "removeUserTag": "टैग हटाएँ",
+ "removeUserTagMessage": "क्या आप इस टैग को हटाना चाहते हैं?",
+ "addChildTag": "उप-टैग जोड़ें",
+ "enterTagName": "टैग का नाम दर्ज करें"
+ },
+ "manageTag": {
+ "title": "टैग विवरण",
+ "addPeopleToTag": "टैग में लोगों को जोड़ें",
+ "viewProfile": "देखें",
+ "noAssignedMembersFound": "कोई असाइन किए गए सदस्य नहीं मिले",
+ "unassignUserTag": "टैग को हटाएं",
+ "unassignUserTagMessage": "क्या आप इस उपयोगकर्ता से टैग हटाना चाहते हैं?",
+ "successfullyUnassigned": "उपयोगकर्ता से टैग हटा दिया गया",
+ "addPeople": "लोगों को जोड़ें",
+ "add": "जोड़ें",
+ "subTags": "उप-टैग्स",
+ "successfullyAssignedToPeople": "टैग सफलतापूर्वक असाइन किया गया",
+ "errorOccurredWhileLoadingMembers": "सदस्यों को लोड करते समय त्रुटि हुई",
+ "userName": "उपयोगकर्ता नाम",
+ "actions": "क्रियाएँ",
+ "noOneSelected": "कोई चयनित नहीं",
+ "assignToTags": "टैग्स को असाइन करें",
+ "removeFromTags": "टैग्स से हटाएं",
+ "assign": "असाइन करें",
+ "remove": "हटाएं",
+ "successfullyAssignedToTags": "सफलतापूर्वक टैग्स को असाइन किया गया",
+ "successfullyRemovedFromTags": "सफलतापूर्वक टैग्स से हटाया गया",
+ "errorOccurredWhileLoadingOrganizationUserTags": "संगठन टैग्स को लोड करते समय त्रुटि हुई",
+ "errorOccurredWhileLoadingSubTags": "उप-टैग लोड करते समय त्रुटि हुई",
+ "removeUserTag": "टैग हटाएं",
+ "removeUserTagMessage": "क्या आप इस टैग को हटाना चाहते हैं? यह सभी उप-टैग्स और सभी संबंधों को हटा देगा।",
+ "tagDetails": "टैग विवरण",
+ "tagName": "नाम",
+ "tagUpdationSuccess": "टैग सफलतापूर्वक अपडेट की गई",
+ "tagRemovalSuccess": "टैग सफलतापूर्वक हटाई गई",
+ "noTagSelected": "कोई टैग चयनित नहीं",
+ "changeNameToEdit": "अपडेट करने के लिए नाम बदलें",
+ "selectTag": "टैग चुनें",
+ "collapse": "संक्षिप्त करें",
+ "expand": "विस्तारित करें",
+ "tagNamePlaceholder": "टैग का नाम लिखें",
+ "allTags": "सभी टैग",
+ "noMoreMembersFound": "कोई और सदस्य नहीं मिला"
+ },
+ "userListCard": {
+ "addAdmin": "व्यवस्थापक जोड़ें",
+ "addedAsAdmin": "उपयोगकर्ता को व्यवस्थापक के रूप में जोड़ा गया है.",
+ "joined": "शामिल हुए",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "orgAdminListCard": {
+ "remove": "निकालना",
+ "removeAdmin": "व्यवस्थापक हटाएँ",
+ "removeAdminMsg": "क्या आप इस व्यवस्थापक को हटाना चाहते हैं?",
+ "adminRemoved": "व्यवस्थापक को हटा दिया गया है.",
+ "joined": "शामिल हुए",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "orgPeopleListCard": {
+ "remove": "निकालना",
+ "removeMember": "सदस्य हटाएँ",
+ "removeMemberMsg": "क्या आप इस सदस्य को हटाना चाहते हैं?",
+ "memberRemoved": "सदस्य को हटा दिया गया है",
+ "joined": "शामिल हुए",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "organizationEvents": {
+ "title": "आयोजन",
+ "filterByTitle": "शीर्षक के अनुसार फ़िल्टर करें",
+ "filterByLocation": "स्थान के अनुसार फ़िल्टर करें",
+ "filterByDescription": "विवरण के अनुसार फ़िल्टर करें",
+ "addEvent": "कार्यक्रम जोड़ें",
+ "eventDetails": "घटना की जानकारी",
+ "searchMemberName": "सदस्य का नाम खोजें",
+ "eventTitle": "शीर्षक",
+ "startTime": "समय शुरू",
+ "endTime": "अंत समय",
+ "allDay": "पूरे दिन",
+ "recurringEvent": "पुनरावर्ती ईवेंट",
+ "isPublic": "सार्वजनिक है",
+ "isRegistrable": "पंजीकरण योग्य है",
+ "createEvent": "कार्यक्रम बनाएँ",
+ "enterFilter": "फ़िल्टर दर्ज करें",
+ "enterTitle": "शीर्षक दर्ज करें",
+ "enterDescrip": "विवरण दर्ज करें",
+ "eventLocation": "स्थान दर्ज करें",
+ "searchEventName": "ईवेंट का नाम खोजें",
+ "eventType": "घटना प्रकार",
+ "eventCreated": "बधाई हो! ",
+ "customRecurrence": "कस्टम पुनरावृत्ति",
+ "repeatsEvery": "प्रत्येक को दोहराता है",
+ "repeatsOn": "पर दोहराता है",
+ "ends": "समाप्त होता है",
+ "never": "कभी नहीं",
+ "on": "पर",
+ "after": "बाद",
+ "occurences": "घटनाओं",
+ "events": "कार्यक्रम",
+ "description": "विवरण",
+ "location": "स्थान",
+ "startDate": "प्रारंभ तिथि",
+ "endDate": "समाप्ति तिथि",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध",
+ "done": "पूर्ण"
+ },
+ "organizationActionItems": {
+ "actionItemCategory": "क्रिया वस्तु श्रेणी",
+ "actionItemDetails": "क्रिया वस्तु विवरण",
+ "actionItemCompleted": "क्रिया वस्तु पूरी",
+ "assignee": "प्राप्तकर्ता",
+ "assigner": "सौंपने वाला",
+ "assignmentDate": "आवंटन तिथि",
+ "active": "सक्रिय",
+ "clearFilters": "फ़िल्टर साफ़ करें",
+ "completionDate": "पूर्णता तिथि",
+ "createActionItem": "क्रिया वस्तु बनाएँ",
+ "deleteActionItem": "क्रिया वस्तु हटाएँ",
+ "deleteActionItemMsg": "क्या आप इस क्रिया वस्तु को हटाना चाहते हैं?",
+ "details": "विवरण",
+ "dueDate": "समाप्ति तिथि",
+ "earliest": "सबसे पहले",
+ "editActionItem": "क्रिया वस्तु संपादित करें",
+ "isCompleted": "पूर्ण",
+ "latest": "नवीनतम",
+ "makeActive": "सक्रिय बनाएं",
+ "noActionItems": "कोई क्रिया वस्तु नहीं",
+ "options": "विकल्प",
+ "preCompletionNotes": "पूर्व-पूर्णता नोट्स",
+ "actionItemActive": "सक्रिय क्रिया वस्तु",
+ "markCompletion": "पूर्णता को चिह्नित करें",
+ "actionItemStatus": "क्रिया वस्तु स्थिति",
+ "postCompletionNotes": "पूर्णता के बाद नोट्स",
+ "selectActionItemCategory": "क्रिया वस्तु श्रेणी चुनें",
+ "selectAssignee": "प्राप्तकर्ता चुनें",
+ "status": "स्थिति",
+ "successfulCreation": "क्रिया वस्तु सफलतापूर्वक बनाई गई",
+ "successfulUpdation": "क्रिया वस्तु सफलतापूर्वक अद्यतन की गई",
+ "successfulDeletion": "क्रिया वस्तु सफलतापूर्वक हटाई गई",
+ "title": "क्रिया वस्तुएँ",
+ "category": "श्रेणी",
+ "allottedHours": "आवंटित घंटे",
+ "latestDueDate": "नवीनतम समाप्ति तिथि",
+ "earliestDueDate": "प्रारंभिक समाप्ति तिथि",
+ "updateActionItem": "क्रिया वस्तु अपडेट करें",
+ "noneUpdated": "कोई फ़ील्ड अपडेट नहीं की गई",
+ "updateStatusMsg": "क्या आप वाकई इस क्रिया वस्तु को लंबित के रूप में चिह्नित करना चाहते हैं?",
+ "close": "बंद करें",
+ "eventActionItems": "घटना क्रिया वस्तुएं",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "individuals": "व्यक्तियों",
+ "groups": "समूहों",
+ "assignTo": "सौंपें",
+ "volunteers": "स्वयंसेवक",
+ "volunteerGroups": "स्वयंसेवक समूह"
+ },
+ "organizationAgendaCategory": {
+ "agendaCategoryDetails": "एजेंडा श्रेणी विवरण",
+ "updateAgendaCategory": "एजेंडा श्रेणी अपडेट करें",
+ "title": "एजेंडा श्रेणियाँ",
+ "name": "श्रेणी",
+ "description": "विवरण",
+ "createdBy": "द्वारा बनाया गया",
+ "options": "विकल्प",
+ "createAgendaCategory": "एजेंडा श्रेणी बनाएं",
+ "noAgendaCategories": "कोई एजेंडा श्रेणी नहीं",
+ "update": "अपडेट करें",
+ "agendaCategoryCreated": "एजेंडा श्रेणी सफलतापूर्वक बनाई गई",
+ "agendaCategoryUpdated": "एजेंडा श्रेणी सफलतापूर्वक अपडेट की गई",
+ "agendaCategoryDeleted": "एजेंडा श्रेणी सफलतापूर्वक हटा दी गई",
+ "deleteAgendaCategory": "एजेंडा श्रेणी हटाएं",
+ "deleteAgendaCategoryMsg": "क्या आप इस एजेंडा श्रेणी को हटाना चाहते हैं?"
+ },
+ "agendaItems": {
+ "agendaItemDetails": "एजेंडा आइटम विवरण",
+ "updateAgendaItem": "एजेंडा आइटम अपडेट करें",
+ "title": "शीर्षक",
+ "enterTitle": "शीर्षक दर्ज करें",
+ "sequence": "क्रम",
+ "description": "विवरण",
+ "enterDescription": "विवरण दर्ज करें",
+ "category": "एजेंडा श्रेणी",
+ "attachments": "संलग्नक",
+ "attachmentLimit": "10MB तक कोई भी छवि फ़ाइल या वीडियो फ़ाइल जोड़ें",
+ "fileSizeExceedsLimit": "फ़ाइल का आकार सीमा 10MB से अधिक है",
+ "urls": "URL",
+ "url": "URL में लिंक जोड़ें",
+ "enterUrl": "https://example.com",
+ "invalidUrl": "कृपया एक वैध URL दर्ज करें",
+ "link": "लिंक",
+ "createdBy": "बनाया गया द्वारा",
+ "regular": "नियमित",
+ "note": "नोट",
+ "duration": "अवधि",
+ "enterDuration": "मिमी:से",
+ "options": "विकल्प",
+ "createAgendaItem": "एजेंडा आइटम बनाएं",
+ "noAgendaItems": "कोई एजेंडा आइटम नहीं",
+ "selectAgendaItemCategory": "एजेंडा आइटम श्रेणी चुनें",
+ "update": "अपडेट करें",
+ "delete": "हटाएं",
+ "agendaItemCreated": "एजेंडा आइटम सफलतापूर्वक बनाया गया",
+ "agendaItemUpdated": "एजेंडा आइटम सफलतापूर्वक अपडेट किया गया",
+ "agendaItemDeleted": "एजेंडा आइटम सफलतापूर्वक हटा दिया गया",
+ "deleteAgendaItem": "एजेंडा आइटम हटाएं",
+ "deleteAgendaItemMsg": "क्या आप इस एजेंडा आइटम को हटाना चाहते हैं?"
+ },
+ "eventListCard": {
+ "deleteEvent": "ईवेंट हटाएँ",
+ "deleteEventMsg": "क्या आप इस ईवेंट को हटाना चाहते हैं?",
+ "editEvent": "इवेंट संपादित करें",
+ "eventTitle": "शीर्षक",
+ "alreadyRegistered": "पहले से ही पंजीकृत",
+ "startTime": "समय शुरू",
+ "endTime": "अंत समय",
+ "allDay": "पूरे दिन",
+ "recurringEvent": "पुनरावर्ती ईवेंट",
+ "isPublic": "सार्वजनिक है",
+ "isRegistrable": "पंजीकरण योग्य है",
+ "updatePost": "पोस्ट अपडेट करें",
+ "eventDetails": "घटना की जानकारी",
+ "eventDeleted": "ईवेंट सफलतापूर्वक हटा दिया गया.",
+ "eventUpdated": "इवेंट सफलतापूर्वक अपडेट किया गया.",
+ "thisInstance": "यह उदाहरण",
+ "thisAndFollowingInstances": "यह और निम्नलिखित उदाहरण",
+ "allInstances": "सभी उदाहरण",
+ "customRecurrence": "कस्टम पुनरावृत्ति",
+ "repeatsEvery": "प्रत्येक को दोहराता है",
+ "repeatsOn": "पर दोहराता है",
+ "ends": "समाप्त होता है",
+ "never": "कभी नहीं",
+ "on": "पर",
+ "after": "बाद",
+ "occurences": "घटनाओं",
+ "location": "स्थान",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "description": "विवरण",
+ "startDate": "प्रारंभ तिथि",
+ "endDate": "समाप्ति तिथि",
+ "registerEvent": "कार्यक्रम के लिए पंजीकरण करें",
+ "close": "बंद करें",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध",
+ "done": "समाप्त"
+ },
+ "funds": {
+ "title": "फंड",
+ "createFund": "फंड बनाएँ",
+ "fundName": "फंड का नाम",
+ "fundId": "फंड (संदर्भ) आईडी",
+ "taxDeductible": "कर ड्यूटी कटाई",
+ "default": "डिफ़ॉल्ट",
+ "archived": "आर्काइव",
+ "fundCreate": "फंड बनाएँ",
+ "fundUpdate": "फंड अपडेट करें",
+ "fundDelete": "फंड को हटाएँ",
+ "searchByName": "नाम से खोजें",
+ "noFundsFound": "कोई फंड नहीं मिला",
+ "createdBy": "द्वारा बनाया गया",
+ "createdOn": "पर बनाया गया",
+ "status": "स्थिति",
+ "fundCreated": "फंड सफलतापूर्वक बनाया गया",
+ "fundUpdated": "फंड सफलतापूर्वक अपडेट किया गया",
+ "fundDeleted": "फंड सफलतापूर्वक हटाया गया",
+ "deleteFundMsg": "क्या आप वाकई इस फंड को हटाना चाहते हैं?",
+ "createdLatest": "सबसे पहले बनाया",
+ "createdEarliest": "सबसे जल्दी बनाया",
+ "viewCampaigns": "कैम्पेंस देखें"
+ },
+ "fundCampaign": {
+ "title": "फंडरेजिंग कैंपेन",
+ "campaignName": "कैंपेन का नाम",
+ "campaignOptions": "विकल्प",
+ "fundingGoal": "फंडिंग उद्देश्य",
+ "addCampaign": "कैंपेन जोड़ें",
+ "createdCampaign": "कैंपेन सफलतापूर्वक बनाई गई",
+ "updatedCampaign": "कैंपेन सफलतापूर्वक अपडेट की गई",
+ "deletedCampaign": "कैंपेन सफलतापूर्वक हटा दी गई",
+ "deleteCampaignMsg": "क्या आप वाकई इस कैंपेन को हटाना चाहते हैं?",
+ "noCampaigns": "कोई कैंपेन नहीं मिली",
+ "createCampaign": "कैंपेन बनाएँ",
+ "updateCampaign": "कैंपेन अपडेट करें",
+ "deleteCampaign": "कैंपेन को हटाएं",
+ "currency": "मुद्रा",
+ "selectCurrency": "मुद्रा का चयन करें",
+ "searchFullName": "नाम से खोजें",
+ "viewPledges": "प्लेज देखें",
+ "noCampaignsFound": "कोई कैंपेन नहीं मिली",
+ "latestEndDate": "अंतिम समाप्ति तिथि",
+ "earliestEndDate": "सबसे पहली समाप्ति तिथि",
+ "lowestGoal": "सबसे कम उद्देश्य",
+ "highestGoal": "सबसे ऊंचा उद्देश्य"
+ },
+ "pledges": {
+ "title": "निधि अभियान प्रतिज्ञाएँ",
+ "pledgeAmount": "प्रतिज्ञा राशि",
+ "pledgeOptions": "विकल्प",
+ "pledgeCreated": "प्रतिज्ञा सफलतापूर्वक बनाई गई",
+ "pledgeUpdated": "प्रतिज्ञा सफलतापूर्वक अद्यतन की गई",
+ "pledgeDeleted": "प्रतिज्ञा सफलतापूर्वक हटा दी गई",
+ "addPledge": "प्रतिज्ञा जोड़ें",
+ "createPledge": "प्रतिज्ञा बनाएँ",
+ "currency": "मुद्रा",
+ "selectCurrency": "मुद्रा चुनें",
+ "updatePledge": "प्रतिज्ञा अद्यतन करें",
+ "deletePledge": "प्रतिज्ञा हटाएँ",
+ "amount": "मात्रा",
+ "editPledge": "प्रतिज्ञा संपादित करें",
+ "deletePledgeMsg": "क्या आप वाकई इस प्रतिज्ञा को हटाना चाहते हैं?",
+ "noPledges": "कोई प्रतिज्ञा नहीं मिली",
+ "searchPledger": "प्लेजर्स के द्वारा खोजें",
+ "highestAmount": "सबसे अधिक राशि",
+ "lowestAmount": "सबसे कम राशि",
+ "latestEndDate": "नवीनतम समाप्ति तिथि",
+ "earliestEndDate": "सबसे प्रारंभिक समाप्ति तिथि",
+ "campaigns": "अभियान",
+ "pledges": "प्रतिज्ञाएँ",
+ "endsOn": "पर समाप्त होता है",
+ "raisedAmount": "उठाया गया राशि",
+ "pledgedAmount": "प्रतिबद्ध राशि",
+ "startDate": "प्रारंभ तिथि",
+ "endDate": "समाप्ति तिथि"
+ },
+ "orgPost": {
+ "title": "पदों",
+ "searchPost": "पोस्ट खोजें",
+ "posts": "पदों",
+ "createPost": "पोस्ट बनाएं",
+ "postDetails": "पोस्ट विवरण",
+ "postTitle1": "पोस्ट का शीर्षक लिखें",
+ "postTitle": "शीर्षक",
+ "addMedia": "मीडिया अपलोड करें",
+ "information": "जानकारी",
+ "information1": "पोस्ट की जानकारी लिखें",
+ "addPost": "पोस्ट जोड़ें",
+ "searchTitle": "शीर्षक से खोजें",
+ "searchText": "पाठ द्वारा खोजें",
+ "ptitle": "शीर्षक पोस्ट करें",
+ "postDes": "तुम्हें किस बारे में बात करनी है?",
+ "Title": "शीर्षक",
+ "Text": "मूलपाठ",
+ "searchBy": "खोज से",
+ "Oldest": "सबसे पुराना पहले",
+ "Latest": "नवीनतम प्रथम",
+ "sortPost": "पोस्ट क्रमबद्ध करें",
+ "tag": " आपका ब्राउज़र में वीडियो टैग समर्थित नहीं है",
+ "postCreatedSuccess": "बधाई हो! ",
+ "pinPost": "पिन पद",
+ "Next": "अगला पृष्ठ",
+ "Previous": "पिछला पृष्ठ",
+ "cancel": "रद्द करें"
+ },
+ "postNotFound": {
+ "post": "डाक",
+ "not found!": "नहीं मिला!",
+ "organization": "संगठन",
+ "post not found!": "पोस्ट नहीं मिली!",
+ "organization not found!": "संगठन नहीं मिला!"
+ },
+ "userNotFound": {
+ "not found!": "नहीं मिला!",
+ "roles": "भूमिकाएँ",
+ "user not found!": "उपयोगकर्ता नहीं मिला!",
+ "member not found!": "सदस्य अनुपस्थित!",
+ "admin not found!": "व्यवस्थापक नहीं मिला!",
+ "roles not found!": "भूमिकाएँ नहीं मिलीं!",
+ "user": "उपयोगकर्ता"
+ },
+ "orgPostCard": {
+ "author": "लेखक",
+ "imageURL": "छवि यूआरएल",
+ "videoURL": "वीडियो यूआरएल",
+ "deletePost": "पोस्ट को हटाएं",
+ "deletePostMsg": "क्या आप इस पोस्ट को हटाना चाहते हैं?",
+ "editPost": "संपादित पोस्ट",
+ "postTitle": "शीर्षक",
+ "postTitle1": "पोस्ट का शीर्षक संपादित करें",
+ "information1": "पोस्ट की जानकारी संपादित करें",
+ "information": "जानकारी",
+ "image": "छवि",
+ "video": "वीडियो",
+ "updatePost": "पोस्ट अपडेट करें",
+ "postDeleted": "पोस्ट सफलतापूर्वक हटा दी गई.",
+ "postUpdated": "पोस्ट सफलतापूर्वक अपडेट किया गया.",
+ "tag": " आपका ब्राउज़र में वीडियो टैग समर्थित नहीं है",
+ "pin": "पिन पद",
+ "edit": "संपादित करें",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "close": "बंद करें",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "blockUnblockUser": {
+ "title": "उपयोगकर्ता को ब्लॉक/अनब्लॉक करें",
+ "pageName": "ब्लॉक/अनब्लॉक करें",
+ "listOfUsers": "स्पैम भेजने वाले उपयोगकर्ताओं की सूची",
+ "block_unblock": "ब्लॉक/अनब्लॉक करें",
+ "unblock": "अनवरोधित",
+ "block": "अवरोध पैदा करना",
+ "orgName": "नाम दर्ज करें",
+ "blockedSuccessfully": "उपयोगकर्ता को सफलतापूर्वक अवरोधित किया गया",
+ "Un-BlockedSuccessfully": "उपयोगकर्ता को सफलतापूर्वक अन-अवरुद्ध किया गया",
+ "allMembers": "सभी सदस्य",
+ "blockedUsers": "रोके गए उपयोगकर्ता",
+ "searchByFirstName": "प्रथम नाम से खोजें",
+ "searchByLastName": "अंतिम नाम से खोजें",
+ "noSpammerFound": "कोई स्पैमर नहीं मिला",
+ "searchByName": "नाम से खोजें",
+ "name": "नाम",
+ "email": "ईमेल",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध",
+ "noResultsFoundFor": "के लिए कोई परिणाम नहीं मिला"
+ },
+ "eventManagement": {
+ "title": "इवेंट मैनेजमेंट",
+ "dashboard": "डैशबोर्ड",
+ "registrants": "कुलसचिव",
+ "attendance": "उपस्थिति",
+ "actions": "कार्य",
+ "agendas": "एजेंडे",
+ "statistics": "आँकड़े",
+ "to": "को",
+ "volunteers": "स्वयंसेवक"
+ },
+ "forgotPassword": {
+ "title": "तलावा पासवर्ड भूल गए",
+ "registeredEmail": "पंजीकृत ईमेल",
+ "getOtp": "ओटीपी प्राप्त करें",
+ "enterOtp": "ओटीपी दर्ज करें",
+ "enterNewPassword": "नया पासवर्ड दर्ज करें",
+ "cofirmNewPassword": "नए पासवर्ड की पुष्टि करें",
+ "changePassword": "पासवर्ड बदलें",
+ "backToLogin": "लॉगिन पर वापस जाएं",
+ "userOtp": "जैसे ",
+ "emailNotRegistered": "ईमेल पंजीकृत नहीं है.",
+ "errorSendingMail": "मेल भेजने में त्रुटि.",
+ "passwordMismatches": "पासवर्ड और पासवर्ड बेमेल होने की पुष्टि करें।",
+ "passwordChanges": "पासवर्ड सफलतापूर्वक बदल गया.",
+ "OTPsent": "ओटीपी आपके पंजीकृत ईमेल पर भेजा जाता है।",
+ "forgotPassword": "पासवर्ड भूल गए",
+ "password": "पासवर्ड",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "pageNotFound": {
+ "404": "404",
+ "title": "404 नहीं मिला",
+ "talawaAdmin": "तलावा प्रशासन",
+ "talawaUser": "तलावा उपयोगकर्ता",
+ "notFoundMsg": "उफ़! ",
+ "backToHome": "घर वापिस जा रहा हूँ"
+ },
+ "orgContribution": {
+ "title": "तलावा योगदान",
+ "filterByName": "नाम से फ़िल्टर करें",
+ "filterByTransId": "ट्रांस द्वारा फ़िल्टर करें। ",
+ "recentStats": "हाल के आँकड़े",
+ "contribution": "योगदान",
+ "orgname": "नाम दर्ज करें",
+ "searchtransaction": "लेनदेन आईडी दर्ज करें"
+ },
+ "contriStats": {
+ "recentContribution": "हालिया योगदान",
+ "highestContribution": "सर्वोच्च योगदान",
+ "totalContribution": "कुल योगदान"
+ },
+ "orgContriCards": {
+ "date": "तारीख",
+ "transactionId": "लेन-देन आईडी",
+ "amount": "मात्रा"
+ },
+ "orgSettings": {
+ "title": "सेटिंग्स",
+ "general": "सामान्य",
+ "actionItemCategories": "कार्य आइटम श्रेणियाँ",
+ "updateOrganization": "संगठन अपडेट करें",
+ "seeRequest": "अनुरोध देखें",
+ "noData": "कोई डेटा नहीं",
+ "otherSettings": "अन्य सेटिंग्स",
+ "changeLanguage": "भाषा बदलें",
+ "manageCustomFields": "कस्टम फ़ील्ड प्रबंधित करें",
+ "agendaItemCategories": "एजेंडा आइटम श्रेणियाँ"
+ },
+ "deleteOrg": {
+ "deleteOrganization": "संगठन हटाएँ",
+ "deleteSampleOrganization": "नमूना संगठन हटाएँ",
+ "deleteMsg": "क्या आप इस संगठन को हटाना चाहते हैं?",
+ "confirmDelete": "हटाने की पुष्टि करें",
+ "longDelOrgMsg": "संगठन हटाएं बटन पर क्लिक करने से संगठन अपने ईवेंट, टैग और सभी संबंधित डेटा के साथ स्थायी रूप से हटा दिया जाएगा।",
+ "successfullyDeletedSampleOrganization": "नमूना संगठन सफलतापूर्वक हटा दिया गया",
+ "cancel": "रद्द करें"
+ },
+ "userUpdate": {
+ "appLanguageCode": "डिफ़ॉल्ट भाषा",
+ "userType": "उपयोगकर्ता का प्रकार",
+ "firstName": "प्रथम नाम",
+ "lastName": "अंतिम नाम",
+ "email": "ईमेल",
+ "password": "पासवर्ड",
+ "admin": "प्रशासक",
+ "superAdmin": "सुपर प्रशासक",
+ "displayImage": "प्रदर्शन छवि",
+ "saveChanges": "परिवर्तन सहेजें",
+ "cancel": "रद्द करें"
+ },
+ "userPasswordUpdate": {
+ "previousPassword": "पिछला पासवर्ड",
+ "newPassword": "नया पासवर्ड",
+ "confirmNewPassword": "नए पासवर्ड की पुष्टि करें",
+ "passCantBeEmpty": "पासवर्ड खाली नहीं हो सकता",
+ "passNoMatch": "नया पासवर्ड और पुष्टि पासवर्ड मेल नहीं खाते।",
+ "saveChanges": "परिवर्तन सहेजें",
+ "cancel": "रद्द करें"
+ },
+ "orgDelete": {
+ "deleteOrg": "संगठन हटाएं"
+ },
+ "membershipRequest": {
+ "accept": "स्वीकार करना",
+ "reject": "अस्वीकार करना",
+ "memberAdded": "यह स्वीकृत है",
+ "joined": "शामिल हुए",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "orgUpdate": {
+ "city": "शहर",
+ "countryCode": "कंट्री कोड",
+ "line1": "लाइन 1",
+ "line2": "लाइन 2",
+ "postalCode": "डाक कोड",
+ "dependentLocality": "आश्रित इलाका",
+ "sortingCode": "कोड क्रमबद्ध करना",
+ "state": "राज्य/प्रान्त",
+ "userRegistrationRequired": "उपयोगकर्ता पंजीकरण आवश्यक",
+ "isVisibleInSearch": "खोज में दृश्यमान",
+ "enterNameOrganization": "संगठन का नाम दर्ज करें",
+ "successfulUpdated": "संगठन सफलतापूर्वक अद्यतन किया गया",
+ "name": "नाम",
+ "description": "विवरण",
+ "location": "स्थान",
+ "address": "पता",
+ "displayImage": "प्रदर्शन छवि",
+ "saveChanges": "परिवर्तन सहेजें",
+ "cancel": "रद्द करें",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "addOnRegister": {
+ "addNew": "नया जोड़ो",
+ "addPlugin": "प्लगइन जोड़ें",
+ "pluginName": "प्लगइन नाम",
+ "creatorName": "निर्माता का नाम",
+ "pluginDesc": "प्लगइन विवरण",
+ "pName": "उदाहरणार्थ: दान",
+ "cName": "उदाहरण: जॉन डो",
+ "pDesc": "यह प्लगइन यूआई को सक्षम बनाता है",
+ "close": "बंद करें",
+ "register": "पंजीकरण करें"
+ },
+ "addOnStore": {
+ "title": "स्टोर पर जोड़ें",
+ "searchName": "उदाहरणार्थ: दान",
+ "search": "खोजें",
+ "enable": "सक्रिय",
+ "disable": "अक्षम",
+ "pHeading": "प्लग-इन",
+ "install": "स्थापित",
+ "available": "उपलब्ध",
+ "pMessage": "प्लगइन मौजूद नहीं है",
+ "filter": "फ़िल्टर"
+ },
+ "addOnEntry": {
+ "enable": "सक्रिय",
+ "install": "स्थापित करना",
+ "uninstall": "स्थापना रद्द करें",
+ "uninstallMsg": "यह सुविधा अब आपके संगठन से हटा दी गई है",
+ "installMsg": "यह सुविधा अब आपके संगठन में सक्षम है"
+ },
+ "memberDetail": {
+ "title": "उपयोगकर्ता विवरण",
+ "addAdmin": "व्यवस्थापक जोड़ें",
+ "noeventsAttended": "कोई कार्यक्रम में भाग नहीं लिया",
+ "alreadyIsAdmin": "सदस्य पहले से ही एक व्यवस्थापक है",
+ "organizations": "संगठनों",
+ "events": "आयोजन",
+ "role": "भूमिका",
+ "createdOn": "पर बनाया",
+ "main": "मुख्य",
+ "firstName": "पहला नाम",
+ "lastName": "उपनाम",
+ "language": "भाषा",
+ "gender": "लिंग",
+ "birthDate": "जन्म तिथि",
+ "educationGrade": "शैक्षिक ग्रेड",
+ "employmentStatus": "रोज़गार की स्थिति",
+ "maritalStatus": "वैवाहिक स्थिति",
+ "phone": "फ़ोन",
+ "countryCode": "कंट्री कोड",
+ "state": "राज्य",
+ "city": "शहर",
+ "personalInfoHeading": "व्यक्तिगत जानकारी",
+ "eventsAttended": "ईवेंट्स जिन्हें भाग लिया गया है",
+ "viewAll": "सभी को देखें",
+ "contactInfoHeading": "संपर्क जानकारी",
+ "actionsHeading": "कार्रवाई",
+ "personalDetailsHeading": "प्रोफ़ाइल विवरण",
+ "appLanguageCode": "भाषा चुनें",
+ "deleteUser": "उपभोक्ता मिटायें",
+ "pluginCreationAllowed": "प्लगइन निर्माण की अनुमति दी गई",
+ "created": "बनाया था",
+ "adminForOrganizations": "संगठनों के लिए व्यवस्थापक",
+ "membershipRequests": "सदस्यता अनुरोध",
+ "adminForEvents": "घटनाओं के लिए व्यवस्थापक",
+ "addedAsAdmin": "उपयोगकर्ता को व्यवस्थापक के रूप में जोड़ा गया है.",
+ "userType": "उपयोगकर्ता का प्रकार",
+ "email": "ईमेल",
+ "displayImage": "प्रदर्शन छवि",
+ "address": "पता",
+ "delete": "हटाएं",
+ "saveChanges": "परिवर्तन सहेजें",
+ "joined": "शामिल हुए",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध",
+ "unassignUserTag": "टैग को हटाएं",
+ "unassignUserTagMessage": "क्या आप इस उपयोगकर्ता से टैग हटाना चाहते हैं?",
+ "successfullyUnassigned": "उपयोगकर्ता से टैग हटा दिया गया",
+ "tagsAssigned": "टैग सौंपे गए",
+ "noTagsAssigned": "कोई टैग सौंपा नहीं गया"
+ },
+ "people": {
+ "title": "लोग",
+ "searchUsers": "उपयोगकर्ताओं को खोजें"
+ },
+ "userLogin": {
+ "login": "लॉग इन करें",
+ "loginIntoYourAccount": "अपने खाते में लॉगिन करें",
+ "invalidDetailsMessage": "कृपया एक वैध ईमेल और पासवर्ड दर्ज करें।",
+ "notAuthorised": "क्षमा मांगना! ",
+ "invalidCredentials": "दर्ज किए गए क्रेडेंशियल ग़लत हैं. ",
+ "forgotPassword": "पासवर्ड भूल गए",
+ "emailAddress": "ईमेल पता",
+ "enterEmail": "ईमेल दर्ज करें",
+ "password": "पासवर्ड",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "register": "पंजीकरण करें",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "userRegister": {
+ "enterFirstName": "अपना पहला नाम दर्ज करें",
+ "enterLastName": "अपना अंतिम नाम दर्ज करें",
+ "enterConfirmPassword": "पुष्टि करने के लिए अपना पासवर्ड दर्ज करें",
+ "alreadyhaveAnAccount": "क्या आपके पास पहले से एक खाता मौजूद है?",
+ "login": "लॉग इन करें",
+ "afterRegister": "पंजीकरण सफलतापूर्वक हो गया है। ",
+ "passwordNotMatch": "पासवर्ड मेल नहीं खाता. ",
+ "invalidDetailsMessage": "कृपया वैध विवरण दर्ज करें.",
+ "register": "पंजीकरण करें",
+ "firstName": "प्रथम नाम",
+ "lastName": "अंतिम नाम",
+ "emailAddress": "ईमेल पता",
+ "enterEmail": "ईमेल दर्ज करें",
+ "password": "पासवर्ड",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "confirmPassword": "पासवर्ड की पुष्टि करें",
+ "talawaApiUnavailable": "Talawa API अनुपलब्ध"
+ },
+ "userNavbar": {
+ "talawa": "तलावा",
+ "home": "घर",
+ "people": "लोग",
+ "events": "आयोजन",
+ "chat": "बात करना",
+ "donate": "दान करें",
+ "language": "भाषा",
+ "settings": "समायोजन",
+ "logout": "लॉग आउट करें",
+ "close": "बंद करें"
+ },
+ "userOrganizations": {
+ "allOrganizations": "सभी संगठन",
+ "joinedOrganizations": "संगठनों से जुड़े",
+ "createdOrganizations": "संगठन बनाये",
+ "selectOrganization": "एक संगठन चुनें",
+ "searchUsers": "उपयोगकर्ता खोजें",
+ "nothingToShow": "यहां दिखाने के लिए कुछ भी नहीं है.",
+ "organizations": "संगठनों",
+ "search": "खोजें",
+ "filter": "फ़िल्टर",
+ "searchByName": "नाम से खोजें",
+ "searchOrganizations": "संगठनों खोजें"
+ },
+ "userSidebarOrg": {
+ "yourOrganizations": "आपके संगठन",
+ "noOrganizations": "आप अभी तक किसी संगठन में शामिल नहीं हुए हैं.",
+ "viewAll": "सभी को देखें",
+ "talawaUserPortal": "तलावा उपयोगकर्ता पोर्टल",
+ "my organizations": "मेरे संगठन",
+ "communityProfile": "सामुदायिक प्रोफ़ाइल",
+ "users": "उपयोगकर्ता",
+ "requests": "अनुरोध",
+ "logout": "लॉग आउट",
+ "settings": "सेटिंग्स",
+ "chat": "चैट",
+ "menu": "मेनू"
+ },
+ "organizationSidebar": {
+ "viewAll": "सभी को देखें",
+ "events": "आयोजन",
+ "noEvents": "दिखाने के लिए कोई ईवेंट नहीं",
+ "noMembers": "दिखाने के लिए कोई सदस्य नहीं",
+ "members": "सदस्य"
+ },
+ "postCard": {
+ "likes": "पसंद है",
+ "comments": "टिप्पणियाँ",
+ "viewPost": "पोस्ट देखें",
+ "editPost": "पोस्ट संपादित करें",
+ "postedOn": "{{date}} को पोस्ट किया गया"
+ },
+ "home": {
+ "title": "पदों",
+ "posts": "पदों",
+ "post": "डाक",
+ "textArea": "आपके मन में कुछ है?",
+ "feed": "खिलाना",
+ "loading": "लोड हो रहा है",
+ "pinnedPosts": "चिपके पत्र",
+ "yourFeed": "आपका फ़ीड",
+ "nothingToShowHere": "यहां दिखाने के लिए कुछ भी नहीं है",
+ "somethingOnYourMind": "आपके मन में कुछ है?",
+ "addPost": "पोस्ट जोड़ें",
+ "startPost": "एक पोस्ट प्रारंभ करें",
+ "media": "मिडिया",
+ "event": "आयोजन",
+ "article": "लेख",
+ "postNowVisibleInFeed": "पोस्ट अब फीड में दिखाई दे रहा है"
+ },
+ "eventAttendance": {
+ "historical_statistics": "ऐतिहासिक आंकड़े",
+ "Search member": "सदस्य खोजें",
+ "Member Name": "सदस्य का नाम",
+ "Status": "स्थिति",
+ "Events Attended": "भाग लिए गए कार्यक्रम",
+ "Task Assigned": "सौंपा गया कार्य",
+ "Member": "सदस्य",
+ "Admin": "व्यवस्थापक",
+ "loading": "लोड हो रहा है",
+ "noAttendees": "कोई प्रतिभागी नहीं मिला"
+ },
+ "onSpotAttendee": {
+ "title": "ऑन-स्पॉट प्रतिभागी",
+ "enterFirstName": "प्रथम नाम दर्ज करें",
+ "enterLastName": "अंतिम नाम दर्ज करें",
+ "enterEmail": "ईमेल दर्ज करें",
+ "enterPhoneNo": "फ़ोन नंबर दर्ज करें",
+ "selectGender": "लिंग चुनें",
+ "invalidDetailsMessage": "कृपया सभी आवश्यक फ़ील्ड भरें",
+ "orgIdMissing": "संगठन आईडी गायब है। कृपया पुनः प्रयास करें।",
+ "attendeeAddedSuccess": "प्रतिभागी सफलतापूर्वक जोड़ा गया!",
+ "addAttendee": "जोड़ें",
+ "phoneNumber": "फ़ोन नंबर",
+ "addingAttendee": "जोड़ा जा रहा है...",
+ "male": "पुरुष",
+ "female": "महिला",
+ "other": "अन्य"
+ },
+ "settings": {
+ "noeventsAttended": "कोई कार्यक्रम में उपस्थित नहीं",
+ "eventAttended": "भाग लिए गए कार्यक्रम",
+ "profileSettings": "पार्श्वचित्र समायोजन",
+ "gender": "लिंग",
+ "phoneNumber": "फ़ोन नंबर",
+ "chooseFile": "फाइलें चुनें",
+ "birthDate": "जन्म तिथि",
+ "grade": "शैक्षिक ग्रेड",
+ "empStatus": "रोज़गार की स्थिति",
+ "maritalStatus": "वैवाहिक स्थिति",
+ "state": "शहरी स्थान",
+ "country": "देश",
+ "resetChanges": "परिवर्तन रीसेट करें",
+ "profileDetails": "प्रोफ़ाइल विवरण",
+ "deleteUserMessage": "डिलीट यूजर बटन पर क्लिक करने से आपका यूजर अपने इवेंट, टैग और सभी संबंधित डेटा के साथ स्थायी रूप से हटा दिया जाएगा।",
+ "copyLink": "प्रोफ़ाइल लिंक कॉपी करें",
+ "deleteUser": "उपभोक्ता मिटायें",
+ "otherSettings": "अन्य सेटिंग",
+ "changeLanguage": "भाषा बदलें",
+ "sgender": "लिंग चुनें",
+ "gradePlaceholder": "ग्रेड दर्ज करें",
+ "sEmpStatus": "रोजगार की स्थिति चुनें",
+ "female": "महिला",
+ "male": "पुरुष",
+ "employed": "कार्यरत",
+ "other": "अन्य",
+ "sMaritalStatus": "वैवाहिक स्थिति चुनें",
+ "unemployed": "बेरोज़गार",
+ "married": "विवाहित",
+ "single": "अकेला",
+ "widowed": "विधवा",
+ "divorced": "तलाकशुदा",
+ "engaged": "काम में लगा हुआ",
+ "separated": "विभाजित",
+ "grade1": "ग्रेड 1",
+ "grade2": "ग्रेड 2",
+ "grade3": "ग्रेड 3",
+ "grade4": "ग्रेड 4",
+ "grade5": "ग्रेड 5",
+ "grade6": "वर्ग 6",
+ "grade7": "श्रेणी 7",
+ "grade8": "कक्षा 8",
+ "grade9": "श्रेणी 9",
+ "grade10": "ग्रेड 10",
+ "grade11": "ग्रेड 11",
+ "grade12": "कक्षा 12",
+ "graduate": "स्नातक",
+ "kg": "किलोग्राम",
+ "preKg": "पूर्व केजी",
+ "noGrade": "कोई ग्रेड नहीं",
+ "fullTime": "पूरा समय",
+ "partTime": "पार्ट टाईम",
+ "selectCountry": "कोई देश चुनें",
+ "enterState": "शहर या राज्य दर्ज करें",
+ "settings": "समायोजन",
+ "firstName": "प्रथम नाम",
+ "lastName": "अंतिम नाम",
+ "emailAddress": "ईमेल पता",
+ "displayImage": "प्रदर्शन छवि",
+ "address": "पता",
+ "saveChanges": "परिवर्तन सहेजें",
+ "joined": "शामिल हुए"
+ },
+ "donate": {
+ "title": "दान",
+ "donations": "दान",
+ "searchDonations": "दान खोजें",
+ "donateForThe": "के लिए दान करें",
+ "amount": "मात्रा",
+ "yourPreviousDonations": "आपका पिछला दान",
+ "donate": "दान करें",
+ "nothingToShow": "यहां दिखाने के लिए कुछ भी नहीं है.",
+ "success": "दान सफल",
+ "invalidAmount": "कृपया दान राशि के लिए संख्यात्मक मान दर्ज करें.",
+ "donationAmountDescription": "कृपया दान राशि के लिए संख्यात्मक मान दर्ज करें.",
+ "donationOutOfRange": "दान राशि {{min}} और {{max}} के बीच होनी चाहिए.",
+ "donateTo": "दान करें"
+ },
+ "userEvents": {
+ "title": "ईवेंट",
+ "nothingToShow": "यहां दिखाने के लिए कुछ भी नहीं है.",
+ "createEvent": "कार्यक्रम बनाएँ",
+ "recurring": "पुनरावर्ती ईवेंट",
+ "startTime": "समय शुरू",
+ "endTime": "अंत समय",
+ "listView": "लिस्ट व्यू",
+ "calendarView": "कैलेंडर दृश्य",
+ "allDay": "पूरे दिन",
+ "eventCreated": "ईवेंट सफलतापूर्वक बनाया और पोस्ट किया गया.",
+ "eventDetails": "घटना की जानकारी",
+ "eventTitle": "शीर्षक",
+ "enterTitle": "शीर्षक दर्ज करें",
+ "enterDescription": "विवरण दर्ज करें",
+ "enterLocation": "स्थान दर्ज करें",
+ "publicEvent": "सार्वजनिक है",
+ "registerable": "पंजीकरण योग्य है",
+ "monthlyCalendarView": "मासिक कैलेंडर",
+ "yearlyCalendarView": "वार्षिक कैलेंडर",
+ "search": "खोजें",
+ "cancel": "रद्द करें",
+ "create": "बनाएं",
+ "eventDescription": "कार्यक्रम विवरण",
+ "eventLocation": "कार्यक्रम स्थान",
+ "startDate": "प्रारंभ तिथि",
+ "endDate": "समाप्ति तिथि"
+ },
+ "userEventCard": {
+ "starts": "प्रारंभ होगा",
+ "ends": "समाप्त होता है",
+ "creator": "निर्माता",
+ "alreadyRegistered": "पहले से ही पंजीकृत",
+ "location": "स्थान",
+ "register": "पंजीकरण करें"
+ },
+ "advertisement": {
+ "title": "विज्ञापनों",
+ "activeAds": "सक्रिय अभियान",
+ "archievedAds": "पूर्ण अभियान",
+ "pMessage": "इस अभियान के लिए विज्ञापन मौजूद नहीं हैं.",
+ "validLink": "लिंक मान्य है",
+ "invalidLink": "लिंक अमान्य है",
+ "Rname": "विज्ञापन का नाम दर्ज करें",
+ "Rtype": "विज्ञापन का प्रकार चुनें",
+ "Rmedia": "प्रदर्शित करने के लिए मीडिया सामग्री प्रदान करें",
+ "RstartDate": "आरंभ तिथि चुनें",
+ "RendDate": "अंतिम तिथि चुनें",
+ "RClose": "खिड़की बंद करो",
+ "addNew": "नया विज्ञापन बनाएं",
+ "EXname": "पूर्व। ",
+ "EXlink": "पूर्व। ",
+ "createAdvertisement": "विज्ञापन बनाएं",
+ "deleteAdvertisement": "विज्ञापन हटाएँ",
+ "deleteAdvertisementMsg": "क्या आप यह विज्ञापन हटाना चाहते हैं?",
+ "view": "देखना",
+ "editAdvertisement": "विज्ञापन संपादित करें",
+ "advertisementDeleted": "विज्ञापन सफलतापूर्वक हटाया गया।",
+ "endDateGreaterOrEqual": "समाप्ति तिथि प्रारंभ तिथि से अधिक या उसके बराबर होनी चाहिए",
+ "advertisementCreated": "विज्ञापन सफलतापूर्वक बनाया गया।",
+ "pHeading": "विज्ञापन शीर्षक",
+ "delete": "हटाएं",
+ "close": "बंद करें",
+ "no": "नहीं",
+ "yes": "हाँ",
+ "edit": "संपादित करें",
+ "saveChanges": "परिवर्तन सहेजें",
+ "endOfResults": "परिणाम समाप्त"
+ },
+ "userChat": {
+ "chat": "बात करना",
+ "contacts": "संपर्क",
+ "search": "खोज",
+ "messages": "संदेश"
+ },
+ "userChatRoom": {
+ "selectContact": "बातचीत शुरू करने के लिए एक संपर्क चुनें",
+ "sendMessage": "मेसेज भेजें"
+ },
+ "orgProfileField": {
+ "loading": "लोड हो रहा है...",
+ "noCustomField": "कोई कस्टम फ़ील्ड उपलब्ध नहीं है",
+ "customFieldName": "कार्यक्षेत्र नाम",
+ "enterCustomFieldName": "फ़ील्ड नाम दर्ज करें",
+ "customFieldType": "क्षेत्र प्रकार",
+ "Remove Custom Field": "कस्टम फ़ील्ड हटाएँ",
+ "fieldSuccessMessage": "फ़ील्ड सफलतापूर्वक जोड़ा गया",
+ "fieldRemovalSuccess": "फ़ील्ड सफलतापूर्वक हटा दी गई",
+ "String": "स्ट्रिंग",
+ "Boolean": "बूलियन",
+ "Date": "तारीख",
+ "Number": "संख्या",
+ "saveChanges": "परिवर्तन सहेजें"
+ },
+ "orgActionItemCategories": {
+ "enableButton": "सक्षम",
+ "disableButton": "अक्षम करना",
+ "updateActionItemCategory": "अद्यतन",
+ "actionItemCategoryName": "नाम",
+ "categoryDetails": "श्रेणी विवरण",
+ "enterName": "नाम दर्ज करें",
+ "successfulCreation": "कार्रवाई आइटम श्रेणी सफलतापूर्वक बनाई गई",
+ "successfulUpdation": "कार्रवाई आइटम श्रेणी सफलतापूर्वक अपडेट की गई",
+ "sameNameConflict": "अपडेट करने के लिए कृपया नाम बदलें",
+ "categoryEnabled": "कार्य आइटम श्रेणी सक्षम",
+ "categoryDisabled": "कार्रवाई आइटम श्रेणी अक्षम",
+ "noActionItemCategories": "कोई कार्रवाई आइटम श्रेणी नहीं",
+ "status": "स्थिति",
+ "categoryDeleted": "कार्रवाई आइटम श्रेणी सफलतापूर्वक हटा दी गई",
+ "deleteCategory": "श्रेणी हटाएं",
+ "deleteCategoryMsg": "क्या आप वाकई इस कार्रवाई आइटम श्रेणी को हटाना चाहते हैं?",
+ "createButton": "बटन बनाएं",
+ "editButton": "संपादित बटन"
+ },
+ "organizationVenues": {
+ "title": "स्थानों",
+ "addVenue": "स्थान जोड़ें",
+ "venueDetails": "स्थल विवरण",
+ "venueName": "आयोजन स्थल का नाम",
+ "enterVenueName": "स्थान का नाम दर्ज करें",
+ "enterVenueDesc": "स्थान विवरण दर्ज करें",
+ "capacity": "क्षमता",
+ "enterVenueCapacity": "स्थान क्षमता दर्ज करें",
+ "image": "स्थल छवि",
+ "uploadVenueImage": "स्थल छवि अपलोड करें",
+ "createVenue": "स्थान बनाएँ",
+ "venueAdded": "स्थान सफलतापूर्वक जोड़ा गया",
+ "editVenue": "स्थान अद्यतन करें",
+ "venueUpdated": "स्थान विवरण सफलतापूर्वक अपडेट किया गया",
+ "sort": "क्रम से लगाना",
+ "highestCapacity": "उच्चतम क्षमता",
+ "lowestCapacity": "सबसे कम क्षमता",
+ "noVenues": "कोई स्थान नहीं मिला!",
+ "view": "देखना",
+ "venueTitleError": "स्थान का शीर्षक खाली नहीं हो सकता!",
+ "venueCapacityError": "क्षमता एक धनात्मक संख्या होनी चाहिए!",
+ "searchBy": "खोज से",
+ "description": "विवरण",
+ "edit": "संपादित करें",
+ "delete": "हटाएं",
+ "name": "नाम",
+ "desc": "विवरण"
+ },
+ "addMember": {
+ "title": "सदस्य जोड़ें",
+ "addMembers": "सदस्य जोड़ें",
+ "existingUser": "मौजूदा उपयोगकर्ता",
+ "newUser": "नए उपयोगकर्ता",
+ "searchFullName": "पूरे नाम से खोजें",
+ "enterFirstName": "प्रथम नाम दर्ज करें",
+ "enterLastName": "अंतिम नाम दर्ज करो",
+ "enterConfirmPassword": "पासवर्ड की पुष्टि करें दर्ज करें",
+ "organization": "संगठन",
+ "invalidDetailsMessage": "कृपया सभी आवश्यक विवरण प्रदान करें।",
+ "passwordNotMatch": "सांकेतिक शब्द मेल नहीं खाते।",
+ "addMember": "सदस्य जोड़ें",
+ "firstName": "प्रथम नाम",
+ "lastName": "अंतिम नाम",
+ "emailAddress": "ईमेल पता",
+ "enterEmail": "ईमेल दर्ज करें",
+ "password": "पासवर्ड",
+ "enterPassword": "पासवर्ड दर्ज करें",
+ "confirmPassword": "पासवर्ड की पुष्टि करें",
+ "cancel": "रद्द करें",
+ "create": "बनाएं",
+ "user": "उपयोगकर्ता",
+ "profile": "प्रोफ़ाइल"
+ },
+ "eventActionItems": {
+ "title": "एक्शन आइटम्स",
+ "createActionItem": "एक्शन आइटम बनाएं",
+ "actionItemCategory": "कार्य आइटम श्रेणी",
+ "selectActionItemCategory": "एक क्रिया आइटम श्रेणी का चयन करें",
+ "selectAssignee": "एक समनुदेशिती का चयन करें",
+ "preCompletionNotes": "टिप्पणियाँ",
+ "postCompletionNotes": "समापन नोट्स",
+ "actionItemDetails": "कार्रवाई मद विवरण",
+ "dueDate": "नियत तारीख",
+ "completionDate": "पूरा करने की तिथि",
+ "editActionItem": "क्रिया आइटम संपादित करें",
+ "deleteActionItem": "क्रिया आइटम हटाएँ",
+ "deleteActionItemMsg": "क्या आप इस क्रिया आइटम को हटाना चाहते हैं?",
+ "successfulDeletion": "कार्रवाई आइटम सफलतापूर्वक हटा दिया गया",
+ "successfulCreation": "कार्रवाई आइटम सफलतापूर्वक बनाया गया",
+ "successfulUpdation": "कार्रवाई आइटम सफलतापूर्वक अपडेट किया गया",
+ "notes": "टिप्पणियाँ",
+ "assignee": "संपत्ति-भागी",
+ "assigner": "असाइनर",
+ "assignmentDate": "असाइनमेंट तिथि",
+ "status": "स्थिति",
+ "actionItemActive": "सक्रिय",
+ "actionItemStatus": "कार्रवाई आइटम स्थिति",
+ "actionItemCompleted": "कार्रवाई आइटम पूर्ण हुआ",
+ "markCompletion": "पूर्णता को चिह्नित करें",
+ "save": "बचाना",
+ "yes": "हाँ",
+ "no": "नहीं"
+ },
+ "checkIn": {
+ "errorCheckingIn": "चेक-इन में त्रुटि",
+ "checkedInSuccessfully": "सफलतापूर्वक चेक-इन किया गया"
+ },
+ "eventRegistrantsModal": {
+ "errorAddingAttendee": "उपस्थित होने वाले को जोड़ने में त्रुटि",
+ "errorRemovingAttendee": "उपस्थित होने वाले को हटाने में त्रुटि"
+ },
+ "userCampaigns": {
+ "title": "धन उगाहने के अभियान",
+ "searchByName": "नाम से खोजें...",
+ "searchBy": "द्वारा खोजें",
+ "pledgers": "प्रतिज्ञाकर्ता",
+ "campaigns": "अभियान",
+ "myPledges": "मेरी प्रतिज्ञाएँ",
+ "lowestAmount": "सबसे कम राशि",
+ "highestAmount": "सबसे अधिक राशि",
+ "lowestGoal": "सबसे कम लक्ष्य",
+ "highestGoal": "सबसे बड़ा लक्ष्य",
+ "latestEndDate": "सबसे अंतिम समाप्ति तिथि",
+ "earliestEndDate": "सबसे पहले समाप्ति तिथि",
+ "addPledge": "प्रतिज्ञा जोड़ें",
+ "viewPledges": "प्रतिज्ञाएँ देखें",
+ "noPledges": "कोई प्रतिज्ञा नहीं मिली",
+ "noCampaigns": "कोई अभियान नहीं मिला"
+ },
+ "userPledges": {
+ "title": "मेरी प्रतिज्ञाएँ"
+ },
+ "eventVolunteers": {
+ "volunteers": "स्वयंसेवक",
+ "volunteer": "स्वयंसेवक",
+ "volunteerGroups": "स्वयंसेवक समूह",
+ "individuals": "व्यक्ति",
+ "groups": "समूह",
+ "status": "स्थिति",
+ "noVolunteers": "कोई स्वयंसेवक नहीं",
+ "noVolunteerGroups": "कोई स्वयंसेवक समूह नहीं",
+ "add": "जोड़ें",
+ "mostHoursVolunteered": "सबसे अधिक घंटे स्वयंसेवा",
+ "leastHoursVolunteered": "सबसे कम घंटे स्वयंसेवा",
+ "accepted": "स्वीकृत",
+ "addVolunteer": "स्वयंसेवक जोड़ें",
+ "removeVolunteer": "स्वयंसेवक हटाएं",
+ "volunteerAdded": "स्वयंसेवक सफलतापूर्वक जोड़ा गया",
+ "volunteerRemoved": "स्वयंसेवक सफलतापूर्वक हटाया गया",
+ "volunteerGroupCreated": "स्वयंसेवक समूह सफलतापूर्वक बनाया गया",
+ "volunteerGroupUpdated": "स्वयंसेवक समूह सफलतापूर्वक अपडेट किया गया",
+ "volunteerGroupDeleted": "स्वयंसेवक समूह सफलतापूर्वक हटाया गया",
+ "removeVolunteerMsg": "क्या आप वाकई इस स्वयंसेवक को हटाना चाहते हैं?",
+ "deleteVolunteerGroupMsg": "क्या आप वाकई इस स्वयंसेवक समूह को हटाना चाहते हैं?",
+ "leader": "नेता",
+ "group": "समूह",
+ "createGroup": "समूह बनाएं",
+ "updateGroup": "समूह अपडेट करें",
+ "deleteGroup": "समूह हटाएं",
+ "volunteersRequired": "आवश्यक स्वयंसेवक",
+ "volunteerDetails": "स्वयंसेवक विवरण",
+ "hoursVolunteered": "स्वयंसेवा घंटे",
+ "groupDetails": "समूह विवरण",
+ "creator": "निर्माता",
+ "requests": "अनुरोध",
+ "noRequests": "कोई अनुरोध नहीं",
+ "latest": "नवीनतम",
+ "earliest": "प्रारंभिक",
+ "requestAccepted": "अनुरोध सफलतापूर्वक स्वीकृत",
+ "requestRejected": "अनुरोध सफलतापूर्वक अस्वीकृत",
+ "details": "विवरण",
+ "manageGroup": "समूह प्रबंधित करें",
+ "mostVolunteers": "सबसे अधिक स्वयंसेवक",
+ "leastVolunteers": "सबसे कम स्वयंसेवक"
+ },
+ "userVolunteer": {
+ "title": "स्वयंसेवकता",
+ "name": "शीर्षक",
+ "upcomingEvents": "आगामी कार्यक्रम",
+ "requests": "अनुरोध",
+ "invitations": "निमंत्रण",
+ "groups": "स्वयंसेवक समूह",
+ "actions": "क्रियाएँ",
+ "searchByName": "नाम से खोजें",
+ "latestEndDate": "नवीनतम समाप्ति तिथि",
+ "earliestEndDate": "प्रारंभिक समाप्ति तिथि",
+ "noEvents": "कोई आगामी कार्यक्रम नहीं",
+ "volunteer": "स्वयंसेवक",
+ "volunteered": "स्वयंसेवित",
+ "join": "शामिल हों",
+ "joined": "शामिल हुआ",
+ "searchByEventName": "कार्यक्रम शीर्षक से खोजें",
+ "filter": "फ़िल्टर",
+ "groupInvite": "समूह निमंत्रण",
+ "individualInvite": "व्यक्तिगत निमंत्रण",
+ "noInvitations": "कोई निमंत्रण नहीं",
+ "accept": "स्वीकारें",
+ "reject": "अस्वीकार करें",
+ "receivedLatest": "हाल में प्राप्त",
+ "receivedEarliest": "सबसे पहले प्राप्त",
+ "invitationAccepted": "निमंत्रण सफलतापूर्वक स्वीकार किया गया",
+ "invitationRejected": "निमंत्रण सफलतापूर्वक अस्वीकृत",
+ "volunteerSuccess": "स्वयंसेवक के रूप में अनुरोध सफलतापूर्वक किया गया",
+ "recurring": "पुनरावृत्ति",
+ "groupInvitationSubject": "स्वयंसेवक समूह में शामिल होने के लिए निमंत्रण",
+ "eventInvitationSubject": "कार्यक्रम के लिए स्वयंसेवक बनने का निमंत्रण"
+ }
+}
diff --git a/public/locales/sp/common.json b/public/locales/sp/common.json
new file mode 100644
index 0000000000..2c06daac5e
--- /dev/null
+++ b/public/locales/sp/common.json
@@ -0,0 +1,98 @@
+{
+ "firstName": "First Name",
+ "lastName": "Last Name",
+ "searchByName": "Search By Name",
+ "loading": "Loading...",
+ "endOfResults": "End of results",
+ "noResultsFoundFor": "No results found for ",
+ "edit": "Edit",
+ "admins": "Admins",
+ "admin": "ADMIN",
+ "user": "USER",
+ "superAdmin": "SUPERADMIN",
+ "members": "Members",
+ "logout": "Logout",
+ "login": "Login",
+ "register": "Register",
+ "menu": "Menu",
+ "settings": "Settings",
+ "gender": "Género",
+ "users": "Users",
+ "requests": "Requests",
+ "OR": "OR",
+ "cancel": "Cancel",
+ "close": "Close",
+ "create": "Create",
+ "delete": "Delete",
+ "done": "Done",
+ "yes": "Yes",
+ "no": "No",
+ "filter": "Filter",
+ "search": "Search",
+ "description": "Description",
+ "saveChanges": "Save Changes",
+ "resetChanges": "Reset Changes",
+ "displayImage": "Display Image",
+ "enterEmail": "Enter Email",
+ "emailAddress": "Email Address",
+ "email": "Email",
+ "name": "Name",
+ "desc": "Description",
+ "enterPassword": "Enter Password",
+ "password": "Password",
+ "confirmPassword": "Confirm Password",
+ "forgotPassword": "Forgot Password ?",
+ "talawaAdminPortal": "Talawa Admin Portal",
+ "address": "Address",
+ "location": "Location",
+ "enterLocation": "Enter Location",
+ "joined": "Joined",
+ "startDate": "Start Date",
+ "endDate": "End Date",
+ "startTime": "Start Time",
+ "endTime": "End Time",
+ "My Organizations": "Mis Organizaciones",
+ "Dashboard": "Tablero",
+ "People": "Gente",
+ "Events": "Eventos",
+ "Venues": "Lugares",
+ "Action Items": "Elementos de Acción",
+ "Posts": "Publicaciones",
+ "Block/Unblock": "Bloquear/Desbloquear",
+ "Advertisement": "Publicidad",
+ "Funds": "Fondos",
+ "Membership Requests": "Solicitudes de Membresía",
+ "Plugins": "Complementos",
+ "Plugin Store": "Tienda de Complementos",
+ "Settings": "Configuraciones",
+ "createdOn": "Creado En",
+ "createdBy": "Creado Por",
+ "usersRole": "Rol del Usuario",
+ "changeRole": "Cambiar Rol",
+ "action": "Acción",
+ "removeUser": "Eliminar Usuario",
+ "remove": "Eliminar",
+ "viewProfile": "Ver Perfil",
+ "profile": "Perfil",
+ "noFiltersApplied": "No se aplicaron filtros",
+ "manage": "Administrar",
+ "searchResultsFor": "Resultados de búsqueda para {{text}}",
+ "none": "Ninguno",
+ "sort": "Ordenar",
+ "Donate": "Donar",
+ "addedSuccessfully": "{{item}} agregado con éxito",
+ "updatedSuccessfully": "{{item}} actualizado con éxito",
+ "removedSuccessfully": "{{item}} eliminado con éxito",
+ "successfullyUpdated": "Actualizado con éxito",
+ "sessionWarning": "Su sesión expirará pronto debido a la inactividad. Por favor, interactúe con la página para extender su sesión.",
+ "sessionLogOut": "Su sesión ha expirado debido a la inactividad. Por favor, inicie sesión nuevamente para continuar.",
+ "all": "Todos",
+ "active": "Activo",
+ "disabled": "Deshabilitado",
+ "pending": "Pendiente",
+ "completed": "Completado",
+ "late": "Tarde",
+ "createdLatest": "Creado más reciente",
+ "createdEarliest": "Creado más temprano",
+ "searchBy": "Buscar por {{item}}"
+}
diff --git a/public/locales/sp/errors.json b/public/locales/sp/errors.json
new file mode 100644
index 0000000000..39b579abac
--- /dev/null
+++ b/public/locales/sp/errors.json
@@ -0,0 +1,11 @@
+{
+ "talawaApiUnavailable": "Talawa-API service is unavailable!. Is it running? Check your network connectivity too.",
+ "notFound": "Not found",
+ "unknownError": "An unknown error occurred. Please try again later. {{msg}}",
+ "notAuthorised": "Sorry! you are not Authorised!",
+ "errorSendingMail": "Error sending mail",
+ "emailNotRegistered": "Email not registered",
+ "notFoundMsg": "Oops! The Page you requested was not found!",
+ "errorOccurredCouldntCreate": "Ocurrió un error. No se pudo crear {{entity}}",
+ "errorLoading": "Ocurrió un error al cargar los datos de {{entity}}"
+}
diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json
new file mode 100644
index 0000000000..7aa1d6ffc0
--- /dev/null
+++ b/public/locales/sp/translation.json
@@ -0,0 +1,1485 @@
+{
+ "leaderboard": {
+ "title": "Tabla de Clasificación",
+ "searchByVolunteer": "Buscar por Voluntario",
+ "mostHours": "Más Horas",
+ "leastHours": "Menos Horas",
+ "timeFrame": "Período",
+ "allTime": "Todo el Tiempo",
+ "weekly": "Esta Semana",
+ "monthly": "Este Mes",
+ "yearly": "Este Año",
+ "noVolunteers": "¡No Se Encontraron Voluntarios!"
+ },
+ "loginPage": {
+ "title": "Administrador Talawa",
+ "fromPalisadoes": "Una aplicación de código abierto de los voluntarios de la Fundación palisados",
+ "talawa_portal": "Portal De Administración Talawa",
+ "login": "Acceso",
+ "userLogin": "Inicio de sesión de usuario",
+ "register": "Registro",
+ "firstName": "Primer nombre",
+ "lastName": "Apellido",
+ "email": "Correo electrónico",
+ "password": "Clave",
+ "atleast_8_char_long": "Al menos 8 caracteres de largo",
+ "atleast_6_char_long": "Al menos 6 caracteres de largo",
+ "firstName_invalid": "El nombre debe contener solo letras minúsculas y mayúsculas.",
+ "lastName_invalid": "El apellido debe contener solo letras minúsculas y mayúsculas.",
+ "password_invalid": "La contraseña debe contener al menos una letra minúscula, una letra mayúscula, un valor numérico y un carácter especial.",
+ "email_invalid": "El correo electrónico debe tener al menos 8 caracteres.",
+ "Password_and_Confirm_password_mismatches.": "Contraseña y Confirmar contraseña no coinciden.",
+ "confirmPassword": "Confirmar contraseña",
+ "forgotPassword": "Has olvidado tu contraseña ?",
+ "enterEmail": "ingrese correo electrónico",
+ "enterPassword": "introducir la contraseña",
+ "doNotOwnAnAccount": "¿No tienes una cuenta?",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Verifica también la conectividad de tu red.",
+ "captchaError": "¡Error de captcha!",
+ "Please_check_the_captcha": "Por favor, revisa el captcha.",
+ "Something_went_wrong": "Algo salió mal. Inténtalo después de un tiempo",
+ "passwordMismatches": "Contraseña y Confirmar contraseña no coinciden.",
+ "fillCorrectly": "Complete todos los detalles correctamente.",
+ "notAuthorised": "¡Lo siento! ¡No estás autorizado!",
+ "notFound": "¡Usuario no encontrado!",
+ "successfullyRegistered": "Registrado con éxito. Espere hasta que sea aprobado",
+ "OR": "O",
+ "admin": "ADMINISTRACIÓN",
+ "user": "USUARIO",
+ "lowercase_check": "Al menos una letra mayuscula",
+ "uppercase_check": "Al menos una letra minúscula",
+ "numeric_value_check": "Al menos un valor numérico",
+ "special_char_check": "Al menos un carácter especial",
+ "loading": "Cargando...",
+ "selectOrg": "Seleccione una organización",
+ "afterRegister": "Registro exitoso. Por favor, espere a que el administrador apruebe su solicitud."
+ },
+ "userLoginPage": {
+ "title": "Administrador Talawa",
+ "fromPalisadoes": "Una aplicación de código abierto de los voluntarios de la Fundación palisados",
+ "talawa_portal": "Portal De Administración Talawa",
+ "login": "Acceso",
+ "register": "Registro",
+ "firstName": "Primer nombre",
+ "lastName": "Apellido",
+ "email": "Correo electrónico",
+ "password": "Clave",
+ "atleast_8_char_long": "Al menos 8 caracteres de largo",
+ "Password_and_Confirm_password_mismatches.": "Contraseña y Confirmar contraseña no coinciden.",
+ "confirmPassword": "Confirmar contraseña",
+ "forgotPassword": "Has olvidado tu contraseña ?",
+ "enterEmail": "ingrese correo electrónico",
+ "enterPassword": "introducir la contraseña",
+ "doNotOwnAnAccount": "¿No tienes una cuenta?",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Verifica también la conectividad de tu red.",
+ "captchaError": "¡Error de captcha!",
+ "Please_check_the_captcha": "Por favor, revisa el captcha.",
+ "Something_went_wrong": "Algo salió mal. Inténtalo después de un tiempo",
+ "passwordMismatches": "Contraseña y Confirmar contraseña no coinciden.",
+ "fillCorrectly": "Complete todos los detalles correctamente.",
+ "notAuthorised": "¡Lo siento! ¡No estás autorizado!",
+ "notFound": "¡Usuario no encontrado!",
+ "successfullyRegistered": "Registrado con éxito. Espere hasta que sea aprobado",
+ "userLogin": "Inicio de sesión de usuario",
+ "afterRegister": "Registrado exitosamente. Espere a que el administrador apruebe su solicitud.",
+ "OR": "O",
+ "loading": "Cargando...",
+ "selectOrg": "Seleccione una organización"
+ },
+ "latestEvents": {
+ "eventCardTitle": "Próximos Eventos",
+ "eventCardSeeAll": "Ver Todos",
+ "noEvents": "No Hay Eventos Próximos"
+ },
+ "latestPosts": {
+ "latestPostsTitle": "Últimas Publicaciones",
+ "seeAllLink": "Ver Todo",
+ "noPostsCreated": "No se han creado publicaciones"
+ },
+ "listNavbar": {
+ "talawa_portal": "Portal De Administración Talawa",
+ "roles": "Roles",
+ "requests": "Peticiones",
+ "logout": "Cerrar sesión"
+ },
+ "leftDrawer": {
+ "talawaAdminPortal": "Portal de administración de Talawa",
+ "menu": "Menú",
+ "my organizations": "Mis Organizaciones",
+ "users": "Usuarios",
+ "requests": "Solicitudes",
+ "communityProfile": "Perfil de la comunidad",
+ "logout": "Cerrar sesión"
+ },
+ "leftDrawerOrg": {
+ "talawaAdminPortal": "Portal de administración de Talawa",
+ "menu": "Menú",
+ "talawa_portal": "Portal De Administración Talawa",
+ "Dashboard": "Tablero",
+ "People": "Gente",
+ "Events": "Eventos",
+ "Contributions": "Contribuciones",
+ "Posts": "Publicaciones",
+ "Block/Unblock": "Bloquear/Desbloquear",
+ "Plugins": "Complementos",
+ "Plugin Store": "Tienda de complementos",
+ "allOrganizations": "Todas las organizaciones",
+ "yourOrganization": "Tu organización",
+ "notification": "Notificación",
+ "settings": "Ajustes",
+ "language": "Idioma",
+ "logout": "Cerrar sesión",
+ "notifications": "Notificaciones",
+ "spamsThe": "envía correo no deseado",
+ "group": "grupo",
+ "noNotifications": "No Notificaciones",
+ "close": "Cerca",
+ "Advertisement": "Publicidad"
+ },
+ "orgList": {
+ "title": "Organizaciones Talawa",
+ "you": "Tú",
+ "name": "Nombre",
+ "designation": "Designacion",
+ "email": "Correo electrónico",
+ "searchByName": "Buscar por nombre",
+ "my organizations": "Mis Organizaciones.",
+ "createOrganization": "Crear organización",
+ "createSampleOrganization": "Crear organización de muestra",
+ "description": "Descripción",
+ "location": "Ubicación",
+ "address": "Dirección",
+ "city": "Ciudad",
+ "countryCode": "Código de País",
+ "line1": "Línea 1",
+ "line2": "Línea 2",
+ "postalCode": "Código Postal",
+ "dependentLocality": "Localidad Dependiente",
+ "sortingCode": "Código de Ordenamiento",
+ "state": "Estado / Provincia",
+ "userRegistrationRequired": "Registro de usuario requerido",
+ "visibleInSearch": "Visible en la búsqueda",
+ "displayImage": "Mostrar imagen",
+ "enterName": "Ingrese su nombre",
+ "sort": "Ordenar",
+ "Earliest": "Más Temprano",
+ "Latest": "El último",
+ "filter": "Filtrar",
+ "cancel": "Cancelar",
+ "endOfResults": "Fin de los resultados",
+ "noOrgErrorTitle": "Organizaciones no encontradas",
+ "sampleOrgDuplicate": "Solo se permite una organización de muestra",
+ "noOrgErrorDescription": "Por favor, crea una organización a través del panel de control",
+ "noResultsFoundFor": "No se encontraron resultados para ",
+ "OR": "O",
+ "sampleOrgSuccess": "Organización de ejemplo creada exitosamente",
+ "manageFeatures": "Gestionar funciones",
+ "manageFeaturesInfo": "Información de gestión de funciones",
+ "goToStore": "Ir a la tienda",
+ "enableEverything": "Habilitar todo"
+ },
+ "orgListCard": {
+ "admins": "Administradores",
+ "members": "Miembros",
+ "manage": "Administrar",
+ "sampleOrganization": "Organización de muestra"
+ },
+ "paginationList": {
+ "rowsPerPage": "filas por página",
+ "all": "Todos"
+ },
+ "requests": {
+ "title": "Solicitudes",
+ "sl_no": "Núm.",
+ "name": "Nombre",
+ "email": "Correo electrónico",
+ "accept": "Aceptar",
+ "reject": "Rechazar",
+ "searchRequests": "Buscar solicitudes",
+ "endOfResults": "Fin de los resultados",
+ "noOrgError": "Organizaciones no encontradas, por favor crea una organización a través del panel",
+ "noResultsFoundFor": "No se encontraron resultados para ",
+ "noRequestsFound": "No se encontraron solicitudes",
+ "acceptedSuccessfully": "Solicitud aceptada exitosamente",
+ "rejectedSuccessfully": "Solicitud rechazada exitosamente",
+ "noOrgErrorTitle": "Organizaciones no encontradas",
+ "noOrgErrorDescription": "Por favor, crea una organización a través del panel de control"
+ },
+ "users": {
+ "title": "Roles Talawa",
+ "searchByName": "Buscar por nombre",
+ "users": "Usuarios",
+ "name": "Nombre",
+ "email": "Correo electrónico",
+ "joined_organizations": "Organizaciones unidas",
+ "blocked_organizations": "Organizaciones bloqueadas",
+ "endOfResults": "Fin de los resultados",
+ "orgJoinedBy": "Organizaciones unidas por",
+ "orgThatBlocked": "Organizaciones bloqueadas por",
+ "hasNotJoinedAnyOrg": "No se ha unido a ninguna organización.",
+ "isNotBlockedByAnyOrg": "No está bloqueado por ninguna organización.",
+ "searchByOrgName": "Buscar por nombre de organización",
+ "view": "Ver",
+ "admin": "ADMINISTRACIÓN",
+ "superAdmin": "SUPERADMIN",
+ "user": "USUARIO",
+ "enterName": "Ingrese su nombre",
+ "loadingUsers": "Cargando usuarios ...",
+ "noUserFound": "No se encontró ningún usuario.",
+ "sort": "Ordenar",
+ "Oldest": "Más Antiguas Primero",
+ "Newest": "Más Recientes Primero",
+ "filter": "Filtrar",
+ "roleUpdated": "Rol actualizado.",
+ "noResultsFoundFor": "No se encontraron resultados para ",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red.",
+ "cancel": "Cancelar",
+ "admins": "Administradores",
+ "members": "Miembros",
+ "joinNow": "Únete ahora",
+ "visit": "visita",
+ "withdraw": "retirar",
+ "orgJoined": "Unido a la organización exitosamente",
+ "MembershipRequestSent": "Solicitud de membresía enviada exitosamente",
+ "AlreadyJoined": "Ya eres miembro de esta organización.",
+ "errorOccured": "Se produjo un error. Por favor, inténtalo de nuevo más tarde.",
+ "removeUserFrom": "Eliminar Usuario de {{org}}",
+ "removeConfirmation": "¿Está seguro de que desea eliminar a '{{name}}' de la organización '{{org}}'?",
+ "noOrgError": "Error sin organización"
+ },
+ "communityProfile": {
+ "title": "Perfil de la comunidad",
+ "editProfile": "Editar perfil",
+ "communityProfileInfo": "Estos detalles aparecerán en la pantalla de inicio de sesión/registro para usted y los miembros de su comunidad.",
+ "communityName": "Nombre de la comunidad",
+ "wesiteLink": "Enlace de página web",
+ "logo": "Logo",
+ "social": "Enlaces de redes sociales",
+ "url": "Introducir URL",
+ "profileChangedMsg": "Se actualizaron correctamente los detalles del perfil.",
+ "resetData": "Restablezca correctamente los detalles del perfil."
+ },
+ "dashboard": {
+ "title": "Panel de",
+ "location": "Ubicación",
+ "about": "Sobre",
+ "deleteThisOrganization": "Eliminar esta organización",
+ "statistics": "Estadísticas",
+ "members": "Miembros",
+ "admins": "Administradores",
+ "posts": "Publicaciones",
+ "events": "Eventos",
+ "blockedUsers": "Usuarios bloqueados",
+ "requests": "Solicitudes",
+ "viewAll": "Ver Todo",
+ "upcomingEvents": "Próximos Eventos",
+ "noUpcomingEvents": "No Hay Próximos Eventos",
+ "latestPosts": "Últimas Publicaciones",
+ "noPostsPresent": "No Hay Publicaciones Presentes",
+ "membershipRequests": "Solicitudes de Membresía",
+ "noMembershipRequests": "No Hay Solicitudes de Membresía",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red.",
+ "volunteerRankings": "Clasificación de Voluntarios",
+ "noVolunteers": "¡No Se Encontraron Voluntarios!"
+ },
+ "organizationPeople": {
+ "title": "Miembros Talawa",
+ "filterByName": "Filtrar por nombre",
+ "filterByLocation": "Filtrar por Ubicación",
+ "filterByEvent": "Filtrar por Evento",
+ "members": "Miembros",
+ "admins": "Administradores",
+ "users": "Usuarios",
+ "searchName": "Ingrese su nombre",
+ "searchevent": "Ingresar evento",
+ "searchFirstName": "Ingrese el nombre",
+ "searchLastName": "Introduzca el apellido",
+ "people": "Personas",
+ "sort": "Ordenar por Rol",
+ "actions": "Acciones",
+ "existingUser": "Usuario existente",
+ "newUser": "Nuevo usuario",
+ "firstName": "Nombre de pila",
+ "enterFirstName": "Ponga su primer nombre",
+ "lastName": "Apellido",
+ "enterLastName": "Ingresa tu apellido",
+ "emailAddress": "correo electrónico",
+ "enterEmail": "Ingrese su dirección de correo electrónico",
+ "password": "Contraseña",
+ "enterPassword": "Ingresa tu contraseña",
+ "confirmPassword": "confirmar Contraseña",
+ "enterConfirmPassword": "Ingrese su contraseña para confirmar",
+ "organization": "Organización",
+ "create": "Crear",
+ "cancel": "Cancelar",
+ "invalidDetailsMessage": "Ingrese detalles válidos.",
+ "addMembers": "Agregar miembros",
+ "searchFullName": "Ingrese el nombre completo",
+ "user": "Usuario",
+ "profile": "Perfil"
+ },
+ "organizationTags": {
+ "title": "Etiquetas de Organización",
+ "createTag": "Crear una nueva etiqueta",
+ "manageTag": "Gestionar",
+ "editTag": "Editar",
+ "removeTag": "Eliminar",
+ "tagDetails": "Detalles de la Etiqueta",
+ "tagName": "Nombre",
+ "tagType": "Tipo",
+ "tagNamePlaceholder": "Escribe el nombre de la etiqueta",
+ "tagCreationSuccess": "Nueva etiqueta creada con éxito",
+ "tagUpdationSuccess": "Etiqueta actualizada con éxito",
+ "tagRemovalSuccess": "Etiqueta eliminada con éxito",
+ "noTagsFound": "No se encontraron etiquetas",
+ "removeUserTag": "Eliminar Etiqueta",
+ "removeUserTagMessage": "¿Desea eliminar esta etiqueta?",
+ "addChildTag": "Agregar una Sub Etiqueta",
+ "enterTagName": "Ingrese el nombre de la etiqueta"
+ },
+ "manageTag": {
+ "title": "Detalles de la Etiqueta",
+ "addPeopleToTag": "Agregar Personas a la etiqueta",
+ "viewProfile": "Ver",
+ "noAssignedMembersFound": "Ningún miembro asignado",
+ "unassignUserTag": "Desasignar Etiqueta",
+ "unassignUserTagMessage": "¿Desea eliminar la etiqueta de este usuario?",
+ "successfullyUnassigned": "Etiqueta desasignada del usuario",
+ "addPeople": "Agregar Personas",
+ "add": "Agregar",
+ "subTags": "Subetiquetas",
+ "successfullyAssignedToPeople": "Etiqueta asignada con éxito",
+ "errorOccurredWhileLoadingMembers": "Error al cargar los miembros",
+ "userName": "Nombre de usuario",
+ "actions": "Acciones",
+ "noOneSelected": "Nadie seleccionado",
+ "assignToTags": "Asignar a etiquetas",
+ "removeFromTags": "Eliminar de etiquetas",
+ "assign": "Asignar",
+ "remove": "Eliminar",
+ "successfullyAssignedToTags": "Asignado a etiquetas con éxito",
+ "successfullyRemovedFromTags": "Eliminado de etiquetas con éxito",
+ "errorOccurredWhileLoadingOrganizationUserTags": "Error al cargar las etiquetas de la organización",
+ "errorOccurredWhileLoadingSubTags": "Ocurrió un error al cargar las subetiquetas",
+ "removeUserTag": "Eliminar etiqueta",
+ "removeUserTagMessage": "¿Desea eliminar esta etiqueta? Esto eliminará todas las subetiquetas y todas las asociaciones.",
+ "tagDetails": "Detalles de la etiqueta",
+ "tagName": "Nombre",
+ "tagUpdationSuccess": "Etiqueta actualizada con éxito",
+ "tagRemovalSuccess": "Etiqueta eliminada con éxito",
+ "noTagSelected": "Ninguna etiqueta seleccionada",
+ "changeNameToEdit": "Cambia el nombre para hacer una actualización",
+ "selectTag": "Seleccionar etiqueta",
+ "collapse": "Colapsar",
+ "expand": "Expandir",
+ "tagNamePlaceholder": "Escribe el nombre de la etiqueta",
+ "allTags": "Todas las etiquetas",
+ "noMoreMembersFound": "No se encontraron más miembros"
+ },
+ "userListCard": {
+ "joined": "Unido",
+ "addAdmin": "Agregar administrador",
+ "addedAsAdmin": "El usuario se agrega como administrador.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red."
+ },
+ "orgAdminListCard": {
+ "joined": "Unido",
+ "remove": "Remover",
+ "removeAdmin": "Eliminar administrador",
+ "removeAdminMsg": "¿Quieres eliminar a este administrador?",
+ "no": "No",
+ "yes": "Sí",
+ "adminRemoved": "Se elimina el administrador.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red."
+ },
+ "orgPeopleListCard": {
+ "joined": "Unido",
+ "remove": "Remover",
+ "removeMember": "Eliminar miembro",
+ "removeMemberMsg": "¿Quieres eliminar a este miembro?",
+ "no": "No",
+ "yes": "Sí",
+ "memberRemoved": "El miembro es eliminado",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red."
+ },
+ "organizationEvents": {
+ "title": "Eventos",
+ "filterByTitle": "Filtrar por Título",
+ "filterByLocation": "Filtrar por Ubicación",
+ "filterByDescription": "Filtrar por descripción",
+ "events": "Eventos",
+ "addEvent": "Añadir evento",
+ "eventDetails": "Detalles del evento",
+ "eventTitle": "Título",
+ "description": "Descripción",
+ "location": "Ubicación",
+ "startDate": "Fecha de inicio",
+ "searchMemberName": "Buscar nombre de miembro",
+ "endDate": "Fecha final",
+ "startTime": "Hora de inicio",
+ "endTime": "Hora de finalización",
+ "allDay": "Todo el dia",
+ "recurringEvent": "Evento recurrente",
+ "isPublic": "Es público",
+ "isRegistrable": "Es registrable",
+ "createEvent": "Crear evento",
+ "enterFilter": "Introducir filtro",
+ "enterTitle": "Ingrese el título",
+ "enterDescrip": "Introduce la descripción",
+ "eventLocation": "Introducir ubicación",
+ "eventCreated": "¡Felicidades! Se crea el Evento.",
+ "eventType": "Tipo de evento",
+ "searchEventName": "Buscar nombre del evento",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red.",
+ "customRecurrence": "Recurrencia personalizada",
+ "repeatsEvery": "Se repite cada",
+ "repeatsOn": "Se repite en",
+ "ends": "Finaliza",
+ "never": "Nunca",
+ "on": "En",
+ "after": "Después de",
+ "occurences": "ocurrencias",
+ "done": "Hecho"
+ },
+ "organizationActionItems": {
+ "actionItemCategory": "Categoría de Acción",
+ "actionItemDetails": "Detalles de la Acción",
+ "actionItemCompleted": "Acción Completada",
+ "assignee": "Asignado",
+ "assigner": "Asignador",
+ "assignmentDate": "Fecha de Asignación",
+ "active": "Activo",
+ "clearFilters": "Limpiar Filtros",
+ "completionDate": "Fecha de Finalización",
+ "createActionItem": "Crear Acción",
+ "deleteActionItem": "Eliminar Acción",
+ "deleteActionItemMsg": "¿Desea eliminar esta acción?",
+ "details": "Detalles",
+ "dueDate": "Fecha de Vencimiento",
+ "earliest": "El Más Antiguo",
+ "editActionItem": "Editar Acción",
+ "isCompleted": "Completado",
+ "latest": "El Más Reciente",
+ "makeActive": "Hacer Activo",
+ "noActionItems": "No Hay Acciones",
+ "options": "Opciones",
+ "preCompletionNotes": "Notas Pre-Compleción",
+ "actionItemActive": "Acción Activa",
+ "markCompletion": "Marcar como Completado",
+ "actionItemStatus": "Estado de la Acción",
+ "postCompletionNotes": "Notas de Finalización",
+ "selectActionItemCategory": "Seleccionar una Categoría de Acción",
+ "selectAssignee": "Seleccionar Asignado",
+ "status": "Estado",
+ "successfulCreation": "Acción creada con éxito",
+ "successfulUpdation": "Acción actualizada con éxito",
+ "successfulDeletion": "Acción eliminada con éxito",
+ "title": "Acciones",
+ "category": "Categoría",
+ "allottedHours": "Horas Asignadas",
+ "latestDueDate": "Fecha de Vencimiento Más Reciente",
+ "earliestDueDate": "Fecha de Vencimiento Más Antigua",
+ "updateActionItem": "Actualizar Acción",
+ "noneUpdated": "Ningún campo fue actualizado",
+ "updateStatusMsg": "¿Está seguro de que desea marcar esta acción como pendiente?",
+ "close": "Cerrar",
+ "eventActionItems": "Acciones del Evento",
+ "no": "No",
+ "yes": "Sí",
+ "individuals": "Individuos",
+ "groups": "Grupos",
+ "assignTo": "Asignar a",
+ "volunteers": "Voluntarios",
+ "volunteerGroups": "Grupos de Voluntarios"
+ },
+ "organizationAgendaCategory": {
+ "agendaCategoryDetails": "Detalles de la categoría de la agenda",
+ "updateAgendaCategory": "Actualizar categoría de la agenda",
+ "title": "Categorías de la agenda",
+ "name": "Categoría",
+ "description": "Descripción",
+ "createdBy": "Creado por",
+ "options": "Opciones",
+ "createAgendaCategory": "Crear categoría de la agenda",
+ "noAgendaCategories": "No hay categorías de la agenda",
+ "update": "Actualizar",
+ "agendaCategoryCreated": "Categoría de la agenda creada exitosamente",
+ "agendaCategoryUpdated": "Categoría de la agenda actualizada exitosamente",
+ "agendaCategoryDeleted": "Categoría de la agenda eliminada exitosamente",
+ "deleteAgendaCategory": "Eliminar categoría de la agenda",
+ "deleteAgendaCategoryMsg": "¿Desea eliminar esta categoría de la agenda?"
+ },
+ "agendaItems": {
+ "agendaItemDetails": "Detalles del punto del orden del día",
+ "updateAgendaItem": "Actualizar punto del orden del día",
+ "title": "Título",
+ "enterTitle": "Ingresar título",
+ "sequence": "Secuencia",
+ "description": "Descripción",
+ "enterDescription": "Ingresar descripción",
+ "category": "Categoría del orden del día",
+ "attachments": "Archivos adjuntos",
+ "attachmentLimit": "Agregar cualquier archivo de imagen o video hasta 10MB",
+ "fileSizeExceedsLimit": "El tamaño del archivo excede el límite de 10MB",
+ "urls": "URLs",
+ "url": "Agregar enlace a URL",
+ "enterUrl": "https://example.com",
+ "invalidUrl": "Ingrese una URL válida",
+ "link": "Enlace",
+ "createdBy": "Creado por",
+ "regular": "Regular",
+ "note": "Nota",
+ "duration": "Duración",
+ "enterDuration": "mm:ss",
+ "options": "Opciones",
+ "createAgendaItem": "Crear punto del orden del día",
+ "noAgendaItems": "No hay puntos del orden del día",
+ "selectAgendaItemCategory": "Seleccionar una categoría de punto del orden del día",
+ "update": "Actualizar",
+ "delete": "Eliminar",
+ "agendaItemCreated": "Punto del orden del día creado exitosamente",
+ "agendaItemUpdated": "Punto del orden del día actualizado exitosamente",
+ "agendaItemDeleted": "Punto del orden del día eliminado exitosamente",
+ "deleteAgendaItem": "Eliminar punto del orden del día",
+ "deleteAgendaItemMsg": "¿Desea eliminar este punto del orden del día?"
+ },
+ "eventListCard": {
+ "location": "Lugar del evento",
+ "deleteEvent": "Eliminar evento",
+ "deleteEventMsg": "¿Quieres eliminar este evento?",
+ "no": "No",
+ "yes": "Sí",
+ "editEvent": "Editar evento",
+ "eventTitle": "Título",
+ "description": "Descripción",
+ "startDate": "Fecha de inicio",
+ "endDate": "Fecha final",
+ "registerEvent": "Registro",
+ "alreadyRegistered": "Ya registrado",
+ "startTime": "Hora de inicio",
+ "endTime": "Hora de finalización",
+ "allDay": "Todo el dia",
+ "recurringEvent": "Evento recurrente",
+ "isPublic": "Es público",
+ "isRegistrable": "Es registrable",
+ "close": "Cerca",
+ "updatePost": "Actualizar publicación",
+ "eventDetails": "Detalles del evento",
+ "eventDeleted": "Evento eliminado con éxito.",
+ "eventUpdated": "Evento actualizado con éxito.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red.",
+ "thisInstance": "Esta Instancia",
+ "thisAndFollowingInstances": "Esta y las Siguientes Instancias",
+ "allInstances": "Todas las Instancias",
+ "customRecurrence": "Recurrencia personalizada",
+ "repeatsEvery": "Se repite cada",
+ "repeatsOn": "Se repite en",
+ "ends": "Finaliza",
+ "never": "Nunca",
+ "on": "En",
+ "after": "Después de",
+ "occurences": "ocurrencias",
+ "done": "Hecho"
+ },
+ "funds": {
+ "title": "Fondos",
+ "createFund": "Crear fondo",
+ "fundName": "Nombre del fondo",
+ "fundId": "ID de referencia del fondo",
+ "taxDeductible": "Deducible de impuestos",
+ "default": "Predeterminado",
+ "archived": "Archivado",
+ "fundCreate": "Crear fondo",
+ "fundUpdate": "Actualizar fondo",
+ "fundDelete": "Eliminar fondo",
+ "searchByName": "Buscar por nombre",
+ "noFundsFound": "No se encontraron fondos",
+ "createdBy": "Creado por",
+ "createdOn": "Creado en",
+ "status": "Estado",
+ "fundCreated": "Fondo creado correctamente",
+ "fundUpdated": "Fondo actualizado correctamente",
+ "fundDeleted": "Fondo eliminado correctamente",
+ "deleteFundMsg": "¿Está seguro de que desea eliminar este fondo?",
+ "createdLatest": "Creado más reciente",
+ "createdEarliest": "Creado más temprano",
+ "viewCampaigns": "Ver campañas"
+ },
+ "fundCampaign": {
+ "title": "Campañas de recaudación de fondos",
+ "campaignName": "Nombre de la campaña",
+ "campaignOptions": "Opciones",
+ "fundingGoal": "Meta de financiación",
+ "addCampaign": "Agregar campaña",
+ "createdCampaign": "Campaña creada correctamente",
+ "updatedCampaign": "Campaña actualizada correctamente",
+ "deletedCampaign": "Campaña eliminada correctamente",
+ "deleteCampaignMsg": "¿Está seguro de que desea eliminar esta campaña?",
+ "noCampaigns": "No se encontraron campañas",
+ "createCampaign": "Crear campaña",
+ "updateCampaign": "Actualizar campaña",
+ "deleteCampaign": "Eliminar campaña",
+ "currency": "Moneda",
+ "selectCurrency": "Seleccionar moneda",
+ "searchFullName": "Buscar por nombre",
+ "viewPledges": "Ver compromisos",
+ "noCampaignsFound": "No se encontraron campañas",
+ "latestEndDate": "Fecha de finalización más reciente",
+ "earliestEndDate": "Fecha de finalización más temprana",
+ "lowestGoal": "Meta más baja",
+ "highestGoal": "Meta más alta"
+ },
+ "pledges": {
+ "title": "Compromisos de Campaña de Financiamiento",
+ "startDate": "Fecha de Inicio",
+ "endDate": "Fecha de Finalización",
+ "pledgeAmount": "Monto del Compromiso",
+ "pledgeOptions": "Opciones",
+ "pledgeCreated": "Compromiso creado exitosamente",
+ "pledgeUpdated": "Compromiso actualizado exitosamente",
+ "pledgeDeleted": "Compromiso eliminado exitosamente",
+ "addPledge": "Agregar Compromiso",
+ "createPledge": "Crear Compromiso",
+ "currency": "Moneda",
+ "selectCurrency": "Seleccionar Moneda",
+ "updatePledge": "Actualizar Compromiso",
+ "deletePledge": "Eliminar Compromiso",
+ "amount": "Monto",
+ "editPledge": "Editar Compromiso",
+ "deletePledgeMsg": "¿Estás seguro de que quieres eliminar este compromiso?",
+ "noPledges": "No se encontraron compromisos",
+ "searchPledger": "Buscar por compromisos",
+ "highestAmount": "Cantidad más alta",
+ "lowestAmount": "Cantidad más baja",
+ "latestEndDate": "Fecha de finalización más reciente",
+ "earliestEndDate": "Fecha de finalización más cercana",
+ "campaigns": "Campañas",
+ "pledges": "Compromisos",
+ "endsOn": "Finaliza el",
+ "raisedAmount": "Monto recaudado",
+ "pledgedAmount": "Monto comprometido"
+ },
+
+ "orgPost": {
+ "title": "Publicaciones de Talawa",
+ "searchPost": "Buscar Publicación",
+ "posts": "Publicaciones",
+ "createPost": "Crear Publicación",
+ "postDetails": "Detalles de la Publicación",
+ "postTitle1": "Escribir título de la publicación",
+ "postTitle": "Título",
+ "addMedia": "Subir foto o video",
+ "information": "Información",
+ "information1": "Escribir información de la publicación",
+ "addPost": "Agregar Publicación",
+ "searchTitle": "Buscar por Título",
+ "searchText": "Buscar por Texto",
+ "ptitle": "Título de la Publicación",
+ "postDes": "¿De qué quieres hablar?",
+ "Title": "Título",
+ "Text": "Texto",
+ "cancel": "Cancelar",
+ "searchBy": "Buscar por",
+ "Oldest": "Más Antiguas Primero",
+ "Latest": "Más Recientes Primero",
+ "sortPost": "Ordenar Publicaciones",
+ "tag": "Su navegador no admite la etiqueta de video",
+ "postCreatedSuccess": "¡Felicidades! Has publicado algo.",
+ "pinPost": "Fijar publicación",
+ "Next": "Siguiente página",
+ "Previous": "Página anterior"
+ },
+ "postNotFound": {
+ "post": "Publicaciones",
+ "not found!": "Extraviado!",
+ "organization": "Organización",
+ "post not found!": "Publicaciones Extraviado!",
+ "organization not found!": "Organización Extraviado!"
+ },
+ "userNotFound": {
+ "user": "usuari(a/o)",
+ "not found!": "extraviado!",
+ "roles": "papeles",
+ "user not found!": "usuario no encontrado!",
+ "member not found!": "Miembro no encontrado!",
+ "admin not found!": "Administrador no encontrado!",
+ "roles not found!": "roles no encontrados!"
+ },
+ "orgPostCard": {
+ "author": "Autor",
+ "imageURL": "URL de la Imagen",
+ "videoURL": "URL del Video",
+ "edit": "Editar Publicación",
+ "deletePost": "Eliminar Publicación",
+ "deletePostMsg": "¿Desea eliminar esta publicación?",
+ "no": "No",
+ "yes": "Sí",
+ "editPost": "Editar Publicación",
+ "postTitle": "Título",
+ "postTitle1": "Editar título de la publicación",
+ "information1": "Editar información de la publicación",
+ "information": "Información",
+ "image": "Imagen",
+ "video": "Video",
+ "close": "Cerrar",
+ "updatePost": "Actualizar Publicación",
+ "postDeleted": "Publicación eliminada exitosamente.",
+ "postUpdated": "Publicación actualizada exitosamente.",
+ "tag": "Su navegador no admite la etiqueta de video",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está en funcionamiento? Compruebe también su conectividad de red.",
+ "pin": "Fijar"
+ },
+ "blockUnblockUser": {
+ "title": "Usuario de bloqueo/desbloqueo de Talawa",
+ "pageName": "Bloqueo/desbloqueo",
+ "searchByName": "Buscar por nombre",
+ "listOfUsers": "Lista de Usuarios que enviaron spam",
+ "name": "Nombre",
+ "email": "Correo electrónico",
+ "block_unblock": "Bloquear/Desbloquear",
+ "unblock": "Desatascar",
+ "block": "Bloquear",
+ "orgName": "Ingrese su nombre",
+ "blockedSuccessfully": "Usuario bloqueado con éxito",
+ "Un-BlockedSuccessfully": "Usuario desbloqueado con éxito",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red.",
+ "allMembers": "Todos los miembros",
+ "blockedUsers": "Usuarios bloqueados",
+ "searchByFirstName": "Buscar por nombre de pila",
+ "searchByLastName": "Buscar por apellido",
+ "noResultsFoundFor": "No se encontraron resultados para ",
+ "noSpammerFound": "No se encontró ningún spammer"
+ },
+ "eventManagement": {
+ "title": "Gestión de eventos",
+ "dashboard": "Tablero",
+ "registrants": "Inscritos",
+ "attendance": "Asistencia",
+ "actions": "Acciones",
+ "agendas": "Agendas",
+ "statistics": "Estadísticas",
+ "to": "A",
+ "volunteers": "Voluntarios"
+ },
+ "forgotPassword": {
+ "title": "Talawa olvidó su contraseña",
+ "forgotPassword": "Has olvidado tu contraseña",
+ "registeredEmail": "Email registrado",
+ "getOtp": "Obtener OTP",
+ "enterOtp": "Ingresar OTP",
+ "enterNewPassword": "Ingrese nueva clave",
+ "cofirmNewPassword": "Confirmar nueva contraseña",
+ "changePassword": "Cambia la contraseña",
+ "backToLogin": "Volver al inicio de sesión",
+ "userOtp": "por ejemplo 12345",
+ "password": "Contraseña",
+ "emailNotRegistered": "El correo electrónico no está registrado.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Verifica también la conectividad de tu red.",
+ "errorSendingMail": "Error al enviar correo.",
+ "passwordMismatches": "Contraseña y Confirmar contraseña no coinciden.",
+ "passwordChanges": "La contraseña cambia correctamente.",
+ "OTPsent": "OTP se envía a su correo electrónico registrado"
+ },
+ "pageNotFound": {
+ "title": "404 No encontrado",
+ "talawaAdmin": "Administrador de Talawa",
+ "talawaUser": "Usuario de Talawa",
+ "404": "404",
+ "notFoundMsg": "¡Ups! ¡No se encontró la página que solicitaste!",
+ "backToHome": "De vuelta a casa"
+ },
+ "orgContribution": {
+ "title": "Contribuciones Talawa",
+ "filterByName": "Filtrar por nombre",
+ "filterByTransId": "Filtrar por ID de transacción",
+ "recentStats": "Estadísticas recientes",
+ "contribution": "Contribución",
+ "orgname": "Ingrese su nombre",
+ "searchtransaction": "Ingrese la identificación de la transacción"
+ },
+ "contriStats": {
+ "recentContribution": "Contribución reciente",
+ "highestContribution": "Contribución más alta",
+ "totalContribution": "Contribución total"
+ },
+ "orgContriCards": {
+ "date": "Fecha",
+ "transactionId": "ID de transacción",
+ "amount": "Monto"
+ },
+ "orgSettings": {
+ "title": "Configuración",
+ "general": "General",
+ "actionItemCategories": "Categorías de elementos de acción",
+ "updateOrganization": "Actualizar organización",
+ "seeRequest": "Ver solicitud",
+ "noData": "Sin datos",
+ "otherSettings": "Otras configuraciones",
+ "changeLanguage": "Cambiar idioma",
+ "manageCustomFields": "Administrar campos personalizados",
+ "agendaItemCategories": "Categorías de elementos de agenda"
+ },
+ "deleteOrg": {
+ "deleteOrganization": "Eliminar organización",
+ "deleteSampleOrganization": "Eliminar organización de muestra",
+ "deleteMsg": "¿Desea eliminar esta organización?",
+ "cancel": "Cancelar",
+ "confirmDelete": "Confirmar eliminación",
+ "longDelOrgMsg": "Al hacer clic en el botón Eliminar organización, la organización se eliminará permanentemente junto con sus eventos, etiquetas y todos los datos relacionados.",
+ "successfullyDeletedSampleOrganization": "Organización de muestra eliminada correctamente"
+ },
+ "userUpdate": {
+ "firstName": "Primer nombre",
+ "lastName": "Apellido",
+ "email": "Correo electrónico",
+ "password": "Clave",
+ "appLanguageCode": "Idioma predeterminado",
+ "userType": "Tipo de usuario",
+ "admin": "Administración",
+ "superAdmin": "Superadministrador",
+ "displayImage": "Mostrar imagen",
+ "saveChanges": "Guardar cambios",
+ "cancel": "Cancelar"
+ },
+ "userPasswordUpdate": {
+ "previousPassword": "Contraseña anterior",
+ "newPassword": "Nueva contraseña",
+ "confirmNewPassword": "Confirmar nueva contraseña",
+ "saveChanges": "Guardar cambios",
+ "cancel": "Cancelar",
+ "passCantBeEmpty": "La contraseña no puede estar vacía",
+ "passNoMatch": "La nueva contraseña y la confirmación no coinciden."
+ },
+ "orgDelete": {
+ "deleteOrg": "Eliminar organización"
+ },
+ "membershipRequest": {
+ "joined": "Unido",
+ "accept": "Aceptar",
+ "reject": "Rechazar",
+ "memberAdded": "es aceptado",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red."
+ },
+ "orgUpdate": {
+ "name": "Nombre",
+ "description": "Descripción",
+ "location": "ubicación",
+ "address": "Dirección",
+ "city": "Ciudad",
+ "countryCode": "Código de País",
+ "line1": "Línea 1",
+ "line2": "Línea 2",
+ "postalCode": "Código Postal",
+ "dependentLocality": "Localidad Dependiente",
+ "sortingCode": "Código de Ordenamiento",
+ "state": "Estado / Provincia",
+ "displayImage": "Mostrar imagen",
+ "userRegistrationRequired": "Registro de usuario requerido",
+ "isVisibleInSearch": "Visible en la búsqueda",
+ "saveChanges": "Guardar cambios",
+ "cancel": "Cancelar",
+ "enterNameOrganization": "Ingrese el nombre de la organización",
+ "successfulUpdated": "Exitoso actualizado",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. ¿Está funcionando? Compruebe también la conectividad de su red."
+ },
+ "addOnRegister": {
+ "addNew": "Agregar nueva",
+ "addPlugin": "Agregar complemento",
+ "pluginName": "Nombre del complemento",
+ "creatorName": "Nombre del creadora",
+ "pluginDesc": "Descripción del complemento",
+ "close": "Cerca",
+ "register": "Registro",
+ "pName": "Ej: Donaciones",
+ "cName": "Ej: John Doe",
+ "pDesc": "Este complemento habilita la interfaz de usuario para"
+ },
+ "addOnStore": {
+ "title": "Tienda de complementos",
+ "searchName": "Ej: Donaciones",
+ "search": "Buscar",
+ "enable": "Activada",
+ "disable": "Desactivada",
+ "pHeading": "Complementos",
+ "install": "Instalada",
+ "available": "Disponible",
+ "pMessage": "El complemento no existe",
+ "filter": "filtros"
+ },
+ "addOnEntry": {
+ "enable": "Activada",
+ "install": "Instalar",
+ "uninstall": "Desinstalar",
+ "uninstallMsg": "Mensaje de desinstalación",
+ "installMsg": "Mensaje de instalación"
+ },
+ "memberDetail": {
+ "title": "Detalles del usuario",
+ "addAdmin": "Agregar administrador",
+ "noeventsAttended": "No hay eventos asistidos",
+ "alreadyIsAdmin": "El Miembro ya es Administrador",
+ "organizations": "Organizaciones",
+ "events": "Eventos",
+ "role": "Rol",
+ "email": "Correo electrónico",
+ "createdOn": "Creado en",
+ "main": "Principal",
+ "firstName": "Nombre",
+ "lastName": "Apellido",
+ "language": "Idioma",
+ "gender": "Género",
+ "birthDate": "Fecha de Nacimiento",
+ "educationGrade": "Nivel Educativo",
+ "employmentStatus": "Estado Laboral",
+ "maritalStatus": "Estado Civil",
+ "displayImage": "Imagen de Perfil",
+ "phone": "Teléfono",
+ "address": "Dirección",
+ "countryCode": "Código de País",
+ "state": "Estado",
+ "city": "Ciudad",
+ "personalInfoHeading": "Información Personal",
+ "viewAll": "Ver todo",
+ "eventsAttended": "Eventos Asistidos",
+ "contactInfoHeading": "Información de Contacto",
+ "actionsHeading": "Acciones",
+ "personalDetailsHeading": "Detalles del perfil",
+ "appLanguageCode": "Elegir Idioma",
+ "delete": "Eliminar Usuario",
+ "saveChanges": "Guardar Cambios",
+ "pluginCreationAllowed": "Permitir creación de complementos",
+ "joined": "Unido",
+ "created": "Creado",
+ "adminForOrganizations": "Administrador de organizaciones",
+ "membershipRequests": "Solicitudes de membresía",
+ "adminForEvents": "Administrador de eventos",
+ "addedAsAdmin": "El usuario se agrega como administrador.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. Compruebe amablemente su conexión de red y espere un momento.",
+ "deleteUser": "Eliminar usuario",
+ "userType": "Tipo de usuario",
+ "unassignUserTag": "Desasignar Etiqueta",
+ "unassignUserTagMessage": "¿Desea eliminar la etiqueta de este usuario?",
+ "successfullyUnassigned": "Etiqueta desasignada del usuario",
+ "tagsAssigned": "Etiquetas asignadas",
+ "noTagsAssigned": "No se asignaron etiquetas"
+ },
+ "userLogin": {
+ "login": "Acceso",
+ "forgotPassword": "Has olvidado tu contraseña ?",
+ "loginIntoYourAccount": "Inicie sesión en su cuenta",
+ "emailAddress": "correo electrónico",
+ "enterEmail": "Ingrese su dirección de correo electrónico",
+ "password": "Contraseña",
+ "enterPassword": "Ingresa tu contraseña",
+ "register": "Registro",
+ "invalidDetailsMessage": "Por favor, introduzca un correo electrónico y una contraseña válidos.",
+ "notAuthorised": "¡Lo siento! usted no está autorizado!",
+ "invalidCredentials": "Las credenciales ingresadas son incorrectas. Ingrese credenciales válidas.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. Compruebe amablemente su conexión de red y espere un momento."
+ },
+ "people": {
+ "title": "Gente",
+ "searchUsers": "Buscar usuarios"
+ },
+ "userRegister": {
+ "register": "Registro",
+ "firstName": "Nombre de pila",
+ "enterFirstName": "Ponga su primer nombre",
+ "lastName": "Apellido",
+ "enterLastName": "Ingresa tu apellido",
+ "emailAddress": "correo electrónico",
+ "enterEmail": "Ingrese su dirección de correo electrónico",
+ "password": "Contraseña",
+ "enterPassword": "Ingresa tu contraseña",
+ "confirmPassword": "confirmar Contraseña",
+ "enterConfirmPassword": "Ingrese su contraseña para confirmar",
+ "alreadyhaveAnAccount": "¿Ya tienes una cuenta?",
+ "login": "Acceso",
+ "afterRegister": "Registrado exitosamente. Espere a que el administrador apruebe su solicitud.",
+ "passwordNotMatch": "La contraseña no coincide. Confirme la contraseña y vuelva a intentarlo.",
+ "invalidDetailsMessage": "Ingrese detalles válidos.",
+ "talawaApiUnavailable": "El servicio Talawa-API no está disponible. Compruebe amablemente su conexión de red y espere un momento."
+ },
+ "userNavbar": {
+ "talawa": "Talawa",
+ "home": "Hogar",
+ "people": "Gente",
+ "events": "Eventos",
+ "chat": "Charlar",
+ "donate": "Donar",
+ "settings": "Ajustes",
+ "language": "Idioma",
+ "logout": "Cerrar sesión",
+ "close": "Cerca"
+ },
+ "userOrganizations": {
+ "allOrganizations": "Todas las organizaciones",
+ "joinedOrganizations": "Organizaciones unidas",
+ "createdOrganizations": "Organizaciones creadas",
+ "search": "Buscar usuarios",
+ "nothingToShow": "Nada que mostrar aquí.",
+ "selectOrganization": "Seleccionar organización",
+ "filter": "Filtrar",
+ "organizations": "Organizaciones",
+ "searchByName": "Buscar por nombre",
+ "searchUsers": "Buscar usuarios",
+ "searchOrganizations": "Buscar Organizaciones"
+ },
+ "userSidebarOrg": {
+ "yourOrganizations": "Tus Organizaciones",
+ "noOrganizations": "Aún no te has unido a ninguna organización.",
+ "viewAll": "Ver todo",
+ "talawaUserPortal": "Talawa portal de usuario",
+ "menu": "Menú",
+ "my organizations": "Mis Organizaciones.",
+ "users": "Usuarios",
+ "requests": "Solicitudes",
+ "communityProfile": "Perfil de la comunidad",
+ "logout": "Cerrar sesión",
+ "settings": "Ajustes",
+ "chat": "Chat"
+ },
+ "organizationSidebar": {
+ "viewAll": "Ver todo",
+ "events": "Eventos",
+ "members": "Miembros",
+ "noEvents": "No hay eventos para mostrar",
+ "noMembers": "No hay miembros para mostrar"
+ },
+ "postCard": {
+ "likes": "Gustos",
+ "comments": "Comentarios",
+ "viewPost": "Ver publicación",
+ "editPost": "Editar publicación",
+ "postedOn": "Publicado el {{date}}"
+ },
+ "home": {
+ "posts": "Publicaciones",
+ "post": "Publicación",
+ "title": "Publicaciones",
+ "textArea": "¿Tienes algo en mente?",
+ "feed": "Feed",
+ "loading": "Cargando",
+ "pinnedPosts": "Publicaciones fijadas",
+ "yourFeed": "Tu feed",
+ "nothingToShowHere": "No hay nada que mostrar aquí",
+ "somethingOnYourMind": "¿Tienes algo en mente?",
+ "addPost": "Añadir publicación",
+ "startPost": "Comenzar una publicación",
+ "media": "Medios",
+ "event": "Evento",
+ "article": "Artículo",
+ "postNowVisibleInFeed": "Publicar ahora visible en el feed"
+ },
+
+ "eventAttendance": {
+ "historical_statistics": "Estadísticas históricas",
+ "Search member": "Buscar miembro",
+ "Member Name": "Nombre del miembro",
+ "Status": "Estado",
+ "Events Attended": "Eventos asistidos",
+ "Task Assigned": "Tarea asignada",
+ "Member": "Miembro",
+ "Admin": "Administrador",
+ "loading": "Cargando...",
+ "noAttendees": "No se encontraron asistentes"
+ },
+ "onSpotAttendee": {
+ "title": "Asistente en el lugar",
+ "enterFirstName": "Ingrese el nombre",
+ "enterLastName": "Ingrese el apellido",
+ "enterEmail": "Ingrese el correo electrónico",
+ "enterPhoneNo": "Ingrese el número de teléfono",
+ "selectGender": "Seleccione el género",
+ "invalidDetailsMessage": "Por favor complete todos los campos requeridos",
+ "orgIdMissing": "Falta el ID de la organización. Por favor, inténtelo de nuevo.",
+ "attendeeAddedSuccess": "¡Asistente agregado exitosamente!",
+ "addAttendee": "Agregar",
+ "phoneNumber": "Número de teléfono",
+ "addingAttendee": "Agregando...",
+ "male": "Masculino",
+ "female": "Femenino",
+ "other": "Otro"
+ },
+ "settings": {
+ "settings": "Ajustes",
+ "eventAttended": "Événements Assistés",
+ "noeventsAttended": "No hay eventos asistidos",
+ "profileSettings": "Configuración de perfil",
+ "firstName": "Nombre de pila",
+ "lastName": "Apellido",
+ "gender": "Género",
+ "emailAddress": "Dirección de correo electrónico",
+ "phoneNumber": "Número de teléfono",
+ "displayImage": "Imagen de perfil",
+ "chooseFile": "Elegir archivo",
+ "birthDate": "Fecha de nacimiento",
+ "grade": "Nivel educativo",
+ "empStatus": "Situación laboral",
+ "maritalStatus": "Estado civil",
+ "address": "Dirección",
+ "state": "Ciudad/Estado",
+ "country": "País",
+ "resetChanges": "Restablecer cambios",
+ "saveChanges": "Guardar cambios",
+ "profileDetails": "Detalles del perfil",
+ "deleteUserMessage": "Al hacer clic en el botón Eliminar usuario, su usuario se eliminará permanentemente junto con sus eventos, etiquetas y todos los datos relacionados.",
+ "copyLink": "Copiar enlace del perfil",
+ "deleteUser": "Eliminar usuario",
+ "otherSettings": "Otras configuraciones",
+ "changeLanguage": "Cambiar idioma",
+ "sgender": "Seleccionar género",
+ "gradePlaceholder": "Ingresar grado",
+ "sEmpStatus": "Seleccionar estado de empleo",
+ "male": "Masculino",
+ "female": "Femenino",
+ "other": "Otro",
+ "employed": "Empleado",
+ "unemployed": "Desempleado",
+ "sMaritalStatus": "Seleccionar estado civil",
+ "single": "Soltero",
+ "married": "Casado",
+ "divorced": "Divorciado",
+ "widowed": "Viudo",
+ "engaged": "Comprometido",
+ "separated": "Separado",
+ "grade1": "1er Grado",
+ "grade2": "2do Grado",
+ "grade3": "3er Grado",
+ "grade4": "4to Grado",
+ "grade5": "5to Grado",
+ "grade6": "6to Grado",
+ "grade7": "7mo Grado",
+ "grade8": "8vo Grado",
+ "grade9": "9no Grado",
+ "grade10": "10mo Grado",
+ "grade11": "11vo Grado",
+ "grade12": "12vo Grado",
+ "graduate": "Graduado",
+ "kg": "KG",
+ "preKg": "Pre-KG",
+ "noGrade": "Sin Grado",
+ "fullTime": "Tiempo Completo",
+ "partTime": "Medio Tiempo",
+ "enterState": "Ingresar ciudad o estado",
+ "selectCountry": "Seleccionar un país",
+ "joined": "Unido"
+ },
+ "donate": {
+ "title": "Donaciones",
+ "donations": "Donaciones",
+ "searchDonations": "Buscar donaciones",
+ "donateForThe": "Donar para el",
+ "donateTo": "Donar a",
+ "amount": "Cantidad",
+ "yourPreviousDonations": "Tus donaciones anteriores",
+ "donate": "Donar",
+ "nothingToShow": "Nada que mostrar aquí.",
+ "success": "Donación exitosa",
+ "invalidAmount": "Ingrese un valor numérico para el monto de la donación.",
+ "donationAmountDescription": "Ingrese el valor numérico del monto de la donación.",
+ "donationOutOfRange": "El monto de la donación debe estar entre {{min}} y {{max}}."
+ },
+ "userEvents": {
+ "title": "Eventos",
+ "nothingToShow": "No hay nada que mostrar aquí.",
+ "search": "Buscar",
+ "createEvent": "Crear evento",
+ "recurring": "Periódica",
+ "startTime": "Hora de inicio",
+ "endTime": "Hora de finalización",
+ "cancel": "Cancelar",
+ "create": "Crear",
+ "listView": "Vista de la lista",
+ "calendarView": "Vista de calendario",
+ "allDay": "Todo el dia",
+ "eventCreated": "Evento creado y publicado exitosamente.",
+ "eventDetails": "Detalles del evento",
+ "eventTitle": "Título",
+ "enterTitle": "Ingrese el título",
+ "eventDescription": "Descripción",
+ "enterDescription": "Ingresar descripción",
+ "eventLocation": "Ubicación",
+ "enterLocation": "Ingresar Ubicación",
+ "startDate": "Fecha de inicio",
+ "endDate": "Fecha de finalización",
+ "publicEvent": "Es público",
+ "registerable": "Es registrable",
+ "monthlyCalendarView": "Calendario mensual",
+ "yearlyCalendarView": "Calendario anual"
+ },
+ "userEventCard": {
+ "location": "Ubicación",
+ "starts": "Empieza",
+ "ends": "Termina",
+ "creator": "Creadora",
+ "alreadyRegistered": "Ya registrado",
+ "register": "Registro"
+ },
+ "advertisement": {
+ "title": "Anuncios",
+ "pHeading": "Gestionar anuncios",
+ "activeAds": "Campañas activas",
+ "archievedAds": "Campañas completadas",
+ "pMessage": "No hay anuncios disponibles para esta campaña.",
+ "delete": "Eliminar",
+ "validLink": "El enlace es válido.",
+ "invalidLink": "El enlace no es válido.",
+ "close": "Cerrar",
+ "deleteAdvertisement": "Eliminar anuncio",
+ "deleteAdvertisementMsg": "¿Desea eliminar este anuncio?",
+ "no": "No",
+ "yes": "Sí",
+ "Rmedia": "Proporcionar contenido multimedia para mostrar",
+ "view": "Ver",
+ "edit": "Editar",
+ "editAdvertisement": "Editar Anuncio",
+ "advertisementDeleted": "Anuncio eliminado con éxito.",
+ "endDateGreaterOrEqual": "La fecha de finalización debe ser mayor o igual a la fecha de inicio",
+ "advertisementCreated": "Anuncio creado con éxito.",
+ "saveChanges": "Guardar Cambios",
+ "endOfResults": "Fin de los resultados",
+ "Rname": "Nombre",
+ "Rtype": "Tipo",
+ "RstartDate": "Fecha de inicio",
+ "RendDate": "Fecha de finalización",
+ "RClose": "Cerrar",
+ "addNew": "Agregar nuevo",
+ "EXname": "Nombre",
+ "EXlink": "Enlace",
+ "createAdvertisement": "Crear publicidad"
+ },
+ "userChat": {
+ "chat": "Charlar",
+ "search": "Buscar",
+ "contacts": "Contactos",
+ "messages": "Mensajes"
+ },
+ "userChatRoom": {
+ "selectContact": "Seleccione un contacto para iniciar una conversación",
+ "sendMessage": "Enviar mensaje"
+ },
+ "orgProfileField": {
+ "loading": "Cargando..",
+ "noCustomField": "No hay campos personalizados disponibles",
+ "customFieldName": "Nombre del Campo",
+ "enterCustomFieldName": "Ingrese el Nombre del Campo",
+ "customFieldType": "Tipo de Campo",
+ "saveChanges": "Guardar Cambios",
+ "Remove Custom Field": "Eliminar Campo Personalizado",
+ "fieldSuccessMessage": "Campo añadido exitosamente",
+ "fieldRemovalSuccess": "Campo eliminado exitosamente",
+ "String": "Cadena",
+ "Boolean": "Booleano",
+ "Date": "Fecha",
+ "Number": "Número"
+ },
+ "orgActionItemCategories": {
+ "createButton": "Crear",
+ "editButton": "Editar",
+ "enableButton": "Habilitar",
+ "disableButton": "Inhabilitar",
+ "updateActionItemCategory": "Actualizar",
+ "actionItemCategoryName": "Nombre",
+ "categoryDetails": "Detalles de la categoría",
+ "enterName": "Introduzca el nombre",
+ "successfulCreation": "Categoría de elemento de acción creada correctamente",
+ "successfulUpdation": "Categoría de elemento de acción actualizada correctamente",
+ "sameNameConflict": "Cambie el nombre para realizar una actualización",
+ "categoryEnabled": "Categoría de elemento de acción habilitada",
+ "categoryDisabled": "Categoría de elemento de acción deshabilitada",
+ "noActionItemCategories": "No hay categorías de elementos de acción",
+ "status": "Estado",
+ "categoryDeleted": "Categoría de elemento de acción eliminada con éxito",
+ "deleteCategory": "Eliminar categoría",
+ "deleteCategoryMsg": "¿Está seguro de que desea eliminar esta categoría de elemento de acción?"
+ },
+ "organizationVenues": {
+ "title": "Lugares",
+ "addVenue": "Agregar lugar",
+ "venueDetails": "Detalles del lugar",
+ "venueName": "Nombre del lugar",
+ "enterVenueName": "Ingrese el nombre del lugar",
+ "description": "Descripción del lugar",
+ "enterVenueDesc": "Ingrese la descripción del lugar",
+ "capacity": "Capacidad",
+ "enterVenueCapacity": "Ingrese la capacidad del lugar",
+ "image": "Imagen del lugar",
+ "uploadVenueImage": "Subir imagen del lugar",
+ "createVenue": "Crear lugar",
+ "venueAdded": "Lugar agregado correctamente",
+ "editVenue": "Actualizar lugar",
+ "venueUpdated": "Detalles del lugar actualizados correctamente",
+ "sort": "Ordenar",
+ "highestCapacity": "Mayor capacidad",
+ "lowestCapacity": "Menor capacidad",
+ "noVenues": "¡No se encontraron lugares!",
+ "edit": "Editar",
+ "view": "Ver",
+ "delete": "Eliminar",
+ "venueTitleError": "¡El título del lugar no puede estar vacío!",
+ "venueCapacityError": "¡La capacidad debe ser un número positivo!",
+ "searchBy": "Buscar por",
+ "name": "Nombre",
+ "desc": "Descripción"
+ },
+ "addMember": {
+ "title": "Agregar miembro",
+ "addMembers": "Agregar miembros",
+ "existingUser": "Usuario existente",
+ "newUser": "Usuario nuevo",
+ "searchFullName": "Buscar por nombre completo",
+ "firstName": "Nombre",
+ "enterFirstName": "Ingrese el nombre",
+ "lastName": "Apellido",
+ "enterLastName": "Ingrese el apellido",
+ "emailAddress": "Dirección de correo electrónico",
+ "enterEmail": "Ingrese el correo electrónico",
+ "password": "Contraseña",
+ "enterPassword": "Ingrese la contraseña",
+ "confirmPassword": "Confirmar contraseña",
+ "enterConfirmPassword": "Ingrese la contraseña de confirmación",
+ "organization": "Organización",
+ "cancel": "Cancelar",
+ "create": "Crear",
+ "invalidDetailsMessage": "Por favor proporcione todos los detalles requeridos.",
+ "passwordNotMatch": "Las contraseñas no coinciden.",
+ "user": "Usuario",
+ "profile": "Perfil",
+ "addMember": "Agregar miembro"
+ },
+ "eventActionItems": {
+ "title": "Elementos de acción",
+ "createActionItem": "Crear elementos de acción",
+ "actionItemCategory": "Categoría de elemento de acción",
+ "selectActionItemCategory": "Seleccione una categoría de elemento de acción",
+ "selectAssignee": "Seleccione un asignado",
+ "preCompletionNotes": "Notas",
+ "postCompletionNotes": "Notas finales",
+ "actionItemDetails": "Detalles del elemento de acción",
+ "dueDate": "Fecha de vencimiento",
+ "completionDate": "Fecha de finalización",
+ "editActionItem": "Editar elemento de acción",
+ "deleteActionItem": "Eliminar elemento de acción",
+ "deleteActionItemMsg": "¿Quieres eliminar este elemento de acción?",
+ "yes": "Sí",
+ "no": "no",
+ "successfulDeletion": "Elemento de acción eliminado exitosamente",
+ "successfulCreation": "Elemento de acción creado exitosamente",
+ "successfulUpdation": "Elemento de acción actualizado correctamente",
+ "notes": "Notas",
+ "assignee": "Cesionario",
+ "assigner": "Asignador",
+ "assignmentDate": "Fecha de asignación",
+ "status": "Estado",
+ "actionItemActive": "Activo",
+ "actionItemStatus": "Estado del elemento de acción",
+ "actionItemCompleted": "Elemento de acción completado",
+ "markCompletion": "Marcar finalización",
+ "save": "Guardar"
+ },
+ "checkIn": {
+ "errorCheckingIn": "Error al registrarse",
+ "checkedInSuccessfully": "Registrado con éxito"
+ },
+ "eventRegistrantsModal": {
+ "errorAddingAttendee": "Error al agregar asistente",
+ "errorRemovingAttendee": "Error al eliminar asistente"
+ },
+ "userCampaigns": {
+ "title": "Campañas de recaudación de fondos",
+ "searchByName": "Buscar por nombre...",
+ "searchBy": "Buscar por",
+ "pledgers": "Contribuyentes",
+ "campaigns": "Campañas",
+ "myPledges": "Mis Promesas",
+ "lowestAmount": "Monto más bajo",
+ "highestAmount": "Monto más alto",
+ "lowestGoal": "Meta más baja",
+ "highestGoal": "Meta más alta",
+ "latestEndDate": "Fecha de finalización más tardía",
+ "earliestEndDate": "Fecha de finalización más temprana",
+ "addPledge": "Añadir Promesa",
+ "viewPledges": "Ver Promesas",
+ "noPledges": "No se encontraron promesas",
+ "noCampaigns": "No se encontraron campañas"
+ },
+ "userPledges": {
+ "title": "Mis Promesas"
+ },
+ "eventVolunteers": {
+ "volunteers": "Voluntarios",
+ "volunteer": "Voluntario",
+ "volunteerGroups": "Grupos de Voluntarios",
+ "individuals": "Individuos",
+ "groups": "Grupos",
+ "status": "Estado",
+ "noVolunteers": "No Hay Voluntarios",
+ "noVolunteerGroups": "No Hay Grupos de Voluntarios",
+ "add": "Agregar",
+ "mostHoursVolunteered": "Más Horas de Voluntariado",
+ "leastHoursVolunteered": "Menos Horas de Voluntariado",
+ "accepted": "Aceptado",
+ "addVolunteer": "Agregar Voluntario",
+ "removeVolunteer": "Eliminar Voluntario",
+ "volunteerAdded": "Voluntario agregado con éxito",
+ "volunteerRemoved": "Voluntario eliminado con éxito",
+ "volunteerGroupCreated": "Grupo de voluntarios creado con éxito",
+ "volunteerGroupUpdated": "Grupo de voluntarios actualizado con éxito",
+ "volunteerGroupDeleted": "Grupo de voluntarios eliminado con éxito",
+ "removeVolunteerMsg": "¿Está seguro de que desea eliminar a este Voluntario?",
+ "deleteVolunteerGroupMsg": "¿Está seguro de que desea eliminar este Grupo de Voluntarios?",
+ "leader": "Líder",
+ "group": "Grupo",
+ "createGroup": "Crear Grupo",
+ "updateGroup": "Actualizar Grupo",
+ "deleteGroup": "Eliminar Grupo",
+ "volunteersRequired": "Voluntarios Necesarios",
+ "volunteerDetails": "Detalles del Voluntario",
+ "hoursVolunteered": "Horas de Voluntariado",
+ "groupDetails": "Detalles del Grupo",
+ "creator": "Creador",
+ "requests": "Solicitudes",
+ "noRequests": "No Hay Solicitudes",
+ "latest": "Más Reciente",
+ "earliest": "Más Antiguo",
+ "requestAccepted": "Solicitud aceptada con éxito",
+ "requestRejected": "Solicitud rechazada con éxito",
+ "details": "Detalles",
+ "manageGroup": "Gestionar Grupo",
+ "mostVolunteers": "La mayoría de voluntarios",
+ "leastVolunteers": "Menos voluntarios"
+ },
+ "userVolunteer": {
+ "title": "Voluntariado",
+ "name": "Título",
+ "upcomingEvents": "Próximos Eventos",
+ "requests": "Solicitudes",
+ "invitations": "Invitaciones",
+ "groups": "Grupos de Voluntarios",
+ "actions": "Acciones",
+ "searchByName": "Buscar por Nombre",
+ "latestEndDate": "Fecha de Finalización Más Reciente",
+ "earliestEndDate": "Fecha de Finalización Más Antigua",
+ "noEvents": "No Hay Próximos Eventos",
+ "volunteer": "Voluntario",
+ "volunteered": "Voluntariado",
+ "join": "Unirse",
+ "joined": "Unido",
+ "searchByEventName": "Buscar por Título del Evento",
+ "filter": "Filtrar",
+ "groupInvite": "Invitación de Grupo",
+ "individualInvite": "Invitación Individual",
+ "noInvitations": "No Hay Invitaciones",
+ "accept": "Aceptar",
+ "reject": "Rechazar",
+ "receivedLatest": "Recibido Recientemente",
+ "receivedEarliest": "Recibido en Primer Lugar",
+ "invitationAccepted": "Invitación aceptada con éxito",
+ "invitationRejected": "Invitación rechazada con éxito",
+ "volunteerSuccess": "Solicitud de voluntariado realizada con éxito",
+ "recurring": "Recurrente",
+ "groupInvitationSubject": "Invitación a unirse al grupo de voluntarios",
+ "eventInvitationSubject": "Invitación a ser voluntario para el evento"
+ }
+}
diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json
new file mode 100644
index 0000000000..e3da0bc32f
--- /dev/null
+++ b/public/locales/zh/common.json
@@ -0,0 +1,98 @@
+{
+ "firstName": "名",
+ "lastName": "姓",
+ "searchByName": "按名称搜索",
+ "loading": "加载中...",
+ "endOfResults": "结果结束",
+ "noResultsFoundFor": "没有找到结果 ",
+ "edit": "编辑",
+ "admins": "管理员",
+ "admin": "行政",
+ "user": "用户",
+ "superAdmin": "超级管理员",
+ "members": "会员",
+ "logout": "登出",
+ "login": "登录",
+ "register": "登记",
+ "menu": "菜单",
+ "gender": "性别",
+ "settings": "设置",
+ "users": "用户",
+ "requests": "要求",
+ "OR": "或者",
+ "cancel": "取消",
+ "close": "关闭",
+ "create": "创造",
+ "delete": "删除",
+ "done": "完毕",
+ "yes": "是的",
+ "no": "不",
+ "filter": "筛选",
+ "search": "搜索",
+ "description": "描述",
+ "saveChanges": "保存更改",
+ "resetChanges": "重置更改",
+ "displayImage": "显示图像",
+ "enterEmail": "输入电子邮件",
+ "emailAddress": "电子邮件地址",
+ "email": "电子邮件",
+ "name": "姓名",
+ "desc": "描述",
+ "enterPassword": "输入密码",
+ "password": "密码",
+ "confirmPassword": "确认密码",
+ "forgotPassword": "忘记密码 ?",
+ "talawaAdminPortal": "塔拉瓦管理门户",
+ "address": "地址",
+ "location": "地点",
+ "enterLocation": "输入位置",
+ "joined": "已加入",
+ "startDate": "开始日期",
+ "endDate": "结束日期",
+ "startTime": "开始时间",
+ "endTime": "时间结束",
+ "My Organizations": "我的组织",
+ "Dashboard": "仪表板",
+ "People": "人们",
+ "Events": "事件",
+ "Venues": "场地",
+ "Action Items": "行动项目",
+ "Posts": "帖子",
+ "Block/Unblock": "封锁/解除封锁",
+ "Advertisement": "广告",
+ "Funds": "资金",
+ "Membership Requests": "会员请求",
+ "Plugins": "插件",
+ "Plugin Store": "插件商店",
+ "Settings": "设置",
+ "createdOn": "创建于",
+ "createdBy": "创建者",
+ "usersRole": "用户角色",
+ "changeRole": "更改角色",
+ "action": "操作",
+ "removeUser": "删除用户",
+ "remove": "删除",
+ "viewProfile": "查看个人资料",
+ "profile": "轮廓",
+ "noFiltersApplied": "未应用筛选器",
+ "manage": "管理",
+ "searchResultsFor": "搜索结果",
+ "none": "没有",
+ "sort": "种类",
+ "Donate": "捐赠",
+ "addedSuccessfully": "{{item}} 添加成功",
+ "updatedSuccessfully": "{{item}} 更新成功",
+ "removedSuccessfully": "{{item}} 删除成功",
+ "successfullyUpdated": "更新成功",
+ "sessionWarning": "由于不活动,您的会话即将过期。请与页面互动以延长您的会话。",
+ "sessionLogOut": "由于不活动,您的会话已过期。请重新登录以继续。",
+ "all": "全部",
+ "active": "活跃",
+ "disabled": "禁用",
+ "pending": "待处理",
+ "completed": "已完成",
+ "late": "迟到",
+ "createdLatest": "最近创建",
+ "createdEarliest": "最早创建",
+ "searchBy": "搜索依据 {{item}}"
+}
diff --git a/public/locales/zh/errors.json b/public/locales/zh/errors.json
new file mode 100644
index 0000000000..c872f367a5
--- /dev/null
+++ b/public/locales/zh/errors.json
@@ -0,0 +1,11 @@
+{
+ "talawaApiUnavailable": "Talawa-API 服务不可用!",
+ "notFound": "未找到",
+ "unknownError": "出现未知错误。 {{msg}}",
+ "notAuthorised": "对不起!",
+ "errorSendingMail": "发送邮件时出错",
+ "emailNotRegistered": "邮箱未注册",
+ "notFoundMsg": "哎呀!",
+ "errorOccurredCouldntCreate": "发生错误。 无法创建{{entity}}",
+ "errorLoading": "加载{{entity}}数据时出错"
+}
diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json
new file mode 100644
index 0000000000..adafbdbe45
--- /dev/null
+++ b/public/locales/zh/translation.json
@@ -0,0 +1,1483 @@
+{
+ "leaderboard": {
+ "title": "排行榜",
+ "searchByVolunteer": "按志愿者搜索",
+ "mostHours": "最多时数",
+ "leastHours": "最少时数",
+ "timeFrame": "时间范围",
+ "allTime": "全部时间",
+ "weekly": "本周",
+ "monthly": "本月",
+ "yearly": "今年",
+ "noVolunteers": "未找到志愿者!"
+ },
+ "loginPage": {
+ "title": "塔拉瓦管理员",
+ "fromPalisadoes": "Palisadoes 基金会志愿者开发的开源应用程序",
+ "userLogin": "用户登录",
+ "atleast_8_char_long": "至少 8 个字符长",
+ "atleast_6_char_long": "至少 6 个字符长",
+ "firstName_invalid": "名字只能包含小写和大写字母",
+ "lastName_invalid": "姓氏只能包含小写和大写字母",
+ "password_invalid": "密码应至少包含1个小写字母、1个大写字母、1个数字和1个特殊字符",
+ "email_invalid": "电子邮件应至少包含 8 个字符",
+ "Password_and_Confirm_password_mismatches.": "密码和确认密码不匹配。",
+ "doNotOwnAnAccount": "没有帐户?",
+ "captchaError": "验证码错误!",
+ "Please_check_the_captcha": "请检查验证码。",
+ "Something_went_wrong": "出了点问题,请稍后再试。",
+ "passwordMismatches": "密码和确认密码不匹配。",
+ "fillCorrectly": "正确填写所有详细信息。",
+ "successfullyRegistered": "注册成功。",
+ "lowercase_check": "至少一个小写字母",
+ "uppercase_check": "至少有一个大写字母",
+ "numeric_value_check": "至少设定一个数值",
+ "special_char_check": "至少一个特殊字符",
+ "selectOrg": "选择一个组织",
+ "afterRegister": "注册成功。",
+ "talawa_portal": "塔拉瓦门户",
+ "login": "登录",
+ "register": "注册",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "email": "电子邮件",
+ "password": "密码",
+ "confirmPassword": "确认密码",
+ "forgotPassword": "忘记密码",
+ "enterEmail": "输入电子邮件",
+ "enterPassword": "输入密码",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "notAuthorised": "未授权",
+ "notFound": "未找到",
+ "OR": "或",
+ "admin": "管理员",
+ "user": "用户",
+ "loading": "加载中"
+ },
+ "userLoginPage": {
+ "title": "塔拉瓦管理员",
+ "fromPalisadoes": "Palisadoes 基金会志愿者开发的开源应用程序",
+ "atleast_8_char_long": "至少 8 个字符长",
+ "Password_and_Confirm_password_mismatches.": "密码和确认密码不匹配。",
+ "doNotOwnAnAccount": "没有帐户?",
+ "captchaError": "验证码错误!",
+ "Please_check_the_captcha": "请检查验证码。",
+ "Something_went_wrong": "出了点问题,请稍后再试。",
+ "passwordMismatches": "密码和确认密码不匹配。",
+ "fillCorrectly": "正确填写所有详细信息。",
+ "successfullyRegistered": "注册成功。",
+ "userLogin": "用户登录",
+ "afterRegister": "注册成功。",
+ "selectOrg": "选择一个组织",
+ "talawa_portal": "塔拉瓦门户",
+ "login": "登录",
+ "register": "注册",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "email": "电子邮件",
+ "password": "密码",
+ "confirmPassword": "确认密码",
+ "forgotPassword": "忘记密码",
+ "enterEmail": "输入电子邮件",
+ "enterPassword": "输入密码",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "notAuthorised": "未授权",
+ "notFound": "未找到",
+ "OR": "或",
+ "loading": "加载中"
+ },
+ "latestEvents": {
+ "eventCardTitle": "即将举行的活动",
+ "eventCardSeeAll": "查看全部",
+ "noEvents": "没有即将举行的活动"
+ },
+ "latestPosts": {
+ "latestPostsTitle": "最新帖子",
+ "seeAllLink": "查看全部",
+ "noPostsCreated": "没有创建帖子"
+ },
+ "listNavbar": {
+ "roles": "角色",
+ "talawa_portal": "塔拉瓦门户",
+ "requests": "请求",
+ "logout": "退出登录"
+ },
+ "leftDrawer": {
+ "my organizations": "我的组织",
+ "requests": "会员申请",
+ "communityProfile": "社区简介",
+ "talawaAdminPortal": "塔拉瓦管理员门户",
+ "menu": "菜单",
+ "users": "用户",
+ "logout": "退出登录"
+ },
+ "leftDrawerOrg": {
+ "Dashboard": "仪表板",
+ "People": "人们",
+ "Events": "活动",
+ "Contributions": "贡献",
+ "Posts": "帖子",
+ "Block/Unblock": "阻止/解除阻止",
+ "Plugins": "插件",
+ "Plugin Store": "插件商店",
+ "Advertisement": "广告",
+ "allOrganizations": "所有组织",
+ "yourOrganization": "您的组织",
+ "notification": "通知",
+ "language": "语言",
+ "notifications": "通知",
+ "spamsThe": "垃圾邮件",
+ "group": "团体",
+ "noNotifications": "无通知",
+ "talawaAdminPortal": "塔拉瓦管理员门户",
+ "menu": "菜单",
+ "talawa_portal": "塔拉瓦门户",
+ "settings": "设置",
+ "logout": "退出登录",
+ "close": "关闭"
+ },
+ "orgList": {
+ "title": "塔拉瓦组织",
+ "you": "你",
+ "designation": "指定",
+ "my organizations": "我的组织",
+ "createOrganization": "创建组织",
+ "createSampleOrganization": "创建样本组织",
+ "city": "城市",
+ "countryCode": "国家代码",
+ "dependentLocality": "附属地点",
+ "line1": "1号线",
+ "line2": "2号线",
+ "postalCode": "邮政编码",
+ "sortingCode": "排序代码",
+ "state": "州/省",
+ "userRegistrationRequired": "需要用户注册",
+ "visibleInSearch": "在搜索中可见",
+ "enterName": "输入名字",
+ "sort": "种类",
+ "Latest": "最新的",
+ "Earliest": "最早",
+ "noOrgErrorTitle": "未找到组织",
+ "sampleOrgDuplicate": "只允许一个样本组织",
+ "noOrgErrorDescription": "请通过仪表板创建组织",
+ "manageFeatures": "管理功能",
+ "manageFeaturesInfo": "创建成功!",
+ "goToStore": "前往插件商店",
+ "enableEverything": "启用一切",
+ "sampleOrgSuccess": "样本组织已成功创建",
+ "name": "名称",
+ "email": "电子邮件",
+ "searchByName": "按名称搜索",
+ "description": "描述",
+ "location": "位置",
+ "address": "地址",
+ "displayImage": "显示图像",
+ "filter": "筛选",
+ "cancel": "取消",
+ "endOfResults": "结果结束",
+ "noResultsFoundFor": "未找到结果",
+ "OR": "或"
+ },
+ "orgListCard": {
+ "manage": "管理",
+ "sampleOrganization": "组织样本",
+ "admins": "管理员",
+ "members": "成员"
+ },
+ "paginationList": {
+ "rowsPerPage": "每页行数",
+ "all": "全部"
+ },
+ "requests": {
+ "title": "会员申请",
+ "sl_no": "SL。",
+ "accept": "接受",
+ "reject": "拒绝",
+ "searchRequests": "搜索会员请求",
+ "noOrgError": "未找到组织,请通过仪表板创建组织",
+ "noRequestsFound": "未找到会员申请",
+ "acceptedSuccessfully": "请求已成功接受",
+ "rejectedSuccessfully": "请求被成功拒绝",
+ "noOrgErrorTitle": "未找到组织",
+ "noOrgErrorDescription": "请通过仪表板创建组织",
+ "name": "名称",
+ "email": "电子邮件",
+ "endOfResults": "结果结束",
+ "noResultsFoundFor": "未找到结果"
+ },
+ "users": {
+ "title": "塔拉瓦角色",
+ "joined_organizations": "加入组织",
+ "blocked_organizations": "被阻止的组织",
+ "orgJoinedBy": "加入组织",
+ "orgThatBlocked": "阻止的组织",
+ "hasNotJoinedAnyOrg": "没有加入任何组织",
+ "isNotBlockedByAnyOrg": "没有被任何组织封锁",
+ "searchByOrgName": "按组织名称搜索",
+ "view": "看法",
+ "enterName": "输入名字",
+ "loadingUsers": "正在加载用户...",
+ "noUserFound": "未找到用户",
+ "sort": "种类",
+ "Newest": "新的先来",
+ "Oldest": "最旧的在前",
+ "noOrgError": "未找到组织,请通过仪表板创建组织",
+ "roleUpdated": "角色已更新。",
+ "joinNow": "立即加入",
+ "visit": "访问",
+ "withdraw": "拉幅",
+ "removeUserFrom": "从{{org}}中删除用户",
+ "removeConfirmation": "您确定要将'{{name}}'从组织'{{org}}'中删除吗?",
+ "searchByName": "按名称搜索",
+ "users": "用户",
+ "name": "名称",
+ "email": "电子邮件",
+ "endOfResults": "结果结束",
+ "admin": "管理员",
+ "superAdmin": "超级管理员",
+ "user": "用户",
+ "filter": "筛选",
+ "noResultsFoundFor": "未找到结果",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "cancel": "取消",
+ "admins": "管理员",
+ "members": "成员",
+ "orgJoined": "已加入组织",
+ "MembershipRequestSent": "会员请求已发送",
+ "AlreadyJoined": "已加入",
+ "errorOccured": "发生错误"
+ },
+ "communityProfile": {
+ "title": "社区简介",
+ "editProfile": "编辑个人资料",
+ "communityProfileInfo": "这些详细信息将显示在您和您的社区成员的登录/注册屏幕上",
+ "communityName": "社区名字",
+ "wesiteLink": "网站链接",
+ "logo": "标识",
+ "social": "社交媒体链接",
+ "url": "输入网址",
+ "profileChangedMsg": "已成功更新个人资料详细信息。",
+ "resetData": "成功重置个人资料详细信息。"
+ },
+ "dashboard": {
+ "title": "仪表板",
+ "about": "关于",
+ "deleteThisOrganization": "删除该组织",
+ "statistics": "统计数据",
+ "posts": "帖子",
+ "events": "活动",
+ "blockedUsers": "被阻止的用户",
+ "viewAll": "查看全部",
+ "upcomingEvents": "即将举行的活动",
+ "noUpcomingEvents": "没有即将举行的活动",
+ "latestPosts": "最新帖子",
+ "noPostsPresent": "没有帖子",
+ "membershipRequests": "会员请求",
+ "noMembershipRequests": "没有会员请求",
+ "location": "位置",
+ "members": "成员",
+ "admins": "管理员",
+ "requests": "请求",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "volunteerRankings": "志愿者排名",
+ "noVolunteers": "未找到志愿者!"
+ },
+ "organizationPeople": {
+ "title": "塔拉瓦会员",
+ "filterByName": "按名称过滤",
+ "filterByLocation": "按地点过滤",
+ "filterByEvent": "按事件过滤",
+ "searchName": "输入名字",
+ "searchevent": "输入事件",
+ "searchFullName": "输入全名",
+ "people": "人们",
+ "sort": "按角色搜索",
+ "actions": "行动",
+ "addMembers": "添加会员",
+ "existingUser": "现有用户",
+ "newUser": "新用户",
+ "enterFirstName": "输入您的名字",
+ "enterLastName": "输入您的姓氏",
+ "enterConfirmPassword": "输入您的密码进行确认",
+ "organization": "组织",
+ "invalidDetailsMessage": "请输入有效的详细信息。",
+ "members": "成员",
+ "admins": "管理员",
+ "users": "用户",
+ "searchFirstName": "按名字搜索",
+ "searchLastName": "按姓氏搜索",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "emailAddress": "电子邮件地址",
+ "enterEmail": "输入你的电子邮件地址",
+ "password": "密码",
+ "enterPassword": "输入你的密码",
+ "confirmPassword": "确认密码",
+ "cancel": "取消",
+ "create": "创建",
+ "user": "用户",
+ "profile": "个人资料"
+ },
+ "organizationTags": {
+ "title": "组织标签",
+ "createTag": "创建新标签",
+ "manageTag": "管理",
+ "editTag": "编辑",
+ "removeTag": "删除",
+ "tagDetails": "标签详情",
+ "tagName": "名称",
+ "tagType": "类型",
+ "tagNamePlaceholder": "输入标签名称",
+ "tagCreationSuccess": "新标签创建成功",
+ "tagUpdationSuccess": "标签更新成功",
+ "tagRemovalSuccess": "标签删除成功",
+ "noTagsFound": "未找到标签",
+ "removeUserTag": "删除标签",
+ "removeUserTagMessage": "您确定要删除此标签吗?",
+ "addChildTag": "添加子标签",
+ "enterTagName": "输入标签名称"
+ },
+ "manageTag": {
+ "title": "标签详情",
+ "addPeopleToTag": "将人员添加到标签",
+ "viewProfile": "查看",
+ "noAssignedMembersFound": "没有分配成员",
+ "unassignUserTag": "取消分配标签",
+ "unassignUserTagMessage": "您想从此用户中删除标签吗?",
+ "successfullyUnassigned": "标签已从用户中取消分配",
+ "addPeople": "添加人员",
+ "add": "添加",
+ "subTags": "子标签",
+ "successfullyAssignedToPeople": "标签分配成功",
+ "errorOccurredWhileLoadingMembers": "加载成员时出错",
+ "userName": "用户名",
+ "actions": "操作",
+ "noOneSelected": "未选择任何人",
+ "assignToTags": "分配到标签",
+ "removeFromTags": "从标签中移除",
+ "assign": "分配",
+ "remove": "移除",
+ "successfullyAssignedToTags": "成功分配到标签",
+ "successfullyRemovedFromTags": "成功从标签中移除",
+ "errorOccurredWhileLoadingOrganizationUserTags": "加载组织标签时出错",
+ "errorOccurredWhileLoadingSubTags": "加载子标签时发生错误",
+ "removeUserTag": "删除标签",
+ "removeUserTagMessage": "您要删除此标签吗?这将删除所有子标签和所有关联。",
+ "tagDetails": "标签详情",
+ "tagName": "名称",
+ "tagUpdationSuccess": "标签更新成功",
+ "tagRemovalSuccess": "标签删除成功",
+ "noTagSelected": "未选择标签",
+ "changeNameToEdit": "更改名称以进行更新",
+ "selectTag": "选择标签",
+ "collapse": "收起",
+ "expand": "展开",
+ "tagNamePlaceholder": "输入标签名称",
+ "allTags": "所有标签",
+ "noMoreMembersFound": "未找到更多成员"
+ },
+ "userListCard": {
+ "addAdmin": "添加管理员",
+ "addedAsAdmin": "用户被添加为管理员。",
+ "joined": "已加入",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "orgAdminListCard": {
+ "remove": "消除",
+ "removeAdmin": "删除管理员",
+ "removeAdminMsg": "您想删除该管理员吗?",
+ "adminRemoved": "管理员被删除。",
+ "joined": "已加入",
+ "no": "否",
+ "yes": "是",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "orgPeopleListCard": {
+ "remove": "消除",
+ "removeMember": "删除会员",
+ "removeMemberMsg": "您想删除该成员吗?",
+ "memberRemoved": "该会员已被删除",
+ "joined": "已加入",
+ "no": "否",
+ "yes": "是",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "organizationEvents": {
+ "title": "活动",
+ "filterByTitle": "按标题过滤",
+ "filterByLocation": "按地点过滤",
+ "filterByDescription": "按描述过滤",
+ "searchMemberName": "搜索成员名称",
+ "addEvent": "添加事件",
+ "eventDetails": "活动详情",
+ "eventTitle": "标题",
+ "startTime": "开始时间",
+ "endTime": "时间结束",
+ "allDay": "一整天",
+ "recurringEvent": "重复事件",
+ "isPublic": "是公开的",
+ "isRegistrable": "可注册",
+ "createEvent": "创建事件",
+ "enterFilter": "输入过滤器",
+ "enterTitle": "输入标题",
+ "enterDescrip": "输入描述",
+ "eventLocation": "输入位置",
+ "searchEventName": "搜索活动名称",
+ "eventType": "事件类型",
+ "eventCreated": "恭喜!",
+ "customRecurrence": "自定义重复",
+ "repeatsEvery": "重复每个",
+ "repeatsOn": "重复开启",
+ "ends": "结束",
+ "never": "绝不",
+ "on": "在",
+ "after": "后",
+ "occurences": "事件",
+ "events": "活动",
+ "description": "描述",
+ "location": "位置",
+ "startDate": "开始日期",
+ "endDate": "结束日期",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "done": "完成"
+ },
+ "organizationActionItems": {
+ "actionItemCategory": "行动项类别",
+ "actionItemDetails": "行动项详情",
+ "actionItemCompleted": "行动项已完成",
+ "assignee": "受托人",
+ "assigner": "分配人",
+ "assignmentDate": "分配日期",
+ "active": "活跃",
+ "clearFilters": "清除过滤器",
+ "completionDate": "完成日期",
+ "createActionItem": "创建行动项",
+ "deleteActionItem": "删除行动项",
+ "deleteActionItemMsg": "是否要删除此行动项?",
+ "details": "详情",
+ "dueDate": "截止日期",
+ "earliest": "最早",
+ "editActionItem": "编辑行动项",
+ "isCompleted": "已完成",
+ "latest": "最新",
+ "makeActive": "激活",
+ "noActionItems": "无行动项",
+ "options": "选项",
+ "preCompletionNotes": "预完成备注",
+ "actionItemActive": "活跃的行动项",
+ "markCompletion": "标记为完成",
+ "actionItemStatus": "行动项状态",
+ "postCompletionNotes": "完成后备注",
+ "selectActionItemCategory": "选择行动项类别",
+ "selectAssignee": "选择受托人",
+ "status": "状态",
+ "successfulCreation": "行动项创建成功",
+ "successfulUpdation": "行动项更新成功",
+ "successfulDeletion": "行动项删除成功",
+ "title": "行动项",
+ "category": "类别",
+ "allottedHours": "分配的小时",
+ "latestDueDate": "最新截止日期",
+ "earliestDueDate": "最早截止日期",
+ "updateActionItem": "更新行动项",
+ "noneUpdated": "没有字段被更新",
+ "updateStatusMsg": "您确定要将此行动项标记为待处理吗?",
+ "close": "关闭",
+ "eventActionItems": "事件行动项",
+ "no": "否",
+ "yes": "是",
+ "individuals": "个人",
+ "groups": "小组",
+ "assignTo": "分配给",
+ "volunteers": "志愿者",
+ "volunteerGroups": "志愿者小组"
+ },
+ "organizationAgendaCategory": {
+ "agendaCategoryDetails": "议程类别详情",
+ "updateAgendaCategory": "更新议程类别",
+ "title": "议程类别",
+ "name": "类别",
+ "description": "描述",
+ "createdBy": "创建人",
+ "options": "选项",
+ "createAgendaCategory": "创建议程类别",
+ "noAgendaCategories": "没有议程类别",
+ "update": "更新",
+ "agendaCategoryCreated": "议程类别创建成功",
+ "agendaCategoryUpdated": "议程类别更新成功",
+ "agendaCategoryDeleted": "议程类别删除成功",
+ "deleteAgendaCategory": "删除议程类别",
+ "deleteAgendaCategoryMsg": "是否要删除此议程类别?"
+ },
+ "agendaItems": {
+ "agendaItemDetails": "议程项目详细信息",
+ "updateAgendaItem": "更新议程项目",
+ "title": "标题",
+ "enterTitle": "输入标题",
+ "sequence": "顺序",
+ "description": "描述",
+ "enterDescription": "输入描述",
+ "category": "议程类别",
+ "attachments": "附件",
+ "attachmentLimit": "添加任何图像文件或视频文件,最大 10MB",
+ "fileSizeExceedsLimit": "文件大小超过 10MB 的限制",
+ "urls": "网址",
+ "url": "添加链接到网址",
+ "enterUrl": "https://example.com",
+ "invalidUrl": "请输入有效的网址",
+ "link": "链接",
+ "createdBy": "创建人",
+ "regular": "常规",
+ "note": "注意",
+ "duration": "持续时间",
+ "enterDuration": "分:秒",
+ "options": "选项",
+ "createAgendaItem": "创建议程项目",
+ "noAgendaItems": "没有议程项目",
+ "selectAgendaItemCategory": "选择议程项目类别",
+ "update": "更新",
+ "delete": "删除",
+ "agendaItemCreated": "议程项目已成功创建",
+ "agendaItemUpdated": "议程项目已成功更新",
+ "agendaItemDeleted": "议程项目已成功删除",
+ "deleteAgendaItem": "删除议程项目",
+ "deleteAgendaItemMsg": "您要删除此议程项目吗?"
+ },
+ "eventListCard": {
+ "deleteEvent": "删除事件",
+ "deleteEventMsg": "您想删除此事件吗?",
+ "editEvent": "编辑事件",
+ "eventTitle": "标题",
+ "alreadyRegistered": "已经注册",
+ "startTime": "开始时间",
+ "endTime": "时间结束",
+ "allDay": "一整天",
+ "recurringEvent": "重复事件",
+ "isPublic": "是公开的",
+ "isRegistrable": "可注册",
+ "updatePost": "更新帖子",
+ "eventDetails": "活动详情",
+ "eventDeleted": "活动删除成功。",
+ "eventUpdated": "活动更新成功。",
+ "thisInstance": "本实例",
+ "thisAndFollowingInstances": "本实例及后续实例",
+ "allInstances": "所有实例",
+ "customRecurrence": "自定义重复",
+ "repeatsEvery": "重复每个",
+ "repeatsOn": "重复开启",
+ "ends": "结束",
+ "never": "绝不",
+ "on": "在",
+ "after": "后",
+ "occurences": "事件",
+ "location": "位置",
+ "no": "否",
+ "yes": "是",
+ "description": "描述",
+ "startDate": "开始日期",
+ "endDate": "结束日期",
+ "registerEvent": "注册活动",
+ "close": "关闭",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "done": "完成"
+ },
+ "funds": {
+ "title": "基金",
+ "createFund": "创建基金",
+ "fundName": "基金名称",
+ "fundId": "基金(参考)ID",
+ "taxDeductible": "税前扣除",
+ "default": "默认",
+ "archived": "已归档",
+ "fundCreate": "创建基金",
+ "fundUpdate": "更新基金",
+ "fundDelete": "删除基金",
+ "searchByName": "按名称搜索",
+ "noFundsFound": "未找到基金",
+ "createdBy": "由...创建",
+ "createdOn": "创建于",
+ "status": "状态",
+ "fundCreated": "基金创建成功",
+ "fundUpdated": "基金更新成功",
+ "fundDeleted": "基金删除成功",
+ "deleteFundMsg": "您确定要删除此基金吗?",
+ "createdLatest": "最近创建",
+ "createdEarliest": "最早创建",
+ "viewCampaigns": "查看活动"
+ },
+ "fundCampaign": {
+ "title": "募捐活动",
+ "campaignName": "活动名称",
+ "campaignOptions": "选项",
+ "fundingGoal": "资金目标",
+ "addCampaign": "添加活动",
+ "createdCampaign": "活动创建成功",
+ "updatedCampaign": "活动更新成功",
+ "deletedCampaign": "活动删除成功",
+ "deleteCampaignMsg": "您确定要删除此活动吗?",
+ "noCampaigns": "未找到活动",
+ "createCampaign": "创建活动",
+ "updateCampaign": "更新活动",
+ "deleteCampaign": "删除活动",
+ "currency": "货币",
+ "selectCurrency": "选择货币",
+ "searchFullName": "按名称搜索",
+ "viewPledges": "查看承诺",
+ "noCampaignsFound": "未找到活动",
+ "latestEndDate": "最新结束日期",
+ "earliestEndDate": "最早结束日期",
+ "lowestGoal": "最低目标",
+ "highestGoal": "最高目标"
+ },
+ "pledges": {
+ "title": "基金活动承诺",
+ "pledgeAmount": "质押金额",
+ "pledgeOptions": "选项",
+ "pledgeCreated": "质押创建成功",
+ "pledgeUpdated": "承诺更新成功",
+ "pledgeDeleted": "承诺删除成功",
+ "addPledge": "添加承诺",
+ "createPledge": "创建承诺",
+ "currency": "货币",
+ "selectCurrency": "选择货币",
+ "updatePledge": "更新承诺",
+ "deletePledge": "删除承诺",
+ "amount": "数量",
+ "editPledge": "编辑承诺",
+ "deletePledgeMsg": "您确定要删除此承诺吗?",
+ "noPledges": "未找到承诺",
+ "searchPledger": "按承诺搜索",
+ "highestAmount": "最高金额",
+ "lowestAmount": "最低金额",
+ "latestEndDate": "最新结束日期",
+ "earliestEndDate": "最早结束日期",
+ "campaigns": "活动",
+ "pledges": "承诺",
+ "endsOn": "结束于",
+ "raisedAmount": "募集金額",
+ "pledgedAmount": "承诺金額",
+ "startDate": "开始日期",
+ "endDate": "结束日期"
+ },
+ "orgPost": {
+ "title": "帖子",
+ "searchPost": "搜索帖子",
+ "posts": "帖子",
+ "createPost": "创建帖子",
+ "postDetails": "帖子详情",
+ "postTitle1": "写下帖子的标题",
+ "postTitle": "标题",
+ "addMedia": "上传媒体",
+ "information": "信息",
+ "information1": "填写帖子信息",
+ "addPost": "添加帖子",
+ "searchTitle": "按标题搜索",
+ "searchText": "按文本搜索",
+ "ptitle": "帖子标题",
+ "postDes": "你要聊什么?",
+ "Title": "标题",
+ "Text": "文本",
+ "searchBy": "搜索依据",
+ "Oldest": "最旧的在前",
+ "Latest": "最新第一",
+ "sortPost": "排序帖子",
+ "tag": " 您的浏览器不支持video标签",
+ "postCreatedSuccess": "恭喜!",
+ "pinPost": "针柱",
+ "Next": "下一页",
+ "Previous": "上一页",
+ "cancel": "取消"
+ },
+ "postNotFound": {
+ "post": "邮政",
+ "not found!": "未找到!",
+ "organization": "组织",
+ "post not found!": "帖子未找到!",
+ "organization not found!": "未找到组织!"
+ },
+ "userNotFound": {
+ "not found!": "未找到!",
+ "roles": "角色",
+ "user not found!": "未找到用户!",
+ "member not found!": "未找到会员!",
+ "admin not found!": "找不到管理员!",
+ "roles not found!": "未找到角色!",
+ "user": "用户"
+ },
+ "orgPostCard": {
+ "author": "作者",
+ "imageURL": "图片网址",
+ "videoURL": "视频网址",
+ "deletePost": "删除帖子",
+ "deletePostMsg": "您想删除此帖子吗?",
+ "editPost": "编辑帖子",
+ "postTitle": "标题",
+ "postTitle1": "编辑帖子标题",
+ "information1": "编辑帖子信息",
+ "information": "信息",
+ "image": "图像",
+ "video": "视频",
+ "updatePost": "更新帖子",
+ "postDeleted": "帖子删除成功。",
+ "postUpdated": "帖子更新成功。",
+ "tag": " 您的浏览器不支持video标签",
+ "pin": "针柱",
+ "edit": "编辑",
+ "no": "否",
+ "yes": "是",
+ "close": "关闭",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "blockUnblockUser": {
+ "title": "阻止/取消阻止用户",
+ "pageName": "阻止/解除阻止",
+ "listOfUsers": "发送垃圾邮件的用户列表",
+ "block_unblock": "阻止/解除阻止",
+ "unblock": "解锁",
+ "block": "堵塞",
+ "orgName": "输入名字",
+ "blockedSuccessfully": "用户被成功屏蔽",
+ "Un-BlockedSuccessfully": "用户解封成功",
+ "allMembers": "所有会员",
+ "blockedUsers": "被阻止的用户",
+ "searchByFirstName": "按名字搜索",
+ "searchByLastName": "按姓氏搜索",
+ "noSpammerFound": "未发现垃圾邮件发送者",
+ "searchByName": "按名称搜索",
+ "name": "名称",
+ "email": "电子邮件",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "noResultsFoundFor": "未找到结果"
+ },
+ "eventManagement": {
+ "title": "事件管理",
+ "dashboard": "仪表板",
+ "registrants": "注册者",
+ "attendance": "出席",
+ "actions": "操作",
+ "agendas": "议程",
+ "statistics": "统计数据",
+ "to": "到",
+ "volunteers": "志愿者"
+ },
+ "forgotPassword": {
+ "title": "塔拉瓦 忘记密码",
+ "registeredEmail": "注册的电子邮件",
+ "getOtp": "获取一次性密码",
+ "enterOtp": "输入一次性密码",
+ "enterNewPassword": "输入新密码",
+ "cofirmNewPassword": "确认新密码",
+ "changePassword": "更改密码",
+ "backToLogin": "回到登入",
+ "userOtp": "例如",
+ "emailNotRegistered": "电子邮件未注册。",
+ "errorSendingMail": "发送邮件时出错。",
+ "passwordMismatches": "密码和确认密码不匹配。",
+ "passwordChanges": "密码修改成功。",
+ "OTPsent": "OTP 已发送至您的注册邮箱。",
+ "forgotPassword": "忘记密码",
+ "password": "密码",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "pageNotFound": {
+ "404": "404",
+ "title": "404 未找到",
+ "talawaAdmin": "塔拉瓦管理员",
+ "talawaUser": "塔拉瓦用户",
+ "notFoundMsg": "哎呀!",
+ "backToHome": "返回首页"
+ },
+ "orgContribution": {
+ "title": "塔拉瓦 贡献",
+ "filterByName": "按名称过滤",
+ "filterByTransId": "按反式过滤。 ",
+ "recentStats": "最近的统计数据",
+ "contribution": "贡献",
+ "orgname": "输入名字",
+ "searchtransaction": "输入交易ID"
+ },
+ "contriStats": {
+ "recentContribution": "最近的贡献",
+ "highestContribution": "最高贡献",
+ "totalContribution": "总贡献"
+ },
+ "orgContriCards": {
+ "date": "日期",
+ "transactionId": "交易ID",
+ "amount": "数量"
+ },
+ "orgSettings": {
+ "title": "设置",
+ "general": "一般的",
+ "actionItemCategories": "行动项目类别",
+ "updateOrganization": "更新组织",
+ "seeRequest": "查看请求",
+ "noData": "没有数据",
+ "otherSettings": "其他设置",
+ "changeLanguage": "改变语言",
+ "manageCustomFields": "管理自定义字段",
+ "agendaItemCategories": "议程项目类别"
+ },
+ "deleteOrg": {
+ "deleteOrganization": "删除组织",
+ "deleteSampleOrganization": "删除样本组织",
+ "deleteMsg": "您想删除该组织吗?",
+ "confirmDelete": "确认删除",
+ "longDelOrgMsg": "通过单击“删除组织”按钮,该组织及其事件、标签和所有相关数据将被永久删除。",
+ "successfullyDeletedSampleOrganization": "已成功删除样本组织",
+ "cancel": "取消"
+ },
+ "userUpdate": {
+ "appLanguageCode": "默认语言",
+ "userType": "用户类型",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "email": "电子邮件",
+ "password": "密码",
+ "admin": "管理员",
+ "superAdmin": "超级管理员",
+ "displayImage": "显示图像",
+ "saveChanges": "保存更改",
+ "cancel": "取消"
+ },
+ "userPasswordUpdate": {
+ "previousPassword": "旧密码",
+ "newPassword": "新密码",
+ "confirmNewPassword": "确认新密码",
+ "passCantBeEmpty": "密码不能为空",
+ "passNoMatch": "新密码和确认密码不匹配。",
+ "saveChanges": "保存更改",
+ "cancel": "取消"
+ },
+ "orgDelete": {
+ "deleteOrg": "删除组织"
+ },
+ "membershipRequest": {
+ "accept": "接受",
+ "reject": "拒绝",
+ "memberAdded": "它被接受",
+ "joined": "已加入",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "orgUpdate": {
+ "city": "城市",
+ "countryCode": "国家代码",
+ "line1": "1号线",
+ "line2": "2号线",
+ "postalCode": "邮政编码",
+ "dependentLocality": "附属地点",
+ "sortingCode": "排序代码",
+ "state": "州/省",
+ "userRegistrationRequired": "需要用户注册",
+ "isVisibleInSearch": "在搜索中可见",
+ "enterNameOrganization": "输入组织名称",
+ "successfulUpdated": "组织更新成功",
+ "name": "名称",
+ "description": "描述",
+ "location": "位置",
+ "address": "地址",
+ "displayImage": "显示图像",
+ "saveChanges": "保存更改",
+ "cancel": "取消",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "addOnRegister": {
+ "addNew": "添新",
+ "addPlugin": "添加插件",
+ "pluginName": "插件名称",
+ "creatorName": "创建者姓名",
+ "pluginDesc": "插件说明",
+ "pName": "例如:捐款",
+ "cName": "例如:约翰·多伊",
+ "pDesc": "该插件启用 UI",
+ "close": "关闭",
+ "register": "注册"
+ },
+ "addOnStore": {
+ "title": "添加商店",
+ "searchName": "例如:捐款",
+ "search": "搜索",
+ "enable": "启用",
+ "disable": "残疾人",
+ "pHeading": "插件",
+ "install": "已安装",
+ "available": "可用的",
+ "pMessage": "插件不存在",
+ "filter": "筛选"
+ },
+ "addOnEntry": {
+ "enable": "启用",
+ "install": "安装",
+ "uninstall": "卸载",
+ "uninstallMsg": "此功能现已从您的组织中删除",
+ "installMsg": "您的组织现已启用此功能"
+ },
+ "memberDetail": {
+ "title": "用户详细信息",
+ "addAdmin": "添加管理员",
+ "noeventsAttended": "未参加任何活动",
+ "alreadyIsAdmin": "会员已经是管理员",
+ "organizations": "组织机构",
+ "events": "活动",
+ "role": "角色",
+ "createdOn": "创建于",
+ "main": "主要的",
+ "firstName": "名",
+ "lastName": "姓",
+ "language": "语言",
+ "gender": "性别",
+ "birthDate": "出生日期",
+ "educationGrade": "教育等级",
+ "employmentStatus": "就业状况",
+ "maritalStatus": "婚姻状况",
+ "phone": "电话",
+ "countryCode": "国家代码",
+ "state": "状态",
+ "city": "城市",
+ "personalInfoHeading": "个人信息",
+ "viewAll": "查看全部",
+ "eventsAttended": "活动参与",
+ "contactInfoHeading": "联系信息",
+ "actionsHeading": "行动",
+ "personalDetailsHeading": "个人资料详情",
+ "appLanguageCode": "选择语言",
+ "deleteUser": "删除用户",
+ "pluginCreationAllowed": "允许创建插件",
+ "created": "已创建",
+ "adminForOrganizations": "组织管理员",
+ "membershipRequests": "会员请求",
+ "adminForEvents": "活动管理员",
+ "addedAsAdmin": "用户被添加为管理员。",
+ "userType": "用户类型",
+ "email": "电子邮件",
+ "displayImage": "显示图像",
+ "address": "地址",
+ "delete": "删除",
+ "saveChanges": "保存更改",
+ "joined": "已加入",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用",
+ "unassignUserTag": "取消分配标签",
+ "unassignUserTagMessage": "您想从此用户中删除标签吗?",
+ "successfullyUnassigned": "标签已从用户中取消分配",
+ "tagsAssigned": "已分配标签",
+ "noTagsAssigned": "未分配标签"
+ },
+ "userLogin": {
+ "login": "登录",
+ "loginIntoYourAccount": "登录您的帐户",
+ "invalidDetailsMessage": "请输入有效的电子邮件和密码。",
+ "notAuthorised": "对不起!",
+ "invalidCredentials": "输入的凭据不正确。",
+ "forgotPassword": "忘记密码",
+ "emailAddress": "电子邮件地址",
+ "enterEmail": "输入电子邮件",
+ "password": "密码",
+ "enterPassword": "输入密码",
+ "register": "注册",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "people": {
+ "title": "人们",
+ "searchUsers": "搜索用户"
+ },
+ "userRegister": {
+ "enterFirstName": "输入您的名字",
+ "enterLastName": "输入您的姓氏",
+ "enterConfirmPassword": "输入您的密码进行确认",
+ "alreadyhaveAnAccount": "已经有帐户?",
+ "login": "登录",
+ "afterRegister": "注册成功。",
+ "passwordNotMatch": "密码不匹配。",
+ "invalidDetailsMessage": "请输入有效的详细信息。",
+ "register": "注册",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "emailAddress": "电子邮件地址",
+ "enterEmail": "输入电子邮件",
+ "password": "密码",
+ "enterPassword": "输入密码",
+ "confirmPassword": "确认密码",
+ "talawaApiUnavailable": "塔拉瓦 API 不可用"
+ },
+ "userNavbar": {
+ "talawa": "塔拉瓦",
+ "home": "家",
+ "people": "人们",
+ "events": "活动",
+ "chat": "聊天",
+ "donate": "捐",
+ "language": "语言",
+ "settings": "设置",
+ "logout": "退出登录",
+ "close": "关闭"
+ },
+ "userOrganizations": {
+ "allOrganizations": "所有组织",
+ "joinedOrganizations": "加入组织",
+ "createdOrganizations": "创建组织",
+ "selectOrganization": "选择一个组织",
+ "searchUsers": "搜索用户",
+ "nothingToShow": "这里没有什么可显示的。",
+ "organizations": "组织机构",
+ "search": "搜索",
+ "filter": "筛选",
+ "searchByName": "按名称搜索",
+ "searchOrganizations": "搜索组织"
+ },
+ "userSidebarOrg": {
+ "yourOrganizations": "您的组织",
+ "noOrganizations": "您还没有加入任何组织。",
+ "viewAll": "查看全部",
+ "talawaUserPortal": "塔拉瓦用户门户",
+ "my organizations": "我的组织",
+ "communityProfile": "社区简介",
+ "users": "用户",
+ "requests": "请求",
+ "logout": "登出",
+ "settings": "设置",
+ "chat": "聊天",
+ "menu": "菜单"
+ },
+ "organizationSidebar": {
+ "viewAll": "查看全部",
+ "events": "活动",
+ "noEvents": "没有可显示的活动",
+ "noMembers": "没有可显示的会员",
+ "members": "成员"
+ },
+ "postCard": {
+ "likes": "喜欢",
+ "comments": "评论",
+ "viewPost": "查看帖子",
+ "editPost": "编辑帖子",
+ "postedOn": "发布于 {{date}}"
+ },
+ "home": {
+ "title": "帖子",
+ "posts": "帖子",
+ "post": "邮政",
+ "textArea": "你有什么心事吗?",
+ "feed": "喂养",
+ "loading": "加载中",
+ "pinnedPosts": "已标记的帖子",
+ "yourFeed": "您的动态",
+ "nothingToShowHere": "这里没有可显示的内容",
+ "somethingOnYourMind": "你有什么心事吗?",
+ "addPost": "添加帖子",
+ "startPost": "开始发帖",
+ "media": "媒体",
+ "event": "事件",
+ "article": "文章",
+ "postNowVisibleInFeed": "帖子现在在动态中可见"
+ },
+ "eventAttendance": {
+ "historical_statistics": "历史统计",
+ "Search member": "搜索成员",
+ "Member Name": "成员姓名",
+ "Status": "状态",
+ "Events Attended": "参加的活动",
+ "Task Assigned": "分配的任务",
+ "Member": "成员",
+ "Admin": "管理员",
+ "loading": "加载中...",
+ "noAttendees": "未找到参与者"
+ },
+ "onSpotAttendee": {
+ "title": "现场参与者",
+ "enterFirstName": "输入名字",
+ "enterLastName": "输入姓氏",
+ "enterEmail": "输入电子邮件",
+ "enterPhoneNo": "输入电话号码",
+ "selectGender": "选择性别",
+ "invalidDetailsMessage": "请填写所有必填字段",
+ "orgIdMissing": "组织ID缺失。请重试。",
+ "attendeeAddedSuccess": "参与者添加成功!",
+ "addAttendee": "添加",
+ "phoneNumber": "电话号码",
+ "addingAttendee": "添加中...",
+ "male": "男性",
+ "female": "女性",
+ "other": "其他"
+ },
+ "settings": {
+ "noeventsAttended": "未参加任何活动",
+ "eventAttended": "参加的活动",
+ "profileSettings": "配置文件设置",
+ "gender": "性别",
+ "phoneNumber": "电话号码",
+ "chooseFile": "选择文件",
+ "birthDate": "出生日期",
+ "grade": "教育等级",
+ "empStatus": "就业状况",
+ "maritalStatus": "婚姻状况",
+ "state": "市,州",
+ "country": "国家",
+ "resetChanges": "重置更改",
+ "profileDetails": "个人资料详情",
+ "deleteUserMessage": "通过单击“删除用户”按钮,您的用户及其事件、标签和所有相关数据将被永久删除。",
+ "copyLink": "复制个人资料链接",
+ "deleteUser": "删除用户",
+ "otherSettings": "其他设置",
+ "changeLanguage": "改变语言",
+ "sgender": "选择性别",
+ "gradePlaceholder": "输入年级",
+ "sEmpStatus": "选择就业状况",
+ "female": "女性",
+ "male": "男性",
+ "employed": "就业",
+ "other": "其他",
+ "sMaritalStatus": "选择婚姻状况",
+ "unemployed": "失业",
+ "married": "已婚",
+ "single": "单身的",
+ "widowed": "寡",
+ "divorced": "离婚",
+ "engaged": "已订婚的",
+ "separated": "分离的",
+ "grade1": "1级",
+ "grade2": "二年级",
+ "grade3": "三年级",
+ "grade4": "四年级",
+ "grade5": "五年级",
+ "grade6": "6年级",
+ "grade7": "7年级",
+ "grade8": "8级",
+ "grade9": "9年级",
+ "grade10": "10年级",
+ "grade11": "11年级",
+ "grade12": "12年级",
+ "graduate": "毕业",
+ "kg": "公斤",
+ "preKg": "预幼稚园",
+ "noGrade": "无等级",
+ "fullTime": "全职",
+ "partTime": "兼职",
+ "selectCountry": "选择一个国家",
+ "enterState": "输入城市或州",
+ "settings": "设置",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "emailAddress": "电子邮件地址",
+ "displayImage": "显示图像",
+ "address": "地址",
+ "saveChanges": "保存更改",
+ "joined": "已加入"
+ },
+ "donate": {
+ "title": "捐款",
+ "donations": "捐款",
+ "searchDonations": "搜索捐款",
+ "donateForThe": "为",
+ "amount": "数量",
+ "yourPreviousDonations": "您之前的捐款",
+ "donate": "捐",
+ "nothingToShow": "这里没有什么可显示的。",
+ "success": "捐赠成功",
+ "invalidAmount": "请输入捐赠金额的数值。",
+ "donationAmountDescription": "请输入捐款金额的数值。",
+ "donationOutOfRange": "捐款金额必须在 {{min}} 和 {{max}} 之间。",
+ "donateTo": "捐赠给"
+ },
+ "userEvents": {
+ "title": "活动",
+ "nothingToShow": "这里没有什么可显示的。",
+ "createEvent": "创建事件",
+ "recurring": "重复事件",
+ "startTime": "开始时间",
+ "endTime": "时间结束",
+ "listView": "列表显示",
+ "calendarView": "日历视图",
+ "allDay": "一整天",
+ "eventCreated": "活动已成功创建并发布。",
+ "eventDetails": "活动详情",
+ "eventTitle": "标题",
+ "enterTitle": "输入标题",
+ "enterDescription": "输入描述",
+ "enterLocation": "输入位置",
+ "publicEvent": "是公开的",
+ "registerable": "可注册",
+ "monthlyCalendarView": "月历",
+ "yearlyCalendarView": "年历",
+ "search": "搜索",
+ "cancel": "取消",
+ "create": "创建",
+ "eventDescription": "活动描述",
+ "eventLocation": "活动位置",
+ "startDate": "开始日期",
+ "endDate": "结束日期"
+ },
+ "userEventCard": {
+ "starts": "开始",
+ "ends": "结束",
+ "creator": "创作者",
+ "alreadyRegistered": "已经注册",
+ "location": "位置",
+ "register": "注册"
+ },
+ "advertisement": {
+ "title": "广告",
+ "activeAds": "活跃活动",
+ "archievedAds": "已完成的活动",
+ "pMessage": "此活动中不存在广告。",
+ "validLink": "链接有效",
+ "invalidLink": "链接无效",
+ "Rname": "输入广告名称",
+ "Rtype": "选择广告类型",
+ "Rmedia": "提供要显示的媒体内容",
+ "RstartDate": "选择开始日期",
+ "RendDate": "选择结束日期",
+ "RClose": "关上窗户",
+ "addNew": "制作新广告",
+ "EXname": "前任。",
+ "EXlink": "前任。 ",
+ "createAdvertisement": "创建广告",
+ "deleteAdvertisement": "删除广告",
+ "deleteAdvertisementMsg": "您想删除该广告吗?",
+ "view": "看法",
+ "editAdvertisement": "编辑广告",
+ "advertisementDeleted": "广告已成功删除。",
+ "endDateGreaterOrEqual": "结束日期应大于或等于开始日期",
+ "advertisementCreated": "广告已成功创建。",
+ "pHeading": "广告标题",
+ "delete": "删除",
+ "close": "关闭",
+ "no": "否",
+ "yes": "是",
+ "edit": "编辑",
+ "saveChanges": "保存更改",
+ "endOfResults": "结果结束"
+ },
+ "userChat": {
+ "chat": "聊天",
+ "contacts": "联系方式",
+ "search": "搜索",
+ "messages": "消息"
+ },
+ "userChatRoom": {
+ "selectContact": "选择联系人开始对话",
+ "sendMessage": "发信息"
+ },
+ "orgProfileField": {
+ "loading": "加载中...",
+ "noCustomField": "没有可用的自定义字段",
+ "customFieldName": "字段名称",
+ "enterCustomFieldName": "输入字段名称",
+ "customFieldType": "字段类型",
+ "Remove Custom Field": "删除自定义字段",
+ "fieldSuccessMessage": "字段添加成功",
+ "fieldRemovalSuccess": "字段删除成功",
+ "String": "字符串",
+ "Boolean": "布尔值",
+ "Date": "日期",
+ "Number": "数字",
+ "saveChanges": "保存更改"
+ },
+ "orgActionItemCategories": {
+ "enableButton": "使能够",
+ "disableButton": "禁用",
+ "updateActionItemCategory": "更新",
+ "actionItemCategoryName": "姓名",
+ "categoryDetails": "类别详情",
+ "enterName": "输入名字",
+ "successfulCreation": "操作项类别创建成功",
+ "successfulUpdation": "行动项目类别已成功更新",
+ "sameNameConflict": "请更改名称以进行更新",
+ "categoryEnabled": "已启用操作项类别",
+ "categoryDisabled": "操作项类别已禁用",
+ "noActionItemCategories": "没有操作项目类别",
+ "status": "地位",
+ "categoryDeleted": "操作项目类别已成功删除",
+ "deleteCategory": "删除类别",
+ "deleteCategoryMsg": "您确定要删除此操作项目类别吗?",
+ "createButton": "创建按钮",
+ "editButton": "编辑按钮"
+ },
+ "organizationVenues": {
+ "title": "场地",
+ "addVenue": "添加场地",
+ "venueDetails": "场地详情",
+ "venueName": "场地名称",
+ "enterVenueName": "输入场地名称",
+ "enterVenueDesc": "输入场地描述",
+ "capacity": "容量",
+ "enterVenueCapacity": "输入场地容量",
+ "image": "场地图片",
+ "uploadVenueImage": "上传场地图片",
+ "createVenue": "创建场地",
+ "venueAdded": "场地添加成功",
+ "editVenue": "更新地点",
+ "venueUpdated": "场地详情更新成功",
+ "sort": "种类",
+ "highestCapacity": "最高容量",
+ "lowestCapacity": "最低容量",
+ "noVenues": "未找到场地!",
+ "view": "看法",
+ "venueTitleError": "场地名称不能为空!",
+ "venueCapacityError": "容量必须是正数!",
+ "searchBy": "搜索依据",
+ "description": "描述",
+ "edit": "编辑",
+ "delete": "删除",
+ "name": "名称",
+ "desc": "描述"
+ },
+ "addMember": {
+ "title": "添加会员",
+ "addMembers": "添加会员",
+ "existingUser": "现有用户",
+ "newUser": "新用户",
+ "searchFullName": "按全名搜索",
+ "enterFirstName": "输入名字",
+ "enterLastName": "输入姓氏",
+ "enterConfirmPassword": "输入确认密码",
+ "organization": "组织",
+ "invalidDetailsMessage": "请提供所有必需的详细信息。",
+ "passwordNotMatch": "密码不匹配。",
+ "firstName": "名字",
+ "lastName": "姓氏",
+ "emailAddress": "电子邮件地址",
+ "enterEmail": "输入电子邮件",
+ "password": "密码",
+ "enterPassword": "输入密码",
+ "confirmPassword": "确认密码",
+ "cancel": "取消",
+ "create": "创建",
+ "addMember": "添加会员",
+ "user": "用户",
+ "profile": "个人资料"
+ },
+ "eventActionItems": {
+ "title": "行动项目",
+ "createActionItem": "创建行动项目",
+ "actionItemCategory": "行动项目类别",
+ "selectActionItemCategory": "选择操作项类别",
+ "selectAssignee": "选择受托人",
+ "preCompletionNotes": "笔记",
+ "postCompletionNotes": "完成说明",
+ "actionItemDetails": "行动项目详情",
+ "dueDate": "到期日",
+ "completionDate": "完成日期",
+ "editActionItem": "编辑操作项",
+ "deleteActionItem": "删除操作项",
+ "deleteActionItemMsg": "您想删除此操作项吗?",
+ "successfulDeletion": "操作项已成功删除",
+ "successfulCreation": "操作项创建成功",
+ "successfulUpdation": "操作项已成功更新",
+ "notes": "笔记",
+ "assignee": "受让人",
+ "assigner": "分配者",
+ "assignmentDate": "任务分配日期",
+ "status": "地位",
+ "actionItemActive": "积极的",
+ "actionItemStatus": "行动项目状态",
+ "actionItemCompleted": "行动项目已完成",
+ "markCompletion": "标记完成",
+ "save": "节省",
+ "yes": "是",
+ "no": "否"
+ },
+ "checkIn": {
+ "errorCheckingIn": "签到错误",
+ "checkedInSuccessfully": "成功签到"
+ },
+ "eventRegistrantsModal": {
+ "errorAddingAttendee": "添加与会者时出错",
+ "errorRemovingAttendee": "删除与会者时出错"
+ },
+ "userCampaigns": {
+ "title": "筹款活动",
+ "searchByName": "按名字搜索...",
+ "searchBy": "按...搜索",
+ "pledgers": "承诺者",
+ "campaigns": "活动",
+ "myPledges": "我的承诺",
+ "lowestAmount": "最低金额",
+ "highestAmount": "最高金额",
+ "lowestGoal": "最低目标",
+ "highestGoal": "最高目标",
+ "latestEndDate": "最晚结束日期",
+ "earliestEndDate": "最早结束日期",
+ "addPledge": "添加承诺",
+ "viewPledges": "查看承诺",
+ "noPledges": "未找到承诺",
+ "noCampaigns": "未找到活动"
+ },
+ "userPledges": {
+ "title": "我的承诺"
+ },
+ "eventVolunteers": {
+ "volunteers": "志愿者",
+ "volunteer": "志愿者",
+ "volunteerGroups": "志愿者小组",
+ "individuals": "个人",
+ "groups": "小组",
+ "status": "状态",
+ "noVolunteers": "无志愿者",
+ "noVolunteerGroups": "无志愿者小组",
+ "add": "添加",
+ "mostHoursVolunteered": "最多志愿时数",
+ "leastHoursVolunteered": "最少志愿时数",
+ "accepted": "已接受",
+ "addVolunteer": "添加志愿者",
+ "removeVolunteer": "移除志愿者",
+ "volunteerAdded": "志愿者已成功添加",
+ "volunteerRemoved": "志愿者已成功移除",
+ "volunteerGroupCreated": "志愿者小组已成功创建",
+ "volunteerGroupUpdated": "志愿者小组已成功更新",
+ "volunteerGroupDeleted": "志愿者小组已成功删除",
+ "removeVolunteerMsg": "您确定要移除此志愿者吗?",
+ "deleteVolunteerGroupMsg": "您确定要删除此志愿者小组吗?",
+ "leader": "领导",
+ "group": "小组",
+ "createGroup": "创建小组",
+ "updateGroup": "更新小组",
+ "deleteGroup": "删除小组",
+ "volunteersRequired": "需要志愿者",
+ "volunteerDetails": "志愿者详情",
+ "hoursVolunteered": "志愿时数",
+ "groupDetails": "小组详情",
+ "creator": "创建者",
+ "requests": "请求",
+ "noRequests": "无请求",
+ "latest": "最新",
+ "earliest": "最早",
+ "requestAccepted": "请求已成功接受",
+ "requestRejected": "请求已成功拒绝",
+ "details": "详情",
+ "manageGroup": "管理小组",
+ "mostVolunteers": "最多的志愿者",
+ "leastVolunteers": "最少的志愿者"
+ },
+ "userVolunteer": {
+ "title": "志愿服务",
+ "name": "标题",
+ "upcomingEvents": "即将举行的活动",
+ "requests": "请求",
+ "invitations": "邀请",
+ "groups": "志愿者小组",
+ "actions": "操作",
+ "searchByName": "按名称搜索",
+ "latestEndDate": "最晚结束日期",
+ "earliestEndDate": "最早结束日期",
+ "noEvents": "无即将举行的活动",
+ "volunteer": "志愿者",
+ "volunteered": "已志愿",
+ "join": "加入",
+ "joined": "已加入",
+ "searchByEventName": "按活动标题搜索",
+ "filter": "筛选",
+ "groupInvite": "小组邀请",
+ "individualInvite": "个人邀请",
+ "noInvitations": "无邀请",
+ "accept": "接受",
+ "reject": "拒绝",
+ "receivedLatest": "最近收到",
+ "receivedEarliest": "最早收到",
+ "invitationAccepted": "邀请已成功接受",
+ "invitationRejected": "邀请已成功拒绝",
+ "volunteerSuccess": "志愿请求成功",
+ "recurring": "重复",
+ "groupInvitationSubject": "邀请加入志愿者小组",
+ "eventInvitationSubject": "邀请参加活动志愿服务"
+ }
+}
diff --git a/public/logo192.png b/public/logo192.png
deleted file mode 100644
index fc44b0a379..0000000000
Binary files a/public/logo192.png and /dev/null differ
diff --git a/public/manifest.json b/public/manifest.json
index 080d6c77ac..23e6e6efe4 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -10,10 +10,10 @@
{
"src": "logo192.png",
"type": "image/png",
- "sizes": "192x192"
+ "sizes": "16x16"
},
{
- "src": "logo512.png",
+ "src": "images/logo512.png",
"type": "image/png",
"sizes": "512x512"
}
diff --git a/public/markdown/images/install1.png b/public/markdown/images/install1.png
new file mode 100644
index 0000000000..632cb4f7a5
Binary files /dev/null and b/public/markdown/images/install1.png differ
diff --git a/public/markdown/images/install2.png b/public/markdown/images/install2.png
new file mode 100644
index 0000000000..308cfdbd56
Binary files /dev/null and b/public/markdown/images/install2.png differ
diff --git a/schema.graphql b/schema.graphql
new file mode 100644
index 0000000000..a0ff7bde8b
--- /dev/null
+++ b/schema.graphql
@@ -0,0 +1,1537 @@
+directive @auth on FIELD_DEFINITION
+
+directive @role(requires: UserType) on FIELD_DEFINITION
+
+type ActionItem {
+ _id: ID!
+ actionItemCategory: ActionItemCategory
+ assignee: User
+ assigner: User
+ assignmentDate: Date!
+ completionDate: Date!
+ createdAt: Date!
+ creator: User
+ dueDate: Date!
+ event: Event
+ isCompleted: Boolean!
+ postCompletionNotes: String
+ preCompletionNotes: String
+ updatedAt: Date!
+}
+
+type ActionItemCategory {
+ _id: ID!
+ createdAt: Date!
+ creator: User
+ isDisabled: Boolean!
+ name: String!
+ organization: Organization
+ updatedAt: Date!
+}
+
+type Address {
+ city: String
+ countryCode: String
+ dependentLocality: String
+ line1: String
+ line2: String
+ postalCode: String
+ sortingCode: String
+ state: String
+}
+
+input AddressInput {
+ city: String
+ countryCode: String
+ dependentLocality: String
+ line1: String
+ line2: String
+ postalCode: String
+ sortingCode: String
+ state: String
+}
+
+type Advertisement {
+ _id: ID!
+ createdAt: DateTime!
+ creator: User
+ endDate: Date!
+ mediaUrl: URL!
+ name: String!
+ orgId: ID!
+ startDate: Date!
+ type: AdvertisementType!
+ updatedAt: DateTime!
+}
+
+enum AdvertisementType {
+ BANNER
+ MENU
+ POPUP
+}
+
+type AdvertisementEdge {
+ cursor: String
+ node: Advertisement
+}
+
+type AdvertisementsConnection {
+ edges: [AdvertisementEdge]
+ pageInfo: DefaultConnectionPageInfo
+ totalCount: Int
+}
+
+type AgendaCategory {
+ _id: ID!
+ createdAt: Date!
+ createdBy: User!
+ description: String
+ name: String!
+ organization: Organization!
+ updatedAt: Date
+ updatedBy: User
+}
+
+type AggregatePost {
+ count: Int!
+}
+
+type AggregateUser {
+ count: Int!
+}
+
+scalar Any
+
+type AppUserProfile {
+ _id: ID!
+ adminFor: [Organization]
+ appLanguageCode: String!
+ createdEvents: [Event]
+ createdOrganizations: [Organization]
+ eventAdmin: [Event]
+ isSuperAdmin: Boolean!
+ pluginCreationAllowed: Boolean!
+ userId: User!
+}
+
+type AuthData {
+ accessToken: String!
+ appUserProfile: AppUserProfile!
+ refreshToken: String!
+ user: User!
+}
+
+type CheckIn {
+ _id: ID!
+ allotedRoom: String
+ allotedSeat: String
+ createdAt: DateTime!
+ event: Event!
+ feedbackSubmitted: Boolean!
+ time: DateTime!
+ updatedAt: DateTime!
+ user: User!
+}
+
+input CheckInInput {
+ allotedRoom: String
+ allotedSeat: String
+ eventId: ID!
+ userId: ID!
+}
+
+type CheckInStatus {
+ _id: ID!
+ checkIn: CheckIn
+ user: User!
+}
+
+type Comment {
+ _id: ID!
+ createdAt: DateTime!
+ creator: User
+ likeCount: Int
+ likedBy: [User]
+ post: Post!
+ text: String!
+ updatedAt: DateTime!
+}
+
+input CommentInput {
+ text: String!
+}
+
+type Community {
+ _id: ID!
+ logoUrl: String
+ name: String!
+ socialMediaUrls: SocialMediaUrls
+ timeout: Int
+ websiteLink: String
+}
+
+union ConnectionError = InvalidCursor | MaximumValueError
+
+type ConnectionPageInfo {
+ endCursor: String
+ hasNextPage: Boolean!
+ hasPreviousPage: Boolean!
+ startCursor: String
+}
+
+scalar CountryCode
+
+input CreateActionItemInput {
+ assigneeId: ID!
+ dueDate: Date
+ eventId: ID
+ preCompletionNotes: String
+}
+
+input CreateAgendaCategoryInput {
+ description: String
+ name: String!
+ organizationId: ID!
+}
+
+input CreateUserTagInput {
+ name: String!
+ organizationId: ID!
+ parentTagId: ID
+}
+
+input CursorPaginationInput {
+ cursor: String
+ direction: PaginationDirection!
+ limit: PositiveInt!
+}
+
+scalar Date
+
+scalar DateTime
+
+type DeletePayload {
+ success: Boolean!
+}
+
+type Donation {
+ _id: ID!
+ amount: Float!
+ createdAt: DateTime!
+ nameOfOrg: String!
+ nameOfUser: String!
+ orgId: ID!
+ payPalId: String!
+ updatedAt: DateTime!
+ userId: ID!
+}
+
+input DonationWhereInput {
+ id: ID
+ id_contains: ID
+ id_in: [ID!]
+ id_not: ID
+ id_not_in: [ID!]
+ id_starts_with: ID
+ name_of_user: String
+ name_of_user_contains: String
+ name_of_user_in: [String!]
+ name_of_user_not: String
+ name_of_user_not_in: [String!]
+ name_of_user_starts_with: String
+}
+
+enum EducationGrade {
+ GRADE_1
+ GRADE_2
+ GRADE_3
+ GRADE_4
+ GRADE_5
+ GRADE_6
+ GRADE_7
+ GRADE_8
+ GRADE_9
+ GRADE_10
+ GRADE_11
+ GRADE_12
+ GRADUATE
+ KG
+ NO_GRADE
+ PRE_KG
+}
+
+scalar EmailAddress
+
+enum EmploymentStatus {
+ FULL_TIME
+ PART_TIME
+ UNEMPLOYED
+}
+
+interface Error {
+ message: String!
+}
+
+type Event {
+ _id: ID!
+ actionItems: [ActionItem]
+ admins(adminId: ID): [User!]
+ allDay: Boolean!
+ attendees: [User]
+ attendeesCheckInStatus: [CheckInStatus!]!
+ averageFeedbackScore: Float
+ createdAt: DateTime!
+ creator: User
+ description: String!
+ endDate: Date
+ endTime: Time
+ feedback: [Feedback!]!
+ isPublic: Boolean!
+ isRegisterable: Boolean!
+ latitude: Latitude
+ location: String
+ longitude: Longitude
+ organization: Organization
+ recurrance: Recurrance
+ recurring: Boolean!
+ startDate: Date!
+ startTime: Time
+ status: Status!
+ title: String!
+ updatedAt: DateTime!
+}
+
+input EventAttendeeInput {
+ eventId: ID!
+ userId: ID!
+}
+
+input EventInput {
+ allDay: Boolean!
+ description: String!
+ endDate: Date
+ endTime: Time
+ isPublic: Boolean!
+ isRegisterable: Boolean!
+ latitude: Latitude
+ location: String
+ longitude: Longitude
+ organizationId: ID!
+ recurrance: Recurrance
+ recurring: Boolean!
+ startDate: Date!
+ startTime: Time
+ title: String!
+}
+
+enum EventOrderByInput {
+ allDay_ASC
+ allDay_DESC
+ description_ASC
+ description_DESC
+ endDate_ASC
+ endDate_DESC
+ endTime_ASC
+ endTime_DESC
+ id_ASC
+ id_DESC
+ location_ASC
+ location_DESC
+ recurrance_ASC
+ recurrance_DESC
+ startDate_ASC
+ startDate_DESC
+ startTime_ASC
+ startTime_DESC
+ title_ASC
+ title_DESC
+}
+
+type EventVolunteer {
+ _id: ID!
+ createdAt: DateTime!
+ creator: User
+ event: Event
+ isAssigned: Boolean
+ isInvited: Boolean
+ response: String
+ updatedAt: DateTime!
+ user: User!
+}
+
+input EventVolunteerInput {
+ eventId: ID!
+ userId: ID!
+}
+
+enum EventVolunteerResponse {
+ NO
+ YES
+}
+
+input EventWhereInput {
+ description: String
+ description_contains: String
+ description_in: [String!]
+ description_not: String
+ description_not_in: [String!]
+ description_starts_with: String
+ id: ID
+ id_contains: ID
+ id_in: [ID!]
+ id_not: ID
+ id_not_in: [ID!]
+ id_starts_with: ID
+ location: String
+ location_contains: String
+ location_in: [String!]
+ location_not: String
+ location_not_in: [String!]
+ location_starts_with: String
+ organization_id: ID
+ title: String
+ title_contains: String
+ title_in: [String!]
+ title_not: String
+ title_not_in: [String!]
+ title_starts_with: String
+}
+
+type ExtendSession {
+ accessToken: String!
+ refreshToken: String!
+}
+
+type Feedback {
+ _id: ID!
+ createdAt: DateTime!
+ event: Event!
+ rating: Int!
+ review: String
+ updatedAt: DateTime!
+}
+
+input FeedbackInput {
+ eventId: ID!
+ rating: Int!
+ review: String
+}
+
+interface FieldError {
+ message: String!
+ path: [String!]!
+}
+
+input ForgotPasswordData {
+ newPassword: String!
+ otpToken: String!
+ userOtp: String!
+}
+
+enum Frequency {
+ DAILY
+ MONTHLY
+ WEEKLY
+ YEARLY
+}
+
+enum Gender {
+ FEMALE
+ MALE
+ OTHER
+}
+
+type Group {
+ _id: ID!
+ admins: [User!]!
+ createdAt: DateTime!
+ description: String
+ organization: Organization!
+ title: String!
+ updatedAt: DateTime!
+}
+
+type InvalidCursor implements FieldError {
+ message: String!
+ path: [String!]!
+}
+
+scalar JSON
+
+type Language {
+ _id: ID!
+ createdAt: String!
+ en: String!
+ translation: [LanguageModel]
+}
+
+input LanguageInput {
+ en_value: String!
+ translation_lang_code: String!
+ translation_value: String!
+}
+
+type LanguageModel {
+ _id: ID!
+ createdAt: DateTime!
+ lang_code: String!
+ value: String!
+ verified: Boolean!
+}
+
+scalar Latitude
+
+input LoginInput {
+ email: EmailAddress!
+ password: String!
+}
+
+scalar Longitude
+
+enum MaritalStatus {
+ DIVORCED
+ ENGAGED
+ MARRIED
+ SEPERATED
+ SINGLE
+ WIDOWED
+}
+
+type Chat {
+ _id: ID!
+ isGroup: Boolean!
+ name: String
+ createdAt: DateTime!
+ creator: User
+ messages: [ChatMessage]
+ organization: Organization
+ updatedAt: DateTime!
+ users: [User!]!
+ admins: [User]
+ lastMessageId: String
+}
+
+type ChatMessage {
+ _id: ID!
+ createdAt: DateTime!
+ chatMessageBelongsTo: Chat!
+ messageContent: String!
+ type: String!
+ replyTo: ChatMessage
+ sender: User!
+ deletedBy: [User]
+ updatedAt: DateTime!
+}
+
+type MaximumLengthError implements FieldError {
+ message: String!
+ path: [String!]!
+}
+
+type MaximumValueError implements FieldError {
+ limit: Int!
+ message: String!
+ path: [String!]!
+}
+
+type MembershipRequest {
+ _id: ID!
+ organization: Organization!
+ user: User!
+}
+
+type MinimumLengthError implements FieldError {
+ limit: Int!
+ message: String!
+ path: [String!]!
+}
+
+type MinimumValueError implements FieldError {
+ message: String!
+ path: [String!]!
+}
+
+enum AdvertisementType {
+ BANNER
+ MENU
+ POPUP
+}
+
+input CreateAdvertisementInput {
+ endDate: Date!
+ name: String!
+ organizationId: ID!
+ startDate: Date!
+ type: AdvertisementType!
+ mediaFile: String!
+}
+
+type CreateAdvertisementPayload {
+ advertisement: Advertisement
+}
+
+input EditVenueInput {
+ capacity: Int
+ description: String
+ file: String
+ id: ID!
+ name: String
+}
+
+type Mutation {
+ acceptMembershipRequest(membershipRequestId: ID!): MembershipRequest!
+ addEventAttendee(data: EventAttendeeInput!): User!
+ addFeedback(data: FeedbackInput!): Feedback!
+ addLanguageTranslation(data: LanguageInput!): Language!
+ addOrganizationCustomField(
+ name: String!
+ organizationId: ID!
+ type: String!
+ ): OrganizationCustomField!
+ addOrganizationImage(file: String!, organizationId: String!): Organization!
+ addUserCustomData(
+ dataName: String!
+ dataValue: Any!
+ organizationId: ID!
+ ): UserCustomData!
+ addUserImage(file: String!): User!
+ addUserToUserFamily(familyId: ID!, userId: ID!): UserFamily!
+ adminRemoveEvent(eventId: ID!): Event!
+ assignUserTag(input: ToggleUserTagAssignInput!): User
+ blockPluginCreationBySuperadmin(
+ blockUser: Boolean!
+ userId: ID!
+ ): AppUserProfile!
+ blockUser(organizationId: ID!, userId: ID!): User!
+ cancelMembershipRequest(membershipRequestId: ID!): MembershipRequest!
+ checkIn(data: CheckInInput!): CheckIn!
+ createActionItem(
+ actionItemCategoryId: ID!
+ data: CreateActionItemInput!
+ ): ActionItem!
+ createActionItemCategory(
+ isDisabled: Boolean!
+ name: String!
+ organizationId: ID!
+ ): ActionItemCategory!
+ createAdmin(data: UserAndOrganizationInput!): AppUserProfile!
+ createAdvertisement(
+ endDate: Date!
+ link: String!
+ name: String!
+ orgId: ID!
+ startDate: Date!
+ type: String!
+ ): Advertisement!
+ createAgendaCategory(input: CreateAgendaCategoryInput!): AgendaCategory!
+ createComment(data: CommentInput!, postId: ID!): Comment
+ createChat(data: chatInput!): Chat!
+ createDonation(
+ amount: Float!
+ nameOfOrg: String!
+ nameOfUser: String!
+ orgId: ID!
+ payPalId: ID!
+ userId: ID!
+ ): Donation!
+ createEvent(
+ data: EventInput!
+ recurrenceRuleData: RecurrenceRuleInput
+ ): Event!
+ createEventVolunteer(data: EventVolunteerInput!): EventVolunteer!
+ createMember(input: UserAndOrganizationInput!): Organization!
+ createOrganization(data: OrganizationInput, file: String): Organization!
+ createPlugin(
+ pluginCreatedBy: String!
+ pluginDesc: String!
+ pluginName: String!
+ uninstalledOrgs: [ID!]
+ ): Plugin!
+ createPost(data: PostInput!, file: String): Post
+ createSampleOrganization: Boolean!
+ createUserFamily(data: createUserFamilyInput!): UserFamily!
+ createUserTag(input: CreateUserTagInput!): UserTag
+ createVenue(data: VenueInput!): Venue
+ deleteAdvertisement(id: ID!): DeletePayload!
+ deleteAdvertisementById(id: ID!): DeletePayload!
+ deleteAgendaCategory(id: ID!): ID!
+ deleteDonationById(id: ID!): DeletePayload!
+ deleteVenue(id: ID!): Venue
+ editVenue(data: EditVenueInput!): Venue
+ forgotPassword(data: ForgotPasswordData!): Boolean!
+ joinPublicOrganization(organizationId: ID!): User!
+ leaveOrganization(organizationId: ID!): User!
+ likeComment(id: ID!): Comment
+ likePost(id: ID!): Post
+ login(data: LoginInput!): AuthData!
+ logout: Boolean!
+ otp(data: OTPInput!): OtpData!
+ recaptcha(data: RecaptchaVerification!): Boolean!
+ refreshToken(refreshToken: String!): ExtendSession!
+ registerForEvent(id: ID!): Event!
+ rejectMembershipRequest(membershipRequestId: ID!): MembershipRequest!
+ removeActionItem(id: ID!): ActionItem!
+ removeAdmin(data: UserAndOrganizationInput!): AppUserProfile!
+ removeAdvertisement(id: ID!): Advertisement
+ removeComment(id: ID!): Comment
+ removeEvent(id: ID!): Event!
+ removeEventAttendee(data: EventAttendeeInput!): User!
+ removeEventVolunteer(id: ID!): EventVolunteer!
+ removeMember(data: UserAndOrganizationInput!): Organization!
+ removeOrganization(id: ID!): UserData!
+ removeOrganizationCustomField(
+ customFieldId: ID!
+ organizationId: ID!
+ ): OrganizationCustomField!
+ removeOrganizationImage(organizationId: String!): Organization!
+ removePost(id: ID!): Post
+ removeSampleOrganization: Boolean!
+ removeUserCustomData(organizationId: ID!): UserCustomData!
+ removeUserFamily(familyId: ID!): UserFamily!
+ removeUserFromUserFamily(familyId: ID!, userId: ID!): UserFamily!
+ removeUserImage: User!
+ removeUserTag(id: ID!): UserTag
+ revokeRefreshTokenForUser: Boolean!
+ saveFcmToken(token: String): Boolean!
+ sendMembershipRequest(organizationId: ID!): MembershipRequest!
+ sendMessageToChat(chatId: ID!, messageContent: String!, type: String!, replyTo: ID): ChatMessage!
+ signUp(data: UserInput!, file: String): AuthData!
+ togglePostPin(id: ID!, title: String): Post!
+ unassignUserTag(input: ToggleUserTagAssignInput!): User
+ unblockUser(organizationId: ID!, userId: ID!): User!
+ unlikeComment(id: ID!): Comment
+ unlikePost(id: ID!): Post
+ unregisterForEventByUser(id: ID!): Event!
+ updateActionItem(data: UpdateActionItemInput!, id: ID!): ActionItem
+ updateActionItemCategory(
+ data: UpdateActionItemCategoryInput!
+ id: ID!
+ ): ActionItemCategory
+ updateAdvertisement(
+ input: UpdateAdvertisementInput!
+ ): UpdateAdvertisementPayload
+ updateAgendaCategory(
+ id: ID!
+ input: UpdateAgendaCategoryInput!
+ ): AgendaCategory
+ updateEvent(data: UpdateEventInput, id: ID!): Event!
+ updateEventVolunteer(
+ data: UpdateEventVolunteerInput
+ id: ID!
+ ): EventVolunteer!
+ updateLanguage(languageCode: String!): User!
+ updateOrganization(
+ data: UpdateOrganizationInput
+ file: String
+ id: ID!
+ ): Organization!
+ updatePluginStatus(id: ID!, orgId: ID!): Plugin!
+ updatePost(data: PostUpdateInput, id: ID!): Post!
+ updateSessionTimeout(timeout: Int!): Boolean!
+ updateUserPassword(data: UpdateUserPasswordInput!): UserData!
+ updateUserProfile(data: UpdateUserInput, file: String): User!
+ updateUserRoleInOrganization(
+ organizationId: ID!
+ role: String!
+ userId: ID!
+ ): Organization!
+ updateUserTag(input: UpdateUserTagInput!): UserTag
+ updateUserType(data: UpdateUserTypeInput!): Boolean!
+ venues: [Venue]
+}
+
+input OTPInput {
+ email: EmailAddress!
+}
+
+type Organization {
+ _id: ID!
+ actionItemCategories: [ActionItemCategory]
+ address: Address
+ admins(adminId: ID): [User!]
+ agendaCategories: [AgendaCategory]
+ apiUrl: URL!
+ blockedUsers: [User]
+ createdAt: DateTime!
+ creator: User
+ customFields: [OrganizationCustomField!]!
+ description: String!
+ image: String
+ members: [User]
+ membershipRequests: [MembershipRequest]
+ name: String!
+ pinnedPosts: [Post]
+ updatedAt: DateTime!
+ userRegistrationRequired: Boolean!
+ userTags(
+ after: String
+ before: String
+ first: PositiveInt
+ last: PositiveInt
+ ): UserTagsConnection
+ visibleInSearch: Boolean!
+ venues: [Venue]
+}
+
+type OrganizationCustomField {
+ _id: ID!
+ name: String!
+ organizationId: String!
+ type: String!
+}
+
+type OrganizationCustomField {
+ _id: ID!
+ name: String!
+ organizationId: String!
+ type: String!
+}
+
+type OrganizationInfoNode {
+ _id: ID!
+ apiUrl: URL!
+ creator: User
+ description: String!
+ image: String
+ name: String!
+ userRegistrationRequired: Boolean!
+ visibleInSearch: Boolean!
+}
+
+input OrganizationInput {
+ address: AddressInput!
+ apiUrl: URL
+ attendees: String
+ description: String!
+ image: String
+ name: String!
+ userRegistrationRequired: Boolean
+ visibleInSearch: Boolean
+}
+
+enum OrganizationOrderByInput {
+ apiUrl_ASC
+ apiUrl_DESC
+ createdAt_ASC
+ createdAt_DESC
+ description_ASC
+ description_DESC
+ id_ASC
+ id_DESC
+ name_ASC
+ name_DESC
+}
+
+input OrganizationWhereInput {
+ apiUrl: URL
+ apiUrl_contains: URL
+ apiUrl_in: [URL!]
+ apiUrl_not: URL
+ apiUrl_not_in: [URL!]
+ apiUrl_starts_with: URL
+ description: String
+ description_contains: String
+ description_in: [String!]
+ description_not: String
+ description_not_in: [String!]
+ description_starts_with: String
+ id: ID
+ id_contains: ID
+ id_in: [ID!]
+ id_not: ID
+ id_not_in: [ID!]
+ id_starts_with: ID
+ name: String
+ name_contains: String
+ name_in: [String!]
+ name_not: String
+ name_not_in: [String!]
+ name_starts_with: String
+ userRegistrationRequired: Boolean
+ visibleInSearch: Boolean
+}
+
+type OtpData {
+ otpToken: String!
+}
+
+"""
+Information about pagination in a connection.
+"""
+type PageInfo {
+ currPageNo: Int
+
+ """
+ When paginating forwards, are there more items?
+ """
+ hasNextPage: Boolean!
+
+ """
+ When paginating backwards, are there more items?
+ """
+ hasPreviousPage: Boolean!
+ nextPageNo: Int
+ prevPageNo: Int
+ totalPages: Int
+}
+
+enum PaginationDirection {
+ BACKWARD
+ FORWARD
+}
+
+scalar PhoneNumber
+
+type Plugin {
+ _id: ID!
+ pluginCreatedBy: String!
+ pluginDesc: String!
+ pluginName: String!
+ uninstalledOrgs: [ID!]
+}
+
+type PluginField {
+ createdAt: DateTime!
+ key: String!
+ status: Status!
+ value: String!
+}
+
+input PluginFieldInput {
+ key: String!
+ value: String!
+}
+
+input PluginInput {
+ fields: [PluginFieldInput]
+ orgId: ID!
+ pluginKey: String
+ pluginName: String!
+ pluginType: Type
+}
+
+scalar PositiveInt
+
+type Post {
+ _id: ID
+ commentCount: Int
+ comments: [Comment]
+ createdAt: DateTime!
+ creator: User
+ imageUrl: URL
+ likeCount: Int
+ likedBy: [User]
+ organization: Organization!
+ pinned: Boolean
+ text: String!
+ title: String
+ updatedAt: DateTime!
+ videoUrl: URL
+}
+
+"""
+A connection to a list of items.
+"""
+type PostConnection {
+ aggregate: AggregatePost!
+
+ """
+ A list of edges.
+ """
+ edges: [Post]!
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+input PostInput {
+ _id: ID
+ imageUrl: URL
+ organizationId: ID!
+ pinned: Boolean
+ text: String!
+ title: String
+ videoUrl: URL
+}
+
+enum PostOrderByInput {
+ commentCount_ASC
+ commentCount_DESC
+ createdAt_ASC
+ createdAt_DESC
+ id_ASC
+ id_DESC
+ imageUrl_ASC
+ imageUrl_DESC
+ likeCount_ASC
+ likeCount_DESC
+ text_ASC
+ text_DESC
+ title_ASC
+ title_DESC
+ videoUrl_ASC
+ videoUrl_DESC
+}
+
+input PostUpdateInput {
+ imageUrl: String
+ text: String
+ title: String
+ videoUrl: String
+}
+
+input PostWhereInput {
+ id: ID
+ id_contains: ID
+ id_in: [ID!]
+ id_not: ID
+ id_not_in: [ID!]
+ id_starts_with: ID
+ text: String
+ text_contains: String
+ text_in: [String!]
+ text_not: String
+ text_not_in: [String!]
+ text_starts_with: String
+ title: String
+ title_contains: String
+ title_in: [String!]
+ title_not: String
+ title_not_in: [String!]
+ title_starts_with: String
+}
+
+type Query {
+ actionItem(id: ID!): ActionItem
+ actionItemCategoriesByOrganization(organizationId: ID!): [ActionItemCategory]
+ actionItemCategory(id: ID!): ActionItemCategory
+ actionItemsByEvent(eventId: ID!): [ActionItem]
+ actionItemsByOrganization(organizationId: ID!): [ActionItem]
+ adminPlugin(orgId: ID!): [Plugin]
+ agendaCategory(id: ID!): AgendaCategory!
+ checkAuth: User!
+ customDataByOrganization(organizationId: ID!): [UserCustomData!]!
+ customFieldsByOrganization(id: ID!): [OrganizationCustomField]
+ chatById(id: ID!): Chat!
+ chatsByUserId(id: ID!): [Chat]
+ event(id: ID!): Event
+ eventsAttendedByUser(id: ID, orderBy: EventOrderByInput): [Event]
+ eventVolunteersByEvent(id: ID!): [EventVolunteer]
+ eventsByOrganization(id: ID, orderBy: EventOrderByInput): [Event]
+ eventsByOrganizationConnection(
+ first: Int
+ orderBy: EventOrderByInput
+ skip: Int
+ where: EventWhereInput
+ ): [Event!]!
+ advertisementsConnection(after: String, before: String, first: PositiveInt, last: PositiveInt): AdvertisementsConnection
+ getDonationById(id: ID!): Donation!
+ getDonationByOrgId(orgId: ID!): [Donation]
+ getDonationByOrgIdConnection(
+ first: Int
+ orgId: ID!
+ skip: Int
+ where: DonationWhereInput
+ ): [Donation!]!
+ getPlugins: [Plugin]
+ getlanguage(lang_code: String!): [Translation]
+ hasSubmittedFeedback(eventId: ID!, userId: ID!): Boolean
+ isSampleOrganization(id: ID!): Boolean!
+ joinedOrganizations(id: ID): [Organization]
+ me: UserData!
+ myLanguage: String
+ fundsByOrganization(organizationId: ID!, where: FundWhereInput): [Fund]
+ organizations(id: ID, orderBy: OrganizationOrderByInput): [Organization]
+ organizationsConnection(
+ first: Int
+ orderBy: OrganizationOrderByInput
+ skip: Int
+ where: OrganizationWhereInput
+ ): [Organization]!
+ organizationsMemberConnection(
+ first: Int
+ orderBy: UserOrderByInput
+ orgId: ID!
+ skip: Int
+ where: UserWhereInput
+ ): UserConnection!
+ plugin(orgId: ID!): [Plugin]
+ getRecurringEvents(baseRecurringEventId: ID!): [Event]
+ post(id: ID!): Post
+ postsByOrganization(id: ID!, orderBy: PostOrderByInput): [Post]
+ postsByOrganizationConnection(
+ first: Int
+ id: ID!
+ orderBy: PostOrderByInput
+ skip: Int
+ where: PostWhereInput
+ ): PostConnection
+ registeredEventsByUser(id: ID, orderBy: EventOrderByInput): [Event]
+ registrantsByEvent(id: ID!): [User]
+ user(id: ID!): UserData!
+ userLanguage(userId: ID!): String
+ users(
+ first: Int
+ orderBy: UserOrderByInput
+ skip: Int
+ where: UserWhereInput
+ ): [UserData]
+ usersConnection(
+ first: Int
+ orderBy: UserOrderByInput
+ skip: Int
+ where: UserWhereInput
+ ): [UserData]!
+ venue(id:ID!):[Venue]
+}
+
+input RecaptchaVerification {
+ recaptchaToken: String!
+}
+
+enum Recurrance {
+ DAILY
+ MONTHLY
+ ONCE
+ WEEKLY
+ YEARLY
+}
+
+input RecurrenceRuleInput {
+ count: Int
+ frequency: Frequency
+ weekDays: [WeekDays]
+}
+
+enum Status {
+ ACTIVE
+ BLOCKED
+ DELETED
+}
+
+type Subscription {
+ messageSentToChat(userId: ID!): ChatMessage
+ onPluginUpdate: Plugin
+}
+
+scalar Time
+
+input ToggleUserTagAssignInput {
+ tagId: ID!
+ userId: ID!
+}
+
+type Translation {
+ en_value: String
+ lang_code: String
+ translation: String
+ verified: Boolean
+}
+
+enum Type {
+ PRIVATE
+ UNIVERSAL
+}
+
+scalar URL
+
+type UnauthenticatedError implements Error {
+ message: String!
+}
+
+type UnauthorizedError implements Error {
+ message: String!
+}
+
+input UpdateActionItemCategoryInput {
+ isDisabled: Boolean
+ name: String
+}
+
+input UpdateActionItemInput {
+ assigneeId: ID
+ completionDate: Date
+ dueDate: Date
+ isCompleted: Boolean
+ postCompletionNotes: String
+ preCompletionNotes: String
+}
+
+input UpdateAdvertisementInput {
+ _id: ID!
+ endDate: Date
+ link: String
+ name: String
+ startDate: Date
+ type: AdvertisementType
+}
+
+type UpdateAdvertisementPayload {
+ advertisement: Advertisement
+}
+
+input UpdateAgendaCategoryInput {
+ description: String
+ name: String
+}
+
+input UpdateEventInput {
+ allDay: Boolean
+ description: String
+ endDate: Date
+ endTime: Time
+ isPublic: Boolean
+ isRegisterable: Boolean
+ latitude: Latitude
+ location: String
+ longitude: Longitude
+ recurrance: Recurrance
+ recurring: Boolean
+ startDate: Date
+ startTime: Time
+ title: String
+}
+
+input UpdateEventVolunteerInput {
+ eventId: ID
+ isAssigned: Boolean
+ isInvited: Boolean
+ response: EventVolunteerResponse
+}
+
+input UpdateOrganizationInput {
+ address: AddressInput
+ description: String
+ name: String
+ userRegistrationRequired: Boolean
+ visibleInSearch: Boolean
+}
+
+input AddressInput {
+ city: String
+ countryCode: String
+ dependentLocality: String
+ line1: String
+ line2: String
+ postalCode: String
+ sortingCode: String
+ state: String
+}
+
+enum EducationGrade {
+ GRADE_1
+ GRADE_2
+ GRADE_3
+ GRADE_4
+ GRADE_5
+ GRADE_6
+ GRADE_7
+ GRADE_8
+ GRADE_9
+ GRADE_10
+ GRADE_11
+ GRADE_12
+ GRADUATE
+ KG
+ NO_GRADE
+ PRE_KG
+}
+
+enum EmploymentStatus {
+ FULL_TIME
+ PART_TIME
+ UNEMPLOYED
+}
+
+enum Gender {
+ FEMALE
+ MALE
+ OTHER
+}
+
+enum MaritalStatus {
+ DIVORCED
+ ENGAGED
+ MARRIED
+ SEPERATED
+ SINGLE
+ WIDOWED
+}
+
+input UpdateUserInput {
+ address: AddressInput
+ birthDate: Date
+ educationGrade: EducationGrade
+ email: EmailAddress
+ employmentStatus: EmploymentStatus
+ firstName: String
+ gender: Gender
+ lastName: String
+ maritalStatus: MaritalStatus
+ phone: UserPhoneInput
+}
+
+input UpdateUserPasswordInput {
+ confirmNewPassword: String!
+ newPassword: String!
+ previousPassword: String!
+}
+
+input UpdateUserTagInput {
+ _id: ID!
+ name: String!
+}
+
+input UpdateUserTypeInput {
+ id: ID
+ userType: String
+}
+
+scalar Upload
+
+type User {
+ _id: ID!
+ address: Address
+ appUserProfileId: AppUserProfile
+ birthDate: Date
+ createdAt: DateTime!
+ educationGrade: EducationGrade
+ email: EmailAddress!
+ employmentStatus: EmploymentStatus
+ firstName: String!
+ gender: Gender
+ image: String
+ joinedOrganizations: [Organization]
+ lastName: String!
+ maritalStatus: MaritalStatus
+ membershipRequests: [MembershipRequest]
+ organizationsBlockedBy: [Organization]
+ phone: UserPhone
+ pluginCreationAllowed: Boolean!
+ registeredEvents: [Event]
+ eventsAttended: [Event]
+ tagsAssignedWith(
+ after: String
+ before: String
+ first: PositiveInt
+ last: PositiveInt
+ organizationId: ID
+ ): UserTagsConnection
+ updatedAt: DateTime!
+}
+
+type Fund {
+ _id: ID!
+ campaigns: [FundraisingCampaign!]
+ createdAt: DateTime!
+ isArchived: Boolean!
+ isDefault: Boolean!
+ name: String!
+ creator: User
+ organizationId: ID!
+ refrenceNumber: String
+ taxDeductible: Boolean!
+ updatedAt: DateTime!
+}
+
+input FundWhereInput {
+ name_contains: String
+}
+
+input UserAndOrganizationInput {
+ organizationId: ID!
+ userId: ID!
+}
+
+type UserPhone {
+ home: PhoneNumber
+ mobile: PhoneNumber
+ work: PhoneNumber
+}
+
+type UserConnection {
+ aggregate: AggregateUser!
+ edges: [User]!
+ pageInfo: PageInfo!
+}
+
+type UserCustomData {
+ _id: ID!
+ organizationId: ID!
+ userId: ID!
+ values: JSON!
+}
+
+type UserData {
+ appUserProfile: AppUserProfile!
+ user: User!
+}
+
+type UserEdge {
+ cursor: String!
+ node: User!
+}
+
+type UserFamily {
+ _id: ID!
+ admins: [User!]!
+ creator: User!
+ title: String
+ users: [User!]!
+}
+
+input UserInput {
+ appLanguageCode: String
+ email: EmailAddress!
+ firstName: String!
+ lastName: String!
+ password: String!
+ selectedOrganization : ID!
+}
+
+enum UserOrderByInput {
+ email_ASC
+ email_DESC
+ firstName_ASC
+ firstName_DESC
+ id_ASC
+ id_DESC
+ lastName_ASC
+ lastName_DESC
+ createdAt_ASC
+ createdAt_DESC
+}
+
+type UserPhone {
+ home: PhoneNumber
+ mobile: PhoneNumber
+ work: PhoneNumber
+}
+
+input UserPhoneInput {
+ home: PhoneNumber
+ mobile: PhoneNumber
+ work: PhoneNumber
+}
+
+type UserTag {
+ _id: ID!
+ childTags(input: UserTagsConnectionInput!): UserTagsConnectionResult!
+ name: String!
+ organization: Organization
+ parentTag: UserTag
+ usersAssignedTo(input: UsersConnectionInput!): UsersConnectionResult!
+}
+
+type UserTagEdge {
+ cursor: String!
+ node: UserTag!
+}
+
+type UserTagsConnection {
+ edges: [UserTagEdge!]!
+ pageInfo: ConnectionPageInfo!
+}
+
+input UserTagsConnectionInput {
+ cursor: String
+ direction: PaginationDirection!
+ limit: PositiveInt!
+}
+
+type UserTagsConnectionResult {
+ data: UserTagsConnection
+ errors: [ConnectionError!]!
+}
+
+enum UserType {
+ ADMIN
+ NON_USER
+ SUPERADMIN
+ USER
+}
+
+input UserWhereInput {
+ email: EmailAddress
+ email_contains: EmailAddress
+ email_in: [EmailAddress!]
+ email_not: EmailAddress
+ email_not_in: [EmailAddress!]
+ email_starts_with: EmailAddress
+ event_title_contains: String
+ firstName: String
+ firstName_contains: String
+ firstName_in: [String!]
+ firstName_not: String
+ firstName_not_in: [String!]
+ firstName_starts_with: String
+ id: ID
+ id_contains: ID
+ id_in: [ID!]
+ id_not: ID
+ id_not_in: [ID!]
+ id_starts_with: ID
+ lastName: String
+ lastName_contains: String
+ lastName_in: [String!]
+ lastName_not: String
+ lastName_not_in: [String!]
+ lastName_starts_with: String
+}
+
+type UsersConnection {
+ edges: [UserEdge!]!
+ pageInfo: ConnectionPageInfo!
+}
+
+input UsersConnectionInput {
+ cursor: String
+ direction: PaginationDirection!
+ limit: PositiveInt!
+}
+
+type UsersConnectionResult {
+ data: UsersConnection
+ errors: [ConnectionError!]!
+}
+
+enum WeekDays {
+ FR
+ MO
+ SA
+ SU
+ TH
+ TU
+ WE
+}
+
+type Venue {
+ _id: ID!
+ capacity: Int!
+ description: String
+ imageUrl: URL
+ name: String!
+ organization: Organization!
+}
+
+input VenueInput {
+ capacity: Int!
+ description: String
+ file: String
+ name: String!
+ organizationId: ID!
+}
+
+input createUserFamilyInput {
+ title: String!
+ userIds: [ID!]!
+}
+
+input chatInput {
+ isGroup: Boolean!
+ organizationId: ID
+ userIds: [ID!]!
+ name: String
+}
\ No newline at end of file
diff --git a/scripts/__mocks__/@dicebear/collection.ts b/scripts/__mocks__/@dicebear/collection.ts
new file mode 100644
index 0000000000..9a7e1e9a57
--- /dev/null
+++ b/scripts/__mocks__/@dicebear/collection.ts
@@ -0,0 +1 @@
+export const initials = jest.fn();
diff --git a/scripts/__mocks__/@dicebear/core.ts b/scripts/__mocks__/@dicebear/core.ts
new file mode 100644
index 0000000000..41811e2f8d
--- /dev/null
+++ b/scripts/__mocks__/@dicebear/core.ts
@@ -0,0 +1,5 @@
+export const createAvatar = jest.fn(() => {
+ return {
+ toDataUri: jest.fn(() => 'mocked-data-uri'),
+ };
+});
diff --git a/scripts/__mocks__/@pdfme/generator.test.ts b/scripts/__mocks__/@pdfme/generator.test.ts
new file mode 100644
index 0000000000..592c193a71
--- /dev/null
+++ b/scripts/__mocks__/@pdfme/generator.test.ts
@@ -0,0 +1,47 @@
+import { generate } from './generator';
+import type { Template } from '@pdfme/common';
+
+describe('Testing mock generate util', () => {
+ test('should return a Promise', async () => {
+ const result = generate({
+ template: { schemas: [] } as Template,
+ inputs: [],
+ });
+ expect(result).toBeInstanceOf(Promise);
+ });
+
+ test('should resolve to a Uint8Array', async () => {
+ const result = generate({
+ template: { schemas: [] } as Template,
+ inputs: [],
+ });
+ expect(result).toBeInstanceOf(Uint8Array);
+ });
+
+ it('should throw an error when template is empty', async () => {
+ const emptyTemplate = { schemas: [] } as Template;
+ const validInputs = [{ field1: 'value1' }];
+
+ await expect(
+ generate({ template: emptyTemplate, inputs: validInputs }),
+ ).rejects.toThrow('Template or inputs cannot be empty.');
+ });
+
+ it('should throw an error when inputs are empty', async () => {
+ const validTemplate = { schemas: [{ name: 'field1' }] } as Template;
+ const emptyInputs: Record[] = [];
+
+ await expect(
+ generate({ template: validTemplate, inputs: emptyInputs }),
+ ).rejects.toThrow('Template or inputs cannot be empty.');
+ });
+
+ it('should throw an error when both template and inputs are empty', async () => {
+ const emptyTemplate = { schemas: [] } as Template;
+ const emptyInputs: Record[] = [];
+
+ await expect(
+ generate({ template: emptyTemplate, inputs: emptyInputs }),
+ ).rejects.toThrow('Template or inputs cannot be empty.');
+ });
+});
diff --git a/scripts/__mocks__/@pdfme/generator.ts b/scripts/__mocks__/@pdfme/generator.ts
new file mode 100644
index 0000000000..ac03ae6210
--- /dev/null
+++ b/scripts/__mocks__/@pdfme/generator.ts
@@ -0,0 +1,22 @@
+import type { Template } from '@pdfme/common';
+
+export const generate = async ({
+ template,
+ inputs,
+}: {
+ template: Template;
+ inputs: Record[];
+}): Promise => {
+ if (template.schemas.length === 0 || inputs.length === 0) {
+ // console.log('pdf error: length : ', template, inputs, inputs.length);
+ throw new Error('Template or inputs cannot be empty.');
+ }
+ // Generate mock PDF-like header bytes
+ const pdfHeader = [0x25, 0x50, 0x44, 0x46]; // %PDF
+ // Add some random content based on input size
+ const contentSize = Math.min(template.schemas.length, inputs.length) * 10;
+ const mockContent = Array.from({ length: contentSize }, () =>
+ Math.floor(Math.random() * 256),
+ );
+ return Promise.resolve(new Uint8Array([...pdfHeader, ...mockContent]));
+};
diff --git a/scripts/__mocks__/fileMock.js b/scripts/__mocks__/fileMock.js
new file mode 100644
index 0000000000..06ad689c8b
--- /dev/null
+++ b/scripts/__mocks__/fileMock.js
@@ -0,0 +1,2 @@
+// __mocks__/fileMock.js
+module.exports = 'test-file-stub';
diff --git a/scripts/custom-test-env.js b/scripts/custom-test-env.js
new file mode 100644
index 0000000000..6174d8cf11
--- /dev/null
+++ b/scripts/custom-test-env.js
@@ -0,0 +1,16 @@
+import Environment from 'jest-environment-jsdom';
+import { TextEncoder, TextDecoder } from 'util';
+
+/**
+ * A custom environment to set the TextEncoder and TextDecoder variables, that is required by @pdfme during testing.
+ * Providing a polyfill to the environment for the same
+ */
+export default class CustomTestEnvironment extends Environment {
+ async setup() {
+ await super.setup();
+ if (typeof this.global.TextEncoder === 'undefined') {
+ this.global.TextEncoder = TextEncoder;
+ this.global.TextDecoder = TextDecoder;
+ }
+ }
+}
diff --git a/scripts/githooks/check-localstorage-usage.js b/scripts/githooks/check-localstorage-usage.js
new file mode 100755
index 0000000000..0a811df307
--- /dev/null
+++ b/scripts/githooks/check-localstorage-usage.js
@@ -0,0 +1,96 @@
+#!/usr/bin/env node
+
+import { readFileSync, existsSync } from 'fs';
+import path from 'path';
+import { execSync } from 'child_process';
+
+const args = process.argv.slice(2);
+const scanEntireRepo = args.includes('--scan-entire-repo');
+
+const containsSkipComment = (file) => {
+ try {
+ const content = readFileSync(file, 'utf-8');
+ return content.includes('// SKIP_LOCALSTORAGE_CHECK');
+ } catch (error) {
+ console.error(`Error reading file ${file}:`, error.message);
+ return false;
+ }
+};
+
+const getModifiedFiles = () => {
+ try {
+ if (scanEntireRepo) {
+ const result = execSync('git ls-files | grep ".tsx\\?$"', {
+ encoding: 'utf-8',
+ });
+ return result.trim().split('\n');
+ }
+
+ const result = execSync('git diff --cached --name-only', {
+ encoding: 'utf-8',
+ });
+ return result.trim().split('\n');
+ } catch (error) {
+ console.error('Error fetching modified files:', error.message);
+ process.exit(1);
+ }
+};
+
+const files = getModifiedFiles();
+
+const filesWithLocalStorage = [];
+
+const checkLocalStorageUsage = (file) => {
+ if (!file) {
+ return;
+ }
+
+ const fileName = path.basename(file);
+
+ // Skip files with specific names or containing a skip comment
+ if (
+ fileName === 'check-localstorage-usage.js' ||
+ fileName === 'useLocalstorage.test.ts' ||
+ fileName === 'useLocalstorage.ts' ||
+ containsSkipComment(file)
+ ) {
+ console.log(`Skipping file: ${file}`);
+ return;
+ }
+
+ try {
+ if (existsSync(file)) {
+ const content = readFileSync(file, 'utf-8');
+
+ if (
+ content.includes('localStorage.getItem') ||
+ content.includes('localStorage.setItem') ||
+ content.includes('localStorage.removeItem')
+ ) {
+ filesWithLocalStorage.push(file);
+ }
+ } else {
+ console.log(`File ${file} does not exist.`);
+ }
+ } catch (error) {
+ console.error(`Error reading file ${file}:`, error.message);
+ }
+};
+
+files.forEach(checkLocalStorageUsage);
+
+if (filesWithLocalStorage.length > 0) {
+ console.error('\x1b[31m%s\x1b[0m', '\nError: Found usage of localStorage');
+ console.error('\nFiles with localStorage usage:');
+ filesWithLocalStorage.forEach((file) => console.error(file));
+
+ console.info(
+ '\x1b[34m%s\x1b[0m',
+ '\nInfo: Consider using custom hook functions.'
+ );
+ console.info(
+ 'Please use the getItem, setItem, and removeItem functions provided by the custom hook useLocalStorage.\n'
+ );
+
+ process.exit(1);
+}
diff --git a/scripts/githooks/update-toc.js b/scripts/githooks/update-toc.js
new file mode 100644
index 0000000000..268becfd13
--- /dev/null
+++ b/scripts/githooks/update-toc.js
@@ -0,0 +1,14 @@
+import fs from 'fs';
+import { execSync } from 'child_process';
+
+const markdownFiles = fs
+ .readdirSync('./')
+ .filter((file) => file.endsWith('.md'));
+
+markdownFiles.forEach((file) => {
+ const command = `markdown-toc -i "${file}" --bullets "-"`;
+ execSync(command, { stdio: 'inherit' });
+
+});
+
+console.log('Table of contents updated successfully.');
diff --git a/setup.ts b/setup.ts
new file mode 100644
index 0000000000..2a6c437fa3
--- /dev/null
+++ b/setup.ts
@@ -0,0 +1,185 @@
+import dotenv from 'dotenv';
+import fs from 'fs';
+import inquirer from 'inquirer';
+import { checkConnection } from './src/setup/checkConnection/checkConnection';
+import { askForTalawaApiUrl } from './src/setup/askForTalawaApiUrl/askForTalawaApiUrl';
+import { checkEnvFile } from './src/setup/checkEnvFile/checkEnvFile';
+import { validateRecaptcha } from './src/setup/validateRecaptcha/validateRecaptcha';
+import { askForCustomPort } from './src/setup/askForCustomPort/askForCustomPort';
+
+export async function main(): Promise {
+ console.log('Welcome to the Talawa Admin setup! 🚀');
+
+ if (!fs.existsSync('.env')) {
+ fs.openSync('.env', 'w');
+ const config = dotenv.parse(fs.readFileSync('.env.example'));
+ for (const key in config) {
+ fs.appendFileSync('.env', `${key}=${config[key]}\n`);
+ }
+ } else {
+ checkEnvFile();
+ }
+
+ let shouldSetCustomPort: boolean;
+
+ if (process.env.PORT) {
+ console.log(
+ `\nCustom port for development server already exists with the value:\n${process.env.PORT}`,
+ );
+ shouldSetCustomPort = true;
+ } else {
+ const { shouldSetCustomPortResponse } = await inquirer.prompt({
+ type: 'confirm',
+ name: 'shouldSetCustomPortResponse',
+ message: 'Would you like to set up a custom port?',
+ default: true,
+ });
+ shouldSetCustomPort = shouldSetCustomPortResponse;
+ }
+
+ if (shouldSetCustomPort) {
+ const customPort = await askForCustomPort();
+
+ const port = dotenv.parse(fs.readFileSync('.env')).PORT;
+
+ fs.readFile('.env', 'utf8', (err, data) => {
+ const result = data.replace(`PORT=${port}`, `PORT=${customPort}`);
+ fs.writeFileSync('.env', result, 'utf8');
+ });
+ }
+
+ let shouldSetTalawaApiUrl: boolean;
+
+ if (process.env.REACT_APP_TALAWA_URL) {
+ console.log(
+ `\nEndpoint for accessing talawa-api graphql service already exists with the value:\n${process.env.REACT_APP_TALAWA_URL}`,
+ );
+ shouldSetTalawaApiUrl = true;
+ } else {
+ const { shouldSetTalawaApiUrlResponse } = await inquirer.prompt({
+ type: 'confirm',
+ name: 'shouldSetTalawaApiUrlResponse',
+ message: 'Would you like to set up talawa-api endpoint?',
+ default: true,
+ });
+ shouldSetTalawaApiUrl = shouldSetTalawaApiUrlResponse;
+ }
+
+ if (shouldSetTalawaApiUrl) {
+ let isConnected = false,
+ endpoint = '';
+
+ while (!isConnected) {
+ endpoint = await askForTalawaApiUrl();
+ const url = new URL(endpoint);
+ isConnected = await checkConnection(url.origin);
+ }
+ const envPath = '.env';
+ const currentEnvContent = fs.readFileSync(envPath, 'utf8');
+ const talawaApiUrl = dotenv.parse(currentEnvContent).REACT_APP_TALAWA_URL;
+
+ const updatedEnvContent = currentEnvContent.replace(
+ `REACT_APP_TALAWA_URL=${talawaApiUrl}`,
+ `REACT_APP_TALAWA_URL=${endpoint}`,
+ );
+
+ fs.writeFileSync(envPath, updatedEnvContent, 'utf8');
+ const websocketUrl = endpoint.replace(/^http(s)?:\/\//, 'ws$1://');
+ const currentWebSocketUrl =
+ dotenv.parse(updatedEnvContent).REACT_APP_BACKEND_WEBSOCKET_URL;
+
+ const finalEnvContent = updatedEnvContent.replace(
+ `REACT_APP_BACKEND_WEBSOCKET_URL=${currentWebSocketUrl}`,
+ `REACT_APP_BACKEND_WEBSOCKET_URL=${websocketUrl}`,
+ );
+
+ fs.writeFileSync(envPath, finalEnvContent, 'utf8');
+ }
+
+ const { shouldUseRecaptcha } = await inquirer.prompt({
+ type: 'confirm',
+ name: 'shouldUseRecaptcha',
+ message: 'Would you like to set up ReCAPTCHA?',
+ default: true,
+ });
+
+ if (shouldUseRecaptcha) {
+ const useRecaptcha = dotenv.parse(
+ fs.readFileSync('.env'),
+ ).REACT_APP_USE_RECAPTCHA;
+
+ fs.readFile('.env', 'utf8', (err, data) => {
+ const result = data.replace(
+ `REACT_APP_USE_RECAPTCHA=${useRecaptcha}`,
+ `REACT_APP_USE_RECAPTCHA=yes`,
+ );
+ fs.writeFileSync('.env', result, 'utf8');
+ });
+ let shouldSetRecaptchaSiteKey: boolean;
+ if (process.env.REACT_APP_RECAPTCHA_SITE_KEY) {
+ console.log(
+ `\nreCAPTCHA site key already exists with the value ${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`,
+ );
+ shouldSetRecaptchaSiteKey = true;
+ } else {
+ const { shouldSetRecaptchaSiteKeyResponse } = await inquirer.prompt({
+ type: 'confirm',
+ name: 'shouldSetRecaptchaSiteKeyResponse',
+ message: 'Would you like to set up a reCAPTCHA site key?',
+ default: true,
+ });
+ shouldSetRecaptchaSiteKey = shouldSetRecaptchaSiteKeyResponse;
+ }
+
+ if (shouldSetRecaptchaSiteKey) {
+ const { recaptchaSiteKeyInput } = await inquirer.prompt([
+ {
+ type: 'input',
+ name: 'recaptchaSiteKeyInput',
+ message: 'Enter your reCAPTCHA site key:',
+ validate: async (input: string): Promise => {
+ if (validateRecaptcha(input)) {
+ return true;
+ }
+ return 'Invalid reCAPTCHA site key. Please try again.';
+ },
+ },
+ ]);
+
+ const recaptchaSiteKey = dotenv.parse(
+ fs.readFileSync('.env'),
+ ).REACT_APP_RECAPTCHA_SITE_KEY;
+
+ fs.readFile('.env', 'utf8', (err, data) => {
+ const result = data.replace(
+ `REACT_APP_RECAPTCHA_SITE_KEY=${recaptchaSiteKey}`,
+ `REACT_APP_RECAPTCHA_SITE_KEY=${recaptchaSiteKeyInput}`,
+ );
+ fs.writeFileSync('.env', result, 'utf8');
+ });
+ }
+ }
+
+ const { shouldLogErrors } = await inquirer.prompt({
+ type: 'confirm',
+ name: 'shouldLogErrors',
+ message:
+ 'Would you like to log Compiletime and Runtime errors in the console?',
+ default: true,
+ });
+
+ if (shouldLogErrors) {
+ const logErrors = dotenv.parse(fs.readFileSync('.env')).ALLOW_LOGS;
+
+ fs.readFile('.env', 'utf8', (err, data) => {
+ const result = data.replace(`ALLOW_LOGS=${logErrors}`, 'ALLOW_LOGS=YES');
+ fs.writeFileSync('.env', result, 'utf8');
+ });
+ }
+
+ console.log(
+ '\nCongratulations! Talawa Admin has been successfully setup! 🥂🎉',
+ );
+}
+
+main();
diff --git a/src/App.test.tsx b/src/App.test.tsx
index 2a68616d98..f4fba2ebf8 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -1,9 +1,111 @@
-import React from 'react';
+import React, { act } from 'react';
import { render, screen } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MockedProvider } from '@apollo/react-testing';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import 'jest-location-mock';
import App from './App';
+import { store } from 'state/store';
+import { CHECK_AUTH } from 'GraphQl/Queries/Queries';
+import i18nForTest from './utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import useLocalStorage from 'utils/useLocalstorage';
-test('renders learn react link', () => {
- render( );
- const linkElement = screen.getByText(/learn react/i);
- expect(linkElement).toBeInTheDocument();
+const { setItem } = useLocalStorage();
+
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+// These modules are used by the Feedback components
+jest.mock('@mui/x-charts/PieChart', () => ({
+ pieArcLabelClasses: jest.fn(),
+ PieChart: jest.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: jest.fn(),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: CHECK_AUTH,
+ },
+ result: {
+ data: {
+ checkAuth: {
+ _id: '123',
+ firstName: 'John',
+ lastName: 'Doe',
+ createdAt: '2023-04-13T04:53:17.742+00:00',
+ image: 'john.jpg',
+ email: 'johndoe@gmail.com',
+ birthDate: '1990-01-01',
+ educationGrade: 'NO_GRADE',
+ employmentStatus: 'EMPLOYED',
+ gender: 'MALE',
+ maritalStatus: 'SINGLE',
+ address: {
+ line1: 'line1',
+ state: 'state',
+ countryCode: 'IND',
+ },
+ phone: {
+ mobile: '+8912313112',
+ },
+ },
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink([], true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing the App Component', () => {
+ test('Component should be rendered properly and user is loggedin', async () => {
+ setItem('AdminFor', [{ name: 'adi', _id: '1234', image: '' }]);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ window.location.assign('/orglist');
+ await wait();
+ expect(window.location).toBeAt('/orglist');
+ expect(
+ screen.getByText(
+ 'An open source application by Palisadoes Foundation volunteers',
+ ),
+ ).toBeTruthy();
+ });
+
+ test('Component should be rendered properly and user is loggedout', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ });
});
diff --git a/src/App.tsx b/src/App.tsx
index c31b4f754d..37f3bc301e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,27 +1,220 @@
-import React from 'react';
-import './App.css';
+import AddOnStore from 'components/AddOn/core/AddOnStore/AddOnStore';
+import OrganizationScreen from 'components/OrganizationScreen/OrganizationScreen';
+import SecuredRoute from 'components/SecuredRoute/SecuredRoute';
+import SuperAdminScreen from 'components/SuperAdminScreen/SuperAdminScreen';
+import * as installedPlugins from 'components/plugins/index';
+import { Route, Routes } from 'react-router-dom';
+import BlockUser from 'screens/BlockUser/BlockUser';
+import EventManagement from 'screens/EventManagement/EventManagement';
+import ForgotPassword from 'screens/ForgotPassword/ForgotPassword';
+import LoginPage from 'screens/LoginPage/LoginPage';
+import MemberDetail from 'screens/MemberDetail/MemberDetail';
+import OrgContribution from 'screens/OrgContribution/OrgContribution';
+import OrgList from 'screens/OrgList/OrgList';
+import OrgPost from 'screens/OrgPost/OrgPost';
+import OrgSettings from 'screens/OrgSettings/OrgSettings';
+import OrganizationActionItems from 'screens/OrganizationActionItems/OrganizationActionItems';
+import OrganizationDashboard from 'screens/OrganizationDashboard/OrganizationDashboard';
+import OrganizationEvents from 'screens/OrganizationEvents/OrganizationEvents';
+import OrganizaitionFundCampiagn from 'screens/OrganizationFundCampaign/OrganizationFundCampagins';
+import OrganizationFunds from 'screens/OrganizationFunds/OrganizationFunds';
+import FundCampaignPledge from 'screens/FundCampaignPledge/FundCampaignPledge';
+import OrganizationPeople from 'screens/OrganizationPeople/OrganizationPeople';
+import OrganizationTags from 'screens/OrganizationTags/OrganizationTags';
+import ManageTag from 'screens/ManageTag/ManageTag';
+import SubTags from 'screens/SubTags/SubTags';
+import PageNotFound from 'screens/PageNotFound/PageNotFound';
+import Requests from 'screens/Requests/Requests';
+import Users from 'screens/Users/Users';
+import CommunityProfile from 'screens/CommunityProfile/CommunityProfile';
+import OrganizationVenues from 'screens/OrganizationVenues/OrganizationVenues';
+import Leaderboard from 'screens/Leaderboard/Leaderboard';
-function App(): JSX.Element {
- return (
-
-
+import React, { useEffect } from 'react';
+// User Portal Components
+import Donate from 'screens/UserPortal/Donate/Donate';
+import Events from 'screens/UserPortal/Events/Events';
+import Posts from 'screens/UserPortal/Posts/Posts';
+import Organizations from 'screens/UserPortal/Organizations/Organizations';
+import People from 'screens/UserPortal/People/People';
+import Settings from 'screens/UserPortal/Settings/Settings';
+import Chat from 'screens/UserPortal/Chat/Chat';
+import { useQuery } from '@apollo/client';
+import { CHECK_AUTH } from 'GraphQl/Queries/Queries';
+import Advertisements from 'components/Advertisements/Advertisements';
+import SecuredRouteForUser from 'components/UserPortal/SecuredRouteForUser/SecuredRouteForUser';
+
+import useLocalStorage from 'utils/useLocalstorage';
+import UserScreen from 'screens/UserPortal/UserScreen/UserScreen';
+import EventDashboardScreen from 'components/EventDashboardScreen/EventDashboardScreen';
+import Campaigns from 'screens/UserPortal/Campaigns/Campaigns';
+import Pledges from 'screens/UserPortal/Pledges/Pledges';
+import VolunteerManagement from 'screens/UserPortal/Volunteer/VolunteerManagement';
+
+const { setItem } = useLocalStorage();
+
+/**
+ * This is the main function for our application. It sets up all the routes and components,
+ * defining how the user can navigate through the app. The function uses React Router's `Routes`
+ * and `Route` components to map different URL paths to corresponding screens and components.
+ *
+ * ## Important Details
+ * - **UseEffect Hook**: This hook checks user authentication status using the `CHECK_AUTH` GraphQL query.
+ * - **Plugins**: It dynamically loads additional routes for any installed plugins.
+ * - **Routes**:
+ * - The root route ("/") takes the user to the `LoginPage`.
+ * - Protected routes are wrapped with the `SecuredRoute` component to ensure they are only accessible to authenticated users.
+ * - Admin and Super Admin routes allow access to organization and user management screens.
+ * - User portal routes allow end-users to interact with organizations, settings, chat, events, etc.
+ *
+ * @returns The rendered routes and components of the application.
+ */
+
+function app(): JSX.Element {
+ /*const { updatePluginLinks, updateInstalled } = bindActionCreators(
+ actionCreators,
+ dispatch
+ );
+
+ const getInstalledPlugins = async () => {
+ const plugins = await fetchInstalled();
+ updateInstalled(plugins);
+ updatePluginLinks(new PluginHelper().generateLinks(plugins));
+ };
+
+ const fetchInstalled = async () => {
+ const result = await fetch(`http://localhost:3005/installed`);
+ return await result.json();
+ };
+
+ useEffect(() => {
+ getInstalledPlugins();
+ }, []);*/
+
+ // const appRoutes = useSelector((state: RootState) => state.appRoutes);
+ // const { components } = appRoutes;
+
+ // TODO: Fetch Installed plugin extras and store for use within MainContent and Side Panel Components.
+
+ const { data, loading } = useQuery(CHECK_AUTH);
+
+ useEffect(() => {
+ if (data) {
+ setItem('name', `${data.checkAuth.firstName} ${data.checkAuth.lastName}`);
+ setItem('id', data.checkAuth._id);
+ setItem('email', data.checkAuth.email);
+ setItem('IsLoggedIn', 'TRUE');
+ setItem('FirstName', data.checkAuth.firstName);
+ setItem('LastName', data.checkAuth.lastName);
+ setItem('UserImage', data.checkAuth.image);
+ setItem('Email', data.checkAuth.email);
+ }
+ }, [data, loading]);
+
+ const extraRoutes = Object.entries(installedPlugins).map(
+ (
+ plugin: [
+ string,
+ (
+ | typeof installedPlugins.DummyPlugin
+ | typeof installedPlugins.DummyPlugin2
+ ),
+ ],
+ index: number,
+ ) => {
+ const ExtraComponent = plugin[1];
+ return (
+
}
+ />
);
-
+ },
+ );
+
+ return (
+ <>
+
+ } />
+ }>
+ }>
+ } />
+ } />
+ } />
+ } />
+
+ }>
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ } />
+ } />
+ } />
+ }
+ />
+ }
+ />
+ } />
+ }
+ />
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ {extraRoutes}
+
+
+ } />
+ {/* User Portal Routes */}
+ }>
+ } />
+ } />
+ } />
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ }>
+ }
+ />
+
+
+
+ {/* */}
+ } />
+
+ >
);
}
-export default App;
+export default app;
diff --git a/src/Constant/constant.spec.ts b/src/Constant/constant.spec.ts
new file mode 100644
index 0000000000..c2e57d29df
--- /dev/null
+++ b/src/Constant/constant.spec.ts
@@ -0,0 +1,29 @@
+import {
+ AUTH_TOKEN,
+ BACKEND_URL,
+ RECAPTCHA_SITE_KEY,
+ REACT_APP_USE_RECAPTCHA,
+} from './constant';
+
+describe('constants', () => {
+ it('AUTH_TOKEN should be an empty string', () => {
+ expect(typeof AUTH_TOKEN).toEqual('string');
+ expect(AUTH_TOKEN).toEqual('');
+ });
+
+ it('BACKEND_URL should be equal to REACT_APP_TALAWA_URL environment variable', () => {
+ expect(BACKEND_URL).toEqual(process.env.REACT_APP_TALAWA_URL);
+ });
+
+ it('RECAPTCHA_SITE_KEY should be equal to REACT_APP_RECAPTCHA_SITE_KEY environment variable', () => {
+ expect(RECAPTCHA_SITE_KEY).toEqual(
+ process.env.REACT_APP_RECAPTCHA_SITE_KEY,
+ );
+ });
+
+ it('REACT_APP_USE_RECAPTCHA should be equal to REACT_APP_USE_RECAPTCHA environment variable', () => {
+ expect(REACT_APP_USE_RECAPTCHA).toEqual(
+ process.env.REACT_APP_USE_RECAPTCHA,
+ );
+ });
+});
diff --git a/src/Constant/constant.ts b/src/Constant/constant.ts
new file mode 100644
index 0000000000..d3b0efe1c1
--- /dev/null
+++ b/src/Constant/constant.ts
@@ -0,0 +1,7 @@
+export const AUTH_TOKEN = '';
+export const BACKEND_URL = process.env.REACT_APP_TALAWA_URL;
+export const RECAPTCHA_SITE_KEY = process.env.REACT_APP_RECAPTCHA_SITE_KEY;
+export const REACT_APP_USE_RECAPTCHA = process.env.REACT_APP_USE_RECAPTCHA;
+export const REACT_APP_CUSTOM_PORT = process.env.PORT;
+export const REACT_APP_BACKEND_WEBSOCKET_URL: string =
+ process.env.REACT_APP_BACKEND_WEBSOCKET_URL || '';
diff --git a/src/GraphQl/Mutations/ActionItemCategoryMutations.ts b/src/GraphQl/Mutations/ActionItemCategoryMutations.ts
new file mode 100644
index 0000000000..92e7b0968c
--- /dev/null
+++ b/src/GraphQl/Mutations/ActionItemCategoryMutations.ts
@@ -0,0 +1,48 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create an action item category.
+ *
+ * @param name - Name of the ActionItemCategory.
+ * @param isDisabled - Disabled status of the ActionItemCategory.
+ * @param organizationId - Organization to which the ActionItemCategory belongs.
+ */
+
+export const CREATE_ACTION_ITEM_CATEGORY_MUTATION = gql`
+ mutation CreateActionItemCategory(
+ $name: String!
+ $isDisabled: Boolean!
+ $organizationId: ID!
+ ) {
+ createActionItemCategory(
+ name: $name
+ isDisabled: $isDisabled
+ organizationId: $organizationId
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update an action item category.
+ *
+ * @param id - The id of the ActionItemCategory to be updated.
+ * @param name - Updated name of the ActionItemCategory.
+ * @param isDisabled - Updated disabled status of the ActionItemCategory.
+ */
+
+export const UPDATE_ACTION_ITEM_CATEGORY_MUTATION = gql`
+ mutation UpdateActionItemCategory(
+ $actionItemCategoryId: ID!
+ $name: String
+ $isDisabled: Boolean
+ ) {
+ updateActionItemCategory(
+ id: $actionItemCategoryId
+ data: { name: $name, isDisabled: $isDisabled }
+ ) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/ActionItemMutations.ts b/src/GraphQl/Mutations/ActionItemMutations.ts
new file mode 100644
index 0000000000..03ff50907f
--- /dev/null
+++ b/src/GraphQl/Mutations/ActionItemMutations.ts
@@ -0,0 +1,94 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create an action item.
+ *
+ * @param actionItemCategoryId - ActionItemCategory to which the ActionItem is related.
+ * @param assigneeId - User to whom the ActionItem is assigned.
+ * @param preCompletionNotes - Notes prior to completion.
+ * @param dueDate - Due date.
+ * @param eventId - Event to which the ActionItem is related.
+ * @param allottedHours - Hours allotted for the ActionItem.
+ */
+
+export const CREATE_ACTION_ITEM_MUTATION = gql`
+ mutation CreateActionItem(
+ $actionItemCategoryId: ID!
+ $assigneeId: ID!
+ $assigneeType: String!
+ $preCompletionNotes: String
+ $dDate: Date
+ $eventId: ID
+ $allottedHours: Float
+ ) {
+ createActionItem(
+ actionItemCategoryId: $actionItemCategoryId
+ data: {
+ assigneeId: $assigneeId
+ assigneeType: $assigneeType
+ preCompletionNotes: $preCompletionNotes
+ dueDate: $dDate
+ eventId: $eventId
+ allottedHours: $allottedHours
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update an action item.
+ *
+ * @param id - Id of the ActionItem to be updated.
+ * @param assigneeId - User to whom the ActionItem is assigned.
+ * @param preCompletionNotes - Notes prior to completion.
+ * @param postCompletionNotes - Notes on completion.
+ * @param dueDate - Due date.
+ * @param completionDate - Completion date.
+ * @param isCompleted - Whether the ActionItem has been completed.
+ */
+
+export const UPDATE_ACTION_ITEM_MUTATION = gql`
+ mutation UpdateActionItem(
+ $actionItemId: ID!
+ $assigneeId: ID!
+ $assigneeType: String!
+ $preCompletionNotes: String
+ $postCompletionNotes: String
+ $dueDate: Date
+ $completionDate: Date
+ $isCompleted: Boolean
+ $allottedHours: Float
+ ) {
+ updateActionItem(
+ id: $actionItemId
+ data: {
+ assigneeId: $assigneeId
+ assigneeType: $assigneeType
+ preCompletionNotes: $preCompletionNotes
+ postCompletionNotes: $postCompletionNotes
+ dueDate: $dueDate
+ completionDate: $completionDate
+ allottedHours: $allottedHours
+ isCompleted: $isCompleted
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete an action item.
+ *
+ * @param id - Id of the ActionItem to be updated.
+ */
+
+export const DELETE_ACTION_ITEM_MUTATION = gql`
+ mutation RemoveActionItem($actionItemId: ID!) {
+ removeActionItem(id: $actionItemId) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/AgendaCategoryMutations.ts b/src/GraphQl/Mutations/AgendaCategoryMutations.ts
new file mode 100644
index 0000000000..c344eca7e2
--- /dev/null
+++ b/src/GraphQl/Mutations/AgendaCategoryMutations.ts
@@ -0,0 +1,45 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create an agenda category.
+ *
+ * @param input - Name, Description, OrganizationID of the AgendaCategory.
+ */
+
+export const CREATE_AGENDA_ITEM_CATEGORY_MUTATION = gql`
+ mutation CreateAgendaCategory($input: CreateAgendaCategoryInput!) {
+ createAgendaCategory(input: $input) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete an agenda category.
+ *
+ * @param deleteAgendaCategoryId - The ID of the AgendaCategory to be deleted.
+ */
+
+export const DELETE_AGENDA_ITEM_CATEGORY_MUTATION = gql`
+ mutation DeleteAgendaCategory($deleteAgendaCategoryId: ID!) {
+ deleteAgendaCategory(id: $deleteAgendaCategoryId)
+ }
+`;
+
+/**
+ * GraphQL mutation to update an agenda category.
+ *
+ * @param updateAgendaCategoryId - The ID of the AgendaCategory to be updated.
+ * @param input - Updated Name, Description, OrganizationID of the AgendaCategory.
+ */
+
+export const UPDATE_AGENDA_ITEM_CATEGORY_MUTATION = gql`
+ mutation UpdateAgendaCategory(
+ $updateAgendaCategoryId: ID!
+ $input: UpdateAgendaCategoryInput!
+ ) {
+ updateAgendaCategory(id: $updateAgendaCategoryId, input: $input) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/AgendaItemMutations.ts b/src/GraphQl/Mutations/AgendaItemMutations.ts
new file mode 100644
index 0000000000..20191b1b7c
--- /dev/null
+++ b/src/GraphQl/Mutations/AgendaItemMutations.ts
@@ -0,0 +1,31 @@
+import gql from 'graphql-tag';
+
+export const CREATE_AGENDA_ITEM_MUTATION = gql`
+ mutation CreateAgendaItem($input: CreateAgendaItemInput!) {
+ createAgendaItem(input: $input) {
+ _id
+ title
+ }
+ }
+`;
+
+export const DELETE_AGENDA_ITEM_MUTATION = gql`
+ mutation RemoveAgendaItem($removeAgendaItemId: ID!) {
+ removeAgendaItem(id: $removeAgendaItemId) {
+ _id
+ }
+ }
+`;
+
+export const UPDATE_AGENDA_ITEM_MUTATION = gql`
+ mutation UpdateAgendaItem(
+ $updateAgendaItemId: ID!
+ $input: UpdateAgendaItemInput!
+ ) {
+ updateAgendaItem(id: $updateAgendaItemId, input: $input) {
+ _id
+ description
+ title
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/CampaignMutation.ts b/src/GraphQl/Mutations/CampaignMutation.ts
new file mode 100644
index 0000000000..d14d318af6
--- /dev/null
+++ b/src/GraphQl/Mutations/CampaignMutation.ts
@@ -0,0 +1,75 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a new fund Campaign.
+ *
+ * @param name - The name of the fund.
+ * @param fundId - The fund ID the campaign is associated with.
+ * @param fundingGoal - The funding goal of the campaign.
+ * @param startDate - The start date of the campaign.
+ * @param endDate - The end date of the campaign.
+ * @param currency - The currency of the campaign.
+ * @returns The ID of the created campaign.
+ */
+
+export const CREATE_CAMPAIGN_MUTATION = gql`
+ mutation createFundraisingCampaign(
+ $fundId: ID!
+ $organizationId: ID!
+ $name: String!
+ $fundingGoal: Float!
+ $startDate: Date!
+ $endDate: Date!
+ $currency: Currency!
+ ) {
+ createFundraisingCampaign(
+ data: {
+ fundId: $fundId
+ organizationId: $organizationId
+ name: $name
+ fundingGoal: $fundingGoal
+ startDate: $startDate
+ endDate: $endDate
+ currency: $currency
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update a fund Campaign.
+ *
+ * @param id - The ID of the campaign being updated.
+ * @param name - The name of the campaign.
+ * @param fundingGoal - The funding goal of the campaign.
+ * @param startDate - The start date of the campaign.
+ * @param endDate - The end date of the campaign.
+ * @param currency - The currency of the campaign.
+ * @returns The ID of the updated campaign.
+ */
+
+export const UPDATE_CAMPAIGN_MUTATION = gql`
+ mutation updateFundraisingCampaign(
+ $id: ID!
+ $name: String
+ $fundingGoal: Float
+ $startDate: Date
+ $endDate: Date
+ $currency: Currency
+ ) {
+ updateFundraisingCampaign(
+ id: $id
+ data: {
+ name: $name
+ fundingGoal: $fundingGoal
+ startDate: $startDate
+ endDate: $endDate
+ currency: $currency
+ }
+ ) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/CommentMutations.ts b/src/GraphQl/Mutations/CommentMutations.ts
new file mode 100644
index 0000000000..7a2ca00c83
--- /dev/null
+++ b/src/GraphQl/Mutations/CommentMutations.ts
@@ -0,0 +1,58 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a new comment on a post.
+ *
+ * @param comment - The text content of the comment.
+ * @param postId - The ID of the post to which the comment is being added.
+ * @returns The created comment object.
+ */
+
+export const CREATE_COMMENT_POST = gql`
+ mutation createComment($comment: String!, $postId: ID!) {
+ createComment(data: { text: $comment }, postId: $postId) {
+ _id
+ creator {
+ _id
+ firstName
+ lastName
+ email
+ }
+ likeCount
+ likedBy {
+ _id
+ }
+ text
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to like a comment.
+ *
+ * @param commentId - The ID of the comment to be liked.
+ * @returns The liked comment object.
+ */
+
+export const LIKE_COMMENT = gql`
+ mutation likeComment($commentId: ID!) {
+ likeComment(id: $commentId) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to unlike a comment.
+ *
+ * @param commentId - The ID of the comment to be unliked.
+ * @returns The unliked comment object.
+ */
+
+export const UNLIKE_COMMENT = gql`
+ mutation unlikeComment($commentId: ID!) {
+ unlikeComment(id: $commentId) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/EventAttendeeMutations.ts b/src/GraphQl/Mutations/EventAttendeeMutations.ts
new file mode 100644
index 0000000000..94d7d97705
--- /dev/null
+++ b/src/GraphQl/Mutations/EventAttendeeMutations.ts
@@ -0,0 +1,49 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to add an attendee to an event.
+ *
+ * @param userId - The ID of the user being added as an attendee.
+ * @param eventId - The ID of the event to which the user is being added as an attendee.
+ * @returns The updated event object with the added attendee.
+ */
+
+export const ADD_EVENT_ATTENDEE = gql`
+ mutation addEventAttendee($userId: ID!, $eventId: ID!) {
+ addEventAttendee(data: { userId: $userId, eventId: $eventId }) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to remove an attendee from an event.
+ *
+ * @param userId - The ID of the user being removed as an attendee.
+ * @param eventId - The ID of the event from which the user is being removed as an attendee.
+ * @returns The updated event object without the removed attendee.
+ */
+
+export const REMOVE_EVENT_ATTENDEE = gql`
+ mutation removeEventAttendee($userId: ID!, $eventId: ID!) {
+ removeEventAttendee(data: { userId: $userId, eventId: $eventId }) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to mark a user's check-in at an event.
+ *
+ * @param userId - The ID of the user checking in.
+ * @param eventId - The ID of the event at which the user is checking in.
+ * @returns The updated event object with the user's check-in information.
+ */
+
+export const MARK_CHECKIN = gql`
+ mutation checkIn($userId: ID!, $eventId: ID!) {
+ checkIn(data: { userId: $userId, eventId: $eventId }) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/EventVolunteerMutation.ts b/src/GraphQl/Mutations/EventVolunteerMutation.ts
new file mode 100644
index 0000000000..eb611361c0
--- /dev/null
+++ b/src/GraphQl/Mutations/EventVolunteerMutation.ts
@@ -0,0 +1,112 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create an event volunteer.
+ *
+ * @param data - The data required to create an event volunteer.
+ * @returns The ID of the created event volunteer.
+ *
+ */
+
+export const ADD_VOLUNTEER = gql`
+ mutation CreateEventVolunteer($data: EventVolunteerInput!) {
+ createEventVolunteer(data: $data) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete an event volunteer.
+ *
+ * @param id - The ID of the event volunteer being deleted.
+ * @returns The ID of the deleted event volunteer.
+ *
+ */
+export const DELETE_VOLUNTEER = gql`
+ mutation RemoveEventVolunteer($id: ID!) {
+ removeEventVolunteer(id: $id) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to create an event volunteer group.
+ *
+ * @param data - The data required to create an event volunteer group.
+ * - data contains following fileds:
+ * - eventId: string
+ * - leaderId: string
+ * - name: string
+ * - description?: string
+ * - volunteers: [string]
+ * - volunteersRequired?: number
+ * @returns The ID of the created event volunteer group.
+ *
+ */
+export const CREATE_VOLUNTEER_GROUP = gql`
+ mutation CreateEventVolunteerGroup($data: EventVolunteerGroupInput!) {
+ createEventVolunteerGroup(data: $data) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update an event volunteer group.
+ * @param id - The ID of the event volunteer group being updated.
+ * @param data - The data required to update an event volunteer group.
+ * @returns The ID of the updated event volunteer group.
+ *
+ */
+
+export const UPDATE_VOLUNTEER_GROUP = gql`
+ mutation UpdateEventVolunteerGroup(
+ $id: ID!
+ $data: UpdateEventVolunteerGroupInput!
+ ) {
+ updateEventVolunteerGroup(id: $id, data: $data) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete an event volunteer group.
+ *
+ * @param id - The ID of the event volunteer group being deleted.
+ * @returns The ID of the deleted event volunteer group.
+ *
+ */
+export const DELETE_VOLUNTEER_GROUP = gql`
+ mutation RemoveEventVolunteerGroup($id: ID!) {
+ removeEventVolunteerGroup(id: $id) {
+ _id
+ }
+ }
+`;
+
+export const CREATE_VOLUNTEER_MEMBERSHIP = gql`
+ mutation CreateVolunteerMembership($data: VolunteerMembershipInput!) {
+ createVolunteerMembership(data: $data) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update an event volunteer group.
+ *
+ * @param id - The ID of the event volunteer group being updated.
+ * @param data - The data required to update an event volunteer group.
+ * @returns The ID of the updated event volunteer group.
+ *
+ */
+export const UPDATE_VOLUNTEER_MEMBERSHIP = gql`
+ mutation UpdateVolunteerMembership($id: ID!, $status: String!) {
+ updateVolunteerMembership(id: $id, status: $status) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/FundMutation.ts b/src/GraphQl/Mutations/FundMutation.ts
new file mode 100644
index 0000000000..dd7bac8e5f
--- /dev/null
+++ b/src/GraphQl/Mutations/FundMutation.ts
@@ -0,0 +1,71 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a new fund.
+ *
+ * @param name - The name of the fund.
+ * @param organizationId - The organization ID the fund is associated with.
+ * @param refrenceNumber - The reference number of the fund.
+ * @param taxDeductible - Whether the fund is tax deductible.
+ * @param isArchived - Whether the fund is archived.
+ * @param isDefault - Whether the fund is the default.
+ * @returns The ID of the created fund.
+ */
+export const CREATE_FUND_MUTATION = gql`
+ mutation CreateFund(
+ $name: String!
+ $organizationId: ID!
+ $refrenceNumber: String
+ $taxDeductible: Boolean!
+ $isArchived: Boolean!
+ $isDefault: Boolean!
+ ) {
+ createFund(
+ data: {
+ name: $name
+ organizationId: $organizationId
+ refrenceNumber: $refrenceNumber
+ taxDeductible: $taxDeductible
+ isArchived: $isArchived
+ isDefault: $isDefault
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update a fund.
+ *
+ * @param id - The ID of the fund being updated.
+ * @param name - The name of the fund.
+ * @param refrenceNumber - The reference number of the fund.
+ * @param taxDeductible - Whether the fund is tax deductible.
+ * @param isArchived - Whether the fund is archived.
+ * @param isDefault - Whether the fund is the default.
+ * @returns The ID of the updated fund.
+ */
+export const UPDATE_FUND_MUTATION = gql`
+ mutation UpdateFund(
+ $id: ID!
+ $name: String
+ $refrenceNumber: String
+ $taxDeductible: Boolean
+ $isArchived: Boolean
+ $isDefault: Boolean
+ ) {
+ updateFund(
+ id: $id
+ data: {
+ name: $name
+ refrenceNumber: $refrenceNumber
+ taxDeductible: $taxDeductible
+ isArchived: $isArchived
+ isDefault: $isDefault
+ }
+ ) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/OrganizationMutations.ts b/src/GraphQl/Mutations/OrganizationMutations.ts
new file mode 100644
index 0000000000..68a0c9026c
--- /dev/null
+++ b/src/GraphQl/Mutations/OrganizationMutations.ts
@@ -0,0 +1,252 @@
+import gql from 'graphql-tag';
+
+// Changes the role of a user in an organization
+/**
+ * GraphQL mutation to update the role of a user in an organization.
+ *
+ * @param organizationId - The ID of the organization in which the user's role is being updated.
+ * @param userId - The ID of the user whose role is being updated.
+ * @param role - The new role to be assigned to the user in the organization.
+ * @returns The updated user object with the new role in the organization.
+ */
+export const UPDATE_USER_ROLE_IN_ORG_MUTATION = gql`
+ mutation updateUserRoleInOrganization(
+ $organizationId: ID!
+ $userId: ID!
+ $role: String!
+ ) {
+ updateUserRoleInOrganization(
+ organizationId: $organizationId
+ userId: $userId
+ role: $role
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to create a sample organization.
+ *
+ * @returns The created sample organization object.
+ */
+
+export const CREATE_SAMPLE_ORGANIZATION_MUTATION = gql`
+ mutation {
+ createSampleOrganization
+ }
+`;
+
+/**
+ * GraphQL mutation to remove a sample organization.
+ *
+ * @returns The removed sample organization object.
+ */
+
+export const REMOVE_SAMPLE_ORGANIZATION_MUTATION = gql`
+ mutation {
+ removeSampleOrganization
+ }
+`;
+
+/**
+ * GraphQL mutation to create a chat between users in an organization.
+ *
+ * @param userIds - An array of user IDs participating in the direct chat.
+ * @param organizationId - The ID of the organization where the direct chat is created.
+ * @returns The created direct chat object.
+ */
+
+export const CREATE_CHAT = gql`
+ mutation createChat(
+ $userIds: [ID!]!
+ $organizationId: ID
+ $isGroup: Boolean!
+ $name: String
+ ) {
+ createChat(
+ data: {
+ userIds: $userIds
+ organizationId: $organizationId
+ isGroup: $isGroup
+ name: $name
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+export const SEND_MESSAGE_TO_CHAT = gql`
+ mutation sendMessageToChat(
+ $chatId: ID!
+ $replyTo: ID
+ $messageContent: String!
+ ) {
+ sendMessageToChat(
+ chatId: $chatId
+ replyTo: $replyTo
+ messageContent: $messageContent
+ ) {
+ _id
+ createdAt
+ messageContent
+ replyTo {
+ _id
+ createdAt
+ messageContent
+ sender {
+ _id
+ firstName
+ lastName
+ }
+ updatedAt
+ }
+ sender {
+ _id
+ firstName
+ lastName
+ }
+ updatedAt
+ }
+ }
+`;
+
+export const MESSAGE_SENT_TO_CHAT = gql`
+ subscription messageSentToChat($userId: ID!) {
+ messageSentToChat(userId: $userId) {
+ _id
+ createdAt
+ chatMessageBelongsTo {
+ _id
+ }
+ messageContent
+ replyTo {
+ _id
+ createdAt
+ messageContent
+ sender {
+ _id
+ firstName
+ lastName
+ }
+ updatedAt
+ }
+ sender {
+ _id
+ firstName
+ lastName
+ }
+ updatedAt
+ }
+ }
+`;
+
+//Plugin WebSocket listner
+
+/**
+ * GraphQL subscription to listen for updates on plugins.
+ *
+ * @returns An object containing information about the updated plugin.
+ */
+
+export const PLUGIN_SUBSCRIPTION = gql`
+ subscription onPluginUpdate {
+ onPluginUpdate {
+ pluginName
+ _id
+ pluginDesc
+ uninstalledOrgs
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to toggle the pinned status of a post.
+ *
+ * @param id - The ID of the post to be toggled.
+ * @returns The updated post object with the new pinned status.
+ */
+
+export const TOGGLE_PINNED_POST = gql`
+ mutation TogglePostPin($id: ID!) {
+ togglePostPin(id: $id) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to add a custom field to an organization.
+ *
+ * @param organizationId - The ID of the organization where the custom field is being added.
+ * @param type - The type of the custom field (e.g., String, Number).
+ * @param name - The name of the custom field.
+ * @returns The added organization custom field object.
+ */
+
+export const ADD_CUSTOM_FIELD = gql`
+ mutation ($organizationId: ID!, $type: String!, $name: String!) {
+ addOrganizationCustomField(
+ organizationId: $organizationId
+ type: $type
+ name: $name
+ ) {
+ name
+ type
+ }
+ }
+`;
+
+// Handles custom organization fields
+
+/**
+ * GraphQL mutation to remove a custom field from an organization.
+ *
+ * @param organizationId - The ID of the organization from which the custom field is being removed.
+ * @param customFieldId - The ID of the custom field to be removed.
+ * @returns The removed organization custom field object.
+ */
+
+export const REMOVE_CUSTOM_FIELD = gql`
+ mutation ($organizationId: ID!, $customFieldId: ID!) {
+ removeOrganizationCustomField(
+ organizationId: $organizationId
+ customFieldId: $customFieldId
+ ) {
+ type
+ name
+ }
+ }
+`;
+
+export const SEND_MEMBERSHIP_REQUEST = gql`
+ mutation ($organizationId: ID!) {
+ sendMembershipRequest(organizationId: $organizationId) {
+ _id
+ organization {
+ _id
+ name
+ }
+ user {
+ _id
+ }
+ }
+ }
+`;
+
+export const JOIN_PUBLIC_ORGANIZATION = gql`
+ mutation ($organizationId: ID!) {
+ joinPublicOrganization(organizationId: $organizationId) {
+ _id
+ }
+ }
+`;
+
+export const CANCEL_MEMBERSHIP_REQUEST = gql`
+ mutation ($membershipRequestId: ID!) {
+ cancelMembershipRequest(membershipRequestId: $membershipRequestId) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/PledgeMutation.ts b/src/GraphQl/Mutations/PledgeMutation.ts
new file mode 100644
index 0000000000..321f7aa697
--- /dev/null
+++ b/src/GraphQl/Mutations/PledgeMutation.ts
@@ -0,0 +1,84 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a pledge.
+ *
+ * @param campaignId - The ID of the campaign the pledge is associated with.
+ * @param amount - The amount of the pledge.
+ * @param currency - The currency of the pledge.
+ * @param startDate - The start date of the pledge.
+ * @param endDate - The end date of the pledge.
+ * @param userIds - The IDs of the users associated with the pledge.
+ * @returns The ID of the created pledge.
+ */
+export const CREATE_PlEDGE = gql`
+ mutation CreateFundraisingCampaignPledge(
+ $campaignId: ID!
+ $amount: Float!
+ $currency: Currency!
+ $startDate: Date!
+ $endDate: Date!
+ $userIds: [ID!]!
+ ) {
+ createFundraisingCampaignPledge(
+ data: {
+ campaignId: $campaignId
+ amount: $amount
+ currency: $currency
+ startDate: $startDate
+ endDate: $endDate
+ userIds: $userIds
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update a pledge.
+ *
+ * @param id - The ID of the pledge being updated.
+ * @param amount - The amount of the pledge.
+ * @param currency - The currency of the pledge.
+ * @param startDate - The start date of the pledge.
+ * @param endDate - The end date of the pledge.
+ * @returns The ID of the updated pledge.
+ */
+export const UPDATE_PLEDGE = gql`
+ mutation UpdateFundraisingCampaignPledge(
+ $id: ID!
+ $amount: Float
+ $currency: Currency
+ $startDate: Date
+ $endDate: Date
+ $users: [ID!]
+ ) {
+ updateFundraisingCampaignPledge(
+ id: $id
+ data: {
+ users: $users
+ amount: $amount
+ currency: $currency
+ startDate: $startDate
+ endDate: $endDate
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete a pledge.
+ *
+ * @param id - The ID of the pledge being deleted.
+ * @returns Whether the pledge was successfully deleted.
+ */
+export const DELETE_PLEDGE = gql`
+ mutation DeleteFundraisingCampaignPledge($id: ID!) {
+ removeFundraisingCampaignPledge(id: $id) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/TagMutations.ts b/src/GraphQl/Mutations/TagMutations.ts
new file mode 100644
index 0000000000..9f8ed1ec61
--- /dev/null
+++ b/src/GraphQl/Mutations/TagMutations.ts
@@ -0,0 +1,123 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a user tag.
+ *
+ * @param name - Name of the tag.
+ * @param tagColor - Color of the tag.
+ * @param parentTagId - Id of the parent tag.
+ * @param organizationId - Organization to which the tag belongs.
+ */
+
+export const CREATE_USER_TAG = gql`
+ mutation CreateUserTag(
+ $name: String!
+ $tagColor: String
+ $parentTagId: ID
+ $organizationId: ID!
+ ) {
+ createUserTag(
+ input: {
+ name: $name
+ organizationId: $organizationId
+ parentTagId: $parentTagId
+ tagColor: $tagColor
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to unsssign a user tag from a user.
+ *
+ * @param tagId - Id the tag.
+ * @param userId - Id of the user to be unassigned.
+ */
+
+export const UNASSIGN_USER_TAG = gql`
+ mutation UnassignUserTag($tagId: ID!, $userId: ID!) {
+ unassignUserTag(input: { tagId: $tagId, userId: $userId }) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update a user tag.
+ *
+ * @param tagId - Id the tag.
+ * @param name - Updated name of the tag.
+ */
+
+export const UPDATE_USER_TAG = gql`
+ mutation UpdateUserTag($tagId: ID!, $name: String!) {
+ updateUserTag(input: { tagId: $tagId, name: $name }) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to remove a user tag.
+ *
+ * @param id - Id of the tag to be removed .
+ */
+
+export const REMOVE_USER_TAG = gql`
+ mutation RemoveUserTag($id: ID!) {
+ removeUserTag(id: $id) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to add people to tag.
+ *
+ * @param tagId - Id of the tag to be assigned.
+ * @param userIds - Ids of the users to assign to.
+ */
+
+export const ADD_PEOPLE_TO_TAG = gql`
+ mutation AddPeopleToUserTag($tagId: ID!, $userIds: [ID!]!) {
+ addPeopleToUserTag(input: { tagId: $tagId, userIds: $userIds }) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to assign people to multiple tags.
+ *
+ * @param currentTagId - Id of the current tag.
+ * @param selectedTagIds - Ids of the selected tags to be assined.
+ */
+
+export const ASSIGN_TO_TAGS = gql`
+ mutation AssignToUserTags($currentTagId: ID!, $selectedTagIds: [ID!]!) {
+ assignToUserTags(
+ input: { currentTagId: $currentTagId, selectedTagIds: $selectedTagIds }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to remove people from multiple tags.
+ *
+ * @param currentTagId - Id of the current tag.
+ * @param selectedTagIds - Ids of the selected tags to be removed from.
+ */
+
+export const REMOVE_FROM_TAGS = gql`
+ mutation RemoveFromUserTags($currentTagId: ID!, $selectedTagIds: [ID!]!) {
+ removeFromUserTags(
+ input: { currentTagId: $currentTagId, selectedTagIds: $selectedTagIds }
+ ) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/VenueMutations.ts b/src/GraphQl/Mutations/VenueMutations.ts
new file mode 100644
index 0000000000..44ccc1f63e
--- /dev/null
+++ b/src/GraphQl/Mutations/VenueMutations.ts
@@ -0,0 +1,79 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL mutation to create a venue.
+ *
+ * @param name - Name of the venue.
+ * @param capacity - Ineteger representing capacity of venue.
+ * @param description - Description of the venue.
+ * @param file - Image file for the venue.
+ * @param organizationId - Organization to which the ActionItemCategory belongs.
+ */
+
+export const CREATE_VENUE_MUTATION = gql`
+ mutation createVenue(
+ $capacity: Int!
+ $description: String
+ $file: String
+ $name: String!
+ $organizationId: ID!
+ ) {
+ createVenue(
+ data: {
+ capacity: $capacity
+ description: $description
+ file: $file
+ name: $name
+ organizationId: $organizationId
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to update a venue.
+ *
+ * @param id - The id of the Venue to be updated.
+ * @param capacity - Ineteger representing capacity of venue.
+ * @param description - Description of the venue.
+ * @param file - Image file for the venue.
+ * @param name - Name of the venue.
+ */
+
+export const UPDATE_VENUE_MUTATION = gql`
+ mutation editVenue(
+ $capacity: Int
+ $description: String
+ $file: String
+ $id: ID!
+ $name: String
+ ) {
+ editVenue(
+ data: {
+ capacity: $capacity
+ description: $description
+ file: $file
+ id: $id
+ name: $name
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+/**
+ * GraphQL mutation to delete a venue.
+ *
+ * @param id - The id of the Venue to be deleted.
+ */
+
+export const DELETE_VENUE_MUTATION = gql`
+ mutation DeleteVenue($id: ID!) {
+ deleteVenue(id: $id) {
+ _id
+ }
+ }
+`;
diff --git a/src/GraphQl/Mutations/mutations.ts b/src/GraphQl/Mutations/mutations.ts
new file mode 100644
index 0000000000..628328987e
--- /dev/null
+++ b/src/GraphQl/Mutations/mutations.ts
@@ -0,0 +1,742 @@
+import gql from 'graphql-tag';
+
+export const UNBLOCK_USER_MUTATION = gql`
+ mutation UnblockUser($userId: ID!, $orgId: ID!) {
+ unblockUser(organizationId: $orgId, userId: $userId) {
+ _id
+ }
+ }
+`;
+
+// to block the user
+
+export const BLOCK_USER_MUTATION = gql`
+ mutation BlockUser($userId: ID!, $orgId: ID!) {
+ blockUser(organizationId: $orgId, userId: $userId) {
+ _id
+ }
+ }
+`;
+
+// to reject the organization request
+
+export const REJECT_ORGANIZATION_REQUEST_MUTATION = gql`
+ mutation RejectMembershipRequest($id: ID!) {
+ rejectMembershipRequest(membershipRequestId: $id) {
+ _id
+ }
+ }
+`;
+
+// to accept the organization request
+
+export const ACCEPT_ORGANIZATION_REQUEST_MUTATION = gql`
+ mutation AcceptMembershipRequest($id: ID!) {
+ acceptMembershipRequest(membershipRequestId: $id) {
+ _id
+ }
+ }
+`;
+
+// to update the organization details
+
+export const UPDATE_ORGANIZATION_MUTATION = gql`
+ mutation UpdateOrganization(
+ $id: ID!
+ $name: String
+ $description: String
+ $address: AddressInput
+ $userRegistrationRequired: Boolean
+ $visibleInSearch: Boolean
+ $file: String
+ ) {
+ updateOrganization(
+ id: $id
+ data: {
+ name: $name
+ description: $description
+ userRegistrationRequired: $userRegistrationRequired
+ visibleInSearch: $visibleInSearch
+ address: $address
+ }
+ file: $file
+ ) {
+ _id
+ }
+ }
+`;
+
+// fragment for defining the Address input type.
+export const ADDRESS_DETAILS_FRAGMENT = gql`
+ fragment AddressDetails on AddressInput {
+ city: String
+ countryCode: String
+ dependentLocality: String
+ line1: String
+ line2: String
+ postalCode: String
+ sortingCode: String
+ state: String
+ }
+`;
+
+// to update the details of the user
+
+export const UPDATE_USER_MUTATION = gql`
+ mutation UpdateUserProfile(
+ $firstName: String
+ $lastName: String
+ $gender: Gender
+ $email: EmailAddress
+ $phoneNumber: PhoneNumber
+ $birthDate: Date
+ $grade: EducationGrade
+ $empStatus: EmploymentStatus
+ $maritalStatus: MaritalStatus
+ $address: String
+ $state: String
+ $country: String
+ $image: String
+ $appLanguageCode: String
+ ) {
+ updateUserProfile(
+ data: {
+ firstName: $firstName
+ lastName: $lastName
+ gender: $gender
+ email: $email
+ phone: { mobile: $phoneNumber }
+ birthDate: $birthDate
+ educationGrade: $grade
+ employmentStatus: $empStatus
+ maritalStatus: $maritalStatus
+ address: { line1: $address, state: $state, countryCode: $country }
+ appLanguageCode: $appLanguageCode
+ }
+ file: $image
+ ) {
+ _id
+ }
+ }
+`;
+
+// to update the password of user
+
+export const UPDATE_USER_PASSWORD_MUTATION = gql`
+ mutation UpdateUserPassword(
+ $previousPassword: String!
+ $newPassword: String!
+ $confirmNewPassword: String!
+ ) {
+ updateUserPassword(
+ data: {
+ previousPassword: $previousPassword
+ newPassword: $newPassword
+ confirmNewPassword: $confirmNewPassword
+ }
+ ) {
+ user {
+ _id
+ }
+ }
+ }
+`;
+
+// to sign up in the talawa admin
+
+export const SIGNUP_MUTATION = gql`
+ mutation SignUp(
+ $firstName: String!
+ $lastName: String!
+ $email: EmailAddress!
+ $password: String!
+ $orgId: ID!
+ ) {
+ signUp(
+ data: {
+ firstName: $firstName
+ lastName: $lastName
+ email: $email
+ password: $password
+ selectedOrganization: $orgId
+ }
+ ) {
+ user {
+ _id
+ }
+ accessToken
+ refreshToken
+ }
+ }
+`;
+
+// to login in the talawa admin
+
+export const LOGIN_MUTATION = gql`
+ mutation Login($email: EmailAddress!, $password: String!) {
+ login(data: { email: $email, password: $password }) {
+ user {
+ _id
+ firstName
+ lastName
+ image
+ email
+ }
+ appUserProfile {
+ adminFor {
+ _id
+ }
+ isSuperAdmin
+ appLanguageCode
+ }
+ accessToken
+ refreshToken
+ }
+ }
+`;
+
+// to get the refresh token
+
+export const REFRESH_TOKEN_MUTATION = gql`
+ mutation RefreshToken($refreshToken: String!) {
+ refreshToken(refreshToken: $refreshToken) {
+ refreshToken
+ accessToken
+ }
+ }
+`;
+
+// to revoke a refresh token
+
+export const REVOKE_REFRESH_TOKEN = gql`
+ mutation RevokeRefreshTokenForUser {
+ revokeRefreshTokenForUser
+ }
+`;
+
+// To verify the google recaptcha
+
+export const RECAPTCHA_MUTATION = gql`
+ mutation Recaptcha($recaptchaToken: String!) {
+ recaptcha(data: { recaptchaToken: $recaptchaToken })
+ }
+`;
+
+// to create the organization
+
+export const CREATE_ORGANIZATION_MUTATION = gql`
+ mutation CreateOrganization(
+ $description: String!
+ $address: AddressInput!
+ $name: String!
+ $visibleInSearch: Boolean!
+ $userRegistrationRequired: Boolean!
+ $image: String
+ ) {
+ createOrganization(
+ data: {
+ description: $description
+ address: $address
+ name: $name
+ visibleInSearch: $visibleInSearch
+ userRegistrationRequired: $userRegistrationRequired
+ }
+ file: $image
+ ) {
+ _id
+ }
+ }
+`;
+
+// to delete the organization
+
+export const DELETE_ORGANIZATION_MUTATION = gql`
+ mutation RemoveOrganization($id: ID!) {
+ removeOrganization(id: $id) {
+ user {
+ _id
+ }
+ }
+ }
+`;
+
+// to create the event by any organization
+
+export const CREATE_EVENT_MUTATION = gql`
+ mutation CreateEvent(
+ $title: String!
+ $description: String!
+ $recurring: Boolean!
+ $isPublic: Boolean!
+ $isRegisterable: Boolean!
+ $organizationId: ID!
+ $startDate: Date!
+ $endDate: Date!
+ $allDay: Boolean!
+ $startTime: Time
+ $endTime: Time
+ $location: String
+ $recurrenceStartDate: Date
+ $recurrenceEndDate: Date
+ $frequency: Frequency
+ $weekDays: [WeekDays]
+ $count: PositiveInt
+ $interval: PositiveInt
+ $weekDayOccurenceInMonth: Int
+ ) {
+ createEvent(
+ data: {
+ title: $title
+ description: $description
+ recurring: $recurring
+ isPublic: $isPublic
+ isRegisterable: $isRegisterable
+ organizationId: $organizationId
+ startDate: $startDate
+ endDate: $endDate
+ allDay: $allDay
+ startTime: $startTime
+ endTime: $endTime
+ location: $location
+ }
+ recurrenceRuleData: {
+ recurrenceStartDate: $recurrenceStartDate
+ recurrenceEndDate: $recurrenceEndDate
+ frequency: $frequency
+ weekDays: $weekDays
+ interval: $interval
+ count: $count
+ weekDayOccurenceInMonth: $weekDayOccurenceInMonth
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+// to delete any event by any organization
+
+export const DELETE_EVENT_MUTATION = gql`
+ mutation RemoveEvent(
+ $id: ID!
+ $recurringEventDeleteType: RecurringEventMutationType
+ ) {
+ removeEvent(id: $id, recurringEventDeleteType: $recurringEventDeleteType) {
+ _id
+ }
+ }
+`;
+
+// to remove an admin from an organization
+export const REMOVE_ADMIN_MUTATION = gql`
+ mutation RemoveAdmin($orgid: ID!, $userid: ID!) {
+ removeAdmin(data: { organizationId: $orgid, userId: $userid }) {
+ _id
+ }
+ }
+`;
+
+// to Remove member from an organization
+export const REMOVE_MEMBER_MUTATION = gql`
+ mutation RemoveMember($orgid: ID!, $userid: ID!) {
+ removeMember(data: { organizationId: $orgid, userId: $userid }) {
+ _id
+ }
+ }
+`;
+
+// to add the admin
+export const ADD_ADMIN_MUTATION = gql`
+ mutation CreateAdmin($orgid: ID!, $userid: ID!) {
+ createAdmin(data: { organizationId: $orgid, userId: $userid }) {
+ user {
+ _id
+ }
+ }
+ }
+`;
+
+export const ADD_MEMBER_MUTATION = gql`
+ mutation CreateMember($orgid: ID!, $userid: ID!) {
+ createMember(input: { organizationId: $orgid, userId: $userid }) {
+ organization {
+ _id
+ }
+ }
+ }
+`;
+
+export const CREATE_POST_MUTATION = gql`
+ mutation CreatePost(
+ $text: String!
+ $title: String!
+ $imageUrl: URL
+ $videoUrl: URL
+ $organizationId: ID!
+ $file: String
+ $pinned: Boolean
+ ) {
+ createPost(
+ data: {
+ text: $text
+ title: $title
+ imageUrl: $imageUrl
+ videoUrl: $videoUrl
+ organizationId: $organizationId
+ pinned: $pinned
+ }
+ file: $file
+ ) {
+ _id
+ }
+ }
+`;
+
+export const DELETE_POST_MUTATION = gql`
+ mutation RemovePost($id: ID!) {
+ removePost(id: $id) {
+ _id
+ }
+ }
+`;
+
+export const GENERATE_OTP_MUTATION = gql`
+ mutation Otp($email: EmailAddress!) {
+ otp(data: { email: $email }) {
+ otpToken
+ }
+ }
+`;
+
+export const FORGOT_PASSWORD_MUTATION = gql`
+ mutation ForgotPassword(
+ $userOtp: String!
+ $newPassword: String!
+ $otpToken: String!
+ ) {
+ forgotPassword(
+ data: {
+ userOtp: $userOtp
+ newPassword: $newPassword
+ otpToken: $otpToken
+ }
+ )
+ }
+`;
+
+/**
+ * {@label UPDATE_INSTALL_STATUS_PLUGIN_MUTATION}
+ * @remarks
+ * used to toggle `installStatus` (boolean value) of a Plugin
+ */
+export const UPDATE_INSTALL_STATUS_PLUGIN_MUTATION = gql`
+ mutation ($id: ID!, $orgId: ID!) {
+ updatePluginStatus(id: $id, orgId: $orgId) {
+ _id
+ pluginName
+ pluginCreatedBy
+ pluginDesc
+ uninstalledOrgs
+ }
+ }
+`;
+
+/**
+ * {@label UPDATE_ORG_STATUS_PLUGIN_MUTATION}
+ * @remarks
+ * used `updatePluginStatus`to add or remove the current Organization the in the plugin list `uninstalledOrgs`
+ */
+export const UPDATE_ORG_STATUS_PLUGIN_MUTATION = gql`
+ mutation update_install_status_plugin_mutation($id: ID!, $orgId: ID!) {
+ updatePluginStatus(id: $id, orgId: $orgId) {
+ _id
+ pluginName
+ pluginCreatedBy
+ pluginDesc
+ uninstalledOrgs
+ }
+ }
+`;
+
+/**
+ * {@label ADD_PLUGIN_MUTATION}
+ * @remarks
+ * used `createPlugin` to add new Plugin in database
+ */
+export const ADD_PLUGIN_MUTATION = gql`
+ mutation add_plugin_mutation(
+ $pluginName: String!
+ $pluginCreatedBy: String!
+ $pluginDesc: String!
+ ) {
+ createPlugin(
+ pluginName: $pluginName
+ pluginCreatedBy: $pluginCreatedBy
+ pluginDesc: $pluginDesc
+ ) {
+ _id
+ pluginName
+ pluginCreatedBy
+ pluginDesc
+ }
+ }
+`;
+export const ADD_ADVERTISEMENT_MUTATION = gql`
+ mutation (
+ $organizationId: ID!
+ $name: String!
+ $type: AdvertisementType!
+ $startDate: Date!
+ $endDate: Date!
+ $file: String!
+ ) {
+ createAdvertisement(
+ input: {
+ organizationId: $organizationId
+ name: $name
+ type: $type
+ startDate: $startDate
+ endDate: $endDate
+ mediaFile: $file
+ }
+ ) {
+ advertisement {
+ _id
+ }
+ }
+ }
+`;
+export const UPDATE_ADVERTISEMENT_MUTATION = gql`
+ mutation UpdateAdvertisement(
+ $id: ID!
+ $name: String
+ $file: String
+ $type: AdvertisementType
+ $startDate: Date
+ $endDate: Date
+ ) {
+ updateAdvertisement(
+ input: {
+ _id: $id
+ name: $name
+ mediaFile: $file
+ type: $type
+ startDate: $startDate
+ endDate: $endDate
+ }
+ ) {
+ advertisement {
+ _id
+ }
+ }
+ }
+`;
+export const DELETE_ADVERTISEMENT_BY_ID = gql`
+ mutation ($id: ID!) {
+ deleteAdvertisement(id: $id) {
+ advertisement {
+ _id
+ }
+ }
+ }
+`;
+export const UPDATE_POST_MUTATION = gql`
+ mutation UpdatePost(
+ $id: ID!
+ $title: String
+ $text: String
+ $imageUrl: String
+ $videoUrl: String
+ ) {
+ updatePost(
+ id: $id
+ data: {
+ title: $title
+ text: $text
+ imageUrl: $imageUrl
+ videoUrl: $videoUrl
+ }
+ ) {
+ _id
+ }
+ }
+`;
+
+export const UPDATE_EVENT_MUTATION = gql`
+ mutation UpdateEvent(
+ $id: ID!
+ $title: String
+ $description: String
+ $recurring: Boolean
+ $recurringEventUpdateType: RecurringEventMutationType
+ $isPublic: Boolean
+ $isRegisterable: Boolean
+ $allDay: Boolean
+ $startDate: Date
+ $endDate: Date
+ $startTime: Time
+ $endTime: Time
+ $location: String
+ $recurrenceStartDate: Date
+ $recurrenceEndDate: Date
+ $frequency: Frequency
+ $weekDays: [WeekDays]
+ $count: PositiveInt
+ $interval: PositiveInt
+ $weekDayOccurenceInMonth: Int
+ ) {
+ updateEvent(
+ id: $id
+ data: {
+ title: $title
+ description: $description
+ recurring: $recurring
+ isPublic: $isPublic
+ isRegisterable: $isRegisterable
+ allDay: $allDay
+ startDate: $startDate
+ endDate: $endDate
+ startTime: $startTime
+ endTime: $endTime
+ location: $location
+ }
+ recurrenceRuleData: {
+ recurrenceStartDate: $recurrenceStartDate
+ recurrenceEndDate: $recurrenceEndDate
+ frequency: $frequency
+ weekDays: $weekDays
+ interval: $interval
+ count: $count
+ weekDayOccurenceInMonth: $weekDayOccurenceInMonth
+ }
+ recurringEventUpdateType: $recurringEventUpdateType
+ ) {
+ _id
+ }
+ }
+`;
+
+export const LIKE_POST = gql`
+ mutation likePost($postId: ID!) {
+ likePost(id: $postId) {
+ _id
+ }
+ }
+`;
+
+export const UNLIKE_POST = gql`
+ mutation unlikePost($postId: ID!) {
+ unlikePost(id: $postId) {
+ _id
+ }
+ }
+`;
+
+export const REGISTER_EVENT = gql`
+ mutation registerForEvent($eventId: ID!) {
+ registerForEvent(id: $eventId) {
+ _id
+ }
+ }
+`;
+
+export const UPDATE_COMMUNITY = gql`
+ mutation updateCommunity($data: UpdateCommunityInput!) {
+ updateCommunity(data: $data)
+ }
+`;
+
+export const UPDATE_SESSION_TIMEOUT = gql`
+ mutation updateSessionTimeout($timeout: Int!) {
+ updateSessionTimeout(timeout: $timeout)
+ }
+`;
+
+export const RESET_COMMUNITY = gql`
+ mutation resetCommunity {
+ resetCommunity
+ }
+`;
+
+export const DONATE_TO_ORGANIZATION = gql`
+ mutation donate(
+ $userId: ID!
+ $createDonationOrgId2: ID!
+ $payPalId: ID!
+ $nameOfUser: String!
+ $amount: Float!
+ $nameOfOrg: String!
+ ) {
+ createDonation(
+ userId: $userId
+ orgId: $createDonationOrgId2
+ payPalId: $payPalId
+ nameOfUser: $nameOfUser
+ amount: $amount
+ nameOfOrg: $nameOfOrg
+ ) {
+ _id
+ amount
+ nameOfUser
+ nameOfOrg
+ }
+ }
+`;
+
+// Create and Update Action Item Categories
+export {
+ CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+} from './ActionItemCategoryMutations';
+
+// Create, Update and Delete Action Items
+export {
+ CREATE_ACTION_ITEM_MUTATION,
+ DELETE_ACTION_ITEM_MUTATION,
+ UPDATE_ACTION_ITEM_MUTATION,
+} from './ActionItemMutations';
+
+export {
+ CREATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+ UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+} from './AgendaCategoryMutations';
+
+export {
+ CREATE_AGENDA_ITEM_MUTATION,
+ DELETE_AGENDA_ITEM_MUTATION,
+ UPDATE_AGENDA_ITEM_MUTATION,
+} from './AgendaItemMutations';
+
+// Changes the role of a event in an organization and add and remove the event from the organization
+export {
+ ADD_EVENT_ATTENDEE,
+ MARK_CHECKIN,
+ REMOVE_EVENT_ATTENDEE,
+} from './EventAttendeeMutations';
+
+// Create the new comment on a post and Like and Unlike the comment
+export {
+ CREATE_COMMENT_POST,
+ LIKE_COMMENT,
+ UNLIKE_COMMENT,
+} from './CommentMutations';
+
+// Changes the role of a user in an organization
+export {
+ ADD_CUSTOM_FIELD,
+ CREATE_SAMPLE_ORGANIZATION_MUTATION,
+ JOIN_PUBLIC_ORGANIZATION,
+ PLUGIN_SUBSCRIPTION,
+ REMOVE_CUSTOM_FIELD,
+ REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+ SEND_MEMBERSHIP_REQUEST,
+ TOGGLE_PINNED_POST,
+ UPDATE_USER_ROLE_IN_ORG_MUTATION,
+} from './OrganizationMutations';
+
+export {
+ CREATE_VENUE_MUTATION,
+ DELETE_VENUE_MUTATION,
+ UPDATE_VENUE_MUTATION,
+} from './VenueMutations';
diff --git a/src/GraphQl/Queries/ActionItemCategoryQueries.ts b/src/GraphQl/Queries/ActionItemCategoryQueries.ts
new file mode 100644
index 0000000000..db0db119e3
--- /dev/null
+++ b/src/GraphQl/Queries/ActionItemCategoryQueries.ts
@@ -0,0 +1,32 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve action item categories by organization.
+ *
+ * @param organizationId - The ID of the organization for which action item categories are being retrieved.
+ * @returns The list of action item categories associated with the organization.
+ */
+
+export const ACTION_ITEM_CATEGORY_LIST = gql`
+ query ActionItemCategoriesByOrganization(
+ $organizationId: ID!
+ $where: ActionItemCategoryWhereInput
+ $orderBy: ActionItemsOrderByInput
+ ) {
+ actionItemCategoriesByOrganization(
+ organizationId: $organizationId
+ where: $where
+ orderBy: $orderBy
+ ) {
+ _id
+ name
+ isDisabled
+ createdAt
+ creator {
+ _id
+ firstName
+ lastName
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/ActionItemQueries.ts b/src/GraphQl/Queries/ActionItemQueries.ts
new file mode 100644
index 0000000000..7b24dd6138
--- /dev/null
+++ b/src/GraphQl/Queries/ActionItemQueries.ts
@@ -0,0 +1,128 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve action item categories by organization.
+ *
+ * @param organizationId - The ID of the organization for which action item categories are being retrieved.
+ * @param orderBy - Sort action items Latest/Earliest first.
+ * @param actionItemCategory_id - Filter action items belonging to an action item category.
+ * @param event_id - Filter action items belonging to an event.
+ * @param is_completed - Filter all the completed action items.
+ * @returns The list of action item categories associated with the organization.
+ */
+
+export const ACTION_ITEM_LIST = gql`
+ query ActionItemsByOrganization(
+ $organizationId: ID!
+ $eventId: ID
+ $where: ActionItemWhereInput
+ $orderBy: ActionItemsOrderByInput
+ ) {
+ actionItemsByOrganization(
+ organizationId: $organizationId
+ eventId: $eventId
+ orderBy: $orderBy
+ where: $where
+ ) {
+ _id
+ assignee {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ assigneeGroup {
+ _id
+ name
+ }
+ assigneeUser {
+ _id
+ firstName
+ lastName
+ image
+ }
+ assigneeType
+ assigner {
+ _id
+ firstName
+ lastName
+ image
+ }
+ actionItemCategory {
+ _id
+ name
+ }
+ preCompletionNotes
+ postCompletionNotes
+ assignmentDate
+ dueDate
+ completionDate
+ isCompleted
+ event {
+ _id
+ title
+ }
+ creator {
+ _id
+ firstName
+ lastName
+ }
+ allottedHours
+ }
+ }
+`;
+
+export const ACTION_ITEMS_BY_USER = gql`
+ query ActionItemsByUser(
+ $userId: ID!
+ $where: ActionItemWhereInput
+ $orderBy: ActionItemsOrderByInput
+ ) {
+ actionItemsByUser(userId: $userId, where: $where, orderBy: $orderBy) {
+ _id
+ assignee {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ assigneeGroup {
+ _id
+ name
+ }
+ assigneeType
+ assigner {
+ _id
+ firstName
+ lastName
+ image
+ }
+ actionItemCategory {
+ _id
+ name
+ }
+ preCompletionNotes
+ postCompletionNotes
+ assignmentDate
+ dueDate
+ completionDate
+ isCompleted
+ event {
+ _id
+ title
+ }
+ creator {
+ _id
+ firstName
+ lastName
+ }
+ allottedHours
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/AgendaCategoryQueries.ts b/src/GraphQl/Queries/AgendaCategoryQueries.ts
new file mode 100644
index 0000000000..f766337c22
--- /dev/null
+++ b/src/GraphQl/Queries/AgendaCategoryQueries.ts
@@ -0,0 +1,23 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve agenda category by id.
+ *
+ * @param agendaCategoryId - The ID of the category which is being retrieved.
+ * @returns Agenda category associated with the id.
+ */
+
+export const AGENDA_ITEM_CATEGORY_LIST = gql`
+ query AgendaItemCategoriesByOrganization($organizationId: ID!) {
+ agendaItemCategoriesByOrganization(organizationId: $organizationId) {
+ _id
+ name
+ description
+ createdBy {
+ _id
+ firstName
+ lastName
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/AgendaItemQueries.ts b/src/GraphQl/Queries/AgendaItemQueries.ts
new file mode 100644
index 0000000000..92957983c8
--- /dev/null
+++ b/src/GraphQl/Queries/AgendaItemQueries.ts
@@ -0,0 +1,73 @@
+import gql from 'graphql-tag';
+
+export const AgendaItemByOrganization = gql`
+ query AgendaItemByOrganization($organizationId: ID!) {
+ agendaItemByOrganization(organizationId: $organizationId) {
+ _id
+ title
+ description
+ duration
+ attachments
+ createdBy {
+ _id
+ firstName
+ lastName
+ }
+ urls
+ users {
+ _id
+ firstName
+ lastName
+ }
+ categories {
+ _id
+ name
+ }
+ sequence
+ organization {
+ _id
+ name
+ }
+ relatedEvent {
+ _id
+ title
+ }
+ }
+ }
+`;
+
+export const AgendaItemByEvent = gql`
+ query AgendaItemByEvent($relatedEventId: ID!) {
+ agendaItemByEvent(relatedEventId: $relatedEventId) {
+ _id
+ title
+ description
+ duration
+ attachments
+ createdBy {
+ _id
+ firstName
+ lastName
+ }
+ urls
+ users {
+ _id
+ firstName
+ lastName
+ }
+ sequence
+ categories {
+ _id
+ name
+ }
+ organization {
+ _id
+ name
+ }
+ relatedEvent {
+ _id
+ title
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/EventVolunteerQueries.ts b/src/GraphQl/Queries/EventVolunteerQueries.ts
new file mode 100644
index 0000000000..f4a3a02564
--- /dev/null
+++ b/src/GraphQl/Queries/EventVolunteerQueries.ts
@@ -0,0 +1,134 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve event volunteers.
+ *
+ * @param where - The filter to apply to the query.
+ * @param orderBy - The order in which to return the results.
+ * @returns The list of event volunteers.
+ *
+ **/
+
+export const EVENT_VOLUNTEER_LIST = gql`
+ query GetEventVolunteers(
+ $where: EventVolunteerWhereInput!
+ $orderBy: EventVolunteersOrderByInput
+ ) {
+ getEventVolunteers(where: $where, orderBy: $orderBy) {
+ _id
+ hasAccepted
+ hoursVolunteered
+ user {
+ _id
+ firstName
+ lastName
+ image
+ }
+ assignments {
+ _id
+ }
+ groups {
+ _id
+ name
+ volunteers {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+export const EVENT_VOLUNTEER_GROUP_LIST = gql`
+ query GetEventVolunteerGroups(
+ $where: EventVolunteerGroupWhereInput!
+ $orderBy: EventVolunteerGroupOrderByInput
+ ) {
+ getEventVolunteerGroups(where: $where, orderBy: $orderBy) {
+ _id
+ name
+ description
+ volunteersRequired
+ createdAt
+ creator {
+ _id
+ firstName
+ lastName
+ image
+ }
+ leader {
+ _id
+ firstName
+ lastName
+ image
+ }
+ volunteers {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ assignments {
+ _id
+ actionItemCategory {
+ _id
+ name
+ }
+ allottedHours
+ isCompleted
+ }
+ event {
+ _id
+ }
+ }
+ }
+`;
+
+export const USER_VOLUNTEER_MEMBERSHIP = gql`
+ query GetVolunteerMembership(
+ $where: VolunteerMembershipWhereInput!
+ $orderBy: VolunteerMembershipOrderByInput
+ ) {
+ getVolunteerMembership(where: $where, orderBy: $orderBy) {
+ _id
+ status
+ createdAt
+ event {
+ _id
+ title
+ startDate
+ }
+ volunteer {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ group {
+ _id
+ name
+ }
+ }
+ }
+`;
+
+export const VOLUNTEER_RANKING = gql`
+ query GetVolunteerRanks($orgId: ID!, $where: VolunteerRankWhereInput!) {
+ getVolunteerRanks(orgId: $orgId, where: $where) {
+ rank
+ hoursVolunteered
+ user {
+ _id
+ lastName
+ firstName
+ image
+ email
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/OrganizationQueries.ts b/src/GraphQl/Queries/OrganizationQueries.ts
new file mode 100644
index 0000000000..3329390783
--- /dev/null
+++ b/src/GraphQl/Queries/OrganizationQueries.ts
@@ -0,0 +1,380 @@
+// OrganizationQueries.js
+import gql from 'graphql-tag';
+
+// display posts
+
+/**
+ * GraphQL query to retrieve the list of organizations.
+ *
+ * @param first - Optional. Number of organizations to retrieve in the first batch.
+ * @param skip - Optional. Number of organizations to skip before starting to collect the result set.
+ * @param filter - Optional. Filter organizations by a specified string.
+ * @param id - Optional. The ID of a specific organization to retrieve.
+ * @returns The list of organizations based on the applied filters.
+ */
+export const ORGANIZATION_POST_LIST = gql`
+ query Organizations(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ ) {
+ organizations(id: $id) {
+ posts(after: $after, before: $before, first: $first, last: $last) {
+ edges {
+ node {
+ _id
+ title
+ text
+ creator {
+ _id
+ firstName
+ lastName
+ email
+ }
+ createdAt
+ likeCount
+ likedBy {
+ _id
+ firstName
+ lastName
+ }
+ commentCount
+ comments {
+ _id
+ text
+ creator {
+ _id
+ }
+ createdAt
+ likeCount
+ likedBy {
+ _id
+ }
+ }
+ pinned
+ }
+ cursor
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve the list of user tags belonging to an organization.
+ *
+ * @param id - ID of the organization.
+ * @param first - Number of tags to retrieve "after" (if provided) a certain tag.
+ * @param after - Id of the last tag on the current page.
+ * @param last - Number of tags to retrieve "before" (if provided) a certain tag.
+ * @param before - Id of the first tag on the current page.
+ * @returns The list of organizations based on the applied filters.
+ */
+
+export const ORGANIZATION_USER_TAGS_LIST = gql`
+ query Organizations(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ $where: UserTagWhereInput
+ $sortedBy: UserTagSortedByInput
+ ) {
+ organizations(id: $id) {
+ userTags(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: $where
+ sortedBy: $sortedBy
+ ) {
+ edges {
+ node {
+ _id
+ name
+ parentTag {
+ _id
+ }
+ usersAssignedTo(first: $first, last: $last) {
+ totalCount
+ }
+ childTags(first: $first, last: $last) {
+ totalCount
+ }
+ ancestorTags {
+ _id
+ name
+ }
+ }
+ cursor
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ }
+ }
+`;
+
+export const ORGANIZATION_ADVERTISEMENT_LIST = gql`
+ query Organizations(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: Int
+ $last: Int
+ ) {
+ organizations(id: $id) {
+ _id
+ advertisements(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ ) {
+ edges {
+ node {
+ _id
+ name
+ startDate
+ endDate
+ mediaUrl
+ }
+ cursor
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve organizations based on user connection.
+ *
+ * @param first - Optional. Number of organizations to retrieve in the first batch.
+ * @param skip - Optional. Number of organizations to skip before starting to collect the result set.
+ * @param filter - Optional. Filter organizations by a specified string.
+ * @param id - Optional. The ID of a specific organization to retrieve.
+ * @returns The list of organizations based on the applied filters.
+ */
+
+export const USER_ORGANIZATION_CONNECTION = gql`
+ query organizationsConnection(
+ $first: Int
+ $skip: Int
+ $filter: String
+ $id: ID
+ ) {
+ organizationsConnection(
+ first: $first
+ skip: $skip
+ where: { name_contains: $filter, id: $id }
+ orderBy: name_ASC
+ ) {
+ _id
+ name
+ image
+ description
+ userRegistrationRequired
+ creator {
+ firstName
+ lastName
+ }
+ members {
+ _id
+ }
+ admins {
+ _id
+ }
+ createdAt
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ membershipRequests {
+ _id
+ user {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve organizations joined by a user.
+ *
+ * @param id - The ID of the user for which joined organizations are being retrieved.
+ * @returns The list of organizations joined by the user.
+ */
+
+export const USER_JOINED_ORGANIZATIONS = gql`
+ query UserJoinedOrganizations($id: ID!) {
+ users(where: { id: $id }) {
+ user {
+ joinedOrganizations {
+ _id
+ name
+ description
+ image
+ members {
+ _id
+ }
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ admins {
+ _id
+ }
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve organizations created by a user.
+ *
+ * @param id - The ID of the user for which created organizations are being retrieved.
+ * @returns The list of organizations created by the user.
+ */
+
+export const USER_CREATED_ORGANIZATIONS = gql`
+ query UserCreatedOrganizations($id: ID!) {
+ users(where: { id: $id }) {
+ appUserProfile {
+ createdOrganizations {
+ _id
+ name
+ description
+ image
+ members {
+ _id
+ }
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ admins {
+ _id
+ }
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve the list of admins for a specific organization.
+ *
+ * @param id - The ID of the organization for which admins are being retrieved.
+ * @returns The list of admins associated with the organization.
+ */
+
+export const ORGANIZATION_ADMINS_LIST = gql`
+ query Organizations($id: ID!) {
+ organizations(id: $id) {
+ _id
+ admins {
+ _id
+ image
+ firstName
+ lastName
+ email
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve the list of members for a specific organization.
+ *
+ * @param id - The ID of the organization for which members are being retrieved.
+ * @returns The list of members associated with the organization.
+ */
+export const ORGANIZATION_FUNDS = gql`
+ query Organizations($id: ID!) {
+ organizations(id: $id) {
+ funds {
+ _id
+ name
+ refrenceNumber
+ taxDeductible
+ isArchived
+ isDefault
+ createdAt
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve the list of venues for a specific organization.
+ *
+ * @param id - The ID of the organization for which venues are being retrieved.
+ * @returns The list of venues associated with the organization.
+ */
+export const VENUE_LIST = gql`
+ query GetVenueByOrgId(
+ $orgId: ID!
+ $first: Int
+ $orderBy: VenueOrderByInput
+ $where: VenueWhereInput
+ ) {
+ getVenueByOrgId(
+ orgId: $orgId
+ first: $first
+ orderBy: $orderBy
+ where: $where
+ ) {
+ _id
+ capacity
+ name
+ description
+ imageUrl
+ organization {
+ _id
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/PlugInQueries.ts b/src/GraphQl/Queries/PlugInQueries.ts
new file mode 100644
index 0000000000..508da522e9
--- /dev/null
+++ b/src/GraphQl/Queries/PlugInQueries.ts
@@ -0,0 +1,267 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve a list of plugins.
+ *
+ * @returns The list of plugins with details such as ID, name, creator, description, and uninstalled organizations.
+ */
+
+export const PLUGIN_GET = gql`
+ query getPluginList {
+ getPlugins {
+ _id
+ pluginName
+ pluginCreatedBy
+ pluginDesc
+ uninstalledOrgs
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve a list of advertisements.
+ *
+ * @returns The list of advertisements with details such as ID, name, type, organization ID, link, start date, and end date.
+ */
+
+export const ADVERTISEMENTS_GET = gql`
+ query getAdvertisements {
+ advertisementsConnection {
+ edges {
+ node {
+ _id
+ name
+ type
+ organization {
+ _id
+ }
+ mediaUrl
+ endDate
+ startDate
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve a list of events based on organization connection.
+ *
+ * @param organization_id - The ID of the organization for which events are being retrieved.
+ * @param title_contains - Optional. Filter events by title containing a specified string.
+ * @param description_contains - Optional. Filter events by description containing a specified string.
+ * @param location_contains - Optional. Filter events by location containing a specified string.
+ * @param first - Optional. Number of events to retrieve in the first batch.
+ * @param skip - Optional. Number of events to skip before starting to collect the result set.
+ * @returns The list of events associated with the organization based on the applied filters.
+ */
+
+export const ORGANIZATION_EVENTS_CONNECTION = gql`
+ query EventsByOrganizationConnection(
+ $organization_id: ID!
+ $title_contains: String
+ $description_contains: String
+ $location_contains: String
+ $first: Int
+ $skip: Int
+ ) {
+ eventsByOrganizationConnection(
+ where: {
+ organization_id: $organization_id
+ title_contains: $title_contains
+ description_contains: $description_contains
+ location_contains: $location_contains
+ }
+ first: $first
+ skip: $skip
+ ) {
+ _id
+ title
+ description
+ startDate
+ endDate
+ location
+ startTime
+ endTime
+ allDay
+ recurring
+ isPublic
+ isRegisterable
+ creator {
+ _id
+ firstName
+ lastName
+ }
+ attendees {
+ _id
+ }
+ }
+ }
+`;
+
+export const USER_EVENTS_VOLUNTEER = gql`
+ query UserEventsVolunteer(
+ $organization_id: ID!
+ $title_contains: String
+ $location_contains: String
+ $first: Int
+ $skip: Int
+ $upcomingOnly: Boolean
+ ) {
+ eventsByOrganizationConnection(
+ where: {
+ organization_id: $organization_id
+ title_contains: $title_contains
+ location_contains: $location_contains
+ }
+ first: $first
+ skip: $skip
+ upcomingOnly: $upcomingOnly
+ ) {
+ _id
+ title
+ startDate
+ endDate
+ location
+ startTime
+ endTime
+ allDay
+ recurring
+ volunteerGroups {
+ _id
+ name
+ volunteersRequired
+ description
+ volunteers {
+ _id
+ }
+ }
+ volunteers {
+ _id
+ user {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve a list of chats based on user ID.
+ *
+ * @param id - The ID of the user for which chats are being retrieved.
+ * @returns The list of chats associated with the user, including details such as ID, creator, messages, organization, and participating users.
+ */
+
+export const CHAT_BY_ID = gql`
+ query chatById($id: ID!) {
+ chatById(id: $id) {
+ _id
+ isGroup
+ name
+ organization {
+ _id
+ }
+ createdAt
+ messages {
+ _id
+ createdAt
+ messageContent
+ replyTo {
+ _id
+ createdAt
+ messageContent
+ sender {
+ _id
+ firstName
+ lastName
+ email
+ image
+ }
+ }
+ sender {
+ _id
+ firstName
+ lastName
+ email
+ image
+ }
+ }
+ users {
+ _id
+ firstName
+ lastName
+ email
+ }
+ }
+ }
+`;
+
+export const CHATS_LIST = gql`
+ query ChatsByUserId($id: ID!) {
+ chatsByUserId(id: $id) {
+ _id
+ isGroup
+ name
+
+ creator {
+ _id
+ firstName
+ lastName
+ email
+ }
+ messages {
+ _id
+ createdAt
+ messageContent
+ sender {
+ _id
+ firstName
+ lastName
+ email
+ }
+ }
+ organization {
+ _id
+ name
+ }
+ users {
+ _id
+ firstName
+ lastName
+ email
+ image
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to check if an organization is a sample organization.
+ *
+ * @param isSampleOrganizationId - The ID of the organization being checked.
+ * @returns A boolean indicating whether the organization is a sample organization.
+ */
+
+export const IS_SAMPLE_ORGANIZATION_QUERY = gql`
+ query ($isSampleOrganizationId: ID!) {
+ isSampleOrganization(id: $isSampleOrganizationId)
+ }
+`;
+
+/**
+ * GraphQL query to retrieve custom fields for a specific organization.
+ *
+ * @param customFieldsByOrganizationId - The ID of the organization for which custom fields are being retrieved.
+ * @returns The list of custom fields associated with the organization, including details such as ID, type, and name.
+ */
+
+export const ORGANIZATION_CUSTOM_FIELDS = gql`
+ query ($customFieldsByOrganizationId: ID!) {
+ customFieldsByOrganization(id: $customFieldsByOrganizationId) {
+ _id
+ type
+ name
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/Queries.ts b/src/GraphQl/Queries/Queries.ts
new file mode 100644
index 0000000000..81442cbad2
--- /dev/null
+++ b/src/GraphQl/Queries/Queries.ts
@@ -0,0 +1,890 @@
+import gql from 'graphql-tag';
+
+//Query List
+// Check Auth
+export const CHECK_AUTH = gql`
+ query {
+ checkAuth {
+ _id
+ firstName
+ lastName
+ createdAt
+ image
+ email
+ birthDate
+ educationGrade
+ employmentStatus
+ gender
+ maritalStatus
+ phone {
+ mobile
+ }
+ address {
+ line1
+ state
+ countryCode
+ }
+ eventsAttended {
+ _id
+ }
+ }
+ }
+`;
+
+// Query to take the Organization list
+export const ORGANIZATION_LIST = gql`
+ query {
+ organizations {
+ _id
+ image
+ creator {
+ firstName
+ lastName
+ }
+ name
+ members {
+ _id
+ }
+ admins {
+ _id
+ }
+ createdAt
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ }
+ }
+`;
+
+// Query to take the Organization list with filter and sort option
+export const ORGANIZATION_CONNECTION_LIST = gql`
+ query OrganizationsConnection(
+ $filter: String
+ $first: Int
+ $skip: Int
+ $orderBy: OrganizationOrderByInput
+ ) {
+ organizationsConnection(
+ where: { name_contains: $filter }
+ first: $first
+ skip: $skip
+ orderBy: $orderBy
+ ) {
+ _id
+ image
+ creator {
+ firstName
+ lastName
+ }
+ name
+ members {
+ _id
+ }
+ admins {
+ _id
+ }
+ createdAt
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ }
+ }
+`;
+
+// Query to take the User list
+export const USER_LIST = gql`
+ query Users(
+ $firstName_contains: String
+ $lastName_contains: String
+ $skip: Int
+ $first: Int
+ $order: UserOrderByInput
+ ) {
+ users(
+ where: {
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ skip: $skip
+ first: $first
+ orderBy: $order
+ ) {
+ user {
+ _id
+ joinedOrganizations {
+ _id
+ name
+ image
+ createdAt
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ creator {
+ _id
+ firstName
+ lastName
+ image
+ email
+ }
+ }
+ firstName
+ lastName
+ email
+ image
+ createdAt
+ registeredEvents {
+ _id
+ }
+ organizationsBlockedBy {
+ _id
+ name
+ image
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ creator {
+ _id
+ firstName
+ lastName
+ image
+ email
+ }
+ createdAt
+ }
+ membershipRequests {
+ _id
+ }
+ }
+ appUserProfile {
+ _id
+ adminFor {
+ _id
+ }
+ isSuperAdmin
+ createdOrganizations {
+ _id
+ }
+ createdEvents {
+ _id
+ }
+ eventAdmin {
+ _id
+ }
+ }
+ }
+ }
+`;
+export const USER_LIST_FOR_TABLE = gql`
+ query Users($firstName_contains: String, $lastName_contains: String) {
+ users(
+ where: {
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ ) {
+ user {
+ _id
+ firstName
+ lastName
+ email
+ image
+ createdAt
+ }
+ }
+ }
+`;
+
+export const USER_LIST_REQUEST = gql`
+ query Users(
+ $firstName_contains: String
+ $lastName_contains: String
+ $first: Int
+ $skip: Int
+ ) {
+ users(
+ where: {
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ skip: $skip
+ first: $first
+ ) {
+ user {
+ firstName
+ lastName
+ image
+ _id
+ email
+ createdAt
+ }
+ appUserProfile {
+ _id
+ adminFor {
+ _id
+ }
+ isSuperAdmin
+ createdOrganizations {
+ _id
+ }
+ createdEvents {
+ _id
+ }
+ eventAdmin {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+export const EVENT_DETAILS = gql`
+ query Event($id: ID!) {
+ event(id: $id) {
+ _id
+ title
+ description
+ startDate
+ endDate
+ startTime
+ endTime
+ allDay
+ location
+ recurring
+ baseRecurringEvent {
+ _id
+ }
+ organization {
+ _id
+ members {
+ _id
+ firstName
+ lastName
+ }
+ }
+ attendees {
+ _id
+ }
+ }
+ }
+`;
+
+export const RECURRING_EVENTS = gql`
+ query RecurringEvents($baseRecurringEventId: ID!) {
+ getRecurringEvents(baseRecurringEventId: $baseRecurringEventId) {
+ _id
+ startDate
+ title
+ attendees {
+ _id
+ gender
+ }
+ }
+ }
+`;
+
+export const EVENT_ATTENDEES = gql`
+ query Event($id: ID!) {
+ event(id: $id) {
+ attendees {
+ _id
+ firstName
+ lastName
+ createdAt
+ gender
+ birthDate
+ eventsAttended {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+export const EVENT_CHECKINS = gql`
+ query eventCheckIns($id: ID!) {
+ event(id: $id) {
+ _id
+ attendeesCheckInStatus {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ }
+ checkIn {
+ _id
+ time
+ }
+ }
+ }
+ }
+`;
+
+export const EVENT_FEEDBACKS = gql`
+ query eventFeedback($id: ID!) {
+ event(id: $id) {
+ _id
+ feedback {
+ _id
+ rating
+ review
+ }
+ averageFeedbackScore
+ }
+ }
+`;
+
+// Query to take the Organization with data
+export const ORGANIZATIONS_LIST = gql`
+ query Organizations($id: ID!) {
+ organizations(id: $id) {
+ _id
+ image
+ creator {
+ firstName
+ lastName
+ email
+ }
+ name
+ description
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ userRegistrationRequired
+ visibleInSearch
+ members {
+ _id
+ firstName
+ lastName
+ email
+ }
+ admins {
+ _id
+ firstName
+ lastName
+ email
+ createdAt
+ }
+ membershipRequests {
+ _id
+ user {
+ firstName
+ lastName
+ email
+ }
+ }
+ blockedUsers {
+ _id
+ firstName
+ lastName
+ email
+ }
+ }
+ }
+`;
+
+// Query to take the Members of a particular organization
+export const MEMBERS_LIST = gql`
+ query Organizations($id: ID!) {
+ organizations(id: $id) {
+ _id
+ members {
+ _id
+ firstName
+ lastName
+ image
+ email
+ createdAt
+ organizationsBlockedBy {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+export const BLOCK_PAGE_MEMBER_LIST = gql`
+ query Organizations(
+ $orgId: ID!
+ $firstName_contains: String
+ $lastName_contains: String
+ ) {
+ organizationsMemberConnection(
+ orgId: $orgId
+ where: {
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ ) {
+ edges {
+ _id
+ firstName
+ lastName
+ email
+ organizationsBlockedBy {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+// Query to filter out all the members with the macthing query and a particular OrgId
+export const ORGANIZATIONS_MEMBER_CONNECTION_LIST = gql`
+ query Organizations(
+ $orgId: ID!
+ $firstName_contains: String
+ $lastName_contains: String
+ $first: Int
+ $skip: Int
+ ) {
+ organizationsMemberConnection(
+ orgId: $orgId
+ first: $first
+ skip: $skip
+ where: {
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ ) {
+ edges {
+ _id
+ firstName
+ lastName
+ image
+ email
+ createdAt
+ }
+ }
+ }
+`;
+
+// To take the list of the oranization joined by a user
+export const USER_ORGANIZATION_LIST = gql`
+ query User($userId: ID!) {
+ user(id: $userId) {
+ user {
+ firstName
+ email
+ image
+ lastName
+ }
+ }
+ }
+`;
+
+// To take the details of a user
+export const USER_DETAILS = gql`
+ query User(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ ) {
+ user(id: $id) {
+ user {
+ _id
+ eventsAttended {
+ _id
+ }
+ joinedOrganizations {
+ _id
+ }
+ firstName
+ lastName
+ email
+ image
+ createdAt
+ birthDate
+ educationGrade
+ employmentStatus
+ gender
+ maritalStatus
+ phone {
+ mobile
+ }
+ address {
+ line1
+ countryCode
+ city
+ state
+ }
+ tagsAssignedWith(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ ) {
+ edges {
+ node {
+ _id
+ name
+ parentTag {
+ _id
+ }
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ registeredEvents {
+ _id
+ }
+ membershipRequests {
+ _id
+ }
+ }
+ appUserProfile {
+ _id
+ adminFor {
+ _id
+ }
+ isSuperAdmin
+ appLanguageCode
+ pluginCreationAllowed
+ createdOrganizations {
+ _id
+ }
+ createdEvents {
+ _id
+ }
+ eventAdmin {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+// to take the organization event list
+export const ORGANIZATION_EVENT_LIST = gql`
+ query EventsByOrganization($id: ID!) {
+ eventsByOrganization(id: $id) {
+ _id
+ title
+ description
+ startDate
+ endDate
+ location
+ startTime
+ endTime
+ allDay
+ recurring
+ isPublic
+ isRegisterable
+ }
+ }
+`;
+
+export const ORGANIZATION_EVENT_CONNECTION_LIST = gql`
+ query EventsByOrganizationConnection(
+ $organization_id: ID!
+ $title_contains: String
+ $description_contains: String
+ $location_contains: String
+ $first: Int
+ $skip: Int
+ ) {
+ eventsByOrganizationConnection(
+ where: {
+ organization_id: $organization_id
+ title_contains: $title_contains
+ description_contains: $description_contains
+ location_contains: $location_contains
+ }
+ first: $first
+ skip: $skip
+ ) {
+ _id
+ title
+ description
+ startDate
+ endDate
+ location
+ startTime
+ endTime
+ allDay
+ recurring
+ attendees {
+ _id
+ createdAt
+ firstName
+ lastName
+ gender
+ eventsAttended {
+ _id
+ endDate
+ }
+ }
+ recurrenceRule {
+ recurrenceStartDate
+ recurrenceEndDate
+ frequency
+ weekDays
+ interval
+ count
+ weekDayOccurenceInMonth
+ }
+ isRecurringEventException
+ isPublic
+ isRegisterable
+ }
+ }
+`;
+
+export const ORGANIZATION_DONATION_CONNECTION_LIST = gql`
+ query GetDonationByOrgIdConnection(
+ $orgId: ID!
+ $id: ID
+ $name_of_user_contains: String
+ ) {
+ getDonationByOrgIdConnection(
+ orgId: $orgId
+ where: { id: $id, name_of_user_contains: $name_of_user_contains }
+ ) {
+ _id
+ nameOfUser
+ amount
+ userId
+ payPalId
+ updatedAt
+ }
+ }
+`;
+
+// to take the list of the admins of a particular
+export const ADMIN_LIST = gql`
+ query Organizations($id: ID!) {
+ organizations(id: $id) {
+ _id
+ admins {
+ _id
+ firstName
+ lastName
+ image
+ email
+ createdAt
+ }
+ }
+ }
+`;
+
+// to take the membership request
+export const MEMBERSHIP_REQUEST = gql`
+ query Organizations(
+ $id: ID!
+ $skip: Int
+ $first: Int
+ $firstName_contains: String
+ ) {
+ organizations(id: $id) {
+ _id
+ membershipRequests(
+ skip: $skip
+ first: $first
+ where: { user: { firstName_contains: $firstName_contains } }
+ ) {
+ _id
+ user {
+ _id
+ firstName
+ lastName
+ email
+ }
+ }
+ }
+ }
+`;
+
+export const USERS_CONNECTION_LIST = gql`
+ query usersConnection(
+ $id_not_in: [ID!]
+ $firstName_contains: String
+ $lastName_contains: String
+ ) {
+ users(
+ where: {
+ id_not_in: $id_not_in
+ firstName_contains: $firstName_contains
+ lastName_contains: $lastName_contains
+ }
+ ) {
+ user {
+ firstName
+ lastName
+ image
+ _id
+ email
+ createdAt
+ organizationsBlockedBy {
+ _id
+ name
+ image
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ createdAt
+ creator {
+ _id
+ firstName
+ lastName
+ image
+ email
+ createdAt
+ }
+ }
+ joinedOrganizations {
+ _id
+ name
+ image
+ address {
+ city
+ countryCode
+ dependentLocality
+ line1
+ line2
+ postalCode
+ sortingCode
+ state
+ }
+ createdAt
+ creator {
+ _id
+ firstName
+ lastName
+ image
+ email
+ createdAt
+ }
+ }
+ }
+ appUserProfile {
+ _id
+ adminFor {
+ _id
+ }
+ isSuperAdmin
+ createdOrganizations {
+ _id
+ }
+ createdEvents {
+ _id
+ }
+ eventAdmin {
+ _id
+ }
+ }
+ }
+ }
+`;
+
+export const GET_COMMUNITY_DATA = gql`
+ query getCommunityData {
+ getCommunityData {
+ _id
+ websiteLink
+ name
+ logoUrl
+ socialMediaUrls {
+ facebook
+ gitHub
+ instagram
+ X
+ linkedIn
+ youTube
+ reddit
+ slack
+ }
+ }
+ }
+`;
+
+export const GET_COMMUNITY_SESSION_TIMEOUT_DATA = gql`
+ query getCommunityData {
+ getCommunityData {
+ timeout
+ }
+ }
+`;
+
+// get the list of Action Item Categories
+export { ACTION_ITEM_CATEGORY_LIST } from './ActionItemCategoryQueries';
+
+// get the list of Action Items
+export { ACTION_ITEM_LIST } from './ActionItemQueries';
+
+export {
+ AgendaItemByEvent,
+ AgendaItemByOrganization,
+} from './AgendaItemQueries';
+
+export { AGENDA_ITEM_CATEGORY_LIST } from './AgendaCategoryQueries';
+// to take the list of the blocked users
+export {
+ ADVERTISEMENTS_GET,
+ IS_SAMPLE_ORGANIZATION_QUERY,
+ ORGANIZATION_CUSTOM_FIELDS,
+ ORGANIZATION_EVENTS_CONNECTION,
+ PLUGIN_GET,
+} from './PlugInQueries';
+
+// display posts
+export {
+ ORGANIZATION_POST_LIST,
+ ORGANIZATION_ADVERTISEMENT_LIST,
+} from './OrganizationQueries';
+
+export {
+ ORGANIZATION_ADMINS_LIST,
+ USER_CREATED_ORGANIZATIONS,
+ USER_JOINED_ORGANIZATIONS,
+ USER_ORGANIZATION_CONNECTION,
+} from './OrganizationQueries';
diff --git a/src/GraphQl/Queries/fundQueries.ts b/src/GraphQl/Queries/fundQueries.ts
new file mode 100644
index 0000000000..f705b87797
--- /dev/null
+++ b/src/GraphQl/Queries/fundQueries.ts
@@ -0,0 +1,132 @@
+/*eslint-disable*/
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve the list of members for a specific organization.
+ *
+ * @param id - The ID of the organization for which members are being retrieved.
+ * @param filter - The filter to search for a specific member.
+ * @returns The list of members associated with the organization.
+ */
+export const FUND_LIST = gql`
+ query FundsByOrganization(
+ $organizationId: ID!
+ $filter: String
+ $orderBy: FundOrderByInput
+ ) {
+ fundsByOrganization(
+ organizationId: $organizationId
+ where: { name_contains: $filter }
+ orderBy: $orderBy
+ ) {
+ _id
+ name
+ refrenceNumber
+ taxDeductible
+ isDefault
+ isArchived
+ createdAt
+ organizationId
+ creator {
+ _id
+ firstName
+ lastName
+ }
+ }
+ }
+`;
+
+export const FUND_CAMPAIGN = gql`
+ query GetFundById(
+ $id: ID!
+ $where: CampaignWhereInput
+ $orderBy: CampaignOrderByInput
+ ) {
+ getFundById(id: $id, where: $where, orderBy: $orderBy) {
+ name
+ isArchived
+ campaigns {
+ _id
+ endDate
+ fundingGoal
+ name
+ startDate
+ currency
+ }
+ }
+ }
+`;
+
+export const FUND_CAMPAIGN_PLEDGE = gql`
+ query GetFundraisingCampaigns(
+ $where: CampaignWhereInput
+ $pledgeOrderBy: PledgeOrderByInput
+ ) {
+ getFundraisingCampaigns(where: $where, pledgeOrderBy: $pledgeOrderBy) {
+ fundId {
+ name
+ }
+ name
+ fundingGoal
+ currency
+ startDate
+ endDate
+ pledges {
+ _id
+ amount
+ currency
+ endDate
+ startDate
+ users {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ }
+ }
+`;
+
+export const USER_FUND_CAMPAIGNS = gql`
+ query GetFundraisingCampaigns(
+ $where: CampaignWhereInput
+ $campaignOrderBy: CampaignOrderByInput
+ ) {
+ getFundraisingCampaigns(where: $where, campaignOrderby: $campaignOrderBy) {
+ _id
+ startDate
+ endDate
+ name
+ fundingGoal
+ currency
+ }
+ }
+`;
+
+export const USER_PLEDGES = gql`
+ query GetPledgesByUserId(
+ $userId: ID!
+ $where: PledgeWhereInput
+ $orderBy: PledgeOrderByInput
+ ) {
+ getPledgesByUserId(userId: $userId, where: $where, orderBy: $orderBy) {
+ _id
+ amount
+ startDate
+ endDate
+ campaign {
+ _id
+ name
+ endDate
+ }
+ currency
+ users {
+ _id
+ firstName
+ lastName
+ image
+ }
+ }
+ }
+`;
diff --git a/src/GraphQl/Queries/userTagQueries.ts b/src/GraphQl/Queries/userTagQueries.ts
new file mode 100644
index 0000000000..d58da19e1b
--- /dev/null
+++ b/src/GraphQl/Queries/userTagQueries.ts
@@ -0,0 +1,154 @@
+import gql from 'graphql-tag';
+
+/**
+ * GraphQL query to retrieve organization members assigned a certain tag.
+ *
+ * @param id - The ID of the tag that is assigned.
+ * @returns The list of organization members.
+ */
+
+export const USER_TAGS_ASSIGNED_MEMBERS = gql`
+ query UserTagDetails(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ $where: UserTagUsersAssignedToWhereInput
+ $sortedBy: UserTagUsersAssignedToSortedByInput
+ ) {
+ getAssignedUsers: getUserTag(id: $id) {
+ name
+ usersAssignedTo(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: $where
+ sortedBy: $sortedBy
+ ) {
+ edges {
+ node {
+ _id
+ firstName
+ lastName
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ ancestorTags {
+ _id
+ name
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve the sub tags of a certain tag.
+ *
+ * @param id - The ID of the parent tag.
+ * @returns The list of sub tags.
+ */
+
+export const USER_TAG_SUB_TAGS = gql`
+ query GetChildTags(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ $where: UserTagWhereInput
+ $sortedBy: UserTagSortedByInput
+ ) {
+ getChildTags: getUserTag(id: $id) {
+ name
+ childTags(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: $where
+ sortedBy: $sortedBy
+ ) {
+ edges {
+ node {
+ _id
+ name
+ usersAssignedTo(first: $first, last: $last) {
+ totalCount
+ }
+ childTags(first: $first, last: $last) {
+ totalCount
+ }
+ ancestorTags {
+ _id
+ name
+ }
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ ancestorTags {
+ _id
+ name
+ }
+ }
+ }
+`;
+
+/**
+ * GraphQL query to retrieve organization members that aren't assigned a certain tag.
+ *
+ * @param id - The ID of the tag.
+ * @returns The list of organization members.
+ */
+
+export const USER_TAGS_MEMBERS_TO_ASSIGN_TO = gql`
+ query GetMembersToAssignTo(
+ $id: ID!
+ $after: String
+ $before: String
+ $first: PositiveInt
+ $last: PositiveInt
+ $where: UserTagUsersToAssignToWhereInput
+ ) {
+ getUsersToAssignTo: getUserTag(id: $id) {
+ name
+ usersToAssignTo(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: $where
+ ) {
+ edges {
+ node {
+ _id
+ firstName
+ lastName
+ }
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ }
+ totalCount
+ }
+ }
+ }
+`;
diff --git a/src/assets/css/app.css b/src/assets/css/app.css
new file mode 100644
index 0000000000..0d23ea0e13
--- /dev/null
+++ b/src/assets/css/app.css
@@ -0,0 +1,14195 @@
+@charset "UTF-8";
+@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
+
+/*!
+ * Bootstrap v5.3.0 (https://getbootstrap.com/)
+ * Copyright 2011-2023 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+:root,
+[data-bs-theme='light'] {
+ --bs-blue: #0d6efd;
+ --bs-indigo: #6610f2;
+ --bs-purple: #6f42c1;
+ --bs-pink: #d63384;
+ --bs-red: #dc3545;
+ --bs-orange: #fd7e14;
+ --bs-yellow: #ffc107;
+ --bs-green: #198754;
+ --bs-teal: #20c997;
+ --bs-cyan: #0dcaf0;
+ --bs-black: #000;
+ --bs-white: #fff;
+ --bs-gray: #6c757d;
+ --bs-gray-dark: #343a40;
+ --bs-greyish-black: #555555;
+ --bs-gray-100: #f8f9fa;
+ --bs-gray-200: #e9ecef;
+ --bs-gray-300: #dee2e6;
+ --bs-gray-400: #ced4da;
+ --bs-gray-500: #adb5bd;
+ --bs-gray-600: #6c757d;
+ --bs-gray-700: #495057;
+ --bs-gray-800: #343a40;
+ --bs-gray-900: #212529;
+ --bs-primary: #eaebef;
+ --bs-secondary: #707070;
+ --bs-success: #31bb6b;
+ --bs-info: #0dcaf0;
+ --bs-warning: #febc59;
+ --bs-danger: #dc3545;
+ --bs-light: #f8f9fa;
+ --bs-dark: #212529;
+ --bs-primary-rgb: 49, 187, 107;
+ --bs-secondary-rgb: 112, 112, 112;
+ --bs-success-rgb: 49, 187, 107;
+ --bs-info-rgb: 13, 202, 240;
+ --bs-warning-rgb: 254, 188, 89;
+ --bs-danger-rgb: 220, 53, 69;
+ --bs-light-rgb: 248, 249, 250;
+ --bs-dark-rgb: 33, 37, 41;
+ --bs-primary-text-emphasis: #144b2b;
+ --bs-secondary-text-emphasis: #2d2d2d;
+ --bs-success-text-emphasis: #144b2b;
+ --bs-info-text-emphasis: #055160;
+ --bs-warning-text-emphasis: #664b24;
+ --bs-danger-text-emphasis: #58151c;
+ --bs-light-text-emphasis: #495057;
+ --bs-dark-text-emphasis: #495057;
+ --bs-primary-bg-subtle: #d6f1e1;
+ --bs-secondary-bg-subtle: #e2e2e2;
+ --bs-success-bg-subtle: #d6f1e1;
+ --bs-info-bg-subtle: #cff4fc;
+ --bs-warning-bg-subtle: #fff2de;
+ --bs-danger-bg-subtle: #f8d7da;
+ --bs-light-bg-subtle: #fcfcfd;
+ --bs-dark-bg-subtle: #ced4da;
+ --bs-primary-border-subtle: #ade4c4;
+ --bs-secondary-border-subtle: #c6c6c6;
+ --bs-success-border-subtle: #ade4c4;
+ --bs-info-border-subtle: #9eeaf9;
+ --bs-warning-border-subtle: #ffe4bd;
+ --bs-danger-border-subtle: #f1aeb5;
+ --bs-light-border-subtle: #e9ecef;
+ --bs-dark-border-subtle: #adb5bd;
+ --bs-white-rgb: 255, 255, 255;
+ --bs-black-rgb: 0, 0, 0;
+ --bs-font-sans-serif: system-ui, -apple-system, 'Segoe UI', Roboto,
+ 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif,
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas,
+ 'Liberation Mono', 'Courier New', monospace;
+ --bs-font-lato: 'Lato';
+ --bs-gradient: linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.15),
+ rgba(255, 255, 255, 0)
+ );
+ --bs-body-font-family: var(--bs-font-sans-serif);
+ --bs-leftDrawer-font-family: var(--bs-font-sans-serif);
+ --bs-body-font-size: 1rem;
+ --bs-body-font-weight: 400;
+ --bs-body-line-height: 1.5;
+ --bs-body-color: #212529;
+ --bs-body-color-rgb: 33, 37, 41;
+ --bs-body-bg: #fff;
+ --bs-body-bg-rgb: 255, 255, 255;
+ --bs-emphasis-color: #000;
+ --bs-emphasis-color-rgb: 0, 0, 0;
+ --bs-secondary-color: rgba(33, 37, 41, 0.75);
+ --bs-secondary-color-rgb: 33, 37, 41;
+ --bs-secondary-bg: #e9ecef;
+ --bs-secondary-bg-rgb: 233, 236, 239;
+ --bs-tertiary-color: rgba(33, 37, 41, 0.5);
+ --bs-tertiary-color-rgb: 33, 37, 41;
+ --bs-tertiary-bg: #f8f9fa;
+ --bs-tertiary-bg-rgb: 248, 249, 250;
+ --bs-heading-color: inherit;
+ --bs-link-color: #0d6efd;
+ --bs-link-color-rgb: 13, 110, 253;
+ --bs-link-decoration: none;
+ --bs-link-hover-color: #0a58ca;
+ --bs-link-hover-color-rgb: 10, 88, 202;
+ --bs-code-color: #d63384;
+ --bs-highlight-bg: #fff3cd;
+ --bs-border-width: 1px;
+ --bs-border-style: solid;
+ --bs-border-color: #dee2e6;
+ --bs-border-color-translucent: rgba(0, 0, 0, 0.175);
+ --bs-border-radius: 0.375rem;
+ --bs-border-radius-sm: 0.25rem;
+ --bs-border-radius-lg: 0.5rem;
+ --bs-border-radius-xl: 1rem;
+ --bs-border-radius-xxl: 2rem;
+ --bs-border-radius-2xl: var(--bs-border-radius-xxl);
+ --bs-border-radius-pill: 50rem;
+ --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+ --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
+ --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
+ --bs-focus-ring-width: 0.25rem;
+ --bs-focus-ring-opacity: 0.25;
+ --bs-focus-ring-color: rgba(49, 187, 107, 0.25);
+ --bs-form-valid-color: #31bb6b;
+ --bs-form-valid-border-color: #31bb6b;
+ --bs-form-invalid-color: #dc3545;
+ --bs-form-invalid-border-color: #dc3545;
+}
+
+[data-bs-theme='dark'] {
+ color-scheme: dark;
+ --bs-body-color: #f6f8fc;
+ --bs-body-color-rgb: 173, 181, 189;
+ --bs-body-bg: #212529;
+ --bs-body-bg-rgb: 33, 37, 41;
+ --bs-emphasis-color: #fff;
+ --bs-emphasis-color-rgb: 255, 255, 255;
+ --bs-secondary-color: rgba(173, 181, 189, 0.75);
+ --bs-secondary-color-rgb: 173, 181, 189;
+ --bs-secondary-bg: #343a40;
+ --bs-secondary-bg-rgb: 52, 58, 64;
+ --bs-tertiary-color: rgba(173, 181, 189, 0.5);
+ --bs-tertiary-color-rgb: 173, 181, 189;
+ --bs-tertiary-bg: #2b3035;
+ --bs-tertiary-bg-rgb: 43, 48, 53;
+ --bs-primary-text-emphasis: #83d6a6;
+ --bs-secondary-text-emphasis: darkgray;
+ --bs-success-text-emphasis: #83d6a6;
+ --bs-info-text-emphasis: #6edff6;
+ --bs-warning-text-emphasis: #fed79b;
+ --bs-danger-text-emphasis: #ea868f;
+ --bs-light-text-emphasis: #f8f9fa;
+ --bs-dark-text-emphasis: #dee2e6;
+ --bs-primary-bg-subtle: #0a2515;
+ --bs-secondary-bg-subtle: #161616;
+ --bs-success-bg-subtle: #0a2515;
+ --bs-info-bg-subtle: #032830;
+ --bs-warning-bg-subtle: #332612;
+ --bs-danger-bg-subtle: #2c0b0e;
+ --bs-light-bg-subtle: #343a40;
+ --bs-dark-bg-subtle: #1a1d20;
+ --bs-primary-border-subtle: #1d7040;
+ --bs-secondary-border-subtle: #434343;
+ --bs-success-border-subtle: #1d7040;
+ --bs-info-border-subtle: #087990;
+ --bs-warning-border-subtle: #987135;
+ --bs-danger-border-subtle: #842029;
+ --bs-light-border-subtle: #495057;
+ --bs-dark-border-subtle: #343a40;
+ --bs-heading-color: inherit;
+ --bs-link-color: #83d6a6;
+ --bs-link-hover-color: #9cdeb8;
+ --bs-link-color-rgb: 131, 214, 166;
+ --bs-link-hover-color-rgb: 156, 222, 184;
+ --bs-code-color: #e685b5;
+ --bs-border-color: #495057;
+ --bs-border-color-translucent: rgba(255, 255, 255, 0.15);
+ --bs-form-valid-color: #75b798;
+ --bs-form-valid-border-color: #75b798;
+ --bs-form-invalid-color: #ea868f;
+ --bs-form-invalid-border-color: #ea868f;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ :root {
+ scroll-behavior: smooth;
+ }
+}
+
+body {
+ margin: 0;
+ font-family: var(--bs-body-font-family);
+ font-size: var(--bs-body-font-size);
+ font-weight: var(--bs-body-font-weight);
+ line-height: var(--bs-body-line-height);
+ color: var(--bs-body-color);
+ text-align: var(--bs-body-text-align);
+ background-color: var(--bs-body-bg);
+ -webkit-text-size-adjust: 100%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+hr {
+ margin: 1rem 0;
+ color: inherit;
+ border: 0;
+ border-top: var(--bs-border-width) solid;
+ opacity: 0.25;
+}
+
+h6,
+.h6,
+h5,
+.h5,
+h4,
+.h4,
+h3,
+.h3,
+h2,
+.h2,
+h1,
+.h1 {
+ margin-top: 0;
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+ line-height: 1.2;
+ color: var(--bs-heading-color);
+}
+
+h1,
+.h1 {
+ font-size: calc(1.375rem + 1.5vw);
+}
+
+@media (min-width: 1200px) {
+ h1,
+ .h1 {
+ font-size: 2.5rem;
+ }
+}
+
+h2,
+.h2 {
+ font-size: calc(1.325rem + 0.9vw);
+}
+
+@media (min-width: 1200px) {
+ h2,
+ .h2 {
+ font-size: 2rem;
+ }
+}
+
+h3,
+.h3 {
+ font-size: calc(1.3rem + 0.6vw);
+}
+
+@media (min-width: 1200px) {
+ h3,
+ .h3 {
+ font-size: 1.75rem;
+ }
+}
+
+h4,
+.h4 {
+ font-size: calc(1.275rem + 0.3vw);
+}
+
+@media (min-width: 1200px) {
+ h4,
+ .h4 {
+ font-size: 1.5rem;
+ }
+}
+
+h5,
+.h5 {
+ font-size: 1.25rem;
+}
+
+h6,
+.h6 {
+ font-size: 1rem;
+}
+
+p {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+abbr[title] {
+ text-decoration: underline dotted;
+ cursor: help;
+ text-decoration-skip-ink: none;
+}
+
+address {
+ margin-bottom: 1rem;
+ font-style: normal;
+ line-height: inherit;
+}
+
+ol,
+ul {
+ padding-left: 2rem;
+}
+
+ol,
+ul,
+dl {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+ margin-bottom: 0;
+}
+
+dt {
+ font-weight: 700;
+}
+
+dd {
+ margin-bottom: 0.5rem;
+ margin-left: 0;
+}
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+small,
+.small {
+ font-size: 0.875em;
+}
+
+mark,
+.mark {
+ padding: 0.1875em;
+ background-color: var(--bs-highlight-bg);
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 0.75em;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+a {
+ color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
+ text-decoration: none;
+}
+
+a:hover {
+ --bs-link-color-rgb: var(--bs-link-hover-color-rgb);
+}
+
+a:not([href]):not([class]),
+a:not([href]):not([class]):hover {
+ color: inherit;
+ text-decoration: none;
+}
+
+pre,
+code,
+kbd,
+samp {
+ font-family: var(--bs-font-monospace);
+ font-size: 1em;
+}
+
+pre {
+ display: block;
+ margin-top: 0;
+ margin-bottom: 1rem;
+ overflow: auto;
+ font-size: 0.875em;
+}
+
+pre code {
+ font-size: inherit;
+ color: inherit;
+ word-break: normal;
+}
+
+code {
+ font-size: 0.875em;
+ color: var(--bs-code-color);
+ word-wrap: break-word;
+}
+
+a > code {
+ color: inherit;
+}
+
+kbd {
+ padding: 0.1875rem 0.375rem;
+ font-size: 0.875em;
+ color: var(--bs-body-bg);
+ background-color: var(--bs-body-color);
+ border-radius: 0.25rem;
+}
+
+kbd kbd {
+ padding: 0;
+ font-size: 1em;
+}
+
+figure {
+ margin: 0 0 1rem;
+}
+
+img,
+svg {
+ vertical-align: middle;
+}
+
+table {
+ caption-side: bottom;
+ border-collapse: collapse;
+}
+
+caption {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ color: var(--bs-secondary-color);
+ text-align: left;
+}
+
+th {
+ text-align: inherit;
+ text-align: -webkit-match-parent;
+}
+
+thead,
+tbody,
+tfoot,
+tr,
+td,
+th {
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+}
+
+label {
+ display: inline-block;
+}
+
+button {
+ border-radius: 0;
+}
+
+button:focus:not(:focus-visible) {
+ outline: 0;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button,
+select {
+ text-transform: none;
+}
+
+[role='button'] {
+ cursor: pointer;
+}
+
+select {
+ word-wrap: normal;
+}
+
+select:disabled {
+ opacity: 1;
+}
+
+[list]:not([type='date']):not([type='datetime-local']):not([type='month']):not(
+ [type='week']
+ ):not([type='time'])::-webkit-calendar-picker-indicator {
+ display: none !important;
+}
+
+button,
+[type='button'],
+[type='reset'],
+[type='submit'] {
+ -webkit-appearance: button;
+}
+
+button:not(:disabled),
+[type='button']:not(:disabled),
+[type='reset']:not(:disabled),
+[type='submit']:not(:disabled) {
+ cursor: pointer;
+}
+
+::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+}
+
+textarea {
+ resize: vertical;
+}
+
+fieldset {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ float: left;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 0.5rem;
+ font-size: calc(1.275rem + 0.3vw);
+ line-height: inherit;
+}
+
+@media (min-width: 1200px) {
+ legend {
+ font-size: 1.5rem;
+ }
+}
+
+legend + * {
+ clear: left;
+}
+
+::-webkit-datetime-edit-fields-wrapper,
+::-webkit-datetime-edit-text,
+::-webkit-datetime-edit-minute,
+::-webkit-datetime-edit-hour-field,
+::-webkit-datetime-edit-day-field,
+::-webkit-datetime-edit-month-field,
+::-webkit-datetime-edit-year-field {
+ padding: 0;
+}
+
+::-webkit-inner-spin-button {
+ height: auto;
+}
+
+[type='search'] {
+ outline-offset: -2px;
+ -webkit-appearance: textfield;
+}
+
+/* rtl:raw:
+ [type="tel"],
+ [type="url"],
+ [type="email"],
+ [type="number"] {
+ direction: ltr;
+ }
+ */
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+::-webkit-color-swatch-wrapper {
+ padding: 0;
+}
+
+::file-selector-button {
+ font: inherit;
+ -webkit-appearance: button;
+}
+
+output {
+ display: inline-block;
+}
+
+iframe {
+ border: 0;
+}
+
+summary {
+ display: list-item;
+ cursor: pointer;
+}
+
+progress {
+ vertical-align: baseline;
+}
+
+[hidden] {
+ display: none !important;
+}
+
+.lead {
+ font-size: 1.25rem;
+ font-weight: 300;
+}
+
+.display-1 {
+ font-size: calc(1.625rem + 4.5vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-1 {
+ font-size: 5rem;
+ }
+}
+
+.display-2 {
+ font-size: calc(1.575rem + 3.9vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-2 {
+ font-size: 4.5rem;
+ }
+}
+
+.display-3 {
+ font-size: calc(1.525rem + 3.3vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-3 {
+ font-size: 4rem;
+ }
+}
+
+.display-4 {
+ font-size: calc(1.475rem + 2.7vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-4 {
+ font-size: 3.5rem;
+ }
+}
+
+.display-5 {
+ font-size: calc(1.425rem + 2.1vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-5 {
+ font-size: 3rem;
+ }
+}
+
+.display-6 {
+ font-size: calc(1.375rem + 1.5vw);
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+@media (min-width: 1200px) {
+ .display-6 {
+ font-size: 2.5rem;
+ }
+}
+
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline-item {
+ display: inline-block;
+}
+
+.list-inline-item:not(:last-child) {
+ margin-right: 0.5rem;
+}
+
+.initialism {
+ font-size: 0.875em;
+ text-transform: uppercase;
+}
+
+.blockquote {
+ margin-bottom: 1rem;
+ font-size: 1.25rem;
+}
+
+.blockquote > :last-child {
+ margin-bottom: 0;
+}
+
+.blockquote-footer {
+ margin-top: -1rem;
+ margin-bottom: 1rem;
+ font-size: 0.875em;
+ color: #6c757d;
+}
+
+.blockquote-footer::before {
+ content: '— ';
+}
+
+.img-fluid {
+ max-width: 100%;
+ height: auto;
+}
+
+.img-thumbnail {
+ padding: 0.25rem;
+ background-color: var(--bs-body-bg);
+ border: var(--bs-border-width) solid var(--bs-border-color);
+ border-radius: var(--bs-border-radius);
+ max-width: 100%;
+ height: auto;
+}
+
+.figure {
+ display: inline-block;
+}
+
+.figure-img {
+ margin-bottom: 0.5rem;
+ line-height: 1;
+}
+
+.figure-caption {
+ font-size: 0.875em;
+ color: var(--bs-secondary-color);
+}
+
+.container,
+.container-fluid,
+.container-xxl,
+.container-xl,
+.container-lg,
+.container-md,
+.container-sm {
+ --bs-gutter-x: 1.5rem;
+ --bs-gutter-y: 0;
+ width: 100%;
+ padding-right: calc(var(--bs-gutter-x) * 0.5);
+ padding-left: calc(var(--bs-gutter-x) * 0.5);
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+ .container-sm,
+ .container {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+ .container-md,
+ .container-sm,
+ .container {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container-lg,
+ .container-md,
+ .container-sm,
+ .container {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container-xl,
+ .container-lg,
+ .container-md,
+ .container-sm,
+ .container {
+ max-width: 1140px;
+ }
+}
+
+@media (min-width: 1400px) {
+ .container-xxl,
+ .container-xl,
+ .container-lg,
+ .container-md,
+ .container-sm,
+ .container {
+ max-width: 1320px;
+ }
+}
+
+:root {
+ --bs-breakpoint-xs: 0;
+ --bs-breakpoint-sm: 576px;
+ --bs-breakpoint-md: 768px;
+ --bs-breakpoint-lg: 992px;
+ --bs-breakpoint-xl: 1200px;
+ --bs-breakpoint-xxl: 1400px;
+}
+
+.row {
+ --bs-gutter-x: 1.5rem;
+ --bs-gutter-y: 0;
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: calc(-1 * var(--bs-gutter-y));
+ margin-right: calc(-0.5 * var(--bs-gutter-x));
+ margin-left: calc(-0.5 * var(--bs-gutter-x));
+}
+
+.row > * {
+ flex-shrink: 0;
+ width: 100%;
+ max-width: 100%;
+ padding-right: calc(var(--bs-gutter-x) * 0.5);
+ padding-left: calc(var(--bs-gutter-x) * 0.5);
+ margin-top: var(--bs-gutter-y);
+}
+
+.col {
+ flex: 1 0 0%;
+}
+
+.row-cols-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+}
+
+.row-cols-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+}
+
+.row-cols-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+}
+
+.row-cols-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+}
+
+.row-cols-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+}
+
+.row-cols-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+}
+
+.row-cols-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+}
+
+.col-auto {
+ flex: 0 0 auto;
+ width: auto;
+}
+
+.col-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+}
+
+.col-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+}
+
+.col-3 {
+ flex: 0 0 auto;
+ width: 25%;
+}
+
+.col-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+}
+
+.col-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+}
+
+.col-6 {
+ flex: 0 0 auto;
+ width: 50%;
+}
+
+.col-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+}
+
+.col-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+}
+
+.col-9 {
+ flex: 0 0 auto;
+ width: 75%;
+}
+
+.col-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+}
+
+.col-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+}
+
+.col-12 {
+ flex: 0 0 auto;
+ width: 100%;
+}
+
+.offset-1 {
+ margin-left: 8.33333333%;
+}
+
+.offset-2 {
+ margin-left: 16.66666667%;
+}
+
+.offset-3 {
+ margin-left: 25%;
+}
+
+.offset-4 {
+ margin-left: 33.33333333%;
+}
+
+.offset-5 {
+ margin-left: 41.66666667%;
+}
+
+.offset-6 {
+ margin-left: 50%;
+}
+
+.offset-7 {
+ margin-left: 58.33333333%;
+}
+
+.offset-8 {
+ margin-left: 66.66666667%;
+}
+
+.offset-9 {
+ margin-left: 75%;
+}
+
+.offset-10 {
+ margin-left: 83.33333333%;
+}
+
+.offset-11 {
+ margin-left: 91.66666667%;
+}
+
+.g-0,
+.gx-0 {
+ --bs-gutter-x: 0;
+}
+
+.g-0,
+.gy-0 {
+ --bs-gutter-y: 0;
+}
+
+.g-1,
+.gx-1 {
+ --bs-gutter-x: 0.25rem;
+}
+
+.g-1,
+.gy-1 {
+ --bs-gutter-y: 0.25rem;
+}
+
+.g-2,
+.gx-2 {
+ --bs-gutter-x: 0.5rem;
+}
+
+.g-2,
+.gy-2 {
+ --bs-gutter-y: 0.5rem;
+}
+
+.g-3,
+.gx-3 {
+ --bs-gutter-x: 1rem;
+}
+
+.g-3,
+.gy-3 {
+ --bs-gutter-y: 1rem;
+}
+
+.g-4,
+.gx-4 {
+ --bs-gutter-x: 1.5rem;
+}
+
+.g-4,
+.gy-4 {
+ --bs-gutter-y: 1.5rem;
+}
+
+.g-5,
+.gx-5 {
+ --bs-gutter-x: 3rem;
+}
+
+.g-5,
+.gy-5 {
+ --bs-gutter-y: 3rem;
+}
+
+@media (min-width: 576px) {
+ .col-sm {
+ flex: 1 0 0%;
+ }
+
+ .row-cols-sm-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .row-cols-sm-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .row-cols-sm-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .row-cols-sm-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+ }
+
+ .row-cols-sm-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .row-cols-sm-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+ }
+
+ .row-cols-sm-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+ }
+
+ .col-sm-auto {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .col-sm-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+ }
+
+ .col-sm-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+ }
+
+ .col-sm-3 {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .col-sm-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+ }
+
+ .col-sm-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+ }
+
+ .col-sm-6 {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .col-sm-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+ }
+
+ .col-sm-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+ }
+
+ .col-sm-9 {
+ flex: 0 0 auto;
+ width: 75%;
+ }
+
+ .col-sm-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+ }
+
+ .col-sm-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+ }
+
+ .col-sm-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .offset-sm-0 {
+ margin-left: 0;
+ }
+
+ .offset-sm-1 {
+ margin-left: 8.33333333%;
+ }
+
+ .offset-sm-2 {
+ margin-left: 16.66666667%;
+ }
+
+ .offset-sm-3 {
+ margin-left: 25%;
+ }
+
+ .offset-sm-4 {
+ margin-left: 33.33333333%;
+ }
+
+ .offset-sm-5 {
+ margin-left: 41.66666667%;
+ }
+
+ .offset-sm-6 {
+ margin-left: 50%;
+ }
+
+ .offset-sm-7 {
+ margin-left: 58.33333333%;
+ }
+
+ .offset-sm-8 {
+ margin-left: 66.66666667%;
+ }
+
+ .offset-sm-9 {
+ margin-left: 75%;
+ }
+
+ .offset-sm-10 {
+ margin-left: 83.33333333%;
+ }
+
+ .offset-sm-11 {
+ margin-left: 91.66666667%;
+ }
+
+ .g-sm-0,
+ .gx-sm-0 {
+ --bs-gutter-x: 0;
+ }
+
+ .g-sm-0,
+ .gy-sm-0 {
+ --bs-gutter-y: 0;
+ }
+
+ .g-sm-1,
+ .gx-sm-1 {
+ --bs-gutter-x: 0.25rem;
+ }
+
+ .g-sm-1,
+ .gy-sm-1 {
+ --bs-gutter-y: 0.25rem;
+ }
+
+ .g-sm-2,
+ .gx-sm-2 {
+ --bs-gutter-x: 0.5rem;
+ }
+
+ .g-sm-2,
+ .gy-sm-2 {
+ --bs-gutter-y: 0.5rem;
+ }
+
+ .g-sm-3,
+ .gx-sm-3 {
+ --bs-gutter-x: 1rem;
+ }
+
+ .g-sm-3,
+ .gy-sm-3 {
+ --bs-gutter-y: 1rem;
+ }
+
+ .g-sm-4,
+ .gx-sm-4 {
+ --bs-gutter-x: 1.5rem;
+ }
+
+ .g-sm-4,
+ .gy-sm-4 {
+ --bs-gutter-y: 1.5rem;
+ }
+
+ .g-sm-5,
+ .gx-sm-5 {
+ --bs-gutter-x: 3rem;
+ }
+
+ .g-sm-5,
+ .gy-sm-5 {
+ --bs-gutter-y: 3rem;
+ }
+}
+
+@media (min-width: 768px) {
+ .col-md {
+ flex: 1 0 0%;
+ }
+
+ .row-cols-md-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .row-cols-md-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .row-cols-md-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .row-cols-md-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+ }
+
+ .row-cols-md-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .row-cols-md-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+ }
+
+ .row-cols-md-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+ }
+
+ .col-md-auto {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .col-md-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+ }
+
+ .col-md-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+ }
+
+ .col-md-3 {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .col-md-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+ }
+
+ .col-md-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+ }
+
+ .col-md-6 {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .col-md-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+ }
+
+ .col-md-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+ }
+
+ .col-md-9 {
+ flex: 0 0 auto;
+ width: 75%;
+ }
+
+ .col-md-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+ }
+
+ .col-md-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+ }
+
+ .col-md-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .offset-md-0 {
+ margin-left: 0;
+ }
+
+ .offset-md-1 {
+ margin-left: 8.33333333%;
+ }
+
+ .offset-md-2 {
+ margin-left: 16.66666667%;
+ }
+
+ .offset-md-3 {
+ margin-left: 25%;
+ }
+
+ .offset-md-4 {
+ margin-left: 33.33333333%;
+ }
+
+ .offset-md-5 {
+ margin-left: 41.66666667%;
+ }
+
+ .offset-md-6 {
+ margin-left: 50%;
+ }
+
+ .offset-md-7 {
+ margin-left: 58.33333333%;
+ }
+
+ .offset-md-8 {
+ margin-left: 66.66666667%;
+ }
+
+ .offset-md-9 {
+ margin-left: 75%;
+ }
+
+ .offset-md-10 {
+ margin-left: 83.33333333%;
+ }
+
+ .offset-md-11 {
+ margin-left: 91.66666667%;
+ }
+
+ .g-md-0,
+ .gx-md-0 {
+ --bs-gutter-x: 0;
+ }
+
+ .g-md-0,
+ .gy-md-0 {
+ --bs-gutter-y: 0;
+ }
+
+ .g-md-1,
+ .gx-md-1 {
+ --bs-gutter-x: 0.25rem;
+ }
+
+ .g-md-1,
+ .gy-md-1 {
+ --bs-gutter-y: 0.25rem;
+ }
+
+ .g-md-2,
+ .gx-md-2 {
+ --bs-gutter-x: 0.5rem;
+ }
+
+ .g-md-2,
+ .gy-md-2 {
+ --bs-gutter-y: 0.5rem;
+ }
+
+ .g-md-3,
+ .gx-md-3 {
+ --bs-gutter-x: 1rem;
+ }
+
+ .g-md-3,
+ .gy-md-3 {
+ --bs-gutter-y: 1rem;
+ }
+
+ .g-md-4,
+ .gx-md-4 {
+ --bs-gutter-x: 1.5rem;
+ }
+
+ .g-md-4,
+ .gy-md-4 {
+ --bs-gutter-y: 1.5rem;
+ }
+
+ .g-md-5,
+ .gx-md-5 {
+ --bs-gutter-x: 3rem;
+ }
+
+ .g-md-5,
+ .gy-md-5 {
+ --bs-gutter-y: 3rem;
+ }
+}
+
+@media (min-width: 992px) {
+ .col-lg {
+ flex: 1 0 0%;
+ }
+
+ .row-cols-lg-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .row-cols-lg-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .row-cols-lg-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .row-cols-lg-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+ }
+
+ .row-cols-lg-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .row-cols-lg-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+ }
+
+ .row-cols-lg-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+ }
+
+ .col-lg-auto {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .col-lg-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+ }
+
+ .col-lg-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+ }
+
+ .col-lg-3 {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .col-lg-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+ }
+
+ .col-lg-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+ }
+
+ .col-lg-6 {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .col-lg-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+ }
+
+ .col-lg-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+ }
+
+ .col-lg-9 {
+ flex: 0 0 auto;
+ width: 75%;
+ }
+
+ .col-lg-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+ }
+
+ .col-lg-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+ }
+
+ .col-lg-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .offset-lg-0 {
+ margin-left: 0;
+ }
+
+ .offset-lg-1 {
+ margin-left: 8.33333333%;
+ }
+
+ .offset-lg-2 {
+ margin-left: 16.66666667%;
+ }
+
+ .offset-lg-3 {
+ margin-left: 25%;
+ }
+
+ .offset-lg-4 {
+ margin-left: 33.33333333%;
+ }
+
+ .offset-lg-5 {
+ margin-left: 41.66666667%;
+ }
+
+ .offset-lg-6 {
+ margin-left: 50%;
+ }
+
+ .offset-lg-7 {
+ margin-left: 58.33333333%;
+ }
+
+ .offset-lg-8 {
+ margin-left: 66.66666667%;
+ }
+
+ .offset-lg-9 {
+ margin-left: 75%;
+ }
+
+ .offset-lg-10 {
+ margin-left: 83.33333333%;
+ }
+
+ .offset-lg-11 {
+ margin-left: 91.66666667%;
+ }
+
+ .g-lg-0,
+ .gx-lg-0 {
+ --bs-gutter-x: 0;
+ }
+
+ .g-lg-0,
+ .gy-lg-0 {
+ --bs-gutter-y: 0;
+ }
+
+ .g-lg-1,
+ .gx-lg-1 {
+ --bs-gutter-x: 0.25rem;
+ }
+
+ .g-lg-1,
+ .gy-lg-1 {
+ --bs-gutter-y: 0.25rem;
+ }
+
+ .g-lg-2,
+ .gx-lg-2 {
+ --bs-gutter-x: 0.5rem;
+ }
+
+ .g-lg-2,
+ .gy-lg-2 {
+ --bs-gutter-y: 0.5rem;
+ }
+
+ .g-lg-3,
+ .gx-lg-3 {
+ --bs-gutter-x: 1rem;
+ }
+
+ .g-lg-3,
+ .gy-lg-3 {
+ --bs-gutter-y: 1rem;
+ }
+
+ .g-lg-4,
+ .gx-lg-4 {
+ --bs-gutter-x: 1.5rem;
+ }
+
+ .g-lg-4,
+ .gy-lg-4 {
+ --bs-gutter-y: 1.5rem;
+ }
+
+ .g-lg-5,
+ .gx-lg-5 {
+ --bs-gutter-x: 3rem;
+ }
+
+ .g-lg-5,
+ .gy-lg-5 {
+ --bs-gutter-y: 3rem;
+ }
+}
+
+@media (min-width: 1200px) {
+ .col-xl {
+ flex: 1 0 0%;
+ }
+
+ .row-cols-xl-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .row-cols-xl-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .row-cols-xl-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .row-cols-xl-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+ }
+
+ .row-cols-xl-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .row-cols-xl-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+ }
+
+ .row-cols-xl-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+ }
+
+ .col-xl-auto {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .col-xl-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+ }
+
+ .col-xl-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+ }
+
+ .col-xl-3 {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .col-xl-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+ }
+
+ .col-xl-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+ }
+
+ .col-xl-6 {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .col-xl-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+ }
+
+ .col-xl-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+ }
+
+ .col-xl-9 {
+ flex: 0 0 auto;
+ width: 75%;
+ }
+
+ .col-xl-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+ }
+
+ .col-xl-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+ }
+
+ .col-xl-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .offset-xl-0 {
+ margin-left: 0;
+ }
+
+ .offset-xl-1 {
+ margin-left: 8.33333333%;
+ }
+
+ .offset-xl-2 {
+ margin-left: 16.66666667%;
+ }
+
+ .offset-xl-3 {
+ margin-left: 25%;
+ }
+
+ .offset-xl-4 {
+ margin-left: 33.33333333%;
+ }
+
+ .offset-xl-5 {
+ margin-left: 41.66666667%;
+ }
+
+ .offset-xl-6 {
+ margin-left: 50%;
+ }
+
+ .offset-xl-7 {
+ margin-left: 58.33333333%;
+ }
+
+ .offset-xl-8 {
+ margin-left: 66.66666667%;
+ }
+
+ .offset-xl-9 {
+ margin-left: 75%;
+ }
+
+ .offset-xl-10 {
+ margin-left: 83.33333333%;
+ }
+
+ .offset-xl-11 {
+ margin-left: 91.66666667%;
+ }
+
+ .g-xl-0,
+ .gx-xl-0 {
+ --bs-gutter-x: 0;
+ }
+
+ .g-xl-0,
+ .gy-xl-0 {
+ --bs-gutter-y: 0;
+ }
+
+ .g-xl-1,
+ .gx-xl-1 {
+ --bs-gutter-x: 0.25rem;
+ }
+
+ .g-xl-1,
+ .gy-xl-1 {
+ --bs-gutter-y: 0.25rem;
+ }
+
+ .g-xl-2,
+ .gx-xl-2 {
+ --bs-gutter-x: 0.5rem;
+ }
+
+ .g-xl-2,
+ .gy-xl-2 {
+ --bs-gutter-y: 0.5rem;
+ }
+
+ .g-xl-3,
+ .gx-xl-3 {
+ --bs-gutter-x: 1rem;
+ }
+
+ .g-xl-3,
+ .gy-xl-3 {
+ --bs-gutter-y: 1rem;
+ }
+
+ .g-xl-4,
+ .gx-xl-4 {
+ --bs-gutter-x: 1.5rem;
+ }
+
+ .g-xl-4,
+ .gy-xl-4 {
+ --bs-gutter-y: 1.5rem;
+ }
+
+ .g-xl-5,
+ .gx-xl-5 {
+ --bs-gutter-x: 3rem;
+ }
+
+ .g-xl-5,
+ .gy-xl-5 {
+ --bs-gutter-y: 3rem;
+ }
+}
+
+@media (min-width: 1400px) {
+ .col-xxl {
+ flex: 1 0 0%;
+ }
+
+ .row-cols-xxl-auto > * {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .row-cols-xxl-1 > * {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .row-cols-xxl-2 > * {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .row-cols-xxl-3 > * {
+ flex: 0 0 auto;
+ width: 33.3333333333%;
+ }
+
+ .row-cols-xxl-4 > * {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .row-cols-xxl-5 > * {
+ flex: 0 0 auto;
+ width: 20%;
+ }
+
+ .row-cols-xxl-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666666667%;
+ }
+
+ .col-xxl-auto {
+ flex: 0 0 auto;
+ width: auto;
+ }
+
+ .col-xxl-1 {
+ flex: 0 0 auto;
+ width: 8.33333333%;
+ }
+
+ .col-xxl-2 {
+ flex: 0 0 auto;
+ width: 16.66666667%;
+ }
+
+ .col-xxl-3 {
+ flex: 0 0 auto;
+ width: 25%;
+ }
+
+ .col-xxl-4 {
+ flex: 0 0 auto;
+ width: 33.33333333%;
+ }
+
+ .col-xxl-5 {
+ flex: 0 0 auto;
+ width: 41.66666667%;
+ }
+
+ .col-xxl-6 {
+ flex: 0 0 auto;
+ width: 50%;
+ }
+
+ .col-xxl-7 {
+ flex: 0 0 auto;
+ width: 58.33333333%;
+ }
+
+ .col-xxl-8 {
+ flex: 0 0 auto;
+ width: 66.66666667%;
+ }
+
+ .col-xxl-9 {
+ flex: 0 0 auto;
+ width: 75%;
+ }
+
+ .col-xxl-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+ }
+
+ .col-xxl-11 {
+ flex: 0 0 auto;
+ width: 91.66666667%;
+ }
+
+ .col-xxl-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+
+ .offset-xxl-0 {
+ margin-left: 0;
+ }
+
+ .offset-xxl-1 {
+ margin-left: 8.33333333%;
+ }
+
+ .offset-xxl-2 {
+ margin-left: 16.66666667%;
+ }
+
+ .offset-xxl-3 {
+ margin-left: 25%;
+ }
+
+ .offset-xxl-4 {
+ margin-left: 33.33333333%;
+ }
+
+ .offset-xxl-5 {
+ margin-left: 41.66666667%;
+ }
+
+ .offset-xxl-6 {
+ margin-left: 50%;
+ }
+
+ .offset-xxl-7 {
+ margin-left: 58.33333333%;
+ }
+
+ .offset-xxl-8 {
+ margin-left: 66.66666667%;
+ }
+
+ .offset-xxl-9 {
+ margin-left: 75%;
+ }
+
+ .offset-xxl-10 {
+ margin-left: 83.33333333%;
+ }
+
+ .offset-xxl-11 {
+ margin-left: 91.66666667%;
+ }
+
+ .g-xxl-0,
+ .gx-xxl-0 {
+ --bs-gutter-x: 0;
+ }
+
+ .g-xxl-0,
+ .gy-xxl-0 {
+ --bs-gutter-y: 0;
+ }
+
+ .g-xxl-1,
+ .gx-xxl-1 {
+ --bs-gutter-x: 0.25rem;
+ }
+
+ .g-xxl-1,
+ .gy-xxl-1 {
+ --bs-gutter-y: 0.25rem;
+ }
+
+ .g-xxl-2,
+ .gx-xxl-2 {
+ --bs-gutter-x: 0.5rem;
+ }
+
+ .g-xxl-2,
+ .gy-xxl-2 {
+ --bs-gutter-y: 0.5rem;
+ }
+
+ .g-xxl-3,
+ .gx-xxl-3 {
+ --bs-gutter-x: 1rem;
+ }
+
+ .g-xxl-3,
+ .gy-xxl-3 {
+ --bs-gutter-y: 1rem;
+ }
+
+ .g-xxl-4,
+ .gx-xxl-4 {
+ --bs-gutter-x: 1.5rem;
+ }
+
+ .g-xxl-4,
+ .gy-xxl-4 {
+ --bs-gutter-y: 1.5rem;
+ }
+
+ .g-xxl-5,
+ .gx-xxl-5 {
+ --bs-gutter-x: 3rem;
+ }
+
+ .g-xxl-5,
+ .gy-xxl-5 {
+ --bs-gutter-y: 3rem;
+ }
+}
+
+.table {
+ --bs-table-color-type: initial;
+ --bs-table-bg-type: initial;
+ --bs-table-color-state: initial;
+ --bs-table-bg-state: initial;
+ --bs-table-color: var(--bs-body-color);
+ --bs-table-bg: var(--bs-body-bg);
+ --bs-table-border-color: var(--bs-border-color);
+ --bs-table-accent-bg: transparent;
+ --bs-table-striped-color: var(--bs-body-color);
+ --bs-table-striped-bg: rgba(0, 0, 0, 0.05);
+ --bs-table-active-color: var(--bs-body-color);
+ --bs-table-active-bg: rgba(0, 0, 0, 0.1);
+ --bs-table-hover-color: var(--bs-body-color);
+ --bs-table-hover-bg: rgba(0, 0, 0, 0.075);
+ width: 100%;
+ margin-bottom: 1rem;
+ vertical-align: top;
+ border-color: var(--bs-table-border-color);
+}
+
+.table > :not(caption) > * > * {
+ padding: 0.5rem 0.5rem;
+ color: var(
+ --bs-table-color-state,
+ var(--bs-table-color-type, var(--bs-table-color))
+ );
+ background-color: var(--bs-table-bg);
+ border-bottom-width: var(--bs-border-width);
+ box-shadow: inset 0 0 0 9999px
+ var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)));
+}
+
+.table > tbody {
+ vertical-align: inherit;
+}
+
+.table > thead {
+ vertical-align: bottom;
+}
+
+.table-group-divider {
+ border-top: calc(var(--bs-border-width) * 2) solid currentcolor;
+}
+
+.caption-top {
+ caption-side: top;
+}
+
+.table-sm > :not(caption) > * > * {
+ padding: 0.25rem 0.25rem;
+}
+
+.table-bordered > :not(caption) > * {
+ border-width: var(--bs-border-width) 0;
+}
+
+.table-bordered > :not(caption) > * > * {
+ border-width: 0 var(--bs-border-width);
+}
+
+.table-borderless > :not(caption) > * > * {
+ border-bottom-width: 0;
+}
+
+.table-borderless > :not(:first-child) {
+ border-top-width: 0;
+}
+
+.table-striped > tbody > tr:nth-of-type(odd) > * {
+ --bs-table-color-type: var(--bs-table-striped-color);
+ --bs-table-bg-type: var(--bs-table-striped-bg);
+}
+
+.table-striped-columns > :not(caption) > tr > :nth-child(even) {
+ --bs-table-color-type: var(--bs-table-striped-color);
+ --bs-table-bg-type: var(--bs-table-striped-bg);
+}
+
+.table-active {
+ --bs-table-color-state: var(--bs-table-active-color);
+ --bs-table-bg-state: var(--bs-table-active-bg);
+}
+
+.table-hover > tbody > tr:hover > * {
+ --bs-table-color-state: var(--bs-table-hover-color);
+ --bs-table-bg-state: var(--bs-table-hover-bg);
+}
+
+.table-primary {
+ --bs-table-color: #000;
+ --bs-table-bg: #d6f1e1;
+ --bs-table-border-color: #c1d9cb;
+ --bs-table-striped-bg: #cbe5d6;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #c1d9cb;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #c6dfd0;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-secondary {
+ --bs-table-color: #000;
+ --bs-table-bg: #e2e2e2;
+ --bs-table-border-color: #cbcbcb;
+ --bs-table-striped-bg: #d7d7d7;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #cbcbcb;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #d1d1d1;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-success {
+ --bs-table-color: #000;
+ --bs-table-bg: #d6f1e1;
+ --bs-table-border-color: #c1d9cb;
+ --bs-table-striped-bg: #cbe5d6;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #c1d9cb;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #c6dfd0;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-info {
+ --bs-table-color: #000;
+ --bs-table-bg: #cff4fc;
+ --bs-table-border-color: #badce3;
+ --bs-table-striped-bg: #c5e8ef;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #badce3;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #bfe2e9;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-warning {
+ --bs-table-color: #000;
+ --bs-table-bg: #fff2de;
+ --bs-table-border-color: #e6dac8;
+ --bs-table-striped-bg: #f2e6d3;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #e6dac8;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #ece0cd;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-danger {
+ --bs-table-color: #000;
+ --bs-table-bg: #f8d7da;
+ --bs-table-border-color: #dfc2c4;
+ --bs-table-striped-bg: #eccccf;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #dfc2c4;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #e5c7ca;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-light {
+ --bs-table-color: #000;
+ --bs-table-bg: #f8f9fa;
+ --bs-table-border-color: #dfe0e1;
+ --bs-table-striped-bg: #ecedee;
+ --bs-table-striped-color: #000;
+ --bs-table-active-bg: #dfe0e1;
+ --bs-table-active-color: #000;
+ --bs-table-hover-bg: #e5e6e7;
+ --bs-table-hover-color: #000;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-dark {
+ --bs-table-color: #fff;
+ --bs-table-bg: #212529;
+ --bs-table-border-color: #373b3e;
+ --bs-table-striped-bg: #2c3034;
+ --bs-table-striped-color: #fff;
+ --bs-table-active-bg: #373b3e;
+ --bs-table-active-color: #fff;
+ --bs-table-hover-bg: #323539;
+ --bs-table-hover-color: #fff;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
+}
+
+.table-responsive {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+@media (max-width: 575.98px) {
+ .table-responsive-sm {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .table-responsive-md {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .table-responsive-lg {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .table-responsive-xl {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+}
+
+@media (max-width: 1399.98px) {
+ .table-responsive-xxl {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+}
+
+.form-label {
+ margin-bottom: 0.5rem;
+}
+
+.col-form-label {
+ padding-top: 0.7rem;
+ padding-bottom: 0.7rem;
+ margin-bottom: 0;
+ font-size: inherit;
+ line-height: 1.5;
+}
+
+.col-form-label-lg {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ font-size: 1.25rem;
+}
+
+.col-form-label-sm {
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+ font-size: 0.875rem;
+}
+
+.form-text {
+ margin-top: 0.25rem;
+ font-size: 0.875em;
+ color: var(--bs-secondary-color);
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ padding: 0.7rem 1rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--bs-body-color);
+ background-color: #f2f2f2;
+ background-clip: padding-box;
+ border: 1px solid var(--bs-border-color);
+ appearance: none;
+ border-radius: var(--bs-border-radius);
+ transition:
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-control {
+ transition: none;
+ }
+}
+
+.form-control[type='file'] {
+ overflow: hidden;
+}
+
+.form-control[type='file']:not(:disabled):not([readonly]) {
+ cursor: pointer;
+}
+
+.form-control:focus {
+ color: var(--bs-body-color);
+ background-color: #f2f2f2;
+ border-color: #98ddb5;
+ outline: 0;
+ box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.form-control::-webkit-date-and-time-value {
+ min-width: 85px;
+ height: 1.5em;
+ margin: 0;
+}
+
+.form-control::-webkit-datetime-edit {
+ display: block;
+ padding: 0;
+}
+
+.form-control::placeholder {
+ color: var(--bs-secondary-color);
+ opacity: 1;
+}
+
+.form-control:disabled {
+ background-color: var(--bs-secondary-bg);
+ opacity: 1;
+}
+
+.form-control::file-selector-button {
+ padding: 0.7rem 1rem;
+ margin: -0.7rem -1rem;
+ margin-inline-end: 1rem;
+ color: var(--bs-body-color);
+ background-color: var(--bs-tertiary-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: 0;
+ border-radius: 0;
+ transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-control::file-selector-button {
+ transition: none;
+ }
+}
+
+.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
+ background-color: var(--bs-secondary-bg);
+}
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: 0.7rem 0;
+ margin-bottom: 0;
+ line-height: 1.5;
+ color: var(--bs-body-color);
+ background-color: transparent;
+ border: solid transparent;
+ border-width: 0 0;
+}
+
+.form-control-plaintext:focus {
+ outline: 0;
+}
+
+.form-control-plaintext.form-control-sm,
+.form-control-plaintext.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.form-control-sm {
+ min-height: calc(1.5em + 0.5rem + calc(0 * 2));
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ border-radius: var(--bs-border-radius-sm);
+}
+
+.form-control-sm::file-selector-button {
+ padding: 0.25rem 0.5rem;
+ margin: -0.25rem -0.5rem;
+ margin-inline-end: 0.5rem;
+}
+
+.form-control-lg {
+ min-height: calc(1.5em + 1rem + calc(0 * 2));
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ border-radius: var(--bs-border-radius-lg);
+}
+
+.form-control-lg::file-selector-button {
+ padding: 0.5rem 1rem;
+ margin: -0.5rem -1rem;
+ margin-inline-end: 1rem;
+}
+
+textarea.form-control {
+ min-height: calc(1.5em + 1.4rem + calc(0 * 2));
+}
+
+textarea.form-control-sm {
+ min-height: calc(1.5em + 0.5rem + calc(0 * 2));
+}
+
+textarea.form-control-lg {
+ min-height: calc(1.5em + 1rem + calc(0 * 2));
+}
+
+.form-control-color {
+ width: 3rem;
+ height: calc(1.5em + 1.4rem + calc(0 * 2));
+ padding: 0.7rem;
+}
+
+.form-control-color:not(:disabled):not([readonly]) {
+ cursor: pointer;
+}
+
+.form-control-color::-moz-color-swatch {
+ border: 0 !important;
+ border-radius: var(--bs-border-radius);
+}
+
+.form-control-color::-webkit-color-swatch {
+ border: 0 !important;
+ border-radius: var(--bs-border-radius);
+}
+
+.form-control-color.form-control-sm {
+ height: calc(1.5em + 0.5rem + calc(0 * 2));
+}
+
+.form-control-color.form-control-lg {
+ height: calc(1.5em + 1rem + calc(0 * 2));
+}
+
+.form-select {
+ --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
+ display: block;
+ width: 100%;
+ padding: 0.7rem 3rem 0.7rem 1rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--bs-body-color);
+ background-color: #f2f2f2;
+ background-image: var(--bs-form-select-bg-img),
+ var(--bs-form-select-bg-icon, none);
+ background-repeat: no-repeat;
+ background-position: right 1rem center;
+ background-size: 16px 12px;
+ border: 0 solid var(--bs-border-color);
+ border-radius: var(--bs-border-radius);
+ transition:
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-select {
+ transition: none;
+ }
+}
+
+.form-select:focus {
+ border-color: #98ddb5;
+ outline: 0;
+ box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.form-select[multiple],
+.form-select[size]:not([size='1']) {
+ padding-right: 1rem;
+ background-image: none;
+}
+
+.form-select:disabled {
+ background-color: var(--bs-secondary-bg);
+}
+
+.form-select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 var(--bs-body-color);
+}
+
+.form-select-sm {
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+ padding-left: 0.5rem;
+ font-size: 0.875rem;
+ border-radius: var(--bs-border-radius-sm);
+}
+
+.form-select-lg {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ padding-left: 1rem;
+ font-size: 1.25rem;
+ border-radius: var(--bs-border-radius-lg);
+}
+
+[data-bs-theme='dark'] .form-select {
+ --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
+}
+
+.form-check {
+ display: block;
+ min-height: 1.5rem;
+ padding-left: 1.5em;
+ margin-bottom: 0.125rem;
+}
+
+.form-check .form-check-input {
+ float: left;
+ margin-left: -1.5em;
+}
+
+.form-check-reverse {
+ padding-right: 1.5em;
+ padding-left: 0;
+ text-align: right;
+}
+
+.form-check-reverse .form-check-input {
+ float: right;
+ margin-right: -1.5em;
+ margin-left: 0;
+}
+
+.form-check-input {
+ --bs-form-check-bg: #f2f2f2;
+ width: 1.3em;
+ height: 1.3em;
+ margin-top: 0.25em;
+ vertical-align: top;
+ background-color: var(--bs-form-check-bg);
+ background-image: var(--bs-form-check-bg-image);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ border: var(--bs-border-width) solid var(--bs-border-color);
+ appearance: none;
+ print-color-adjust: exact;
+}
+
+.form-check-input[type='checkbox'] {
+ border-radius: 0.25em;
+}
+
+.form-check-input[type='radio'] {
+ border-radius: 50%;
+}
+
+.form-check-input:active {
+ filter: brightness(90%);
+}
+
+.form-check-input:focus {
+ border-color: #98ddb5;
+ outline: 0;
+ box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.form-check-input:checked {
+ background-color: #31bb6b;
+ border-color: #31bb6b;
+}
+
+.form-check-input:checked[type='checkbox'] {
+ --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
+}
+
+.form-check-input:checked[type='radio'] {
+ --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
+}
+
+.form-check-input[type='checkbox']:indeterminate {
+ background-color: #31bb6b;
+ border-color: #31bb6b;
+ --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
+}
+
+.form-check-input:disabled {
+ pointer-events: none;
+ filter: none;
+ opacity: 0.5;
+}
+
+.form-check-input[disabled] ~ .form-check-label,
+.form-check-input:disabled ~ .form-check-label {
+ cursor: default;
+ opacity: 0.5;
+}
+
+.form-switch {
+ padding-left: 2.5em;
+}
+
+.form-switch .form-check-input {
+ --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");
+ width: 2em;
+ margin-left: -2.5em;
+ background-image: var(--bs-form-switch-bg);
+ background-position: left center;
+ border-radius: 2em;
+ transition: background-position 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-switch .form-check-input {
+ transition: none;
+ }
+}
+
+.form-switch .form-check-input:focus {
+ --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2398ddb5'/%3e%3c/svg%3e");
+}
+
+.form-switch .form-check-input:checked {
+ background-position: right center;
+ --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
+}
+
+.form-switch.form-check-reverse {
+ padding-right: 2.5em;
+ padding-left: 0;
+}
+
+.form-switch.form-check-reverse .form-check-input {
+ margin-right: -2.5em;
+ margin-left: 0;
+}
+
+.form-check-inline {
+ display: inline-block;
+ margin-right: 1rem;
+}
+
+.btn-check {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+}
+
+.btn-check[disabled] + .btn,
+.btn-check:disabled + .btn {
+ pointer-events: none;
+ filter: none;
+ opacity: 0.65;
+}
+
+[data-bs-theme='dark']
+ .form-switch
+ .form-check-input:not(:checked):not(:focus) {
+ --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e");
+}
+
+.form-range {
+ width: 100%;
+ height: 1.5rem;
+ padding: 0;
+ background-color: transparent;
+ appearance: none;
+}
+
+.form-range:focus {
+ outline: 0;
+}
+
+.form-range:focus::-webkit-slider-thumb {
+ box-shadow:
+ 0 0 0 1px #fff,
+ 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.form-range:focus::-moz-range-thumb {
+ box-shadow:
+ 0 0 0 1px #fff,
+ 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.form-range::-moz-focus-outer {
+ border: 0;
+}
+
+.form-range::-webkit-slider-thumb {
+ width: 1rem;
+ height: 1rem;
+ margin-top: -0.25rem;
+ background-color: #31bb6b;
+ border: 0;
+ border-radius: 1rem;
+ transition:
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-range::-webkit-slider-thumb {
+ transition: none;
+ }
+}
+
+.form-range::-webkit-slider-thumb:active {
+ background-color: #c1ebd3;
+}
+
+.form-range::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: var(--bs-tertiary-bg);
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.form-range::-moz-range-thumb {
+ width: 1rem;
+ height: 1rem;
+ background-color: #31bb6b;
+ border: 0;
+ border-radius: 1rem;
+ transition:
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-range::-moz-range-thumb {
+ transition: none;
+ }
+}
+
+.form-range::-moz-range-thumb:active {
+ background-color: #c1ebd3;
+}
+
+.form-range::-moz-range-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: var(--bs-tertiary-bg);
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.form-range:disabled {
+ pointer-events: none;
+}
+
+.form-range:disabled::-webkit-slider-thumb {
+ background-color: var(--bs-secondary-color);
+}
+
+.form-range:disabled::-moz-range-thumb {
+ background-color: var(--bs-secondary-color);
+}
+
+.form-floating {
+ position: relative;
+}
+
+.form-floating > .form-control,
+.form-floating > .form-control-plaintext,
+.form-floating > .form-select {
+ height: calc(3.5rem + calc(0 * 2));
+ min-height: calc(3.5rem + calc(0 * 2));
+ line-height: 1.25;
+}
+
+.form-floating > label {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ height: 100%;
+ padding: 1rem 1rem;
+ overflow: hidden;
+ text-align: start;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ pointer-events: none;
+ border: 0 solid transparent;
+ transform-origin: 0 0;
+ transition:
+ opacity 0.1s ease-in-out,
+ transform 0.1s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-floating > label {
+ transition: none;
+ }
+}
+
+.form-floating > .form-control,
+.form-floating > .form-control-plaintext {
+ padding: 1rem 1rem;
+}
+
+.form-floating > .form-control::placeholder,
+.form-floating > .form-control-plaintext::placeholder {
+ color: transparent;
+}
+
+.form-floating > .form-control:focus,
+.form-floating > .form-control:not(:placeholder-shown),
+.form-floating > .form-control-plaintext:focus,
+.form-floating > .form-control-plaintext:not(:placeholder-shown) {
+ padding-top: 1.625rem;
+ padding-bottom: 0.625rem;
+}
+
+.form-floating > .form-control:-webkit-autofill,
+.form-floating > .form-control-plaintext:-webkit-autofill {
+ padding-top: 1.625rem;
+ padding-bottom: 0.625rem;
+}
+
+.form-floating > .form-select {
+ padding-top: 1.625rem;
+ padding-bottom: 0.625rem;
+}
+
+.form-floating > .form-control:focus ~ label,
+.form-floating > .form-control:not(:placeholder-shown) ~ label,
+.form-floating > .form-control-plaintext ~ label,
+.form-floating > .form-select ~ label {
+ color: rgba(var(--bs-body-color-rgb), 0.65);
+ transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
+}
+
+.form-floating > .form-control:focus ~ label::after,
+.form-floating > .form-control:not(:placeholder-shown) ~ label::after,
+.form-floating > .form-control-plaintext ~ label::after,
+.form-floating > .form-select ~ label::after {
+ position: absolute;
+ inset: 1rem 0.5rem;
+ z-index: -1;
+ height: 1.5em;
+ content: '';
+ background-color: #f2f2f2;
+ border-radius: var(--bs-border-radius);
+}
+
+.form-floating > .form-control:-webkit-autofill ~ label {
+ color: rgba(var(--bs-body-color-rgb), 0.65);
+ transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
+}
+
+.form-floating > .form-control-plaintext ~ label {
+ border-width: 0 0;
+}
+
+.form-floating > :disabled ~ label {
+ color: #6c757d;
+}
+
+.form-floating > :disabled ~ label::after {
+ background-color: var(--bs-secondary-bg);
+}
+
+.input-group {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: stretch;
+ width: 100%;
+}
+
+.input-group > .form-control,
+.input-group > .form-select,
+.input-group > .form-floating {
+ position: relative;
+ flex: 1 1 auto;
+ width: 1%;
+ min-width: 0;
+}
+
+.input-group > .form-control:focus,
+.input-group > .form-select:focus,
+.input-group > .form-floating:focus-within {
+ z-index: 5;
+}
+
+.input-group .btn {
+ position: relative;
+ z-index: 2;
+}
+
+.input-group .btn:focus {
+ z-index: 5;
+}
+
+.input-group-text {
+ display: flex;
+ align-items: center;
+ padding: 0.7rem 1rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--bs-body-color);
+ text-align: center;
+ white-space: nowrap;
+ background-color: var(--bs-tertiary-bg);
+ border: 0 solid var(--bs-border-color);
+ border-radius: var(--bs-border-radius);
+}
+
+.input-group-lg > .form-control,
+.input-group-lg > .form-select,
+.input-group-lg > .input-group-text,
+.input-group-lg > .btn {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ border-radius: var(--bs-border-radius-lg);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .form-select,
+.input-group-sm > .input-group-text,
+.input-group-sm > .btn {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ border-radius: var(--bs-border-radius-sm);
+}
+
+.input-group-lg > .form-select,
+.input-group-sm > .form-select {
+ padding-right: 4rem;
+}
+
+.input-group:not(.has-validation)
+ > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(
+ .form-floating
+ ),
+.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n + 3),
+.input-group:not(.has-validation)
+ > .form-floating:not(:last-child)
+ > .form-control,
+.input-group:not(.has-validation)
+ > .form-floating:not(:last-child)
+ > .form-select {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group.has-validation
+ > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu):not(
+ .form-floating
+ ),
+.input-group.has-validation > .dropdown-toggle:nth-last-child(n + 4),
+.input-group.has-validation
+ > .form-floating:nth-last-child(n + 3)
+ > .form-control,
+.input-group.has-validation
+ > .form-floating:nth-last-child(n + 3)
+ > .form-select {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group
+ > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(
+ .valid-feedback
+ ):not(.invalid-tooltip):not(.invalid-feedback) {
+ margin-left: calc(0 * -1);
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group > .form-floating:not(:first-child) > .form-control,
+.input-group > .form-floating:not(:first-child) > .form-select {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.valid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 0.875em;
+ color: var(--bs-form-valid-color);
+}
+
+.valid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: 0.1rem;
+ font-size: 0.875rem;
+ color: #fff;
+ background-color: var(--bs-success);
+ border-radius: var(--bs-border-radius);
+}
+
+.was-validated :valid ~ .valid-feedback,
+.was-validated :valid ~ .valid-tooltip,
+.is-valid ~ .valid-feedback,
+.is-valid ~ .valid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:valid,
+.form-control.is-valid {
+ border-color: var(--bs-form-valid-border-color);
+ padding-right: calc(1.5em + 1.4rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2331bb6b' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.35rem) center;
+ background-size: calc(0.75em + 0.7rem) calc(0.75em + 0.7rem);
+}
+
+.was-validated .form-control:valid:focus,
+.form-control.is-valid:focus {
+ border-color: var(--bs-form-valid-border-color);
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+
+.was-validated textarea.form-control:valid,
+textarea.form-control.is-valid {
+ padding-right: calc(1.5em + 1.4rem);
+ background-position: top calc(0.375em + 0.35rem) right calc(0.375em + 0.35rem);
+}
+
+.was-validated .form-select:valid,
+.form-select.is-valid {
+ border-color: var(--bs-form-valid-border-color);
+}
+
+.was-validated .form-select:valid:not([multiple]):not([size]),
+.was-validated .form-select:valid:not([multiple])[size='1'],
+.form-select.is-valid:not([multiple]):not([size]),
+.form-select.is-valid:not([multiple])[size='1'] {
+ --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2331bb6b' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ padding-right: 5.5rem;
+ background-position:
+ right 1rem center,
+ center right 3rem;
+ background-size:
+ 16px 12px,
+ calc(0.75em + 0.7rem) calc(0.75em + 0.7rem);
+}
+
+.was-validated .form-select:valid:focus,
+.form-select.is-valid:focus {
+ border-color: var(--bs-form-valid-border-color);
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+
+.was-validated .form-control-color:valid,
+.form-control-color.is-valid {
+ width: calc(3rem + calc(1.5em + 1.4rem));
+}
+
+.was-validated .form-check-input:valid,
+.form-check-input.is-valid {
+ border-color: var(--bs-form-valid-border-color);
+}
+
+.was-validated .form-check-input:valid:checked,
+.form-check-input.is-valid:checked {
+ background-color: var(--bs-form-valid-color);
+}
+
+.was-validated .form-check-input:valid:focus,
+.form-check-input.is-valid:focus {
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);
+}
+
+.was-validated .form-check-input:valid ~ .form-check-label,
+.form-check-input.is-valid ~ .form-check-label {
+ color: var(--bs-form-valid-color);
+}
+
+.form-check-inline .form-check-input ~ .valid-feedback {
+ margin-left: 0.5em;
+}
+
+.was-validated .input-group > .form-control:not(:focus):valid,
+.input-group > .form-control:not(:focus).is-valid,
+.was-validated .input-group > .form-select:not(:focus):valid,
+.input-group > .form-select:not(:focus).is-valid,
+.was-validated .input-group > .form-floating:not(:focus-within):valid,
+.input-group > .form-floating:not(:focus-within).is-valid {
+ z-index: 3;
+}
+
+.invalid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 0.875em;
+ color: var(--bs-form-invalid-color);
+}
+
+.invalid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: 0.1rem;
+ font-size: 0.875rem;
+ color: #fff;
+ background-color: var(--bs-danger);
+ border-radius: var(--bs-border-radius);
+}
+
+.was-validated :invalid ~ .invalid-feedback,
+.was-validated :invalid ~ .invalid-tooltip,
+.is-invalid ~ .invalid-feedback,
+.is-invalid ~ .invalid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:invalid,
+.form-control.is-invalid {
+ border-color: var(--bs-form-invalid-border-color);
+ padding-right: calc(1.5em + 1.4rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.35rem) center;
+ background-size: calc(0.75em + 0.7rem) calc(0.75em + 0.7rem);
+}
+
+.was-validated .form-control:invalid:focus,
+.form-control.is-invalid:focus {
+ border-color: var(--bs-form-invalid-border-color);
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+
+.was-validated textarea.form-control:invalid,
+textarea.form-control.is-invalid {
+ padding-right: calc(1.5em + 1.4rem);
+ background-position: top calc(0.375em + 0.35rem) right calc(0.375em + 0.35rem);
+}
+
+.was-validated .form-select:invalid,
+.form-select.is-invalid {
+ border-color: var(--bs-form-invalid-border-color);
+}
+
+.was-validated .form-select:invalid:not([multiple]):not([size]),
+.was-validated .form-select:invalid:not([multiple])[size='1'],
+.form-select.is-invalid:not([multiple]):not([size]),
+.form-select.is-invalid:not([multiple])[size='1'] {
+ --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
+ padding-right: 5.5rem;
+ background-position:
+ right 1rem center,
+ center right 3rem;
+ background-size:
+ 16px 12px,
+ calc(0.75em + 0.7rem) calc(0.75em + 0.7rem);
+}
+
+.was-validated .form-select:invalid:focus,
+.form-select.is-invalid:focus {
+ border-color: var(--bs-form-invalid-border-color);
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+
+.was-validated .form-control-color:invalid,
+.form-control-color.is-invalid {
+ width: calc(3rem + calc(1.5em + 1.4rem));
+}
+
+.was-validated .form-check-input:invalid,
+.form-check-input.is-invalid {
+ border-color: var(--bs-form-invalid-border-color);
+}
+
+.was-validated .form-check-input:invalid:checked,
+.form-check-input.is-invalid:checked {
+ background-color: var(--bs-form-invalid-color);
+}
+
+.was-validated .form-check-input:invalid:focus,
+.form-check-input.is-invalid:focus {
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
+}
+
+.was-validated .form-check-input:invalid ~ .form-check-label,
+.form-check-input.is-invalid ~ .form-check-label {
+ color: var(--bs-form-invalid-color);
+}
+
+.form-check-inline .form-check-input ~ .invalid-feedback {
+ margin-left: 0.5em;
+}
+
+.was-validated .input-group > .form-control:not(:focus):invalid,
+.input-group > .form-control:not(:focus).is-invalid,
+.was-validated .input-group > .form-select:not(:focus):invalid,
+.input-group > .form-select:not(:focus).is-invalid,
+.was-validated .input-group > .form-floating:not(:focus-within):invalid,
+.input-group > .form-floating:not(:focus-within).is-invalid {
+ z-index: 4;
+}
+
+.btn {
+ --bs-btn-padding-x: 1rem;
+ --bs-btn-padding-y: 0.7rem;
+ --bs-btn-font-family: ;
+ --bs-btn-font-size: 1rem;
+ --bs-btn-font-weight: 400;
+ --bs-btn-line-height: 1.5;
+ --bs-btn-color: var(--bs-body-color);
+ --bs-btn-bg: transparent;
+ --bs-btn-border-width: var(--bs-border-width);
+ --bs-btn-border-color: transparent;
+ --bs-btn-border-radius: var(--bs-border-radius);
+ --bs-btn-hover-border-color: transparent;
+ --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15),
+ 0 1px 1px rgba(0, 0, 0, 0.075);
+ --bs-btn-disabled-opacity: 0.65;
+ --bs-btn-focus-box-shadow: 0 0 0 0.25rem
+ rgba(var(--bs-btn-focus-shadow-rgb), 0.5);
+ display: inline-block;
+ padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
+ font-family: var(--bs-btn-font-family);
+ font-size: var(--bs-btn-font-size);
+ font-weight: var(--bs-btn-font-weight);
+ line-height: var(--bs-btn-line-height);
+ color: var(--bs-btn-color);
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ user-select: none;
+ border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
+ border-radius: var(--bs-btn-border-radius);
+ background-color: var(--bs-btn-bg);
+ transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .btn {
+ transition: none;
+ }
+}
+
+.btn:hover {
+ color: var(--bs-btn-hover-color);
+ background-color: var(--bs-btn-hover-bg);
+ border-color: var(--bs-btn-hover-border-color);
+}
+
+.btn-check + .btn:hover {
+ color: var(--bs-btn-color);
+ background-color: var(--bs-btn-bg);
+ border-color: var(--bs-btn-border-color);
+}
+
+.btn:focus-visible {
+ color: var(--bs-btn-hover-color);
+ background-color: var(--bs-btn-hover-bg);
+ border-color: var(--bs-btn-hover-border-color);
+ outline: 0;
+ box-shadow: var(--bs-btn-focus-box-shadow);
+}
+
+.btn-check:focus-visible + .btn {
+ border-color: var(--bs-btn-hover-border-color);
+ outline: 0;
+ box-shadow: var(--bs-btn-focus-box-shadow);
+}
+
+.btn-check:checked + .btn,
+:not(.btn-check) + .btn:active,
+.btn:first-child:active,
+.btn.active,
+.btn.show {
+ color: var(--bs-btn-active-color);
+ background-color: var(--bs-btn-active-bg);
+ border-color: var(--bs-btn-active-border-color);
+}
+
+.btn-check:checked + .btn:focus-visible,
+:not(.btn-check) + .btn:active:focus-visible,
+.btn:first-child:active:focus-visible,
+.btn.active:focus-visible,
+.btn.show:focus-visible {
+ box-shadow: var(--bs-btn-focus-box-shadow);
+}
+
+.btn:disabled,
+.btn.disabled,
+fieldset:disabled .btn {
+ color: var(--bs-btn-disabled-color);
+ pointer-events: none;
+ background-color: var(--bs-btn-disabled-bg);
+ border-color: var(--bs-btn-disabled-border-color);
+ opacity: var(--bs-btn-disabled-opacity);
+}
+
+.btn-primary {
+ --bs-btn-color: #000;
+ --bs-btn-bg: #31bb6b;
+ --bs-btn-border-color: #31bb6b;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #50c581;
+ --bs-btn-hover-border-color: #46c27a;
+ --bs-btn-focus-shadow-rgb: 42, 159, 91;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #5ac989;
+ --bs-btn-active-border-color: #46c27a;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #31bb6b;
+ --bs-btn-disabled-border-color: #31bb6b;
+}
+
+.btn-secondary {
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #707070;
+ --bs-btn-border-color: #707070;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #5f5f5f;
+ --bs-btn-hover-border-color: #5a5a5a;
+ --bs-btn-focus-shadow-rgb: 133, 133, 133;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #5a5a5a;
+ --bs-btn-active-border-color: #545454;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #707070;
+ --bs-btn-disabled-border-color: #707070;
+}
+
+.btn-success {
+ --bs-btn-color: #000;
+ --bs-btn-bg: #31bb6b;
+ --bs-btn-border-color: #31bb6b;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #50c581;
+ --bs-btn-hover-border-color: #46c27a;
+ --bs-btn-focus-shadow-rgb: 42, 159, 91;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #5ac989;
+ --bs-btn-active-border-color: #46c27a;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #31bb6b;
+ --bs-btn-disabled-border-color: #31bb6b;
+}
+
+.btn-info {
+ --bs-btn-color: #000;
+ --bs-btn-bg: #0dcaf0;
+ --bs-btn-border-color: #0dcaf0;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #31d2f2;
+ --bs-btn-hover-border-color: #25cff2;
+ --bs-btn-focus-shadow-rgb: 11, 172, 204;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #3dd5f3;
+ --bs-btn-active-border-color: #25cff2;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #0dcaf0;
+ --bs-btn-disabled-border-color: #0dcaf0;
+}
+
+.btn-warning {
+ --bs-btn-color: #000;
+ --bs-btn-bg: #febc59;
+ --bs-btn-border-color: #febc59;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #fec672;
+ --bs-btn-hover-border-color: #fec36a;
+ --bs-btn-focus-shadow-rgb: 216, 160, 76;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #fec97a;
+ --bs-btn-active-border-color: #fec36a;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #febc59;
+ --bs-btn-disabled-border-color: #febc59;
+}
+
+.btn-danger {
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #dc3545;
+ --bs-btn-border-color: #dc3545;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #bb2d3b;
+ --bs-btn-hover-border-color: #b02a37;
+ --bs-btn-focus-shadow-rgb: 225, 83, 97;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #b02a37;
+ --bs-btn-active-border-color: #a52834;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #dc3545;
+ --bs-btn-disabled-border-color: #dc3545;
+}
+
+.btn-light {
+ --bs-btn-color: #000;
+ --bs-btn-bg: #f8f9fa;
+ --bs-btn-border-color: #f8f9fa;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #d3d4d5;
+ --bs-btn-hover-border-color: #c6c7c8;
+ --bs-btn-focus-shadow-rgb: 211, 212, 213;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #c6c7c8;
+ --bs-btn-active-border-color: #babbbc;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #f8f9fa;
+ --bs-btn-disabled-border-color: #f8f9fa;
+}
+
+.btn-dark {
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #212529;
+ --bs-btn-border-color: #212529;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #424649;
+ --bs-btn-hover-border-color: #373b3e;
+ --bs-btn-focus-shadow-rgb: 66, 70, 73;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #4d5154;
+ --bs-btn-active-border-color: #373b3e;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #212529;
+ --bs-btn-disabled-border-color: #212529;
+}
+
+.btn-outline-primary {
+ --bs-btn-color: #31bb6b;
+ --bs-btn-border-color: #31bb6b;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #31bb6b;
+ --bs-btn-hover-border-color: #31bb6b;
+ --bs-btn-focus-shadow-rgb: 49, 187, 107;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #31bb6b;
+ --bs-btn-active-border-color: #31bb6b;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #31bb6b;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #31bb6b;
+ --bs-gradient: none;
+}
+
+.btn-outline-secondary {
+ --bs-btn-color: #707070;
+ --bs-btn-border-color: #707070;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #707070;
+ --bs-btn-hover-border-color: #707070;
+ --bs-btn-focus-shadow-rgb: 112, 112, 112;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #707070;
+ --bs-btn-active-border-color: #707070;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #707070;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #707070;
+ --bs-gradient: none;
+}
+
+.btn-outline-success {
+ --bs-btn-color: #31bb6b;
+ --bs-btn-border-color: #31bb6b;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #31bb6b;
+ --bs-btn-hover-border-color: #31bb6b;
+ --bs-btn-focus-shadow-rgb: 49, 187, 107;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #31bb6b;
+ --bs-btn-active-border-color: #31bb6b;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #31bb6b;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #31bb6b;
+ --bs-gradient: none;
+}
+
+.btn-outline-info {
+ --bs-btn-color: #0dcaf0;
+ --bs-btn-border-color: #0dcaf0;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #0dcaf0;
+ --bs-btn-hover-border-color: #0dcaf0;
+ --bs-btn-focus-shadow-rgb: 13, 202, 240;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #0dcaf0;
+ --bs-btn-active-border-color: #0dcaf0;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #0dcaf0;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #0dcaf0;
+ --bs-gradient: none;
+}
+
+.btn-outline-warning {
+ --bs-btn-color: #febc59;
+ --bs-btn-border-color: #febc59;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #febc59;
+ --bs-btn-hover-border-color: #febc59;
+ --bs-btn-focus-shadow-rgb: 254, 188, 89;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #febc59;
+ --bs-btn-active-border-color: #febc59;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #febc59;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #febc59;
+ --bs-gradient: none;
+}
+
+.btn-outline-danger {
+ --bs-btn-color: #dc3545;
+ --bs-btn-border-color: #dc3545;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #dc3545;
+ --bs-btn-hover-border-color: #dc3545;
+ --bs-btn-focus-shadow-rgb: 220, 53, 69;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #dc3545;
+ --bs-btn-active-border-color: #dc3545;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #dc3545;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #dc3545;
+ --bs-gradient: none;
+}
+
+.btn-outline-light {
+ --bs-btn-color: #f8f9fa;
+ --bs-btn-border-color: #f8f9fa;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #f8f9fa;
+ --bs-btn-hover-border-color: #f8f9fa;
+ --bs-btn-focus-shadow-rgb: 248, 249, 250;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #f8f9fa;
+ --bs-btn-active-border-color: #f8f9fa;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #f8f9fa;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #f8f9fa;
+ --bs-gradient: none;
+}
+
+.btn-outline-dark {
+ --bs-btn-color: #212529;
+ --bs-btn-border-color: #212529;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #212529;
+ --bs-btn-hover-border-color: #212529;
+ --bs-btn-focus-shadow-rgb: 33, 37, 41;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #212529;
+ --bs-btn-active-border-color: #212529;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #212529;
+ --bs-btn-disabled-bg: transparent;
+ --bs-btn-disabled-border-color: #212529;
+ --bs-gradient: none;
+}
+
+.btn-link {
+ --bs-btn-font-weight: 400;
+ --bs-btn-color: var(--bs-link-color);
+ --bs-btn-bg: transparent;
+ --bs-btn-border-color: transparent;
+ --bs-btn-hover-color: var(--bs-link-hover-color);
+ --bs-btn-hover-border-color: transparent;
+ --bs-btn-active-color: var(--bs-link-hover-color);
+ --bs-btn-active-border-color: transparent;
+ --bs-btn-disabled-color: #6c757d;
+ --bs-btn-disabled-border-color: transparent;
+ --bs-btn-box-shadow: 0 0 0 #000;
+ --bs-btn-focus-shadow-rgb: 49, 132, 253;
+ text-decoration: none;
+}
+
+.btn-link:focus-visible {
+ color: var(--bs-btn-color);
+}
+
+.btn-link:hover {
+ color: var(--bs-btn-hover-color);
+}
+
+.btn-lg,
+.btn-group-lg > .btn {
+ --bs-btn-padding-y: 0.5rem;
+ --bs-btn-padding-x: 1rem;
+ --bs-btn-font-size: 1.25rem;
+ --bs-btn-border-radius: var(--bs-border-radius-lg);
+}
+
+.btn-sm,
+.btn-group-sm > .btn {
+ --bs-btn-padding-y: 0.25rem;
+ --bs-btn-padding-x: 0.5rem;
+ --bs-btn-font-size: 0.875rem;
+ --bs-btn-border-radius: var(--bs-border-radius-sm);
+}
+
+.fade {
+ transition: opacity 0.15s linear;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .fade {
+ transition: none;
+ }
+}
+
+.fade:not(.show) {
+ opacity: 0;
+}
+
+.collapse:not(.show) {
+ display: none;
+}
+
+.collapsing {
+ height: 0;
+ overflow: hidden;
+ transition: height 0.35s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .collapsing {
+ transition: none;
+ }
+}
+
+.collapsing.collapse-horizontal {
+ width: 0;
+ height: auto;
+ transition: width 0.35s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .collapsing.collapse-horizontal {
+ transition: none;
+ }
+}
+
+.dropup,
+.dropend,
+.dropdown,
+.dropstart,
+.dropup-center,
+.dropdown-center {
+ position: relative;
+}
+
+.dropdown-toggle {
+ white-space: nowrap;
+}
+
+.dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: '';
+ border-top: 0.3em solid;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0;
+ border-left: 0.3em solid transparent;
+}
+
+.dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropdown-menu {
+ --bs-dropdown-zindex: 1000;
+ --bs-dropdown-min-width: 10rem;
+ --bs-dropdown-padding-x: 0;
+ --bs-dropdown-padding-y: 0.5rem;
+ --bs-dropdown-spacer: 0.125rem;
+ --bs-dropdown-font-size: 1rem;
+ --bs-dropdown-color: var(--bs-body-color);
+ --bs-dropdown-bg: var(--bs-body-bg);
+ --bs-dropdown-border-color: var(--bs-border-color-translucent);
+ --bs-dropdown-border-radius: var(--bs-border-radius);
+ --bs-dropdown-border-width: var(--bs-border-width);
+ --bs-dropdown-inner-border-radius: calc(
+ var(--bs-border-radius) - var(--bs-border-width)
+ );
+ --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
+ --bs-dropdown-divider-margin-y: 0.5rem;
+ --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-dropdown-link-color: var(--bs-body-color);
+ --bs-dropdown-link-hover-color: var(--bs-body-color);
+ --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);
+ --bs-dropdown-link-active-color: #fff;
+ --bs-dropdown-link-active-bg: #31bb6b;
+ --bs-dropdown-link-disabled-color: var(--bs-tertiary-color);
+ --bs-dropdown-item-padding-x: 1rem;
+ --bs-dropdown-item-padding-y: 0.25rem;
+ --bs-dropdown-header-color: #6c757d;
+ --bs-dropdown-header-padding-x: 1rem;
+ --bs-dropdown-header-padding-y: 0.5rem;
+ position: absolute;
+ z-index: var(--bs-dropdown-zindex);
+ display: none;
+ min-width: var(--bs-dropdown-min-width);
+ padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);
+ margin: 0;
+ font-size: var(--bs-dropdown-font-size);
+ color: var(--bs-dropdown-color);
+ text-align: left;
+ list-style: none;
+ background-color: var(--bs-dropdown-bg);
+ background-clip: padding-box;
+ border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);
+ border-radius: var(--bs-dropdown-border-radius);
+}
+
+.dropdown-menu[data-bs-popper] {
+ top: 100%;
+ left: 0;
+ margin-top: var(--bs-dropdown-spacer);
+}
+
+.dropdown-menu-start {
+ --bs-position: start;
+}
+
+.dropdown-menu-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+}
+
+.dropdown-menu-end {
+ --bs-position: end;
+}
+
+.dropdown-menu-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+}
+
+@media (min-width: 576px) {
+ .dropdown-menu-sm-start {
+ --bs-position: start;
+ }
+
+ .dropdown-menu-sm-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-sm-end {
+ --bs-position: end;
+ }
+
+ .dropdown-menu-sm-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 768px) {
+ .dropdown-menu-md-start {
+ --bs-position: start;
+ }
+
+ .dropdown-menu-md-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-md-end {
+ --bs-position: end;
+ }
+
+ .dropdown-menu-md-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 992px) {
+ .dropdown-menu-lg-start {
+ --bs-position: start;
+ }
+
+ .dropdown-menu-lg-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-lg-end {
+ --bs-position: end;
+ }
+
+ .dropdown-menu-lg-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 1200px) {
+ .dropdown-menu-xl-start {
+ --bs-position: start;
+ }
+
+ .dropdown-menu-xl-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-xl-end {
+ --bs-position: end;
+ }
+
+ .dropdown-menu-xl-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 1400px) {
+ .dropdown-menu-xxl-start {
+ --bs-position: start;
+ }
+
+ .dropdown-menu-xxl-start[data-bs-popper] {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-xxl-end {
+ --bs-position: end;
+ }
+
+ .dropdown-menu-xxl-end[data-bs-popper] {
+ right: 0;
+ left: auto;
+ }
+}
+
+.dropup .dropdown-menu[data-bs-popper] {
+ top: auto;
+ bottom: 100%;
+ margin-top: 0;
+ margin-bottom: var(--bs-dropdown-spacer);
+}
+
+.dropup .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: '';
+ border-top: 0;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0.3em solid;
+ border-left: 0.3em solid transparent;
+}
+
+.dropup .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropend .dropdown-menu[data-bs-popper] {
+ top: 0;
+ right: auto;
+ left: 100%;
+ margin-top: 0;
+ margin-left: var(--bs-dropdown-spacer);
+}
+
+.dropend .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: '';
+ border-top: 0.3em solid transparent;
+ border-right: 0;
+ border-bottom: 0.3em solid transparent;
+ border-left: 0.3em solid;
+}
+
+.dropend .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropend .dropdown-toggle::after {
+ vertical-align: 0;
+}
+
+.dropstart .dropdown-menu[data-bs-popper] {
+ top: 0;
+ right: 100%;
+ left: auto;
+ margin-top: 0;
+ margin-right: var(--bs-dropdown-spacer);
+}
+
+.dropstart .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: '';
+}
+
+.dropstart .dropdown-toggle::after {
+ display: none;
+}
+
+.dropstart .dropdown-toggle::before {
+ display: inline-block;
+ margin-right: 0.255em;
+ vertical-align: 0.255em;
+ content: '';
+ border-top: 0.3em solid transparent;
+ border-right: 0.3em solid;
+ border-bottom: 0.3em solid transparent;
+}
+
+.dropstart .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropstart .dropdown-toggle::before {
+ vertical-align: 0;
+}
+
+.dropdown-divider {
+ height: 0;
+ margin: var(--bs-dropdown-divider-margin-y) 0;
+ overflow: hidden;
+ border-top: 1px solid var(--bs-dropdown-divider-bg);
+ opacity: 1;
+}
+
+.dropdown-item {
+ display: block;
+ width: 100%;
+ padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
+ clear: both;
+ font-weight: 400;
+ color: var(--bs-dropdown-link-color);
+ text-align: inherit;
+ white-space: nowrap;
+ background-color: transparent;
+ border: 0;
+ border-radius: var(--bs-dropdown-item-border-radius, 0);
+}
+
+.dropdown-item:hover,
+.dropdown-item:focus {
+ color: var(--bs-dropdown-link-hover-color);
+ background-color: var(--bs-dropdown-link-hover-bg);
+}
+
+.dropdown-item.active,
+.dropdown-item:active {
+ color: var(--bs-dropdown-link-active-color);
+ text-decoration: none;
+ background-color: var(--bs-dropdown-link-active-bg);
+}
+
+.dropdown-item.disabled,
+.dropdown-item:disabled {
+ color: var(--bs-dropdown-link-disabled-color);
+ pointer-events: none;
+ background-color: transparent;
+}
+
+.dropdown-menu.show {
+ display: block;
+}
+
+.dropdown-header {
+ display: block;
+ padding: var(--bs-dropdown-header-padding-y)
+ var(--bs-dropdown-header-padding-x);
+ margin-bottom: 0;
+ font-size: 0.875rem;
+ color: var(--bs-dropdown-header-color);
+ white-space: nowrap;
+}
+
+.dropdown-item-text {
+ display: block;
+ padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
+ color: var(--bs-dropdown-link-color);
+}
+
+.dropdown-menu-dark {
+ --bs-dropdown-color: #dee2e6;
+ --bs-dropdown-bg: #343a40;
+ --bs-dropdown-border-color: var(--bs-border-color-translucent);
+ --bs-dropdown-box-shadow: ;
+ --bs-dropdown-link-color: #dee2e6;
+ --bs-dropdown-link-hover-color: #fff;
+ --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
+ --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);
+ --bs-dropdown-link-active-color: #fff;
+ --bs-dropdown-link-active-bg: #31bb6b;
+ --bs-dropdown-link-disabled-color: #adb5bd;
+ --bs-dropdown-header-color: #adb5bd;
+}
+
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-flex;
+ vertical-align: middle;
+}
+
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ flex: 1 1 auto;
+}
+
+.btn-group > .btn-check:checked + .btn,
+.btn-group > .btn-check:focus + .btn,
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn-check:checked + .btn,
+.btn-group-vertical > .btn-check:focus + .btn,
+.btn-group-vertical > .btn:hover,
+.btn-group-vertical > .btn:focus,
+.btn-group-vertical > .btn:active,
+.btn-group-vertical > .btn.active {
+ z-index: 1;
+}
+
+.btn-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+}
+
+.btn-toolbar .input-group {
+ width: auto;
+}
+
+.btn-group {
+ border-radius: var(--bs-border-radius);
+}
+
+.btn-group > :not(.btn-check:first-child) + .btn,
+.btn-group > .btn-group:not(:first-child) {
+ margin-left: calc(var(--bs-border-width) * -1);
+}
+
+.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group > .btn.dropdown-toggle-split:first-child,
+.btn-group > .btn-group:not(:last-child) > .btn {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.btn-group > .btn:nth-child(n + 3),
+.btn-group > :not(.btn-check) + .btn,
+.btn-group > .btn-group:not(:first-child) > .btn {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.dropdown-toggle-split {
+ padding-right: 0.75rem;
+ padding-left: 0.75rem;
+}
+
+.dropdown-toggle-split::after,
+.dropup .dropdown-toggle-split::after,
+.dropend .dropdown-toggle-split::after {
+ margin-left: 0;
+}
+
+.dropstart .dropdown-toggle-split::before {
+ margin-right: 0;
+}
+
+.btn-sm + .dropdown-toggle-split,
+.btn-group-sm > .btn + .dropdown-toggle-split {
+ padding-right: 0.375rem;
+ padding-left: 0.375rem;
+}
+
+.btn-lg + .dropdown-toggle-split,
+.btn-group-lg > .btn + .dropdown-toggle-split {
+ padding-right: 0.75rem;
+ padding-left: 0.75rem;
+}
+
+.btn-group-vertical {
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+}
+
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group {
+ width: 100%;
+}
+
+.btn-group-vertical > .btn:not(:first-child),
+.btn-group-vertical > .btn-group:not(:first-child) {
+ margin-top: calc(var(--bs-border-width) * -1);
+}
+
+.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group-vertical > .btn-group:not(:last-child) > .btn {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.btn-group-vertical > .btn ~ .btn,
+.btn-group-vertical > .btn-group:not(:first-child) > .btn {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.nav {
+ --bs-nav-link-padding-x: 1rem;
+ --bs-nav-link-padding-y: 0.5rem;
+ --bs-nav-link-font-weight: ;
+ --bs-nav-link-color: var(--bs-link-color);
+ --bs-nav-link-hover-color: var(--bs-link-hover-color);
+ --bs-nav-link-disabled-color: var(--bs-secondary-color);
+ display: flex;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
+ font-size: var(--bs-nav-link-font-size);
+ font-weight: var(--bs-nav-link-font-weight);
+ color: var(--bs-nav-link-color);
+ background: none;
+ border: 0;
+ transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .nav-link {
+ transition: none;
+ }
+}
+
+.nav-link:hover,
+.nav-link:focus {
+ color: var(--bs-nav-link-hover-color);
+}
+
+.nav-link:focus-visible {
+ outline: 0;
+ box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+}
+
+.nav-link.disabled {
+ color: var(--bs-nav-link-disabled-color);
+ pointer-events: none;
+ cursor: default;
+}
+
+.nav-tabs {
+ --bs-nav-tabs-border-width: var(--bs-border-width);
+ --bs-nav-tabs-border-color: var(--bs-border-color);
+ --bs-nav-tabs-border-radius: var(--bs-border-radius);
+ --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg)
+ var(--bs-secondary-bg) var(--bs-border-color);
+ --bs-nav-tabs-link-active-color: var(--bs-emphasis-color);
+ --bs-nav-tabs-link-active-bg: var(--bs-body-bg);
+ --bs-nav-tabs-link-active-border-color: var(--bs-border-color)
+ var(--bs-border-color) var(--bs-body-bg);
+ border-bottom: var(--bs-nav-tabs-border-width) solid
+ var(--bs-nav-tabs-border-color);
+}
+
+.nav-tabs .nav-link {
+ margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width));
+ border: var(--bs-nav-tabs-border-width) solid transparent;
+ border-top-left-radius: var(--bs-nav-tabs-border-radius);
+ border-top-right-radius: var(--bs-nav-tabs-border-radius);
+}
+
+.nav-tabs .nav-link:hover,
+.nav-tabs .nav-link:focus {
+ isolation: isolate;
+ border-color: var(--bs-nav-tabs-link-hover-border-color);
+}
+
+.nav-tabs .nav-link.disabled,
+.nav-tabs .nav-link:disabled {
+ color: var(--bs-nav-link-disabled-color);
+ background-color: transparent;
+ border-color: transparent;
+}
+
+.nav-tabs .nav-link.active,
+.nav-tabs .nav-item.show .nav-link {
+ color: var(--bs-nav-tabs-link-active-color);
+ background-color: var(--bs-nav-tabs-link-active-bg);
+ border-color: var(--bs-nav-tabs-link-active-border-color);
+}
+
+.nav-tabs .dropdown-menu {
+ margin-top: calc(-1 * var(--bs-nav-tabs-border-width));
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.nav-pills {
+ --bs-nav-pills-border-radius: var(--bs-border-radius);
+ --bs-nav-pills-link-active-color: #fff;
+ --bs-nav-pills-link-active-bg: #31bb6b;
+}
+
+.nav-pills .nav-link {
+ border-radius: var(--bs-nav-pills-border-radius);
+}
+
+.nav-pills .nav-link:disabled {
+ color: var(--bs-nav-link-disabled-color);
+ background-color: transparent;
+ border-color: transparent;
+}
+
+.nav-pills .nav-link.active,
+.nav-pills .show > .nav-link {
+ color: var(--bs-nav-pills-link-active-color);
+ background-color: var(--bs-nav-pills-link-active-bg);
+}
+
+.nav-underline {
+ --bs-nav-underline-gap: 1rem;
+ --bs-nav-underline-border-width: 0.125rem;
+ --bs-nav-underline-link-active-color: var(--bs-emphasis-color);
+ gap: var(--bs-nav-underline-gap);
+}
+
+.nav-underline .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+ border-bottom: var(--bs-nav-underline-border-width) solid transparent;
+}
+
+.nav-underline .nav-link:hover,
+.nav-underline .nav-link:focus {
+ border-bottom-color: currentcolor;
+}
+
+.nav-underline .nav-link.active,
+.nav-underline .show > .nav-link {
+ font-weight: 700;
+ color: var(--bs-nav-underline-link-active-color);
+ border-bottom-color: currentcolor;
+}
+
+.nav-fill > .nav-link,
+.nav-fill .nav-item {
+ flex: 1 1 auto;
+ text-align: center;
+}
+
+.nav-justified > .nav-link,
+.nav-justified .nav-item {
+ flex-basis: 0;
+ flex-grow: 1;
+ text-align: center;
+}
+
+.nav-fill .nav-item .nav-link,
+.nav-justified .nav-item .nav-link {
+ width: 100%;
+}
+
+.tab-content > .tab-pane {
+ display: none;
+}
+
+.tab-content > .active {
+ display: block;
+}
+
+.navbar {
+ --bs-navbar-padding-x: 0;
+ --bs-navbar-padding-y: 0.5rem;
+ --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65);
+ --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8);
+ --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3);
+ --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1);
+ --bs-navbar-brand-padding-y: 0.3125rem;
+ --bs-navbar-brand-margin-end: 1rem;
+ --bs-navbar-brand-font-size: 1.25rem;
+ --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1);
+ --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1);
+ --bs-navbar-nav-link-padding-x: 0.5rem;
+ --bs-navbar-toggler-padding-y: 0.25rem;
+ --bs-navbar-toggler-padding-x: 0.75rem;
+ --bs-navbar-toggler-font-size: 1.25rem;
+ --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+ --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15);
+ --bs-navbar-toggler-border-radius: var(--bs-border-radius);
+ --bs-navbar-toggler-focus-width: 0.25rem;
+ --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);
+}
+
+.navbar > .container,
+.navbar > .container-fluid,
+.navbar > .container-sm,
+.navbar > .container-md,
+.navbar > .container-lg,
+.navbar > .container-xl,
+.navbar > .container-xxl {
+ display: flex;
+ flex-wrap: inherit;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.navbar-brand {
+ padding-top: var(--bs-navbar-brand-padding-y);
+ padding-bottom: var(--bs-navbar-brand-padding-y);
+ margin-right: var(--bs-navbar-brand-margin-end);
+ font-size: var(--bs-navbar-brand-font-size);
+ color: var(--bs-navbar-brand-color);
+ white-space: nowrap;
+}
+
+.navbar-brand:hover,
+.navbar-brand:focus {
+ color: var(--bs-navbar-brand-hover-color);
+}
+
+.navbar-nav {
+ --bs-nav-link-padding-x: 0;
+ --bs-nav-link-padding-y: 0.5rem;
+ --bs-nav-link-font-weight: ;
+ --bs-nav-link-color: var(--bs-navbar-color);
+ --bs-nav-link-hover-color: var(--bs-navbar-hover-color);
+ --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);
+ display: flex;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.navbar-nav .nav-link.active,
+.navbar-nav .nav-link.show {
+ color: var(--bs-navbar-active-color);
+}
+
+.navbar-nav .dropdown-menu {
+ position: static;
+}
+
+.navbar-text {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ color: var(--bs-navbar-color);
+}
+
+.navbar-text a,
+.navbar-text a:hover,
+.navbar-text a:focus {
+ color: var(--bs-navbar-active-color);
+}
+
+.navbar-collapse {
+ flex-basis: 100%;
+ flex-grow: 1;
+ align-items: center;
+}
+
+.navbar-toggler {
+ padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);
+ font-size: var(--bs-navbar-toggler-font-size);
+ line-height: 1;
+ color: var(--bs-navbar-color);
+ background-color: transparent;
+ border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);
+ border-radius: var(--bs-navbar-toggler-border-radius);
+ transition: var(--bs-navbar-toggler-transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .navbar-toggler {
+ transition: none;
+ }
+}
+
+.navbar-toggler:hover {
+ text-decoration: none;
+}
+
+.navbar-toggler:focus {
+ text-decoration: none;
+ outline: 0;
+ box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width);
+}
+
+.navbar-toggler-icon {
+ display: inline-block;
+ width: 1.5em;
+ height: 1.5em;
+ vertical-align: middle;
+ background-image: var(--bs-navbar-toggler-icon-bg);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 100%;
+}
+
+.navbar-nav-scroll {
+ max-height: var(--bs-scroll-height, 75vh);
+ overflow-y: auto;
+}
+
+@media (min-width: 576px) {
+ .navbar-expand-sm {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-sm .navbar-nav {
+ flex-direction: row;
+ }
+
+ .navbar-expand-sm .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-sm .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+ }
+
+ .navbar-expand-sm .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-expand-sm .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-sm .navbar-toggler {
+ display: none;
+ }
+
+ .navbar-expand-sm .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+ }
+
+ .navbar-expand-sm .offcanvas .offcanvas-header {
+ display: none;
+ }
+
+ .navbar-expand-sm .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+}
+
+@media (min-width: 768px) {
+ .navbar-expand-md {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-md .navbar-nav {
+ flex-direction: row;
+ }
+
+ .navbar-expand-md .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-md .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+ }
+
+ .navbar-expand-md .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-expand-md .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-md .navbar-toggler {
+ display: none;
+ }
+
+ .navbar-expand-md .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+ }
+
+ .navbar-expand-md .offcanvas .offcanvas-header {
+ display: none;
+ }
+
+ .navbar-expand-md .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+}
+
+@media (min-width: 992px) {
+ .navbar-expand-lg {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-lg .navbar-nav {
+ flex-direction: row;
+ }
+
+ .navbar-expand-lg .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-lg .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+ }
+
+ .navbar-expand-lg .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-expand-lg .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-lg .navbar-toggler {
+ display: none;
+ }
+
+ .navbar-expand-lg .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+ }
+
+ .navbar-expand-lg .offcanvas .offcanvas-header {
+ display: none;
+ }
+
+ .navbar-expand-lg .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+}
+
+@media (min-width: 1200px) {
+ .navbar-expand-xl {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-xl .navbar-nav {
+ flex-direction: row;
+ }
+
+ .navbar-expand-xl .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-xl .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+ }
+
+ .navbar-expand-xl .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-expand-xl .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-xl .navbar-toggler {
+ display: none;
+ }
+
+ .navbar-expand-xl .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+ }
+
+ .navbar-expand-xl .offcanvas .offcanvas-header {
+ display: none;
+ }
+
+ .navbar-expand-xl .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+}
+
+@media (min-width: 1400px) {
+ .navbar-expand-xxl {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-xxl .navbar-nav {
+ flex-direction: row;
+ }
+
+ .navbar-expand-xxl .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-xxl .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+ }
+
+ .navbar-expand-xxl .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-expand-xxl .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-xxl .navbar-toggler {
+ display: none;
+ }
+
+ .navbar-expand-xxl .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+ }
+
+ .navbar-expand-xxl .offcanvas .offcanvas-header {
+ display: none;
+ }
+
+ .navbar-expand-xxl .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+}
+
+.navbar-expand {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+}
+
+.navbar-expand .navbar-nav {
+ flex-direction: row;
+}
+
+.navbar-expand .navbar-nav .dropdown-menu {
+ position: absolute;
+}
+
+.navbar-expand .navbar-nav .nav-link {
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
+}
+
+.navbar-expand .navbar-nav-scroll {
+ overflow: visible;
+}
+
+.navbar-expand .navbar-collapse {
+ display: flex !important;
+ flex-basis: auto;
+}
+
+.navbar-expand .navbar-toggler {
+ display: none;
+}
+
+.navbar-expand .offcanvas {
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ transition: none;
+}
+
+.navbar-expand .offcanvas .offcanvas-header {
+ display: none;
+}
+
+.navbar-expand .offcanvas .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+}
+
+.navbar-dark,
+.navbar[data-bs-theme='dark'] {
+ --bs-navbar-color: rgba(255, 255, 255, 0.55);
+ --bs-navbar-hover-color: rgba(255, 255, 255, 0.75);
+ --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);
+ --bs-navbar-active-color: #fff;
+ --bs-navbar-brand-color: #fff;
+ --bs-navbar-brand-hover-color: #fff;
+ --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);
+ --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+[data-bs-theme='dark'] .navbar-toggler-icon {
+ --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+.card {
+ --bs-card-spacer-y: 1rem;
+ --bs-card-spacer-x: 1rem;
+ --bs-card-title-spacer-y: 0.5rem;
+ --bs-card-title-color: ;
+ --bs-card-subtitle-color: ;
+ --bs-card-border-width: var(--bs-border-width);
+ --bs-card-border-color: var(--bs-border-color-translucent);
+ --bs-card-border-radius: var(--bs-border-radius);
+ --bs-card-box-shadow: ;
+ --bs-card-inner-border-radius: calc(
+ var(--bs-border-radius) - (var(--bs-border-width))
+ );
+ --bs-card-cap-padding-y: 0.5rem;
+ --bs-card-cap-padding-x: 1rem;
+ --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03);
+ --bs-card-cap-color: ;
+ --bs-card-height: ;
+ --bs-card-color: ;
+ --bs-card-bg: var(--bs-body-bg);
+ --bs-card-img-overlay-padding: 1rem;
+ --bs-card-group-margin: 0.75rem;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ height: var(--bs-card-height);
+ color: var(--bs-body-color);
+ word-wrap: break-word;
+ background-color: var(--bs-card-bg);
+ background-clip: border-box;
+ border: var(--bs-card-border-width) solid var(--bs-card-border-color);
+ border-radius: var(--bs-card-border-radius);
+}
+
+.card > hr {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.card > .list-group {
+ border-top: inherit;
+ border-bottom: inherit;
+}
+
+.card > .list-group:first-child {
+ border-top-width: 0;
+ border-top-left-radius: var(--bs-card-inner-border-radius);
+ border-top-right-radius: var(--bs-card-inner-border-radius);
+}
+
+.card > .list-group:last-child {
+ border-bottom-width: 0;
+ border-bottom-right-radius: var(--bs-card-inner-border-radius);
+ border-bottom-left-radius: var(--bs-card-inner-border-radius);
+}
+
+.card > .card-header + .list-group,
+.card > .list-group + .card-footer {
+ border-top: 0;
+}
+
+.card-body {
+ flex: 1 1 auto;
+ padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x);
+ color: var(--bs-card-color);
+}
+
+.card-title {
+ margin-bottom: var(--bs-card-title-spacer-y);
+ color: var(--bs-card-title-color);
+}
+
+.card-subtitle {
+ margin-top: calc(-0.5 * var(--bs-card-title-spacer-y));
+ margin-bottom: 0;
+ color: var(--bs-card-subtitle-color);
+}
+
+.card-text:last-child {
+ margin-bottom: 0;
+}
+
+.card-link + .card-link {
+ margin-left: var(--bs-card-spacer-x);
+}
+
+.card-header {
+ padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);
+ margin-bottom: 0;
+ color: var(--bs-card-cap-color);
+ background-color: var(--bs-card-cap-bg);
+ border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);
+}
+
+.card-header:first-child {
+ border-radius: var(--bs-card-inner-border-radius)
+ var(--bs-card-inner-border-radius) 0 0;
+}
+
+.card-footer {
+ padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);
+ color: var(--bs-card-cap-color);
+ background-color: var(--bs-card-cap-bg);
+ border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);
+}
+
+.card-footer:last-child {
+ border-radius: 0 0 var(--bs-card-inner-border-radius)
+ var(--bs-card-inner-border-radius);
+}
+
+.card-header-tabs {
+ margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));
+ margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));
+ margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));
+ border-bottom: 0;
+}
+
+.card-header-tabs .nav-link.active {
+ background-color: var(--bs-card-bg);
+ border-bottom-color: var(--bs-card-bg);
+}
+
+.card-header-pills {
+ margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));
+ margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));
+}
+
+.card-img-overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: var(--bs-card-img-overlay-padding);
+ border-radius: var(--bs-card-inner-border-radius);
+}
+
+.card-img,
+.card-img-top,
+.card-img-bottom {
+ width: 100%;
+}
+
+.card-img,
+.card-img-top {
+ border-top-left-radius: var(--bs-card-inner-border-radius);
+ border-top-right-radius: var(--bs-card-inner-border-radius);
+}
+
+.card-img,
+.card-img-bottom {
+ border-bottom-right-radius: var(--bs-card-inner-border-radius);
+ border-bottom-left-radius: var(--bs-card-inner-border-radius);
+}
+
+.card-group > .card {
+ margin-bottom: var(--bs-card-group-margin);
+}
+
+@media (min-width: 576px) {
+ .card-group {
+ display: flex;
+ flex-flow: row wrap;
+ }
+
+ .card-group > .card {
+ flex: 1 0 0%;
+ margin-bottom: 0;
+ }
+
+ .card-group > .card + .card {
+ margin-left: 0;
+ border-left: 0;
+ }
+
+ .card-group > .card:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .card-group > .card:not(:last-child) .card-img-top,
+ .card-group > .card:not(:last-child) .card-header {
+ border-top-right-radius: 0;
+ }
+
+ .card-group > .card:not(:last-child) .card-img-bottom,
+ .card-group > .card:not(:last-child) .card-footer {
+ border-bottom-right-radius: 0;
+ }
+
+ .card-group > .card:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ .card-group > .card:not(:first-child) .card-img-top,
+ .card-group > .card:not(:first-child) .card-header {
+ border-top-left-radius: 0;
+ }
+
+ .card-group > .card:not(:first-child) .card-img-bottom,
+ .card-group > .card:not(:first-child) .card-footer {
+ border-bottom-left-radius: 0;
+ }
+}
+
+.accordion {
+ --bs-accordion-color: var(--bs-body-color);
+ --bs-accordion-bg: var(--bs-body-bg);
+ --bs-accordion-transition: color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out, border-radius 0.15s ease;
+ --bs-accordion-border-color: var(--bs-border-color);
+ --bs-accordion-border-width: var(--bs-border-width);
+ --bs-accordion-border-radius: var(--bs-border-radius);
+ --bs-accordion-inner-border-radius: calc(
+ var(--bs-border-radius) - (var(--bs-border-width))
+ );
+ --bs-accordion-btn-padding-x: 1.25rem;
+ --bs-accordion-btn-padding-y: 1rem;
+ --bs-accordion-btn-color: var(--bs-body-color);
+ --bs-accordion-btn-bg: var(--bs-accordion-bg);
+ --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ --bs-accordion-btn-icon-width: 1.25rem;
+ --bs-accordion-btn-icon-transform: rotate(-180deg);
+ --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;
+ --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23144b2b'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ --bs-accordion-btn-focus-border-color: #98ddb5;
+ --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+ --bs-accordion-body-padding-x: 1.25rem;
+ --bs-accordion-body-padding-y: 1rem;
+ --bs-accordion-active-color: var(--bs-primary-text-emphasis);
+ --bs-accordion-active-bg: var(--bs-primary-bg-subtle);
+}
+
+.accordion-button {
+ position: relative;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);
+ font-size: 1rem;
+ color: var(--bs-accordion-btn-color);
+ text-align: left;
+ background-color: var(--bs-accordion-btn-bg);
+ border: 0;
+ border-radius: 0;
+ overflow-anchor: none;
+ transition: var(--bs-accordion-transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .accordion-button {
+ transition: none;
+ }
+}
+
+.accordion-button:not(.collapsed) {
+ color: var(--bs-accordion-active-color);
+ background-color: var(--bs-accordion-active-bg);
+ box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0
+ var(--bs-accordion-border-color);
+}
+
+.accordion-button:not(.collapsed)::after {
+ background-image: var(--bs-accordion-btn-active-icon);
+ transform: var(--bs-accordion-btn-icon-transform);
+}
+
+.accordion-button::after {
+ flex-shrink: 0;
+ width: var(--bs-accordion-btn-icon-width);
+ height: var(--bs-accordion-btn-icon-width);
+ margin-left: auto;
+ content: '';
+ background-image: var(--bs-accordion-btn-icon);
+ background-repeat: no-repeat;
+ background-size: var(--bs-accordion-btn-icon-width);
+ transition: var(--bs-accordion-btn-icon-transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .accordion-button::after {
+ transition: none;
+ }
+}
+
+.accordion-button:hover {
+ z-index: 2;
+}
+
+.accordion-button:focus {
+ z-index: 3;
+ border-color: var(--bs-accordion-btn-focus-border-color);
+ outline: 0;
+ box-shadow: var(--bs-accordion-btn-focus-box-shadow);
+}
+
+.accordion-header {
+ margin-bottom: 0;
+}
+
+.accordion-item {
+ color: var(--bs-accordion-color);
+ background-color: var(--bs-accordion-bg);
+ border: var(--bs-accordion-border-width) solid
+ var(--bs-accordion-border-color);
+}
+
+.accordion-item:first-of-type {
+ border-top-left-radius: var(--bs-accordion-border-radius);
+ border-top-right-radius: var(--bs-accordion-border-radius);
+}
+
+.accordion-item:first-of-type .accordion-button {
+ border-top-left-radius: var(--bs-accordion-inner-border-radius);
+ border-top-right-radius: var(--bs-accordion-inner-border-radius);
+}
+
+.accordion-item:not(:first-of-type) {
+ border-top: 0;
+}
+
+.accordion-item:last-of-type {
+ border-bottom-right-radius: var(--bs-accordion-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-border-radius);
+}
+
+.accordion-item:last-of-type .accordion-button.collapsed {
+ border-bottom-right-radius: var(--bs-accordion-inner-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-inner-border-radius);
+}
+
+.accordion-item:last-of-type .accordion-collapse {
+ border-bottom-right-radius: var(--bs-accordion-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-border-radius);
+}
+
+.accordion-body {
+ padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);
+}
+
+.accordion-flush .accordion-collapse {
+ border-width: 0;
+}
+
+.accordion-flush .accordion-item {
+ border-right: 0;
+ border-left: 0;
+ border-radius: 0;
+}
+
+.accordion-flush .accordion-item:first-child {
+ border-top: 0;
+}
+
+.accordion-flush .accordion-item:last-child {
+ border-bottom: 0;
+}
+
+.accordion-flush .accordion-item .accordion-button,
+.accordion-flush .accordion-item .accordion-button.collapsed {
+ border-radius: 0;
+}
+
+[data-bs-theme='dark'] .accordion-button::after {
+ --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2383d6a6'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2383d6a6'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+}
+
+.breadcrumb {
+ --bs-breadcrumb-padding-x: 0;
+ --bs-breadcrumb-padding-y: 0;
+ --bs-breadcrumb-margin-bottom: 1rem;
+ --bs-breadcrumb-bg: ;
+ --bs-breadcrumb-border-radius: ;
+ --bs-breadcrumb-divider-color: var(--bs-secondary-color);
+ --bs-breadcrumb-item-padding-x: 0.5rem;
+ --bs-breadcrumb-item-active-color: var(--bs-secondary-color);
+ display: flex;
+ flex-wrap: wrap;
+ padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
+ margin-bottom: var(--bs-breadcrumb-margin-bottom);
+ font-size: var(--bs-breadcrumb-font-size);
+ list-style: none;
+ background-color: var(--bs-breadcrumb-bg);
+ border-radius: var(--bs-breadcrumb-border-radius);
+}
+
+.breadcrumb-item + .breadcrumb-item {
+ padding-left: var(--bs-breadcrumb-item-padding-x);
+}
+
+.breadcrumb-item + .breadcrumb-item::before {
+ float: left;
+ padding-right: var(--bs-breadcrumb-item-padding-x);
+ color: var(--bs-breadcrumb-divider-color);
+ content: var(--bs-breadcrumb-divider, '/')
+ /* rtl: var(--bs-breadcrumb-divider, "/") */;
+}
+
+.breadcrumb-item.active {
+ color: var(--bs-breadcrumb-item-active-color);
+}
+
+.pagination {
+ --bs-pagination-padding-x: 0.75rem;
+ --bs-pagination-padding-y: 0.375rem;
+ --bs-pagination-font-size: 1rem;
+ --bs-pagination-color: var(--bs-link-color);
+ --bs-pagination-bg: var(--bs-body-bg);
+ --bs-pagination-border-width: var(--bs-border-width);
+ --bs-pagination-border-color: var(--bs-border-color);
+ --bs-pagination-border-radius: var(--bs-border-radius);
+ --bs-pagination-hover-color: var(--bs-link-hover-color);
+ --bs-pagination-hover-bg: var(--bs-tertiary-bg);
+ --bs-pagination-hover-border-color: var(--bs-border-color);
+ --bs-pagination-focus-color: var(--bs-link-hover-color);
+ --bs-pagination-focus-bg: var(--bs-secondary-bg);
+ --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+ --bs-pagination-active-color: #fff;
+ --bs-pagination-active-bg: #31bb6b;
+ --bs-pagination-active-border-color: #31bb6b;
+ --bs-pagination-disabled-color: var(--bs-secondary-color);
+ --bs-pagination-disabled-bg: var(--bs-secondary-bg);
+ --bs-pagination-disabled-border-color: var(--bs-border-color);
+ display: flex;
+ padding-left: 0;
+ list-style: none;
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);
+ font-size: var(--bs-pagination-font-size);
+ color: var(--bs-pagination-color);
+ background-color: var(--bs-pagination-bg);
+ border: var(--bs-pagination-border-width) solid
+ var(--bs-pagination-border-color);
+ transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .page-link {
+ transition: none;
+ }
+}
+
+.page-link:hover {
+ z-index: 2;
+ color: var(--bs-pagination-hover-color);
+ background-color: var(--bs-pagination-hover-bg);
+ border-color: var(--bs-pagination-hover-border-color);
+}
+
+.page-link:focus {
+ z-index: 3;
+ color: var(--bs-pagination-focus-color);
+ background-color: var(--bs-pagination-focus-bg);
+ outline: 0;
+ box-shadow: var(--bs-pagination-focus-box-shadow);
+}
+
+.page-link.active,
+.active > .page-link {
+ z-index: 3;
+ color: var(--bs-pagination-active-color);
+ background-color: var(--bs-pagination-active-bg);
+ border-color: var(--bs-pagination-active-border-color);
+}
+
+.page-link.disabled,
+.disabled > .page-link {
+ color: var(--bs-pagination-disabled-color);
+ pointer-events: none;
+ background-color: var(--bs-pagination-disabled-bg);
+ border-color: var(--bs-pagination-disabled-border-color);
+}
+
+.page-item:not(:first-child) .page-link {
+ margin-left: calc(var(--bs-border-width) * -1);
+}
+
+.page-item:first-child .page-link {
+ border-top-left-radius: var(--bs-pagination-border-radius);
+ border-bottom-left-radius: var(--bs-pagination-border-radius);
+}
+
+.page-item:last-child .page-link {
+ border-top-right-radius: var(--bs-pagination-border-radius);
+ border-bottom-right-radius: var(--bs-pagination-border-radius);
+}
+
+.pagination-lg {
+ --bs-pagination-padding-x: 1.5rem;
+ --bs-pagination-padding-y: 0.75rem;
+ --bs-pagination-font-size: 1.25rem;
+ --bs-pagination-border-radius: var(--bs-border-radius-lg);
+}
+
+.pagination-sm {
+ --bs-pagination-padding-x: 0.5rem;
+ --bs-pagination-padding-y: 0.25rem;
+ --bs-pagination-font-size: 0.875rem;
+ --bs-pagination-border-radius: var(--bs-border-radius-sm);
+}
+
+.badge {
+ --bs-badge-padding-x: 0.65em;
+ --bs-badge-padding-y: 0.35em;
+ --bs-badge-font-size: 0.75em;
+ --bs-badge-font-weight: 700;
+ --bs-badge-color: #fff;
+ --bs-badge-border-radius: var(--bs-border-radius);
+ display: inline-block;
+ padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);
+ font-size: var(--bs-badge-font-size);
+ font-weight: var(--bs-badge-font-weight);
+ line-height: 1;
+ color: var(--bs-badge-color);
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: var(--bs-badge-border-radius);
+}
+
+.badge:empty {
+ display: none;
+}
+
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.alert {
+ --bs-alert-bg: transparent;
+ --bs-alert-padding-x: 1rem;
+ --bs-alert-padding-y: 1rem;
+ --bs-alert-margin-bottom: 1rem;
+ --bs-alert-color: inherit;
+ --bs-alert-border-color: transparent;
+ --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color);
+ --bs-alert-border-radius: var(--bs-border-radius);
+ --bs-alert-link-color: inherit;
+ position: relative;
+ padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
+ margin-bottom: var(--bs-alert-margin-bottom);
+ color: var(--bs-alert-color);
+ background-color: var(--bs-alert-bg);
+ border: var(--bs-alert-border);
+ border-radius: var(--bs-alert-border-radius);
+}
+
+.alert-heading {
+ color: inherit;
+}
+
+.alert-link {
+ font-weight: 700;
+ color: var(--bs-alert-link-color);
+}
+
+.alert-dismissible {
+ padding-right: 3rem;
+}
+
+.alert-dismissible .btn-close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 2;
+ padding: 1.25rem 1rem;
+}
+
+.alert-primary {
+ --bs-alert-color: var(--bs-primary-text-emphasis);
+ --bs-alert-bg: var(--bs-primary-bg-subtle);
+ --bs-alert-border-color: var(--bs-primary-border-subtle);
+ --bs-alert-link-color: var(--bs-primary-text-emphasis);
+}
+
+.alert-secondary {
+ --bs-alert-color: var(--bs-secondary-text-emphasis);
+ --bs-alert-bg: var(--bs-secondary-bg-subtle);
+ --bs-alert-border-color: var(--bs-secondary-border-subtle);
+ --bs-alert-link-color: var(--bs-secondary-text-emphasis);
+}
+
+.alert-success {
+ --bs-alert-color: var(--bs-success-text-emphasis);
+ --bs-alert-bg: var(--bs-success-bg-subtle);
+ --bs-alert-border-color: var(--bs-success-border-subtle);
+ --bs-alert-link-color: var(--bs-success-text-emphasis);
+}
+
+.alert-info {
+ --bs-alert-color: var(--bs-info-text-emphasis);
+ --bs-alert-bg: var(--bs-info-bg-subtle);
+ --bs-alert-border-color: var(--bs-info-border-subtle);
+ --bs-alert-link-color: var(--bs-info-text-emphasis);
+}
+
+.alert-warning {
+ --bs-alert-color: var(--bs-warning-text-emphasis);
+ --bs-alert-bg: var(--bs-warning-bg-subtle);
+ --bs-alert-border-color: var(--bs-warning-border-subtle);
+ --bs-alert-link-color: var(--bs-warning-text-emphasis);
+}
+
+.alert-danger {
+ --bs-alert-color: var(--bs-danger-text-emphasis);
+ --bs-alert-bg: var(--bs-danger-bg-subtle);
+ --bs-alert-border-color: var(--bs-danger-border-subtle);
+ --bs-alert-link-color: var(--bs-danger-text-emphasis);
+}
+
+.alert-light {
+ --bs-alert-color: var(--bs-light-text-emphasis);
+ --bs-alert-bg: var(--bs-light-bg-subtle);
+ --bs-alert-border-color: var(--bs-light-border-subtle);
+ --bs-alert-link-color: var(--bs-light-text-emphasis);
+}
+
+.alert-dark {
+ --bs-alert-color: var(--bs-dark-text-emphasis);
+ --bs-alert-bg: var(--bs-dark-bg-subtle);
+ --bs-alert-border-color: var(--bs-dark-border-subtle);
+ --bs-alert-link-color: var(--bs-dark-text-emphasis);
+}
+
+@keyframes progress-bar-stripes {
+ 0% {
+ background-position-x: 1rem;
+ }
+}
+
+.progress,
+.progress-stacked {
+ --bs-progress-height: 1rem;
+ --bs-progress-font-size: 0.75rem;
+ --bs-progress-bg: var(--bs-secondary-bg);
+ --bs-progress-border-radius: var(--bs-border-radius);
+ --bs-progress-box-shadow: var(--bs-box-shadow-inset);
+ --bs-progress-bar-color: #fff;
+ --bs-progress-bar-bg: #31bb6b;
+ --bs-progress-bar-transition: width 0.6s ease;
+ display: flex;
+ height: var(--bs-progress-height);
+ overflow: hidden;
+ font-size: var(--bs-progress-font-size);
+ background-color: var(--bs-progress-bg);
+ border-radius: var(--bs-progress-border-radius);
+}
+
+.progress-bar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ overflow: hidden;
+ color: var(--bs-progress-bar-color);
+ text-align: center;
+ white-space: nowrap;
+ background-color: var(--bs-progress-bar-bg);
+ transition: var(--bs-progress-bar-transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar {
+ transition: none;
+ }
+}
+
+.progress-bar-striped {
+ background-image: linear-gradient(
+ 45deg,
+ rgba(255, 255, 255, 0.15) 25%,
+ transparent 25%,
+ transparent 50%,
+ rgba(255, 255, 255, 0.15) 50%,
+ rgba(255, 255, 255, 0.15) 75%,
+ transparent 75%,
+ transparent
+ );
+ background-size: var(--bs-progress-height) var(--bs-progress-height);
+}
+
+.progress-stacked > .progress {
+ overflow: visible;
+}
+
+.progress-stacked > .progress > .progress-bar {
+ width: 100%;
+}
+
+.progress-bar-animated {
+ animation: 1s linear infinite progress-bar-stripes;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar-animated {
+ animation: none;
+ }
+}
+
+.list-group {
+ --bs-list-group-color: var(--bs-body-color);
+ --bs-list-group-bg: var(--bs-body-bg);
+ --bs-list-group-border-color: var(--bs-border-color);
+ --bs-list-group-border-width: var(--bs-border-width);
+ --bs-list-group-border-radius: var(--bs-border-radius);
+ --bs-list-group-item-padding-x: 1rem;
+ --bs-list-group-item-padding-y: 0.5rem;
+ --bs-list-group-action-color: var(--bs-secondary-color);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-tertiary-bg);
+ --bs-list-group-action-active-color: var(--bs-body-color);
+ --bs-list-group-action-active-bg: var(--bs-secondary-bg);
+ --bs-list-group-disabled-color: var(--bs-secondary-color);
+ --bs-list-group-disabled-bg: var(--bs-body-bg);
+ --bs-list-group-active-color: #fff;
+ --bs-list-group-active-bg: #31bb6b;
+ --bs-list-group-active-border-color: #31bb6b;
+ display: flex;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+ border-radius: var(--bs-list-group-border-radius);
+}
+
+.list-group-numbered {
+ list-style-type: none;
+ counter-reset: section;
+}
+
+.list-group-numbered > .list-group-item::before {
+ content: counters(section, '.') '. ';
+ counter-increment: section;
+}
+
+.list-group-item-action {
+ width: 100%;
+ color: var(--bs-list-group-action-color);
+ text-align: inherit;
+}
+
+.list-group-item-action:hover,
+.list-group-item-action:focus {
+ z-index: 1;
+ color: var(--bs-list-group-action-hover-color);
+ text-decoration: none;
+ background-color: var(--bs-list-group-action-hover-bg);
+}
+
+.list-group-item-action:active {
+ color: var(--bs-list-group-action-active-color);
+ background-color: var(--bs-list-group-action-active-bg);
+}
+
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: var(--bs-list-group-item-padding-y)
+ var(--bs-list-group-item-padding-x);
+ color: var(--bs-list-group-color);
+ background-color: var(--bs-list-group-bg);
+ border: var(--bs-list-group-border-width) solid
+ var(--bs-list-group-border-color);
+}
+
+.list-group-item:first-child {
+ border-top-left-radius: inherit;
+ border-top-right-radius: inherit;
+}
+
+.list-group-item:last-child {
+ border-bottom-right-radius: inherit;
+ border-bottom-left-radius: inherit;
+}
+
+.list-group-item.disabled,
+.list-group-item:disabled {
+ color: var(--bs-list-group-disabled-color);
+ pointer-events: none;
+ background-color: var(--bs-list-group-disabled-bg);
+}
+
+.list-group-item.active {
+ z-index: 2;
+ color: var(--bs-list-group-active-color);
+ background-color: var(--bs-list-group-active-bg);
+ border-color: var(--bs-list-group-active-border-color);
+}
+
+.list-group-item + .list-group-item {
+ border-top-width: 0;
+}
+
+.list-group-item + .list-group-item.active {
+ margin-top: calc(-1 * var(--bs-list-group-border-width));
+ border-top-width: var(--bs-list-group-border-width);
+}
+
+.list-group-horizontal {
+ flex-direction: row;
+}
+
+.list-group-horizontal > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+}
+
+.list-group-horizontal > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+}
+
+.list-group-horizontal > .list-group-item.active {
+ margin-top: 0;
+}
+
+.list-group-horizontal > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+}
+
+.list-group-horizontal > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+}
+
+@media (min-width: 576px) {
+ .list-group-horizontal-sm {
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-sm > .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-sm > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-sm > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+ }
+}
+
+@media (min-width: 768px) {
+ .list-group-horizontal-md {
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-md > .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-md > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-md > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+ }
+}
+
+@media (min-width: 992px) {
+ .list-group-horizontal-lg {
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-lg > .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-lg > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-lg > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+ }
+}
+
+@media (min-width: 1200px) {
+ .list-group-horizontal-xl {
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-xl > .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-xl > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-xl > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+ }
+}
+
+@media (min-width: 1400px) {
+ .list-group-horizontal-xxl {
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) {
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) {
+ border-top-right-radius: var(--bs-list-group-border-radius);
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-xxl > .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-xxl > .list-group-item + .list-group-item {
+ border-top-width: var(--bs-list-group-border-width);
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-xxl > .list-group-item + .list-group-item.active {
+ margin-left: calc(-1 * var(--bs-list-group-border-width));
+ border-left-width: var(--bs-list-group-border-width);
+ }
+}
+
+.list-group-flush {
+ border-radius: 0;
+}
+
+.list-group-flush > .list-group-item {
+ border-width: 0 0 var(--bs-list-group-border-width);
+}
+
+.list-group-flush > .list-group-item:last-child {
+ border-bottom-width: 0;
+}
+
+.list-group-item-primary {
+ --bs-list-group-color: var(--bs-primary-text-emphasis);
+ --bs-list-group-bg: var(--bs-primary-bg-subtle);
+ --bs-list-group-border-color: var(--bs-primary-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-primary-border-subtle);
+ --bs-list-group-active-color: var(--bs-primary-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-primary-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-primary-text-emphasis);
+}
+
+.list-group-item-secondary {
+ --bs-list-group-color: var(--bs-secondary-text-emphasis);
+ --bs-list-group-bg: var(--bs-secondary-bg-subtle);
+ --bs-list-group-border-color: var(--bs-secondary-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle);
+ --bs-list-group-active-color: var(--bs-secondary-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-secondary-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis);
+}
+
+.list-group-item-success {
+ --bs-list-group-color: var(--bs-success-text-emphasis);
+ --bs-list-group-bg: var(--bs-success-bg-subtle);
+ --bs-list-group-border-color: var(--bs-success-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-success-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-success-border-subtle);
+ --bs-list-group-active-color: var(--bs-success-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-success-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-success-text-emphasis);
+}
+
+.list-group-item-info {
+ --bs-list-group-color: var(--bs-info-text-emphasis);
+ --bs-list-group-bg: var(--bs-info-bg-subtle);
+ --bs-list-group-border-color: var(--bs-info-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-info-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-info-border-subtle);
+ --bs-list-group-active-color: var(--bs-info-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-info-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-info-text-emphasis);
+}
+
+.list-group-item-warning {
+ --bs-list-group-color: var(--bs-warning-text-emphasis);
+ --bs-list-group-bg: var(--bs-warning-bg-subtle);
+ --bs-list-group-border-color: var(--bs-warning-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-warning-border-subtle);
+ --bs-list-group-active-color: var(--bs-warning-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-warning-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-warning-text-emphasis);
+}
+
+.list-group-item-danger {
+ --bs-list-group-color: var(--bs-danger-text-emphasis);
+ --bs-list-group-bg: var(--bs-danger-bg-subtle);
+ --bs-list-group-border-color: var(--bs-danger-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-danger-border-subtle);
+ --bs-list-group-active-color: var(--bs-danger-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-danger-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-danger-text-emphasis);
+}
+
+.list-group-item-light {
+ --bs-list-group-color: var(--bs-light-text-emphasis);
+ --bs-list-group-bg: var(--bs-light-bg-subtle);
+ --bs-list-group-border-color: var(--bs-light-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-light-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-light-border-subtle);
+ --bs-list-group-active-color: var(--bs-light-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-light-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-light-text-emphasis);
+}
+
+.list-group-item-dark {
+ --bs-list-group-color: var(--bs-dark-text-emphasis);
+ --bs-list-group-bg: var(--bs-dark-bg-subtle);
+ --bs-list-group-border-color: var(--bs-dark-border-subtle);
+ --bs-list-group-action-hover-color: var(--bs-emphasis-color);
+ --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle);
+ --bs-list-group-action-active-color: var(--bs-emphasis-color);
+ --bs-list-group-action-active-bg: var(--bs-dark-border-subtle);
+ --bs-list-group-active-color: var(--bs-dark-bg-subtle);
+ --bs-list-group-active-bg: var(--bs-dark-text-emphasis);
+ --bs-list-group-active-border-color: var(--bs-dark-text-emphasis);
+}
+
+.btn-close {
+ --bs-btn-close-color: #000;
+ --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
+ --bs-btn-close-opacity: 0.5;
+ --bs-btn-close-hover-opacity: 0.75;
+ --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(49, 187, 107, 0.25);
+ --bs-btn-close-focus-opacity: 1;
+ --bs-btn-close-disabled-opacity: 0.25;
+ --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
+ box-sizing: content-box;
+ width: 1em;
+ height: 1em;
+ padding: 0.25em 0.25em;
+ color: var(--bs-btn-close-color);
+ background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat;
+ border: 0;
+ border-radius: 0.375rem;
+ opacity: var(--bs-btn-close-opacity);
+}
+
+.btn-close:hover {
+ color: var(--bs-btn-close-color);
+ text-decoration: none;
+ opacity: var(--bs-btn-close-hover-opacity);
+}
+
+.btn-close:focus {
+ outline: 0;
+ box-shadow: var(--bs-btn-close-focus-shadow);
+ opacity: var(--bs-btn-close-focus-opacity);
+}
+
+.btn-close:disabled,
+.btn-close.disabled {
+ pointer-events: none;
+ user-select: none;
+ opacity: var(--bs-btn-close-disabled-opacity);
+}
+
+.btn-close-white {
+ filter: var(--bs-btn-close-white-filter);
+}
+
+[data-bs-theme='dark'] .btn-close {
+ filter: var(--bs-btn-close-white-filter);
+}
+
+.toast {
+ --bs-toast-zindex: 1090;
+ --bs-toast-padding-x: 0.75rem;
+ --bs-toast-padding-y: 0.5rem;
+ --bs-toast-spacing: 1.5rem;
+ --bs-toast-max-width: 350px;
+ --bs-toast-font-size: 0.875rem;
+ --bs-toast-color: ;
+ --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85);
+ --bs-toast-border-width: var(--bs-border-width);
+ --bs-toast-border-color: var(--bs-border-color-translucent);
+ --bs-toast-border-radius: var(--bs-border-radius);
+ --bs-toast-box-shadow: var(--bs-box-shadow);
+ --bs-toast-header-color: var(--bs-secondary-color);
+ --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85);
+ --bs-toast-header-border-color: var(--bs-border-color-translucent);
+ width: var(--bs-toast-max-width);
+ max-width: 100%;
+ font-size: var(--bs-toast-font-size);
+ color: var(--bs-toast-color);
+ pointer-events: auto;
+ background-color: var(--bs-toast-bg);
+ background-clip: padding-box;
+ border: var(--bs-toast-border-width) solid var(--bs-toast-border-color);
+ box-shadow: var(--bs-toast-box-shadow);
+ border-radius: var(--bs-toast-border-radius);
+}
+
+.toast.showing {
+ opacity: 0;
+}
+
+.toast:not(.show) {
+ display: none;
+}
+
+.toast-container {
+ --bs-toast-zindex: 1090;
+ position: absolute;
+ z-index: var(--bs-toast-zindex);
+ width: max-content;
+ max-width: 100%;
+ pointer-events: none;
+}
+
+.toast-container > :not(:last-child) {
+ margin-bottom: var(--bs-toast-spacing);
+}
+
+.toast-header {
+ display: flex;
+ align-items: center;
+ padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);
+ color: var(--bs-toast-header-color);
+ background-color: var(--bs-toast-header-bg);
+ background-clip: padding-box;
+ border-bottom: var(--bs-toast-border-width) solid
+ var(--bs-toast-header-border-color);
+ border-top-left-radius: calc(
+ var(--bs-toast-border-radius) - var(--bs-toast-border-width)
+ );
+ border-top-right-radius: calc(
+ var(--bs-toast-border-radius) - var(--bs-toast-border-width)
+ );
+}
+
+.toast-header .btn-close {
+ margin-right: calc(-0.5 * var(--bs-toast-padding-x));
+ margin-left: var(--bs-toast-padding-x);
+}
+
+.toast-body {
+ padding: var(--bs-toast-padding-x);
+ word-wrap: break-word;
+}
+
+.modal {
+ --bs-modal-zindex: 1055;
+ --bs-modal-width: 500px;
+ --bs-modal-padding: 1rem;
+ --bs-modal-margin: 0.5rem;
+ --bs-modal-color: ;
+ --bs-modal-bg: var(--bs-body-bg);
+ --bs-modal-border-color: var(--bs-border-color-translucent);
+ --bs-modal-border-width: var(--bs-border-width);
+ --bs-modal-border-radius: var(--bs-border-radius-lg);
+ --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+ --bs-modal-inner-border-radius: calc(
+ var(--bs-border-radius-lg) - (var(--bs-border-width))
+ );
+ --bs-modal-header-padding-x: 1rem;
+ --bs-modal-header-padding-y: 1rem;
+ --bs-modal-header-padding: 1rem 1rem;
+ --bs-modal-header-border-color: var(--bs-border-color);
+ --bs-modal-header-border-width: var(--bs-border-width);
+ --bs-modal-title-line-height: 1.5;
+ --bs-modal-footer-gap: 0.5rem;
+ --bs-modal-footer-bg: ;
+ --bs-modal-footer-border-color: var(--bs-border-color);
+ --bs-modal-footer-border-width: var(--bs-border-width);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: var(--bs-modal-zindex);
+ display: none;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ outline: 0;
+}
+
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: var(--bs-modal-margin);
+ pointer-events: none;
+}
+
+.modal.fade .modal-dialog {
+ transition: transform 0.3s ease-out;
+ transform: translate(0, -50px);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .modal.fade .modal-dialog {
+ transition: none;
+ }
+}
+
+.modal.show .modal-dialog {
+ transform: none;
+}
+
+.modal.modal-static .modal-dialog {
+ transform: scale(1.02);
+}
+
+.modal-dialog-scrollable {
+ height: calc(100% - var(--bs-modal-margin) * 2);
+}
+
+.modal-dialog-scrollable .modal-content {
+ max-height: 100%;
+ overflow: hidden;
+}
+
+.modal-dialog-scrollable .modal-body {
+ overflow-y: auto;
+}
+
+.modal-dialog-centered {
+ display: flex;
+ align-items: center;
+ min-height: calc(100% - var(--bs-modal-margin) * 2);
+}
+
+.modal-content {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ color: var(--bs-modal-color);
+ pointer-events: auto;
+ background-color: var(--bs-modal-bg);
+ background-clip: padding-box;
+ border: var(--bs-modal-border-width) solid var(--bs-modal-border-color);
+ border-radius: var(--bs-modal-border-radius);
+ outline: 0;
+}
+
+.modal-backdrop {
+ --bs-backdrop-zindex: 1050;
+ --bs-backdrop-bg: #000;
+ --bs-backdrop-opacity: 0.5;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: var(--bs-backdrop-zindex);
+ width: 100vw;
+ height: 100vh;
+ background-color: var(--bs-backdrop-bg);
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop.show {
+ opacity: var(--bs-backdrop-opacity);
+}
+
+.modal-header {
+ display: flex;
+ flex-shrink: 0;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--bs-modal-header-padding);
+ border-bottom: var(--bs-modal-header-border-width) solid
+ var(--bs-modal-header-border-color);
+ border-top-left-radius: var(--bs-modal-inner-border-radius);
+ border-top-right-radius: var(--bs-modal-inner-border-radius);
+}
+
+.modal-header .btn-close {
+ padding: calc(var(--bs-modal-header-padding-y) * 0.5)
+ calc(var(--bs-modal-header-padding-x) * 0.5);
+ margin: calc(-0.5 * var(--bs-modal-header-padding-y))
+ calc(-0.5 * var(--bs-modal-header-padding-x))
+ calc(-0.5 * var(--bs-modal-header-padding-y)) auto;
+}
+
+.modal-title {
+ margin-bottom: 0;
+ line-height: var(--bs-modal-title-line-height);
+}
+
+.modal-body {
+ position: relative;
+ flex: 1 1 auto;
+ padding: var(--bs-modal-padding);
+}
+
+.modal-footer {
+ display: flex;
+ flex-shrink: 0;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: flex-end;
+ padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5);
+ background-color: var(--bs-modal-footer-bg);
+ border-top: var(--bs-modal-footer-border-width) solid
+ var(--bs-modal-footer-border-color);
+ border-bottom-right-radius: var(--bs-modal-inner-border-radius);
+ border-bottom-left-radius: var(--bs-modal-inner-border-radius);
+}
+
+.modal-footer > * {
+ margin: calc(var(--bs-modal-footer-gap) * 0.5);
+}
+
+@media (min-width: 576px) {
+ .modal {
+ --bs-modal-margin: 1.75rem;
+ --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ }
+
+ .modal-dialog {
+ max-width: var(--bs-modal-width);
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .modal-sm {
+ --bs-modal-width: 300px;
+ }
+}
+
+@media (min-width: 992px) {
+ .modal-lg,
+ .modal-xl {
+ --bs-modal-width: 800px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .modal-xl {
+ --bs-modal-width: 1140px;
+ }
+}
+
+.modal-fullscreen {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+}
+
+.modal-fullscreen .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+}
+
+.modal-fullscreen .modal-header,
+.modal-fullscreen .modal-footer {
+ border-radius: 0;
+}
+
+.modal-fullscreen .modal-body {
+ overflow-y: auto;
+}
+
+@media (max-width: 575.98px) {
+ .modal-fullscreen-sm-down {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+ }
+
+ .modal-fullscreen-sm-down .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-sm-down .modal-header,
+ .modal-fullscreen-sm-down .modal-footer {
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-sm-down .modal-body {
+ overflow-y: auto;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .modal-fullscreen-md-down {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+ }
+
+ .modal-fullscreen-md-down .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-md-down .modal-header,
+ .modal-fullscreen-md-down .modal-footer {
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-md-down .modal-body {
+ overflow-y: auto;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .modal-fullscreen-lg-down {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+ }
+
+ .modal-fullscreen-lg-down .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-lg-down .modal-header,
+ .modal-fullscreen-lg-down .modal-footer {
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-lg-down .modal-body {
+ overflow-y: auto;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .modal-fullscreen-xl-down {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+ }
+
+ .modal-fullscreen-xl-down .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-xl-down .modal-header,
+ .modal-fullscreen-xl-down .modal-footer {
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-xl-down .modal-body {
+ overflow-y: auto;
+ }
+}
+
+@media (max-width: 1399.98px) {
+ .modal-fullscreen-xxl-down {
+ width: 100vw;
+ max-width: none;
+ height: 100%;
+ margin: 0;
+ }
+
+ .modal-fullscreen-xxl-down .modal-content {
+ height: 100%;
+ border: 0;
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-xxl-down .modal-header,
+ .modal-fullscreen-xxl-down .modal-footer {
+ border-radius: 0;
+ }
+
+ .modal-fullscreen-xxl-down .modal-body {
+ overflow-y: auto;
+ }
+}
+
+.tooltip {
+ --bs-tooltip-zindex: 1080;
+ --bs-tooltip-max-width: 200px;
+ --bs-tooltip-padding-x: 0.5rem;
+ --bs-tooltip-padding-y: 0.25rem;
+ --bs-tooltip-margin: ;
+ --bs-tooltip-font-size: 0.875rem;
+ --bs-tooltip-color: var(--bs-body-bg);
+ --bs-tooltip-bg: var(--bs-emphasis-color);
+ --bs-tooltip-border-radius: var(--bs-border-radius);
+ --bs-tooltip-opacity: 0.9;
+ --bs-tooltip-arrow-width: 0.8rem;
+ --bs-tooltip-arrow-height: 0.4rem;
+ z-index: var(--bs-tooltip-zindex);
+ display: block;
+ margin: var(--bs-tooltip-margin);
+ font-family: var(--bs-font-sans-serif);
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ white-space: normal;
+ word-spacing: normal;
+ line-break: auto;
+ font-size: var(--bs-tooltip-font-size);
+ word-wrap: break-word;
+ opacity: 0;
+}
+
+.tooltip.show {
+ opacity: var(--bs-tooltip-opacity);
+}
+
+.tooltip .tooltip-arrow {
+ display: block;
+ width: var(--bs-tooltip-arrow-width);
+ height: var(--bs-tooltip-arrow-height);
+}
+
+.tooltip .tooltip-arrow::before {
+ position: absolute;
+ content: '';
+ border-color: transparent;
+ border-style: solid;
+}
+
+.bs-tooltip-top .tooltip-arrow,
+.bs-tooltip-auto[data-popper-placement^='top'] .tooltip-arrow {
+ bottom: calc(-1 * var(--bs-tooltip-arrow-height));
+}
+
+.bs-tooltip-top .tooltip-arrow::before,
+.bs-tooltip-auto[data-popper-placement^='top'] .tooltip-arrow::before {
+ top: -1px;
+ border-width: var(--bs-tooltip-arrow-height)
+ calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+ border-top-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-end .tooltip-arrow,
+.bs-tooltip-auto[data-popper-placement^='right'] .tooltip-arrow {
+ left: calc(-1 * var(--bs-tooltip-arrow-height));
+ width: var(--bs-tooltip-arrow-height);
+ height: var(--bs-tooltip-arrow-width);
+}
+
+.bs-tooltip-end .tooltip-arrow::before,
+.bs-tooltip-auto[data-popper-placement^='right'] .tooltip-arrow::before {
+ right: -1px;
+ border-width: calc(var(--bs-tooltip-arrow-width) * 0.5)
+ var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+ border-right-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:end:ignore */
+.bs-tooltip-bottom .tooltip-arrow,
+.bs-tooltip-auto[data-popper-placement^='bottom'] .tooltip-arrow {
+ top: calc(-1 * var(--bs-tooltip-arrow-height));
+}
+
+.bs-tooltip-bottom .tooltip-arrow::before,
+.bs-tooltip-auto[data-popper-placement^='bottom'] .tooltip-arrow::before {
+ bottom: -1px;
+ border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5)
+ var(--bs-tooltip-arrow-height);
+ border-bottom-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-start .tooltip-arrow,
+.bs-tooltip-auto[data-popper-placement^='left'] .tooltip-arrow {
+ right: calc(-1 * var(--bs-tooltip-arrow-height));
+ width: var(--bs-tooltip-arrow-height);
+ height: var(--bs-tooltip-arrow-width);
+}
+
+.bs-tooltip-start .tooltip-arrow::before,
+.bs-tooltip-auto[data-popper-placement^='left'] .tooltip-arrow::before {
+ left: -1px;
+ border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0
+ calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);
+ border-left-color: var(--bs-tooltip-bg);
+}
+
+/* rtl:end:ignore */
+.tooltip-inner {
+ max-width: var(--bs-tooltip-max-width);
+ padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);
+ color: var(--bs-tooltip-color);
+ text-align: center;
+ background-color: var(--bs-tooltip-bg);
+ border-radius: var(--bs-tooltip-border-radius);
+}
+
+.popover {
+ --bs-popover-zindex: 1070;
+ --bs-popover-max-width: 276px;
+ --bs-popover-font-size: 0.875rem;
+ --bs-popover-bg: var(--bs-body-bg);
+ --bs-popover-border-width: var(--bs-border-width);
+ --bs-popover-border-color: var(--bs-border-color-translucent);
+ --bs-popover-border-radius: var(--bs-border-radius-lg);
+ --bs-popover-inner-border-radius: calc(
+ var(--bs-border-radius-lg) - var(--bs-border-width)
+ );
+ --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-popover-header-padding-x: 1rem;
+ --bs-popover-header-padding-y: 0.5rem;
+ --bs-popover-header-font-size: 1rem;
+ --bs-popover-header-color: inherit;
+ --bs-popover-header-bg: var(--bs-secondary-bg);
+ --bs-popover-body-padding-x: 1rem;
+ --bs-popover-body-padding-y: 1rem;
+ --bs-popover-body-color: var(--bs-body-color);
+ --bs-popover-arrow-width: 1rem;
+ --bs-popover-arrow-height: 0.5rem;
+ --bs-popover-arrow-border: var(--bs-popover-border-color);
+ z-index: var(--bs-popover-zindex);
+ display: block;
+ max-width: var(--bs-popover-max-width);
+ font-family: var(--bs-font-sans-serif);
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ white-space: normal;
+ word-spacing: normal;
+ line-break: auto;
+ font-size: var(--bs-popover-font-size);
+ word-wrap: break-word;
+ background-color: var(--bs-popover-bg);
+ background-clip: padding-box;
+ border: var(--bs-popover-border-width) solid var(--bs-popover-border-color);
+ border-radius: var(--bs-popover-border-radius);
+}
+
+.popover .popover-arrow {
+ display: block;
+ width: var(--bs-popover-arrow-width);
+ height: var(--bs-popover-arrow-height);
+}
+
+.popover .popover-arrow::before,
+.popover .popover-arrow::after {
+ position: absolute;
+ display: block;
+ content: '';
+ border-color: transparent;
+ border-style: solid;
+ border-width: 0;
+}
+
+.bs-popover-top > .popover-arrow,
+.bs-popover-auto[data-popper-placement^='top'] > .popover-arrow {
+ bottom: calc(
+ -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)
+ );
+}
+
+.bs-popover-top > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='top'] > .popover-arrow::before,
+.bs-popover-top > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='top'] > .popover-arrow::after {
+ border-width: var(--bs-popover-arrow-height)
+ calc(var(--bs-popover-arrow-width) * 0.5) 0;
+}
+
+.bs-popover-top > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='top'] > .popover-arrow::before {
+ bottom: 0;
+ border-top-color: var(--bs-popover-arrow-border);
+}
+
+.bs-popover-top > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='top'] > .popover-arrow::after {
+ bottom: var(--bs-popover-border-width);
+ border-top-color: var(--bs-popover-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-popover-end > .popover-arrow,
+.bs-popover-auto[data-popper-placement^='right'] > .popover-arrow {
+ left: calc(
+ -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)
+ );
+ width: var(--bs-popover-arrow-height);
+ height: var(--bs-popover-arrow-width);
+}
+
+.bs-popover-end > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='right'] > .popover-arrow::before,
+.bs-popover-end > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='right'] > .popover-arrow::after {
+ border-width: calc(var(--bs-popover-arrow-width) * 0.5)
+ var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;
+}
+
+.bs-popover-end > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='right'] > .popover-arrow::before {
+ left: 0;
+ border-right-color: var(--bs-popover-arrow-border);
+}
+
+.bs-popover-end > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='right'] > .popover-arrow::after {
+ left: var(--bs-popover-border-width);
+ border-right-color: var(--bs-popover-bg);
+}
+
+/* rtl:end:ignore */
+.bs-popover-bottom > .popover-arrow,
+.bs-popover-auto[data-popper-placement^='bottom'] > .popover-arrow {
+ top: calc(
+ -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)
+ );
+}
+
+.bs-popover-bottom > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='bottom'] > .popover-arrow::before,
+.bs-popover-bottom > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='bottom'] > .popover-arrow::after {
+ border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5)
+ var(--bs-popover-arrow-height);
+}
+
+.bs-popover-bottom > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='bottom'] > .popover-arrow::before {
+ top: 0;
+ border-bottom-color: var(--bs-popover-arrow-border);
+}
+
+.bs-popover-bottom > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='bottom'] > .popover-arrow::after {
+ top: var(--bs-popover-border-width);
+ border-bottom-color: var(--bs-popover-bg);
+}
+
+.bs-popover-bottom .popover-header::before,
+.bs-popover-auto[data-popper-placement^='bottom'] .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: var(--bs-popover-arrow-width);
+ margin-left: calc(-0.5 * var(--bs-popover-arrow-width));
+ content: '';
+ border-bottom: var(--bs-popover-border-width) solid
+ var(--bs-popover-header-bg);
+}
+
+/* rtl:begin:ignore */
+.bs-popover-start > .popover-arrow,
+.bs-popover-auto[data-popper-placement^='left'] > .popover-arrow {
+ right: calc(
+ -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)
+ );
+ width: var(--bs-popover-arrow-height);
+ height: var(--bs-popover-arrow-width);
+}
+
+.bs-popover-start > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='left'] > .popover-arrow::before,
+.bs-popover-start > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='left'] > .popover-arrow::after {
+ border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0
+ calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);
+}
+
+.bs-popover-start > .popover-arrow::before,
+.bs-popover-auto[data-popper-placement^='left'] > .popover-arrow::before {
+ right: 0;
+ border-left-color: var(--bs-popover-arrow-border);
+}
+
+.bs-popover-start > .popover-arrow::after,
+.bs-popover-auto[data-popper-placement^='left'] > .popover-arrow::after {
+ right: var(--bs-popover-border-width);
+ border-left-color: var(--bs-popover-bg);
+}
+
+/* rtl:end:ignore */
+.popover-header {
+ padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);
+ margin-bottom: 0;
+ font-size: var(--bs-popover-header-font-size);
+ color: var(--bs-popover-header-color);
+ background-color: var(--bs-popover-header-bg);
+ border-bottom: var(--bs-popover-border-width) solid
+ var(--bs-popover-border-color);
+ border-top-left-radius: var(--bs-popover-inner-border-radius);
+ border-top-right-radius: var(--bs-popover-inner-border-radius);
+}
+
+.popover-header:empty {
+ display: none;
+}
+
+.popover-body {
+ padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);
+ color: var(--bs-popover-body-color);
+}
+
+.carousel {
+ position: relative;
+}
+
+.carousel.pointer-event {
+ touch-action: pan-y;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel-inner::after {
+ display: block;
+ clear: both;
+ content: '';
+}
+
+.carousel-item {
+ position: relative;
+ display: none;
+ float: left;
+ width: 100%;
+ margin-right: -100%;
+ backface-visibility: hidden;
+ transition: transform 0.6s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-item {
+ transition: none;
+ }
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+ display: block;
+}
+
+.carousel-item-next:not(.carousel-item-start),
+.active.carousel-item-end {
+ transform: translateX(100%);
+}
+
+.carousel-item-prev:not(.carousel-item-end),
+.active.carousel-item-start {
+ transform: translateX(-100%);
+}
+
+.carousel-fade .carousel-item {
+ opacity: 0;
+ transition-property: opacity;
+ transform: none;
+}
+
+.carousel-fade .carousel-item.active,
+.carousel-fade .carousel-item-next.carousel-item-start,
+.carousel-fade .carousel-item-prev.carousel-item-end {
+ z-index: 1;
+ opacity: 1;
+}
+
+.carousel-fade .active.carousel-item-start,
+.carousel-fade .active.carousel-item-end {
+ z-index: 0;
+ opacity: 0;
+ transition: opacity 0s 0.6s;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-fade .active.carousel-item-start,
+ .carousel-fade .active.carousel-item-end {
+ transition: none;
+ }
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 15%;
+ padding: 0;
+ color: #fff;
+ text-align: center;
+ background: none;
+ border: 0;
+ opacity: 0.5;
+ transition: opacity 0.15s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-control-prev,
+ .carousel-control-next {
+ transition: none;
+ }
+}
+
+.carousel-control-prev:hover,
+.carousel-control-prev:focus,
+.carousel-control-next:hover,
+.carousel-control-next:focus {
+ color: #fff;
+ text-decoration: none;
+ outline: 0;
+ opacity: 0.9;
+}
+
+.carousel-control-prev {
+ left: 0;
+}
+
+.carousel-control-next {
+ right: 0;
+}
+
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+ display: inline-block;
+ width: 2rem;
+ height: 2rem;
+ background-repeat: no-repeat;
+ background-position: 50%;
+ background-size: 100% 100%;
+}
+
+/* rtl:options: {
+ "autoRename": true,
+ "stringMap":[ {
+ "name" : "prev-next",
+ "search" : "prev",
+ "replace" : "next"
+ } ]
+ } */
+.carousel-control-prev-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
+}
+
+.carousel-control-next-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+}
+
+.carousel-indicators {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 2;
+ display: flex;
+ justify-content: center;
+ padding: 0;
+ margin-right: 15%;
+ margin-bottom: 1rem;
+ margin-left: 15%;
+}
+
+.carousel-indicators [data-bs-target] {
+ box-sizing: content-box;
+ flex: 0 1 auto;
+ width: 30px;
+ height: 3px;
+ padding: 0;
+ margin-right: 3px;
+ margin-left: 3px;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 0;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ opacity: 0.5;
+ transition: opacity 0.6s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-indicators [data-bs-target] {
+ transition: none;
+ }
+}
+
+.carousel-indicators .active {
+ opacity: 1;
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 1.25rem;
+ left: 15%;
+ padding-top: 1.25rem;
+ padding-bottom: 1.25rem;
+ color: #fff;
+ text-align: center;
+}
+
+.carousel-dark .carousel-control-prev-icon,
+.carousel-dark .carousel-control-next-icon {
+ filter: invert(1) grayscale(100);
+}
+
+.carousel-dark .carousel-indicators [data-bs-target] {
+ background-color: #000;
+}
+
+.carousel-dark .carousel-caption {
+ color: #000;
+}
+
+[data-bs-theme='dark'] .carousel .carousel-control-prev-icon,
+[data-bs-theme='dark'] .carousel .carousel-control-next-icon,
+[data-bs-theme='dark'].carousel .carousel-control-prev-icon,
+[data-bs-theme='dark'].carousel .carousel-control-next-icon {
+ filter: invert(1) grayscale(100);
+}
+
+[data-bs-theme='dark'] .carousel .carousel-indicators [data-bs-target],
+[data-bs-theme='dark'].carousel .carousel-indicators [data-bs-target] {
+ background-color: #000;
+}
+
+[data-bs-theme='dark'] .carousel .carousel-caption,
+[data-bs-theme='dark'].carousel .carousel-caption {
+ color: #000;
+}
+
+.spinner-grow,
+.spinner-border {
+ display: inline-block;
+ width: var(--bs-spinner-width);
+ height: var(--bs-spinner-height);
+ vertical-align: var(--bs-spinner-vertical-align);
+ border-radius: 50%;
+ animation: var(--bs-spinner-animation-speed) linear infinite
+ var(--bs-spinner-animation-name);
+}
+
+@keyframes spinner-border {
+ to {
+ transform: rotate(360deg) /* rtl:ignore */;
+ }
+}
+
+.spinner-border {
+ --bs-spinner-width: 2rem;
+ --bs-spinner-height: 2rem;
+ --bs-spinner-vertical-align: -0.125em;
+ --bs-spinner-border-width: 0.25em;
+ --bs-spinner-animation-speed: 0.75s;
+ --bs-spinner-animation-name: spinner-border;
+ border: var(--bs-spinner-border-width) solid currentcolor;
+ border-right-color: transparent;
+}
+
+.spinner-border-sm {
+ --bs-spinner-width: 1rem;
+ --bs-spinner-height: 1rem;
+ --bs-spinner-border-width: 0.2em;
+}
+
+@keyframes spinner-grow {
+ 0% {
+ transform: scale(0);
+ }
+
+ 50% {
+ opacity: 1;
+ transform: none;
+ }
+}
+
+.spinner-grow {
+ --bs-spinner-width: 2rem;
+ --bs-spinner-height: 2rem;
+ --bs-spinner-vertical-align: -0.125em;
+ --bs-spinner-animation-speed: 0.75s;
+ --bs-spinner-animation-name: spinner-grow;
+ background-color: currentcolor;
+ opacity: 0;
+}
+
+.spinner-grow-sm {
+ --bs-spinner-width: 1rem;
+ --bs-spinner-height: 1rem;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .spinner-border,
+ .spinner-grow {
+ --bs-spinner-animation-speed: 1.5s;
+ }
+}
+
+.offcanvas,
+.offcanvas-xxl,
+.offcanvas-xl,
+.offcanvas-lg,
+.offcanvas-md,
+.offcanvas-sm {
+ --bs-offcanvas-zindex: 1045;
+ --bs-offcanvas-width: 400px;
+ --bs-offcanvas-height: 30vh;
+ --bs-offcanvas-padding-x: 1rem;
+ --bs-offcanvas-padding-y: 1rem;
+ --bs-offcanvas-color: var(--bs-body-color);
+ --bs-offcanvas-bg: var(--bs-body-bg);
+ --bs-offcanvas-border-width: var(--bs-border-width);
+ --bs-offcanvas-border-color: var(--bs-border-color-translucent);
+ --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+ --bs-offcanvas-transition: transform 0.3s ease-in-out;
+ --bs-offcanvas-title-line-height: 1.5;
+}
+
+@media (max-width: 575.98px) {
+ .offcanvas-sm {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+ }
+}
+
+@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-sm {
+ transition: none;
+ }
+}
+
+@media (max-width: 575.98px) {
+ .offcanvas-sm.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ .offcanvas-sm.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ .offcanvas-sm.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ .offcanvas-sm.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ .offcanvas-sm.showing,
+ .offcanvas-sm.show:not(.hiding) {
+ transform: none;
+ }
+
+ .offcanvas-sm.showing,
+ .offcanvas-sm.hiding,
+ .offcanvas-sm.show {
+ visibility: visible;
+ }
+}
+
+@media (min-width: 576px) {
+ .offcanvas-sm {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+
+ .offcanvas-sm .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-sm .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .offcanvas-md {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+ }
+}
+
+@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-md {
+ transition: none;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .offcanvas-md.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ .offcanvas-md.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ .offcanvas-md.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ .offcanvas-md.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ .offcanvas-md.showing,
+ .offcanvas-md.show:not(.hiding) {
+ transform: none;
+ }
+
+ .offcanvas-md.showing,
+ .offcanvas-md.hiding,
+ .offcanvas-md.show {
+ visibility: visible;
+ }
+}
+
+@media (min-width: 768px) {
+ .offcanvas-md {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+
+ .offcanvas-md .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-md .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .offcanvas-lg {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+ }
+}
+
+@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-lg {
+ transition: none;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .offcanvas-lg.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ .offcanvas-lg.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ .offcanvas-lg.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ .offcanvas-lg.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ .offcanvas-lg.showing,
+ .offcanvas-lg.show:not(.hiding) {
+ transform: none;
+ }
+
+ .offcanvas-lg.showing,
+ .offcanvas-lg.hiding,
+ .offcanvas-lg.show {
+ visibility: visible;
+ }
+}
+
+@media (min-width: 992px) {
+ .offcanvas-lg {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+
+ .offcanvas-lg .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-lg .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .offcanvas-xl {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+ }
+}
+
+@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-xl {
+ transition: none;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ .offcanvas-xl.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ .offcanvas-xl.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ .offcanvas-xl.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ .offcanvas-xl.showing,
+ .offcanvas-xl.show:not(.hiding) {
+ transform: none;
+ }
+
+ .offcanvas-xl.showing,
+ .offcanvas-xl.hiding,
+ .offcanvas-xl.show {
+ visibility: visible;
+ }
+}
+
+@media (min-width: 1200px) {
+ .offcanvas-xl {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+
+ .offcanvas-xl .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-xl .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+ }
+}
+
+@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-xxl {
+ transition: none;
+ }
+}
+
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ .offcanvas-xxl.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ .offcanvas-xxl.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ .offcanvas-xxl.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ .offcanvas-xxl.showing,
+ .offcanvas-xxl.show:not(.hiding) {
+ transform: none;
+ }
+
+ .offcanvas-xxl.showing,
+ .offcanvas-xxl.hiding,
+ .offcanvas-xxl.show {
+ visibility: visible;
+ }
+}
+
+@media (min-width: 1400px) {
+ .offcanvas-xxl {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+
+ .offcanvas-xxl .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-xxl .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+.offcanvas {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--bs-offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: var(--bs-offcanvas-transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .offcanvas {
+ transition: none;
+ }
+}
+
+.offcanvas.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+}
+
+.offcanvas.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+}
+
+.offcanvas.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+}
+
+.offcanvas.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid
+ var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+}
+
+.offcanvas.showing,
+.offcanvas.show:not(.hiding) {
+ transform: none;
+}
+
+.offcanvas.showing,
+.offcanvas.hiding,
+.offcanvas.show {
+ visibility: visible;
+}
+
+.offcanvas-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1040;
+ width: 100vw;
+ height: 100vh;
+ background-color: #000;
+}
+
+.offcanvas-backdrop.fade {
+ opacity: 0;
+}
+
+.offcanvas-backdrop.show {
+ opacity: 0.5;
+}
+
+.offcanvas-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);
+}
+
+.offcanvas-header .btn-close {
+ padding: calc(var(--bs-offcanvas-padding-y) * 0.5)
+ calc(var(--bs-offcanvas-padding-x) * 0.5);
+ margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y));
+ margin-right: calc(-0.5 * var(--bs-offcanvas-padding-x));
+ margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y));
+}
+
+.offcanvas-title {
+ margin-bottom: 0;
+ line-height: var(--bs-offcanvas-title-line-height);
+}
+
+.offcanvas-body {
+ flex-grow: 1;
+ padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);
+ overflow-y: auto;
+}
+
+.placeholder {
+ display: inline-block;
+ min-height: 1em;
+ vertical-align: middle;
+ cursor: wait;
+ background-color: currentcolor;
+ opacity: 0.5;
+}
+
+.placeholder.btn::before {
+ display: inline-block;
+ content: '';
+}
+
+.placeholder-xs {
+ min-height: 0.6em;
+}
+
+.placeholder-sm {
+ min-height: 0.8em;
+}
+
+.placeholder-lg {
+ min-height: 1.2em;
+}
+
+.placeholder-glow .placeholder {
+ animation: placeholder-glow 2s ease-in-out infinite;
+}
+
+@keyframes placeholder-glow {
+ 50% {
+ opacity: 0.2;
+ }
+}
+
+.placeholder-wave {
+ mask-image: linear-gradient(
+ 130deg,
+ #000 55%,
+ rgba(0, 0, 0, 0.8) 75%,
+ #000 95%
+ );
+ mask-size: 200% 100%;
+ animation: placeholder-wave 2s linear infinite;
+}
+
+@keyframes placeholder-wave {
+ 100% {
+ mask-position: -200% 0%;
+ }
+}
+
+.clearfix::after {
+ display: block;
+ clear: both;
+ content: '';
+}
+
+.text-bg-primary {
+ color: #000 !important;
+ background-color: RGBA(49, 187, 107, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-secondary {
+ color: #fff !important;
+ background-color: RGBA(112, 112, 112, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-success {
+ color: #000 !important;
+ background-color: RGBA(49, 187, 107, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-info {
+ color: #000 !important;
+ background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-warning {
+ color: #000 !important;
+ background-color: RGBA(254, 188, 89, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-danger {
+ color: #fff !important;
+ background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-light {
+ color: #000 !important;
+ background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-dark {
+ color: #fff !important;
+ background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;
+}
+
+.link-primary {
+ color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-primary-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-primary:hover,
+.link-primary:focus {
+ color: RGBA(90, 201, 137, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 90,
+ 201,
+ 137,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-secondary {
+ color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-secondary-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-secondary:hover,
+.link-secondary:focus {
+ color: RGBA(90, 90, 90, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 90,
+ 90,
+ 90,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-success {
+ color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-success-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-success:hover,
+.link-success:focus {
+ color: RGBA(90, 201, 137, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 90,
+ 201,
+ 137,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-info {
+ color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-info-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-info:hover,
+.link-info:focus {
+ color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 61,
+ 213,
+ 243,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-warning {
+ color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-warning-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-warning:hover,
+.link-warning:focus {
+ color: RGBA(254, 201, 122, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 254,
+ 201,
+ 122,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-danger {
+ color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-danger-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-danger:hover,
+.link-danger:focus {
+ color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 176,
+ 42,
+ 55,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-light {
+ color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-light-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-light:hover,
+.link-light:focus {
+ color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 249,
+ 250,
+ 251,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-dark {
+ color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ var(--bs-dark-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-dark:hover,
+.link-dark:focus {
+ color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important;
+ text-decoration-color: RGBA(
+ 26,
+ 30,
+ 33,
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-body-emphasis {
+ color: RGBA(
+ var(--bs-emphasis-color-rgb),
+ var(--bs-link-opacity, 1)
+ ) !important;
+ text-decoration-color: RGBA(
+ var(--bs-emphasis-color-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-body-emphasis:hover,
+.link-body-emphasis:focus {
+ color: RGBA(
+ var(--bs-emphasis-color-rgb),
+ var(--bs-link-opacity, 0.75)
+ ) !important;
+ text-decoration-color: RGBA(
+ var(--bs-emphasis-color-rgb),
+ var(--bs-link-underline-opacity, 0.75)
+ ) !important;
+}
+
+.focus-ring:focus {
+ outline: 0;
+ box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0)
+ var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width)
+ var(--bs-focus-ring-color);
+}
+
+.icon-link {
+ display: inline-flex;
+ gap: 0.375rem;
+ align-items: center;
+ text-decoration-color: rgba(
+ var(--bs-link-color-rgb),
+ var(--bs-link-opacity, 0.5)
+ );
+ text-underline-offset: 0.25em;
+ backface-visibility: hidden;
+}
+
+.icon-link > .bi {
+ flex-shrink: 0;
+ width: 1em;
+ height: 1em;
+ fill: currentcolor;
+ transition: 0.2s ease-in-out transform;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .icon-link > .bi {
+ transition: none;
+ }
+}
+
+.icon-link-hover:hover > .bi,
+.icon-link-hover:focus-visible > .bi {
+ transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0));
+}
+
+.ratio {
+ position: relative;
+ width: 100%;
+}
+
+.ratio::before {
+ display: block;
+ padding-top: var(--bs-aspect-ratio);
+ content: '';
+}
+
+.ratio > * {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.ratio-1x1 {
+ --bs-aspect-ratio: 100%;
+}
+
+.ratio-4x3 {
+ --bs-aspect-ratio: 75%;
+}
+
+.ratio-16x9 {
+ --bs-aspect-ratio: 56.25%;
+}
+
+.ratio-21x9 {
+ --bs-aspect-ratio: 42.8571428571%;
+}
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+.sticky-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+}
+
+.sticky-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+}
+
+@media (min-width: 576px) {
+ .sticky-sm-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+
+ .sticky-sm-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
+}
+
+@media (min-width: 768px) {
+ .sticky-md-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+
+ .sticky-md-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
+}
+
+@media (min-width: 992px) {
+ .sticky-lg-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+
+ .sticky-lg-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
+}
+
+@media (min-width: 1200px) {
+ .sticky-xl-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+
+ .sticky-xl-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
+}
+
+@media (min-width: 1400px) {
+ .sticky-xxl-top {
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+
+ .sticky-xxl-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
+}
+
+.hstack {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+}
+
+.vstack {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ align-self: stretch;
+}
+
+.visually-hidden,
+.visually-hidden-focusable:not(:focus):not(:focus-within) {
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+
+.visually-hidden:not(caption),
+.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) {
+ position: absolute !important;
+}
+
+.stretched-link::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1;
+ content: '';
+}
+
+.text-truncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.vr {
+ display: inline-block;
+ align-self: stretch;
+ width: 1px;
+ min-height: 1em;
+ background-color: currentcolor;
+ opacity: 0.25;
+}
+
+.align-baseline {
+ vertical-align: baseline !important;
+}
+
+.align-top {
+ vertical-align: top !important;
+}
+
+.align-middle {
+ vertical-align: middle !important;
+}
+
+.align-bottom {
+ vertical-align: bottom !important;
+}
+
+.align-text-bottom {
+ vertical-align: text-bottom !important;
+}
+
+.align-text-top {
+ vertical-align: text-top !important;
+}
+
+.float-start {
+ float: left !important;
+}
+
+.float-end {
+ float: right !important;
+}
+
+.float-none {
+ float: none !important;
+}
+
+.object-fit-contain {
+ object-fit: contain !important;
+}
+
+.object-fit-cover {
+ object-fit: cover !important;
+}
+
+.object-fit-fill {
+ object-fit: fill !important;
+}
+
+.object-fit-scale {
+ object-fit: scale-down !important;
+}
+
+.object-fit-none {
+ object-fit: none !important;
+}
+
+.opacity-0 {
+ opacity: 0 !important;
+}
+
+.opacity-25 {
+ opacity: 0.25 !important;
+}
+
+.opacity-50 {
+ opacity: 0.5 !important;
+}
+
+.opacity-75 {
+ opacity: 0.75 !important;
+}
+
+.opacity-100 {
+ opacity: 1 !important;
+}
+
+.overflow-auto {
+ overflow: auto !important;
+}
+
+.overflow-hidden {
+ overflow: hidden !important;
+}
+
+.overflow-visible {
+ overflow: visible !important;
+}
+
+.overflow-scroll {
+ overflow: scroll !important;
+}
+
+.overflow-x-auto {
+ overflow-x: auto !important;
+}
+
+.overflow-x-hidden {
+ overflow-x: hidden !important;
+}
+
+.overflow-x-visible {
+ overflow-x: visible !important;
+}
+
+.overflow-x-scroll {
+ overflow-x: scroll !important;
+}
+
+.overflow-y-auto {
+ overflow-y: auto !important;
+}
+
+.overflow-y-hidden {
+ overflow-y: hidden !important;
+}
+
+.overflow-y-visible {
+ overflow-y: visible !important;
+}
+
+.overflow-y-scroll {
+ overflow-y: scroll !important;
+}
+
+.d-inline {
+ display: inline !important;
+}
+
+.d-inline-block {
+ display: inline-block !important;
+}
+
+.d-block {
+ display: block !important;
+}
+
+.d-grid {
+ display: grid !important;
+}
+
+.d-inline-grid {
+ display: inline-grid !important;
+}
+
+.d-table {
+ display: table !important;
+}
+
+.d-table-row {
+ display: table-row !important;
+}
+
+.d-table-cell {
+ display: table-cell !important;
+}
+
+.d-flex {
+ display: flex !important;
+}
+
+.d-inline-flex {
+ display: inline-flex !important;
+}
+
+.d-none {
+ display: none !important;
+}
+
+.shadow {
+ box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
+}
+
+.shadow-sm {
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
+}
+
+.shadow-lg {
+ box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
+}
+
+.shadow-none {
+ box-shadow: none !important;
+}
+
+.focus-ring-primary {
+ --bs-focus-ring-color: rgba(
+ var(--bs-primary-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-secondary {
+ --bs-focus-ring-color: rgba(
+ var(--bs-secondary-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-success {
+ --bs-focus-ring-color: rgba(
+ var(--bs-success-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-info {
+ --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity));
+}
+
+.focus-ring-warning {
+ --bs-focus-ring-color: rgba(
+ var(--bs-warning-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-danger {
+ --bs-focus-ring-color: rgba(
+ var(--bs-danger-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-light {
+ --bs-focus-ring-color: rgba(
+ var(--bs-light-rgb),
+ var(--bs-focus-ring-opacity)
+ );
+}
+
+.focus-ring-dark {
+ --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity));
+}
+
+.position-static {
+ position: static !important;
+}
+
+.position-relative {
+ position: relative !important;
+}
+
+.position-absolute {
+ position: absolute !important;
+}
+
+.position-fixed {
+ position: fixed !important;
+}
+
+.position-sticky {
+ position: sticky !important;
+}
+
+.top-0 {
+ top: 0 !important;
+}
+
+.top-50 {
+ top: 50% !important;
+}
+
+.top-100 {
+ top: 100% !important;
+}
+
+.bottom-0 {
+ bottom: 0 !important;
+}
+
+.bottom-50 {
+ bottom: 50% !important;
+}
+
+.bottom-100 {
+ bottom: 100% !important;
+}
+
+.start-0 {
+ left: 0 !important;
+}
+
+.start-50 {
+ left: 50% !important;
+}
+
+.start-100 {
+ left: 100% !important;
+}
+
+.end-0 {
+ right: 0 !important;
+}
+
+.end-50 {
+ right: 50% !important;
+}
+
+.end-100 {
+ right: 100% !important;
+}
+
+.translate-middle {
+ transform: translate(-50%, -50%) !important;
+}
+
+.translate-middle-x {
+ transform: translateX(-50%) !important;
+}
+
+.translate-middle-y {
+ transform: translateY(-50%) !important;
+}
+
+.border {
+ border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
+}
+
+.border-0 {
+ border: 0 !important;
+}
+
+.border-top {
+ border-top: var(--bs-border-width) var(--bs-border-style)
+ var(--bs-border-color) !important;
+}
+
+.border-top-0 {
+ border-top: 0 !important;
+}
+
+.border-end {
+ border-right: var(--bs-border-width) var(--bs-border-style)
+ var(--bs-border-color) !important;
+}
+
+.border-end-0 {
+ border-right: 0 !important;
+}
+
+.border-bottom {
+ border-bottom: var(--bs-border-width) var(--bs-border-style)
+ var(--bs-border-color) !important;
+}
+
+.border-bottom-0 {
+ border-bottom: 0 !important;
+}
+
+.border-start {
+ border-left: var(--bs-border-width) var(--bs-border-style)
+ var(--bs-border-color) !important;
+}
+
+.border-start-0 {
+ border-left: 0 !important;
+}
+
+.border-primary {
+ --bs-border-opacity: 1;
+ border-color: rgba(
+ var(--bs-primary-rgb),
+ var(--bs-border-opacity)
+ ) !important;
+}
+
+.border-secondary {
+ --bs-border-opacity: 1;
+ border-color: rgba(
+ var(--bs-secondary-rgb),
+ var(--bs-border-opacity)
+ ) !important;
+}
+
+.border-success {
+ --bs-border-opacity: 1;
+ border-color: rgba(
+ var(--bs-success-rgb),
+ var(--bs-border-opacity)
+ ) !important;
+}
+
+.border-info {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-warning {
+ --bs-border-opacity: 1;
+ border-color: rgba(
+ var(--bs-warning-rgb),
+ var(--bs-border-opacity)
+ ) !important;
+}
+
+.border-danger {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-light {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-dark {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-black {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-white {
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;
+}
+
+.border-primary-subtle {
+ border-color: var(--bs-primary-border-subtle) !important;
+}
+
+.border-secondary-subtle {
+ border-color: var(--bs-secondary-border-subtle) !important;
+}
+
+.border-success-subtle {
+ border-color: var(--bs-success-border-subtle) !important;
+}
+
+.border-info-subtle {
+ border-color: var(--bs-info-border-subtle) !important;
+}
+
+.border-warning-subtle {
+ border-color: var(--bs-warning-border-subtle) !important;
+}
+
+.border-danger-subtle {
+ border-color: var(--bs-danger-border-subtle) !important;
+}
+
+.border-light-subtle {
+ border-color: var(--bs-light-border-subtle) !important;
+}
+
+.border-dark-subtle {
+ border-color: var(--bs-dark-border-subtle) !important;
+}
+
+.border-1 {
+ border-width: 1px !important;
+}
+
+.border-2 {
+ border-width: 2px !important;
+}
+
+.border-3 {
+ border-width: 3px !important;
+}
+
+.border-4 {
+ border-width: 4px !important;
+}
+
+.border-5 {
+ border-width: 5px !important;
+}
+
+.border-opacity-10 {
+ --bs-border-opacity: 0.1;
+}
+
+.border-opacity-25 {
+ --bs-border-opacity: 0.25;
+}
+
+.border-opacity-50 {
+ --bs-border-opacity: 0.5;
+}
+
+.border-opacity-75 {
+ --bs-border-opacity: 0.75;
+}
+
+.border-opacity-100 {
+ --bs-border-opacity: 1;
+}
+
+.w-25 {
+ width: 25% !important;
+}
+
+.w-50 {
+ width: 50% !important;
+}
+
+.w-75 {
+ width: 75% !important;
+}
+
+.w-100 {
+ width: 100% !important;
+}
+
+.w-auto {
+ width: auto !important;
+}
+
+.mw-100 {
+ max-width: 100% !important;
+}
+
+.vw-100 {
+ width: 100vw !important;
+}
+
+.min-vw-100 {
+ min-width: 100vw !important;
+}
+
+.h-25 {
+ height: 25% !important;
+}
+
+.h-50 {
+ height: 50% !important;
+}
+
+.h-75 {
+ height: 75% !important;
+}
+
+.h-100 {
+ height: 100% !important;
+}
+
+.h-auto {
+ height: auto !important;
+}
+
+.mh-100 {
+ max-height: 100% !important;
+}
+
+.vh-100 {
+ height: 100vh !important;
+}
+
+.min-vh-100 {
+ min-height: 100vh !important;
+}
+
+.flex-fill {
+ flex: 1 1 auto !important;
+}
+
+.flex-row {
+ flex-direction: row !important;
+}
+
+.flex-column {
+ flex-direction: column !important;
+}
+
+.flex-row-reverse {
+ flex-direction: row-reverse !important;
+}
+
+.flex-column-reverse {
+ flex-direction: column-reverse !important;
+}
+
+.flex-grow-0 {
+ flex-grow: 0 !important;
+}
+
+.flex-grow-1 {
+ flex-grow: 1 !important;
+}
+
+.flex-shrink-0 {
+ flex-shrink: 0 !important;
+}
+
+.flex-shrink-1 {
+ flex-shrink: 1 !important;
+}
+
+.flex-wrap {
+ flex-wrap: wrap !important;
+}
+
+.flex-nowrap {
+ flex-wrap: nowrap !important;
+}
+
+.flex-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+}
+
+.justify-content-start {
+ justify-content: flex-start !important;
+}
+
+.justify-content-end {
+ justify-content: flex-end !important;
+}
+
+.justify-content-center {
+ justify-content: center !important;
+}
+
+.justify-content-between {
+ justify-content: space-between !important;
+}
+
+.justify-content-around {
+ justify-content: space-around !important;
+}
+
+.justify-content-evenly {
+ justify-content: space-evenly !important;
+}
+
+.align-items-start {
+ align-items: flex-start !important;
+}
+
+.align-items-end {
+ align-items: flex-end !important;
+}
+
+.align-items-center {
+ align-items: center !important;
+}
+
+.align-items-baseline {
+ align-items: baseline !important;
+}
+
+.align-items-stretch {
+ align-items: stretch !important;
+}
+
+.align-content-start {
+ align-content: flex-start !important;
+}
+
+.align-content-end {
+ align-content: flex-end !important;
+}
+
+.align-content-center {
+ align-content: center !important;
+}
+
+.align-content-between {
+ align-content: space-between !important;
+}
+
+.align-content-around {
+ align-content: space-around !important;
+}
+
+.align-content-stretch {
+ align-content: stretch !important;
+}
+
+.align-self-auto {
+ align-self: auto !important;
+}
+
+.align-self-start {
+ align-self: flex-start !important;
+}
+
+.align-self-end {
+ align-self: flex-end !important;
+}
+
+.align-self-center {
+ align-self: center !important;
+}
+
+.align-self-baseline {
+ align-self: baseline !important;
+}
+
+.align-self-stretch {
+ align-self: stretch !important;
+}
+
+.order-first {
+ order: -1 !important;
+}
+
+.order-0 {
+ order: 0 !important;
+}
+
+.order-1 {
+ order: 1 !important;
+}
+
+.order-2 {
+ order: 2 !important;
+}
+
+.order-3 {
+ order: 3 !important;
+}
+
+.order-4 {
+ order: 4 !important;
+}
+
+.order-5 {
+ order: 5 !important;
+}
+
+.order-last {
+ order: 6 !important;
+}
+
+.m-0 {
+ margin: 0 !important;
+}
+
+.m-1 {
+ margin: 0.25rem !important;
+}
+
+.m-2 {
+ margin: 0.5rem !important;
+}
+
+.m-3 {
+ margin: 1rem !important;
+}
+
+.m-4 {
+ margin: 1.5rem !important;
+}
+
+.m-5 {
+ margin: 3rem !important;
+}
+
+.m-auto {
+ margin: auto !important;
+}
+
+.mx-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+}
+
+.mx-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+}
+
+.mx-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+}
+
+.mx-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+}
+
+.mx-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+}
+
+.mx-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+}
+
+.mx-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+}
+
+.my-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+}
+
+.my-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+}
+
+.my-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+}
+
+.my-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+}
+
+.my-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+}
+
+.my-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+}
+
+.my-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+}
+
+.mt-0 {
+ margin-top: 0 !important;
+}
+
+.mt-1 {
+ margin-top: 0.25rem !important;
+}
+
+.mt-2 {
+ margin-top: 0.5rem !important;
+}
+
+.mt-3 {
+ margin-top: 1rem !important;
+}
+
+.mt-4 {
+ margin-top: 1.5rem !important;
+}
+
+.mt-5 {
+ margin-top: 3rem !important;
+}
+
+.mt-auto {
+ margin-top: auto !important;
+}
+
+.me-0 {
+ margin-right: 0 !important;
+}
+
+.me-1 {
+ margin-right: 0.25rem !important;
+}
+
+.me-2 {
+ margin-right: 0.5rem !important;
+}
+
+.me-3 {
+ margin-right: 1rem !important;
+}
+
+.me-4 {
+ margin-right: 1.5rem !important;
+}
+
+.me-5 {
+ margin-right: 3rem !important;
+}
+
+.me-auto {
+ margin-right: auto !important;
+}
+
+.mb-0 {
+ margin-bottom: 0 !important;
+}
+
+.mb-1 {
+ margin-bottom: 0.25rem !important;
+}
+
+.mb-2 {
+ margin-bottom: 0.5rem !important;
+}
+
+.mb-3 {
+ margin-bottom: 1rem !important;
+}
+
+.mb-4 {
+ margin-bottom: 1.5rem !important;
+}
+
+.mb-5 {
+ margin-bottom: 3rem !important;
+}
+
+.mb-auto {
+ margin-bottom: auto !important;
+}
+
+.ms-0 {
+ margin-left: 0 !important;
+}
+
+.ms-1 {
+ margin-left: 0.25rem !important;
+}
+
+.ms-2 {
+ margin-left: 0.5rem !important;
+}
+
+.ms-3 {
+ margin-left: 1rem !important;
+}
+
+.ms-4 {
+ margin-left: 1.5rem !important;
+}
+
+.ms-5 {
+ margin-left: 3rem !important;
+}
+
+.ms-auto {
+ margin-left: auto !important;
+}
+
+.p-0 {
+ padding: 0 !important;
+}
+
+.p-1 {
+ padding: 0.25rem !important;
+}
+
+.p-2 {
+ padding: 0.5rem !important;
+}
+
+.p-3 {
+ padding: 1rem !important;
+}
+
+.p-4 {
+ padding: 1.5rem !important;
+}
+
+.p-5 {
+ padding: 3rem !important;
+}
+
+.px-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+}
+
+.px-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+}
+
+.px-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+}
+
+.px-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+}
+
+.px-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+}
+
+.px-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+}
+
+.py-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+}
+
+.py-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+}
+
+.py-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+}
+
+.py-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+}
+
+.py-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+}
+
+.py-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+}
+
+.pt-0 {
+ padding-top: 0 !important;
+}
+
+.pt-1 {
+ padding-top: 0.25rem !important;
+}
+
+.pt-2 {
+ padding-top: 0.5rem !important;
+}
+
+.pt-3 {
+ padding-top: 1rem !important;
+}
+
+.pt-4 {
+ padding-top: 1.5rem !important;
+}
+
+.pt-5 {
+ padding-top: 3rem !important;
+}
+
+.pe-0 {
+ padding-right: 0 !important;
+}
+
+.pe-1 {
+ padding-right: 0.25rem !important;
+}
+
+.pe-2 {
+ padding-right: 0.5rem !important;
+}
+
+.pe-3 {
+ padding-right: 1rem !important;
+}
+
+.pe-4 {
+ padding-right: 1.5rem !important;
+}
+
+.pe-5 {
+ padding-right: 3rem !important;
+}
+
+.pb-0 {
+ padding-bottom: 0 !important;
+}
+
+.pb-1 {
+ padding-bottom: 0.25rem !important;
+}
+
+.pb-2 {
+ padding-bottom: 0.5rem !important;
+}
+
+.pb-3 {
+ padding-bottom: 1rem !important;
+}
+
+.pb-4 {
+ padding-bottom: 1.5rem !important;
+}
+
+.pb-5 {
+ padding-bottom: 3rem !important;
+}
+
+.ps-0 {
+ padding-left: 0 !important;
+}
+
+.ps-1 {
+ padding-left: 0.25rem !important;
+}
+
+.ps-2 {
+ padding-left: 0.5rem !important;
+}
+
+.ps-3 {
+ padding-left: 1rem !important;
+}
+
+.ps-4 {
+ padding-left: 1.5rem !important;
+}
+
+.ps-5 {
+ padding-left: 3rem !important;
+}
+
+.gap-0 {
+ gap: 0 !important;
+}
+
+.gap-1 {
+ gap: 0.25rem !important;
+}
+
+.gap-2 {
+ gap: 0.5rem !important;
+}
+
+.gap-3 {
+ gap: 1rem !important;
+}
+
+.gap-4 {
+ gap: 1.5rem !important;
+}
+
+.gap-5 {
+ gap: 3rem !important;
+}
+
+.row-gap-0 {
+ row-gap: 0 !important;
+}
+
+.row-gap-1 {
+ row-gap: 0.25rem !important;
+}
+
+.row-gap-2 {
+ row-gap: 0.5rem !important;
+}
+
+.row-gap-3 {
+ row-gap: 1rem !important;
+}
+
+.row-gap-4 {
+ row-gap: 1.5rem !important;
+}
+
+.row-gap-5 {
+ row-gap: 3rem !important;
+}
+
+.column-gap-0 {
+ column-gap: 0 !important;
+}
+
+.column-gap-1 {
+ column-gap: 0.25rem !important;
+}
+
+.column-gap-2 {
+ column-gap: 0.5rem !important;
+}
+
+.column-gap-3 {
+ column-gap: 1rem !important;
+}
+
+.column-gap-4 {
+ column-gap: 1.5rem !important;
+}
+
+.column-gap-5 {
+ column-gap: 3rem !important;
+}
+
+.font-monospace {
+ font-family: var(--bs-font-monospace) !important;
+}
+
+.fs-1 {
+ font-size: calc(1.375rem + 1.5vw) !important;
+}
+
+.fs-2 {
+ font-size: calc(1.325rem + 0.9vw) !important;
+}
+
+.fs-3 {
+ font-size: calc(1.3rem + 0.6vw) !important;
+}
+
+.fs-4 {
+ font-size: calc(1.275rem + 0.3vw) !important;
+}
+
+.fs-5 {
+ font-size: 1.25rem !important;
+}
+
+.fs-6 {
+ font-size: 1rem !important;
+}
+
+.fst-italic {
+ font-style: italic !important;
+}
+
+.fst-normal {
+ font-style: normal !important;
+}
+
+.fw-lighter {
+ font-weight: lighter !important;
+}
+
+.fw-light {
+ font-weight: 300 !important;
+}
+
+.fw-normal {
+ font-weight: 400 !important;
+}
+
+.fw-medium {
+ font-weight: 500 !important;
+}
+
+.fw-semibold {
+ font-weight: 600 !important;
+}
+
+.fw-bold {
+ font-weight: 700 !important;
+}
+
+.fw-bolder {
+ font-weight: bolder !important;
+}
+
+.lh-1 {
+ line-height: 1 !important;
+}
+
+.lh-sm {
+ line-height: 1.25 !important;
+}
+
+.lh-base {
+ line-height: 1.5 !important;
+}
+
+.lh-lg {
+ line-height: 2 !important;
+}
+
+.text-start {
+ text-align: left !important;
+}
+
+.text-end {
+ text-align: right !important;
+}
+
+.text-center {
+ text-align: center !important;
+}
+
+.text-decoration-none {
+ text-decoration: none !important;
+}
+
+.text-decoration-underline {
+ text-decoration: underline !important;
+}
+
+.text-decoration-line-through {
+ text-decoration: line-through !important;
+}
+
+.text-lowercase {
+ text-transform: lowercase !important;
+}
+
+.text-uppercase {
+ text-transform: uppercase !important;
+}
+
+.text-capitalize {
+ text-transform: capitalize !important;
+}
+
+.text-wrap {
+ white-space: normal !important;
+}
+
+.text-nowrap {
+ white-space: nowrap !important;
+}
+
+/* rtl:begin:remove */
+.text-break {
+ word-wrap: break-word !important;
+ word-break: break-word !important;
+}
+
+/* rtl:end:remove */
+.text-primary {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-secondary {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-success {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-info {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-warning {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-danger {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-light {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-dark {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-black {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-white {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-body {
+ --bs-text-opacity: 1;
+ color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;
+}
+
+.text-muted {
+ --bs-text-opacity: 1;
+ color: var(--bs-secondary-color) !important;
+}
+
+.text-black-50 {
+ --bs-text-opacity: 1;
+ color: rgba(0, 0, 0, 0.5) !important;
+}
+
+.text-white-50 {
+ --bs-text-opacity: 1;
+ color: rgba(255, 255, 255, 0.5) !important;
+}
+
+.text-body-secondary {
+ --bs-text-opacity: 1;
+ color: var(--bs-secondary-color) !important;
+}
+
+.text-body-tertiary {
+ --bs-text-opacity: 1;
+ color: var(--bs-tertiary-color) !important;
+}
+
+.text-body-emphasis {
+ --bs-text-opacity: 1;
+ color: var(--bs-emphasis-color) !important;
+}
+
+.text-reset {
+ --bs-text-opacity: 1;
+ color: inherit !important;
+}
+
+.text-opacity-25 {
+ --bs-text-opacity: 0.25;
+}
+
+.text-opacity-50 {
+ --bs-text-opacity: 0.5;
+}
+
+.text-opacity-75 {
+ --bs-text-opacity: 0.75;
+}
+
+.text-opacity-100 {
+ --bs-text-opacity: 1;
+}
+
+.text-primary-emphasis {
+ color: var(--bs-primary-text-emphasis) !important;
+}
+
+.text-secondary-emphasis {
+ color: var(--bs-secondary-text-emphasis) !important;
+}
+
+.text-success-emphasis {
+ color: var(--bs-success-text-emphasis) !important;
+}
+
+.text-info-emphasis {
+ color: var(--bs-info-text-emphasis) !important;
+}
+
+.text-warning-emphasis {
+ color: var(--bs-warning-text-emphasis) !important;
+}
+
+.text-danger-emphasis {
+ color: var(--bs-danger-text-emphasis) !important;
+}
+
+.text-light-emphasis {
+ color: var(--bs-light-text-emphasis) !important;
+}
+
+.text-dark-emphasis {
+ color: var(--bs-dark-text-emphasis) !important;
+}
+
+.link-opacity-10 {
+ --bs-link-opacity: 0.1;
+}
+
+.link-opacity-10-hover:hover {
+ --bs-link-opacity: 0.1;
+}
+
+.link-opacity-25 {
+ --bs-link-opacity: 0.25;
+}
+
+.link-opacity-25-hover:hover {
+ --bs-link-opacity: 0.25;
+}
+
+.link-opacity-50 {
+ --bs-link-opacity: 0.5;
+}
+
+.link-opacity-50-hover:hover {
+ --bs-link-opacity: 0.5;
+}
+
+.link-opacity-75 {
+ --bs-link-opacity: 0.75;
+}
+
+.link-opacity-75-hover:hover {
+ --bs-link-opacity: 0.75;
+}
+
+.link-opacity-100 {
+ --bs-link-opacity: 1;
+}
+
+.link-opacity-100-hover:hover {
+ --bs-link-opacity: 1;
+}
+
+.link-offset-1 {
+ text-underline-offset: 0.125em !important;
+}
+
+.link-offset-1-hover:hover {
+ text-underline-offset: 0.125em !important;
+}
+
+.link-offset-2 {
+ text-underline-offset: 0.25em !important;
+}
+
+.link-offset-2-hover:hover {
+ text-underline-offset: 0.25em !important;
+}
+
+.link-offset-3 {
+ text-underline-offset: 0.375em !important;
+}
+
+.link-offset-3-hover:hover {
+ text-underline-offset: 0.375em !important;
+}
+
+.link-underline-primary {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-primary-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-secondary {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-secondary-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-success {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-success-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-info {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-info-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-warning {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-warning-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-danger {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-danger-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-light {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-light-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline-dark {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-dark-rgb),
+ var(--bs-link-underline-opacity)
+ ) !important;
+}
+
+.link-underline {
+ --bs-link-underline-opacity: 1;
+ text-decoration-color: rgba(
+ var(--bs-link-color-rgb),
+ var(--bs-link-underline-opacity, 1)
+ ) !important;
+}
+
+.link-underline-opacity-0 {
+ --bs-link-underline-opacity: 0;
+}
+
+.link-underline-opacity-0-hover:hover {
+ --bs-link-underline-opacity: 0;
+}
+
+.link-underline-opacity-10 {
+ --bs-link-underline-opacity: 0.1;
+}
+
+.link-underline-opacity-10-hover:hover {
+ --bs-link-underline-opacity: 0.1;
+}
+
+.link-underline-opacity-25 {
+ --bs-link-underline-opacity: 0.25;
+}
+
+.link-underline-opacity-25-hover:hover {
+ --bs-link-underline-opacity: 0.25;
+}
+
+.link-underline-opacity-50 {
+ --bs-link-underline-opacity: 0.5;
+}
+
+.link-underline-opacity-50-hover:hover {
+ --bs-link-underline-opacity: 0.5;
+}
+
+.link-underline-opacity-75 {
+ --bs-link-underline-opacity: 0.75;
+}
+
+.link-underline-opacity-75-hover:hover {
+ --bs-link-underline-opacity: 0.75;
+}
+
+.link-underline-opacity-100 {
+ --bs-link-underline-opacity: 1;
+}
+
+.link-underline-opacity-100-hover:hover {
+ --bs-link-underline-opacity: 1;
+}
+
+.bg-primary {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-primary-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-secondary {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-secondary-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-success {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-success-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-info {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-warning {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-warning-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-danger {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-light {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-dark {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-black {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-white {
+ --bs-bg-opacity: 1;
+ background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;
+}
+
+.bg-body {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-body-bg-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-transparent {
+ --bs-bg-opacity: 1;
+ background-color: transparent !important;
+}
+
+.bg-body-secondary {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-secondary-bg-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-body-tertiary {
+ --bs-bg-opacity: 1;
+ background-color: rgba(
+ var(--bs-tertiary-bg-rgb),
+ var(--bs-bg-opacity)
+ ) !important;
+}
+
+.bg-opacity-10 {
+ --bs-bg-opacity: 0.1;
+}
+
+.bg-opacity-25 {
+ --bs-bg-opacity: 0.25;
+}
+
+.bg-opacity-50 {
+ --bs-bg-opacity: 0.5;
+}
+
+.bg-opacity-75 {
+ --bs-bg-opacity: 0.75;
+}
+
+.bg-opacity-100 {
+ --bs-bg-opacity: 1;
+}
+
+.bg-primary-subtle {
+ background-color: var(--bs-primary-bg-subtle) !important;
+}
+
+.bg-secondary-subtle {
+ background-color: var(--bs-secondary-bg-subtle) !important;
+}
+
+.bg-success-subtle {
+ background-color: var(--bs-success-bg-subtle) !important;
+}
+
+.bg-info-subtle {
+ background-color: var(--bs-info-bg-subtle) !important;
+}
+
+.bg-warning-subtle {
+ background-color: var(--bs-warning-bg-subtle) !important;
+}
+
+.bg-danger-subtle {
+ background-color: var(--bs-danger-bg-subtle) !important;
+}
+
+.bg-light-subtle {
+ background-color: var(--bs-light-bg-subtle) !important;
+}
+
+.bg-dark-subtle {
+ background-color: var(--bs-dark-bg-subtle) !important;
+}
+
+.bg-gradient {
+ background-image: var(--bs-gradient) !important;
+}
+
+.user-select-all {
+ user-select: all !important;
+}
+
+.user-select-auto {
+ user-select: auto !important;
+}
+
+.user-select-none {
+ user-select: none !important;
+}
+
+.pe-none {
+ pointer-events: none !important;
+}
+
+.pe-auto {
+ pointer-events: auto !important;
+}
+
+.rounded {
+ border-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-0 {
+ border-radius: 0 !important;
+}
+
+.rounded-1 {
+ border-radius: var(--bs-border-radius-sm) !important;
+}
+
+.rounded-2 {
+ border-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-3 {
+ border-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-4 {
+ border-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-5 {
+ border-radius: var(--bs-border-radius-xxl) !important;
+}
+
+.rounded-circle {
+ border-radius: 50% !important;
+}
+
+.rounded-pill {
+ border-radius: var(--bs-border-radius-pill) !important;
+}
+
+.rounded-top {
+ border-top-left-radius: var(--bs-border-radius) !important;
+ border-top-right-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-top-0 {
+ border-top-left-radius: 0 !important;
+ border-top-right-radius: 0 !important;
+}
+
+.rounded-top-1 {
+ border-top-left-radius: var(--bs-border-radius-sm) !important;
+ border-top-right-radius: var(--bs-border-radius-sm) !important;
+}
+
+.rounded-top-2 {
+ border-top-left-radius: var(--bs-border-radius) !important;
+ border-top-right-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-top-3 {
+ border-top-left-radius: var(--bs-border-radius-lg) !important;
+ border-top-right-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-top-4 {
+ border-top-left-radius: var(--bs-border-radius-xl) !important;
+ border-top-right-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-top-5 {
+ border-top-left-radius: var(--bs-border-radius-xxl) !important;
+ border-top-right-radius: var(--bs-border-radius-xxl) !important;
+}
+
+.rounded-top-circle {
+ border-top-left-radius: 50% !important;
+ border-top-right-radius: 50% !important;
+}
+
+.rounded-top-pill {
+ border-top-left-radius: var(--bs-border-radius-pill) !important;
+ border-top-right-radius: var(--bs-border-radius-pill) !important;
+}
+
+.rounded-end {
+ border-top-right-radius: var(--bs-border-radius) !important;
+ border-bottom-right-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-end-0 {
+ border-top-right-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+}
+
+.rounded-end-1 {
+ border-top-right-radius: var(--bs-border-radius-sm) !important;
+ border-bottom-right-radius: var(--bs-border-radius-sm) !important;
+}
+
+.rounded-end-2 {
+ border-top-right-radius: var(--bs-border-radius) !important;
+ border-bottom-right-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-end-3 {
+ border-top-right-radius: var(--bs-border-radius-lg) !important;
+ border-bottom-right-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-end-4 {
+ border-top-right-radius: var(--bs-border-radius-xl) !important;
+ border-bottom-right-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-end-5 {
+ border-top-right-radius: var(--bs-border-radius-xxl) !important;
+ border-bottom-right-radius: var(--bs-border-radius-xxl) !important;
+}
+
+.rounded-end-circle {
+ border-top-right-radius: 50% !important;
+ border-bottom-right-radius: 50% !important;
+}
+
+.rounded-end-pill {
+ border-top-right-radius: var(--bs-border-radius-pill) !important;
+ border-bottom-right-radius: var(--bs-border-radius-pill) !important;
+}
+
+.rounded-bottom {
+ border-bottom-right-radius: var(--bs-border-radius) !important;
+ border-bottom-left-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-bottom-0 {
+ border-bottom-right-radius: 0 !important;
+ border-bottom-left-radius: 0 !important;
+}
+
+.rounded-bottom-1 {
+ border-bottom-right-radius: var(--bs-border-radius-sm) !important;
+ border-bottom-left-radius: var(--bs-border-radius-sm) !important;
+}
+
+.rounded-bottom-2 {
+ border-bottom-right-radius: var(--bs-border-radius) !important;
+ border-bottom-left-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-bottom-3 {
+ border-bottom-right-radius: var(--bs-border-radius-lg) !important;
+ border-bottom-left-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-bottom-4 {
+ border-bottom-right-radius: var(--bs-border-radius-xl) !important;
+ border-bottom-left-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-bottom-5 {
+ border-bottom-right-radius: var(--bs-border-radius-xxl) !important;
+ border-bottom-left-radius: var(--bs-border-radius-xxl) !important;
+}
+
+.rounded-bottom-circle {
+ border-bottom-right-radius: 50% !important;
+ border-bottom-left-radius: 50% !important;
+}
+
+.rounded-bottom-pill {
+ border-bottom-right-radius: var(--bs-border-radius-pill) !important;
+ border-bottom-left-radius: var(--bs-border-radius-pill) !important;
+}
+
+.rounded-start {
+ border-bottom-left-radius: var(--bs-border-radius) !important;
+ border-top-left-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-start-0 {
+ border-bottom-left-radius: 0 !important;
+ border-top-left-radius: 0 !important;
+}
+
+.rounded-start-1 {
+ border-bottom-left-radius: var(--bs-border-radius-sm) !important;
+ border-top-left-radius: var(--bs-border-radius-sm) !important;
+}
+
+.rounded-start-2 {
+ border-bottom-left-radius: var(--bs-border-radius) !important;
+ border-top-left-radius: var(--bs-border-radius) !important;
+}
+
+.rounded-start-3 {
+ border-bottom-left-radius: var(--bs-border-radius-lg) !important;
+ border-top-left-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-start-4 {
+ border-bottom-left-radius: var(--bs-border-radius-xl) !important;
+ border-top-left-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-start-5 {
+ border-bottom-left-radius: var(--bs-border-radius-xxl) !important;
+ border-top-left-radius: var(--bs-border-radius-xxl) !important;
+}
+
+.rounded-start-circle {
+ border-bottom-left-radius: 50% !important;
+ border-top-left-radius: 50% !important;
+}
+
+.rounded-start-pill {
+ border-bottom-left-radius: var(--bs-border-radius-pill) !important;
+ border-top-left-radius: var(--bs-border-radius-pill) !important;
+}
+
+.visible {
+ visibility: visible !important;
+}
+
+.invisible {
+ visibility: hidden !important;
+}
+
+.z-n1 {
+ z-index: -1 !important;
+}
+
+.z-0 {
+ z-index: 0 !important;
+}
+
+.z-1 {
+ z-index: 1 !important;
+}
+
+.z-2 {
+ z-index: 2 !important;
+}
+
+.z-3 {
+ z-index: 3 !important;
+}
+
+@media (min-width: 576px) {
+ .float-sm-start {
+ float: left !important;
+ }
+
+ .float-sm-end {
+ float: right !important;
+ }
+
+ .float-sm-none {
+ float: none !important;
+ }
+
+ .object-fit-sm-contain {
+ object-fit: contain !important;
+ }
+
+ .object-fit-sm-cover {
+ object-fit: cover !important;
+ }
+
+ .object-fit-sm-fill {
+ object-fit: fill !important;
+ }
+
+ .object-fit-sm-scale {
+ object-fit: scale-down !important;
+ }
+
+ .object-fit-sm-none {
+ object-fit: none !important;
+ }
+
+ .d-sm-inline {
+ display: inline !important;
+ }
+
+ .d-sm-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-sm-block {
+ display: block !important;
+ }
+
+ .d-sm-grid {
+ display: grid !important;
+ }
+
+ .d-sm-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-sm-table {
+ display: table !important;
+ }
+
+ .d-sm-table-row {
+ display: table-row !important;
+ }
+
+ .d-sm-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-sm-flex {
+ display: flex !important;
+ }
+
+ .d-sm-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-sm-none {
+ display: none !important;
+ }
+
+ .flex-sm-fill {
+ flex: 1 1 auto !important;
+ }
+
+ .flex-sm-row {
+ flex-direction: row !important;
+ }
+
+ .flex-sm-column {
+ flex-direction: column !important;
+ }
+
+ .flex-sm-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-sm-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-sm-grow-0 {
+ flex-grow: 0 !important;
+ }
+
+ .flex-sm-grow-1 {
+ flex-grow: 1 !important;
+ }
+
+ .flex-sm-shrink-0 {
+ flex-shrink: 0 !important;
+ }
+
+ .flex-sm-shrink-1 {
+ flex-shrink: 1 !important;
+ }
+
+ .flex-sm-wrap {
+ flex-wrap: wrap !important;
+ }
+
+ .flex-sm-nowrap {
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-sm-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .justify-content-sm-start {
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-sm-end {
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-sm-center {
+ justify-content: center !important;
+ }
+
+ .justify-content-sm-between {
+ justify-content: space-between !important;
+ }
+
+ .justify-content-sm-around {
+ justify-content: space-around !important;
+ }
+
+ .justify-content-sm-evenly {
+ justify-content: space-evenly !important;
+ }
+
+ .align-items-sm-start {
+ align-items: flex-start !important;
+ }
+
+ .align-items-sm-end {
+ align-items: flex-end !important;
+ }
+
+ .align-items-sm-center {
+ align-items: center !important;
+ }
+
+ .align-items-sm-baseline {
+ align-items: baseline !important;
+ }
+
+ .align-items-sm-stretch {
+ align-items: stretch !important;
+ }
+
+ .align-content-sm-start {
+ align-content: flex-start !important;
+ }
+
+ .align-content-sm-end {
+ align-content: flex-end !important;
+ }
+
+ .align-content-sm-center {
+ align-content: center !important;
+ }
+
+ .align-content-sm-between {
+ align-content: space-between !important;
+ }
+
+ .align-content-sm-around {
+ align-content: space-around !important;
+ }
+
+ .align-content-sm-stretch {
+ align-content: stretch !important;
+ }
+
+ .align-self-sm-auto {
+ align-self: auto !important;
+ }
+
+ .align-self-sm-start {
+ align-self: flex-start !important;
+ }
+
+ .align-self-sm-end {
+ align-self: flex-end !important;
+ }
+
+ .align-self-sm-center {
+ align-self: center !important;
+ }
+
+ .align-self-sm-baseline {
+ align-self: baseline !important;
+ }
+
+ .align-self-sm-stretch {
+ align-self: stretch !important;
+ }
+
+ .order-sm-first {
+ order: -1 !important;
+ }
+
+ .order-sm-0 {
+ order: 0 !important;
+ }
+
+ .order-sm-1 {
+ order: 1 !important;
+ }
+
+ .order-sm-2 {
+ order: 2 !important;
+ }
+
+ .order-sm-3 {
+ order: 3 !important;
+ }
+
+ .order-sm-4 {
+ order: 4 !important;
+ }
+
+ .order-sm-5 {
+ order: 5 !important;
+ }
+
+ .order-sm-last {
+ order: 6 !important;
+ }
+
+ .m-sm-0 {
+ margin: 0 !important;
+ }
+
+ .m-sm-1 {
+ margin: 0.25rem !important;
+ }
+
+ .m-sm-2 {
+ margin: 0.5rem !important;
+ }
+
+ .m-sm-3 {
+ margin: 1rem !important;
+ }
+
+ .m-sm-4 {
+ margin: 1.5rem !important;
+ }
+
+ .m-sm-5 {
+ margin: 3rem !important;
+ }
+
+ .m-sm-auto {
+ margin: auto !important;
+ }
+
+ .mx-sm-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+ }
+
+ .mx-sm-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+ }
+
+ .mx-sm-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+ }
+
+ .mx-sm-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+ }
+
+ .mx-sm-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+ }
+
+ .mx-sm-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+ }
+
+ .mx-sm-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+ }
+
+ .my-sm-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .my-sm-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+ }
+
+ .my-sm-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+ }
+
+ .my-sm-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+ }
+
+ .my-sm-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+ }
+
+ .my-sm-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+ }
+
+ .my-sm-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ }
+
+ .mt-sm-0 {
+ margin-top: 0 !important;
+ }
+
+ .mt-sm-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mt-sm-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mt-sm-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mt-sm-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mt-sm-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mt-sm-auto {
+ margin-top: auto !important;
+ }
+
+ .me-sm-0 {
+ margin-right: 0 !important;
+ }
+
+ .me-sm-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .me-sm-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .me-sm-3 {
+ margin-right: 1rem !important;
+ }
+
+ .me-sm-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .me-sm-5 {
+ margin-right: 3rem !important;
+ }
+
+ .me-sm-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-sm-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .mb-sm-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .mb-sm-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .mb-sm-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .mb-sm-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .mb-sm-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .mb-sm-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ms-sm-0 {
+ margin-left: 0 !important;
+ }
+
+ .ms-sm-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .ms-sm-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .ms-sm-3 {
+ margin-left: 1rem !important;
+ }
+
+ .ms-sm-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .ms-sm-5 {
+ margin-left: 3rem !important;
+ }
+
+ .ms-sm-auto {
+ margin-left: auto !important;
+ }
+
+ .p-sm-0 {
+ padding: 0 !important;
+ }
+
+ .p-sm-1 {
+ padding: 0.25rem !important;
+ }
+
+ .p-sm-2 {
+ padding: 0.5rem !important;
+ }
+
+ .p-sm-3 {
+ padding: 1rem !important;
+ }
+
+ .p-sm-4 {
+ padding: 1.5rem !important;
+ }
+
+ .p-sm-5 {
+ padding: 3rem !important;
+ }
+
+ .px-sm-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+ }
+
+ .px-sm-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+ }
+
+ .px-sm-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+ }
+
+ .px-sm-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+ }
+
+ .px-sm-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+ }
+
+ .px-sm-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+ }
+
+ .py-sm-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ }
+
+ .py-sm-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+ }
+
+ .py-sm-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+ }
+
+ .py-sm-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+ }
+
+ .py-sm-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+ }
+
+ .py-sm-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+ }
+
+ .pt-sm-0 {
+ padding-top: 0 !important;
+ }
+
+ .pt-sm-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pt-sm-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pt-sm-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pt-sm-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pt-sm-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pe-sm-0 {
+ padding-right: 0 !important;
+ }
+
+ .pe-sm-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pe-sm-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pe-sm-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pe-sm-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pe-sm-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-sm-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pb-sm-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pb-sm-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pb-sm-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pb-sm-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pb-sm-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .ps-sm-0 {
+ padding-left: 0 !important;
+ }
+
+ .ps-sm-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .ps-sm-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .ps-sm-3 {
+ padding-left: 1rem !important;
+ }
+
+ .ps-sm-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .ps-sm-5 {
+ padding-left: 3rem !important;
+ }
+
+ .gap-sm-0 {
+ gap: 0 !important;
+ }
+
+ .gap-sm-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-sm-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-sm-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-sm-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-sm-5 {
+ gap: 3rem !important;
+ }
+
+ .row-gap-sm-0 {
+ row-gap: 0 !important;
+ }
+
+ .row-gap-sm-1 {
+ row-gap: 0.25rem !important;
+ }
+
+ .row-gap-sm-2 {
+ row-gap: 0.5rem !important;
+ }
+
+ .row-gap-sm-3 {
+ row-gap: 1rem !important;
+ }
+
+ .row-gap-sm-4 {
+ row-gap: 1.5rem !important;
+ }
+
+ .row-gap-sm-5 {
+ row-gap: 3rem !important;
+ }
+
+ .column-gap-sm-0 {
+ column-gap: 0 !important;
+ }
+
+ .column-gap-sm-1 {
+ column-gap: 0.25rem !important;
+ }
+
+ .column-gap-sm-2 {
+ column-gap: 0.5rem !important;
+ }
+
+ .column-gap-sm-3 {
+ column-gap: 1rem !important;
+ }
+
+ .column-gap-sm-4 {
+ column-gap: 1.5rem !important;
+ }
+
+ .column-gap-sm-5 {
+ column-gap: 3rem !important;
+ }
+
+ .text-sm-start {
+ text-align: left !important;
+ }
+
+ .text-sm-end {
+ text-align: right !important;
+ }
+
+ .text-sm-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .float-md-start {
+ float: left !important;
+ }
+
+ .float-md-end {
+ float: right !important;
+ }
+
+ .float-md-none {
+ float: none !important;
+ }
+
+ .object-fit-md-contain {
+ object-fit: contain !important;
+ }
+
+ .object-fit-md-cover {
+ object-fit: cover !important;
+ }
+
+ .object-fit-md-fill {
+ object-fit: fill !important;
+ }
+
+ .object-fit-md-scale {
+ object-fit: scale-down !important;
+ }
+
+ .object-fit-md-none {
+ object-fit: none !important;
+ }
+
+ .d-md-inline {
+ display: inline !important;
+ }
+
+ .d-md-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-md-block {
+ display: block !important;
+ }
+
+ .d-md-grid {
+ display: grid !important;
+ }
+
+ .d-md-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-md-table {
+ display: table !important;
+ }
+
+ .d-md-table-row {
+ display: table-row !important;
+ }
+
+ .d-md-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-md-flex {
+ display: flex !important;
+ }
+
+ .d-md-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-md-none {
+ display: none !important;
+ }
+
+ .flex-md-fill {
+ flex: 1 1 auto !important;
+ }
+
+ .flex-md-row {
+ flex-direction: row !important;
+ }
+
+ .flex-md-column {
+ flex-direction: column !important;
+ }
+
+ .flex-md-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-md-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-md-grow-0 {
+ flex-grow: 0 !important;
+ }
+
+ .flex-md-grow-1 {
+ flex-grow: 1 !important;
+ }
+
+ .flex-md-shrink-0 {
+ flex-shrink: 0 !important;
+ }
+
+ .flex-md-shrink-1 {
+ flex-shrink: 1 !important;
+ }
+
+ .flex-md-wrap {
+ flex-wrap: wrap !important;
+ }
+
+ .flex-md-nowrap {
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-md-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .justify-content-md-start {
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-md-end {
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-md-center {
+ justify-content: center !important;
+ }
+
+ .justify-content-md-between {
+ justify-content: space-between !important;
+ }
+
+ .justify-content-md-around {
+ justify-content: space-around !important;
+ }
+
+ .justify-content-md-evenly {
+ justify-content: space-evenly !important;
+ }
+
+ .align-items-md-start {
+ align-items: flex-start !important;
+ }
+
+ .align-items-md-end {
+ align-items: flex-end !important;
+ }
+
+ .align-items-md-center {
+ align-items: center !important;
+ }
+
+ .align-items-md-baseline {
+ align-items: baseline !important;
+ }
+
+ .align-items-md-stretch {
+ align-items: stretch !important;
+ }
+
+ .align-content-md-start {
+ align-content: flex-start !important;
+ }
+
+ .align-content-md-end {
+ align-content: flex-end !important;
+ }
+
+ .align-content-md-center {
+ align-content: center !important;
+ }
+
+ .align-content-md-between {
+ align-content: space-between !important;
+ }
+
+ .align-content-md-around {
+ align-content: space-around !important;
+ }
+
+ .align-content-md-stretch {
+ align-content: stretch !important;
+ }
+
+ .align-self-md-auto {
+ align-self: auto !important;
+ }
+
+ .align-self-md-start {
+ align-self: flex-start !important;
+ }
+
+ .align-self-md-end {
+ align-self: flex-end !important;
+ }
+
+ .align-self-md-center {
+ align-self: center !important;
+ }
+
+ .align-self-md-baseline {
+ align-self: baseline !important;
+ }
+
+ .align-self-md-stretch {
+ align-self: stretch !important;
+ }
+
+ .order-md-first {
+ order: -1 !important;
+ }
+
+ .order-md-0 {
+ order: 0 !important;
+ }
+
+ .order-md-1 {
+ order: 1 !important;
+ }
+
+ .order-md-2 {
+ order: 2 !important;
+ }
+
+ .order-md-3 {
+ order: 3 !important;
+ }
+
+ .order-md-4 {
+ order: 4 !important;
+ }
+
+ .order-md-5 {
+ order: 5 !important;
+ }
+
+ .order-md-last {
+ order: 6 !important;
+ }
+
+ .m-md-0 {
+ margin: 0 !important;
+ }
+
+ .m-md-1 {
+ margin: 0.25rem !important;
+ }
+
+ .m-md-2 {
+ margin: 0.5rem !important;
+ }
+
+ .m-md-3 {
+ margin: 1rem !important;
+ }
+
+ .m-md-4 {
+ margin: 1.5rem !important;
+ }
+
+ .m-md-5 {
+ margin: 3rem !important;
+ }
+
+ .m-md-auto {
+ margin: auto !important;
+ }
+
+ .mx-md-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+ }
+
+ .mx-md-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+ }
+
+ .mx-md-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+ }
+
+ .mx-md-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+ }
+
+ .mx-md-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+ }
+
+ .mx-md-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+ }
+
+ .mx-md-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+ }
+
+ .my-md-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .my-md-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+ }
+
+ .my-md-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+ }
+
+ .my-md-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+ }
+
+ .my-md-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+ }
+
+ .my-md-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+ }
+
+ .my-md-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ }
+
+ .mt-md-0 {
+ margin-top: 0 !important;
+ }
+
+ .mt-md-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mt-md-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mt-md-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mt-md-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mt-md-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mt-md-auto {
+ margin-top: auto !important;
+ }
+
+ .me-md-0 {
+ margin-right: 0 !important;
+ }
+
+ .me-md-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .me-md-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .me-md-3 {
+ margin-right: 1rem !important;
+ }
+
+ .me-md-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .me-md-5 {
+ margin-right: 3rem !important;
+ }
+
+ .me-md-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-md-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .mb-md-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .mb-md-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .mb-md-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .mb-md-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .mb-md-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .mb-md-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ms-md-0 {
+ margin-left: 0 !important;
+ }
+
+ .ms-md-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .ms-md-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .ms-md-3 {
+ margin-left: 1rem !important;
+ }
+
+ .ms-md-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .ms-md-5 {
+ margin-left: 3rem !important;
+ }
+
+ .ms-md-auto {
+ margin-left: auto !important;
+ }
+
+ .p-md-0 {
+ padding: 0 !important;
+ }
+
+ .p-md-1 {
+ padding: 0.25rem !important;
+ }
+
+ .p-md-2 {
+ padding: 0.5rem !important;
+ }
+
+ .p-md-3 {
+ padding: 1rem !important;
+ }
+
+ .p-md-4 {
+ padding: 1.5rem !important;
+ }
+
+ .p-md-5 {
+ padding: 3rem !important;
+ }
+
+ .px-md-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+ }
+
+ .px-md-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+ }
+
+ .px-md-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+ }
+
+ .px-md-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+ }
+
+ .px-md-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+ }
+
+ .px-md-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+ }
+
+ .py-md-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ }
+
+ .py-md-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+ }
+
+ .py-md-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+ }
+
+ .py-md-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+ }
+
+ .py-md-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+ }
+
+ .py-md-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+ }
+
+ .pt-md-0 {
+ padding-top: 0 !important;
+ }
+
+ .pt-md-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pt-md-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pt-md-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pt-md-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pt-md-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pe-md-0 {
+ padding-right: 0 !important;
+ }
+
+ .pe-md-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pe-md-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pe-md-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pe-md-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pe-md-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-md-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pb-md-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pb-md-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pb-md-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pb-md-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pb-md-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .ps-md-0 {
+ padding-left: 0 !important;
+ }
+
+ .ps-md-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .ps-md-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .ps-md-3 {
+ padding-left: 1rem !important;
+ }
+
+ .ps-md-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .ps-md-5 {
+ padding-left: 3rem !important;
+ }
+
+ .gap-md-0 {
+ gap: 0 !important;
+ }
+
+ .gap-md-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-md-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-md-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-md-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-md-5 {
+ gap: 3rem !important;
+ }
+
+ .row-gap-md-0 {
+ row-gap: 0 !important;
+ }
+
+ .row-gap-md-1 {
+ row-gap: 0.25rem !important;
+ }
+
+ .row-gap-md-2 {
+ row-gap: 0.5rem !important;
+ }
+
+ .row-gap-md-3 {
+ row-gap: 1rem !important;
+ }
+
+ .row-gap-md-4 {
+ row-gap: 1.5rem !important;
+ }
+
+ .row-gap-md-5 {
+ row-gap: 3rem !important;
+ }
+
+ .column-gap-md-0 {
+ column-gap: 0 !important;
+ }
+
+ .column-gap-md-1 {
+ column-gap: 0.25rem !important;
+ }
+
+ .column-gap-md-2 {
+ column-gap: 0.5rem !important;
+ }
+
+ .column-gap-md-3 {
+ column-gap: 1rem !important;
+ }
+
+ .column-gap-md-4 {
+ column-gap: 1.5rem !important;
+ }
+
+ .column-gap-md-5 {
+ column-gap: 3rem !important;
+ }
+
+ .text-md-start {
+ text-align: left !important;
+ }
+
+ .text-md-end {
+ text-align: right !important;
+ }
+
+ .text-md-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .float-lg-start {
+ float: left !important;
+ }
+
+ .float-lg-end {
+ float: right !important;
+ }
+
+ .float-lg-none {
+ float: none !important;
+ }
+
+ .object-fit-lg-contain {
+ object-fit: contain !important;
+ }
+
+ .object-fit-lg-cover {
+ object-fit: cover !important;
+ }
+
+ .object-fit-lg-fill {
+ object-fit: fill !important;
+ }
+
+ .object-fit-lg-scale {
+ object-fit: scale-down !important;
+ }
+
+ .object-fit-lg-none {
+ object-fit: none !important;
+ }
+
+ .d-lg-inline {
+ display: inline !important;
+ }
+
+ .d-lg-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-lg-block {
+ display: block !important;
+ }
+
+ .d-lg-grid {
+ display: grid !important;
+ }
+
+ .d-lg-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-lg-table {
+ display: table !important;
+ }
+
+ .d-lg-table-row {
+ display: table-row !important;
+ }
+
+ .d-lg-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-lg-flex {
+ display: flex !important;
+ }
+
+ .d-lg-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-lg-none {
+ display: none !important;
+ }
+
+ .flex-lg-fill {
+ flex: 1 1 auto !important;
+ }
+
+ .flex-lg-row {
+ flex-direction: row !important;
+ }
+
+ .flex-lg-column {
+ flex-direction: column !important;
+ }
+
+ .flex-lg-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-lg-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-lg-grow-0 {
+ flex-grow: 0 !important;
+ }
+
+ .flex-lg-grow-1 {
+ flex-grow: 1 !important;
+ }
+
+ .flex-lg-shrink-0 {
+ flex-shrink: 0 !important;
+ }
+
+ .flex-lg-shrink-1 {
+ flex-shrink: 1 !important;
+ }
+
+ .flex-lg-wrap {
+ flex-wrap: wrap !important;
+ }
+
+ .flex-lg-nowrap {
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-lg-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .justify-content-lg-start {
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-lg-end {
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-lg-center {
+ justify-content: center !important;
+ }
+
+ .justify-content-lg-between {
+ justify-content: space-between !important;
+ }
+
+ .justify-content-lg-around {
+ justify-content: space-around !important;
+ }
+
+ .justify-content-lg-evenly {
+ justify-content: space-evenly !important;
+ }
+
+ .align-items-lg-start {
+ align-items: flex-start !important;
+ }
+
+ .align-items-lg-end {
+ align-items: flex-end !important;
+ }
+
+ .align-items-lg-center {
+ align-items: center !important;
+ }
+
+ .align-items-lg-baseline {
+ align-items: baseline !important;
+ }
+
+ .align-items-lg-stretch {
+ align-items: stretch !important;
+ }
+
+ .align-content-lg-start {
+ align-content: flex-start !important;
+ }
+
+ .align-content-lg-end {
+ align-content: flex-end !important;
+ }
+
+ .align-content-lg-center {
+ align-content: center !important;
+ }
+
+ .align-content-lg-between {
+ align-content: space-between !important;
+ }
+
+ .align-content-lg-around {
+ align-content: space-around !important;
+ }
+
+ .align-content-lg-stretch {
+ align-content: stretch !important;
+ }
+
+ .align-self-lg-auto {
+ align-self: auto !important;
+ }
+
+ .align-self-lg-start {
+ align-self: flex-start !important;
+ }
+
+ .align-self-lg-end {
+ align-self: flex-end !important;
+ }
+
+ .align-self-lg-center {
+ align-self: center !important;
+ }
+
+ .align-self-lg-baseline {
+ align-self: baseline !important;
+ }
+
+ .align-self-lg-stretch {
+ align-self: stretch !important;
+ }
+
+ .order-lg-first {
+ order: -1 !important;
+ }
+
+ .order-lg-0 {
+ order: 0 !important;
+ }
+
+ .order-lg-1 {
+ order: 1 !important;
+ }
+
+ .order-lg-2 {
+ order: 2 !important;
+ }
+
+ .order-lg-3 {
+ order: 3 !important;
+ }
+
+ .order-lg-4 {
+ order: 4 !important;
+ }
+
+ .order-lg-5 {
+ order: 5 !important;
+ }
+
+ .order-lg-last {
+ order: 6 !important;
+ }
+
+ .m-lg-0 {
+ margin: 0 !important;
+ }
+
+ .m-lg-1 {
+ margin: 0.25rem !important;
+ }
+
+ .m-lg-2 {
+ margin: 0.5rem !important;
+ }
+
+ .m-lg-3 {
+ margin: 1rem !important;
+ }
+
+ .m-lg-4 {
+ margin: 1.5rem !important;
+ }
+
+ .m-lg-5 {
+ margin: 3rem !important;
+ }
+
+ .m-lg-auto {
+ margin: auto !important;
+ }
+
+ .mx-lg-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+ }
+
+ .mx-lg-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+ }
+
+ .mx-lg-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+ }
+
+ .mx-lg-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+ }
+
+ .mx-lg-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+ }
+
+ .mx-lg-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+ }
+
+ .mx-lg-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+ }
+
+ .my-lg-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .my-lg-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+ }
+
+ .my-lg-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+ }
+
+ .my-lg-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+ }
+
+ .my-lg-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+ }
+
+ .my-lg-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+ }
+
+ .my-lg-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ }
+
+ .mt-lg-0 {
+ margin-top: 0 !important;
+ }
+
+ .mt-lg-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mt-lg-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mt-lg-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mt-lg-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mt-lg-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mt-lg-auto {
+ margin-top: auto !important;
+ }
+
+ .me-lg-0 {
+ margin-right: 0 !important;
+ }
+
+ .me-lg-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .me-lg-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .me-lg-3 {
+ margin-right: 1rem !important;
+ }
+
+ .me-lg-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .me-lg-5 {
+ margin-right: 3rem !important;
+ }
+
+ .me-lg-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-lg-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .mb-lg-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .mb-lg-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .mb-lg-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .mb-lg-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .mb-lg-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .mb-lg-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ms-lg-0 {
+ margin-left: 0 !important;
+ }
+
+ .ms-lg-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .ms-lg-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .ms-lg-3 {
+ margin-left: 1rem !important;
+ }
+
+ .ms-lg-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .ms-lg-5 {
+ margin-left: 3rem !important;
+ }
+
+ .ms-lg-auto {
+ margin-left: auto !important;
+ }
+
+ .p-lg-0 {
+ padding: 0 !important;
+ }
+
+ .p-lg-1 {
+ padding: 0.25rem !important;
+ }
+
+ .p-lg-2 {
+ padding: 0.5rem !important;
+ }
+
+ .p-lg-3 {
+ padding: 1rem !important;
+ }
+
+ .p-lg-4 {
+ padding: 1.5rem !important;
+ }
+
+ .p-lg-5 {
+ padding: 3rem !important;
+ }
+
+ .px-lg-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+ }
+
+ .px-lg-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+ }
+
+ .px-lg-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+ }
+
+ .px-lg-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+ }
+
+ .px-lg-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+ }
+
+ .px-lg-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+ }
+
+ .py-lg-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ }
+
+ .py-lg-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+ }
+
+ .py-lg-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+ }
+
+ .py-lg-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+ }
+
+ .py-lg-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+ }
+
+ .py-lg-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+ }
+
+ .pt-lg-0 {
+ padding-top: 0 !important;
+ }
+
+ .pt-lg-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pt-lg-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pt-lg-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pt-lg-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pt-lg-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pe-lg-0 {
+ padding-right: 0 !important;
+ }
+
+ .pe-lg-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pe-lg-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pe-lg-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pe-lg-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pe-lg-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-lg-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pb-lg-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pb-lg-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pb-lg-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pb-lg-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pb-lg-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .ps-lg-0 {
+ padding-left: 0 !important;
+ }
+
+ .ps-lg-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .ps-lg-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .ps-lg-3 {
+ padding-left: 1rem !important;
+ }
+
+ .ps-lg-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .ps-lg-5 {
+ padding-left: 3rem !important;
+ }
+
+ .gap-lg-0 {
+ gap: 0 !important;
+ }
+
+ .gap-lg-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-lg-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-lg-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-lg-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-lg-5 {
+ gap: 3rem !important;
+ }
+
+ .row-gap-lg-0 {
+ row-gap: 0 !important;
+ }
+
+ .row-gap-lg-1 {
+ row-gap: 0.25rem !important;
+ }
+
+ .row-gap-lg-2 {
+ row-gap: 0.5rem !important;
+ }
+
+ .row-gap-lg-3 {
+ row-gap: 1rem !important;
+ }
+
+ .row-gap-lg-4 {
+ row-gap: 1.5rem !important;
+ }
+
+ .row-gap-lg-5 {
+ row-gap: 3rem !important;
+ }
+
+ .column-gap-lg-0 {
+ column-gap: 0 !important;
+ }
+
+ .column-gap-lg-1 {
+ column-gap: 0.25rem !important;
+ }
+
+ .column-gap-lg-2 {
+ column-gap: 0.5rem !important;
+ }
+
+ .column-gap-lg-3 {
+ column-gap: 1rem !important;
+ }
+
+ .column-gap-lg-4 {
+ column-gap: 1.5rem !important;
+ }
+
+ .column-gap-lg-5 {
+ column-gap: 3rem !important;
+ }
+
+ .text-lg-start {
+ text-align: left !important;
+ }
+
+ .text-lg-end {
+ text-align: right !important;
+ }
+
+ .text-lg-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .float-xl-start {
+ float: left !important;
+ }
+
+ .float-xl-end {
+ float: right !important;
+ }
+
+ .float-xl-none {
+ float: none !important;
+ }
+
+ .object-fit-xl-contain {
+ object-fit: contain !important;
+ }
+
+ .object-fit-xl-cover {
+ object-fit: cover !important;
+ }
+
+ .object-fit-xl-fill {
+ object-fit: fill !important;
+ }
+
+ .object-fit-xl-scale {
+ object-fit: scale-down !important;
+ }
+
+ .object-fit-xl-none {
+ object-fit: none !important;
+ }
+
+ .d-xl-inline {
+ display: inline !important;
+ }
+
+ .d-xl-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-xl-block {
+ display: block !important;
+ }
+
+ .d-xl-grid {
+ display: grid !important;
+ }
+
+ .d-xl-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-xl-table {
+ display: table !important;
+ }
+
+ .d-xl-table-row {
+ display: table-row !important;
+ }
+
+ .d-xl-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-xl-flex {
+ display: flex !important;
+ }
+
+ .d-xl-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-xl-none {
+ display: none !important;
+ }
+
+ .flex-xl-fill {
+ flex: 1 1 auto !important;
+ }
+
+ .flex-xl-row {
+ flex-direction: row !important;
+ }
+
+ .flex-xl-column {
+ flex-direction: column !important;
+ }
+
+ .flex-xl-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-xl-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-xl-grow-0 {
+ flex-grow: 0 !important;
+ }
+
+ .flex-xl-grow-1 {
+ flex-grow: 1 !important;
+ }
+
+ .flex-xl-shrink-0 {
+ flex-shrink: 0 !important;
+ }
+
+ .flex-xl-shrink-1 {
+ flex-shrink: 1 !important;
+ }
+
+ .flex-xl-wrap {
+ flex-wrap: wrap !important;
+ }
+
+ .flex-xl-nowrap {
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-xl-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .justify-content-xl-start {
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-xl-end {
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-xl-center {
+ justify-content: center !important;
+ }
+
+ .justify-content-xl-between {
+ justify-content: space-between !important;
+ }
+
+ .justify-content-xl-around {
+ justify-content: space-around !important;
+ }
+
+ .justify-content-xl-evenly {
+ justify-content: space-evenly !important;
+ }
+
+ .align-items-xl-start {
+ align-items: flex-start !important;
+ }
+
+ .align-items-xl-end {
+ align-items: flex-end !important;
+ }
+
+ .align-items-xl-center {
+ align-items: center !important;
+ }
+
+ .align-items-xl-baseline {
+ align-items: baseline !important;
+ }
+
+ .align-items-xl-stretch {
+ align-items: stretch !important;
+ }
+
+ .align-content-xl-start {
+ align-content: flex-start !important;
+ }
+
+ .align-content-xl-end {
+ align-content: flex-end !important;
+ }
+
+ .align-content-xl-center {
+ align-content: center !important;
+ }
+
+ .align-content-xl-between {
+ align-content: space-between !important;
+ }
+
+ .align-content-xl-around {
+ align-content: space-around !important;
+ }
+
+ .align-content-xl-stretch {
+ align-content: stretch !important;
+ }
+
+ .align-self-xl-auto {
+ align-self: auto !important;
+ }
+
+ .align-self-xl-start {
+ align-self: flex-start !important;
+ }
+
+ .align-self-xl-end {
+ align-self: flex-end !important;
+ }
+
+ .align-self-xl-center {
+ align-self: center !important;
+ }
+
+ .align-self-xl-baseline {
+ align-self: baseline !important;
+ }
+
+ .align-self-xl-stretch {
+ align-self: stretch !important;
+ }
+
+ .order-xl-first {
+ order: -1 !important;
+ }
+
+ .order-xl-0 {
+ order: 0 !important;
+ }
+
+ .order-xl-1 {
+ order: 1 !important;
+ }
+
+ .order-xl-2 {
+ order: 2 !important;
+ }
+
+ .order-xl-3 {
+ order: 3 !important;
+ }
+
+ .order-xl-4 {
+ order: 4 !important;
+ }
+
+ .order-xl-5 {
+ order: 5 !important;
+ }
+
+ .order-xl-last {
+ order: 6 !important;
+ }
+
+ .m-xl-0 {
+ margin: 0 !important;
+ }
+
+ .m-xl-1 {
+ margin: 0.25rem !important;
+ }
+
+ .m-xl-2 {
+ margin: 0.5rem !important;
+ }
+
+ .m-xl-3 {
+ margin: 1rem !important;
+ }
+
+ .m-xl-4 {
+ margin: 1.5rem !important;
+ }
+
+ .m-xl-5 {
+ margin: 3rem !important;
+ }
+
+ .m-xl-auto {
+ margin: auto !important;
+ }
+
+ .mx-xl-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+ }
+
+ .mx-xl-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+ }
+
+ .mx-xl-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+ }
+
+ .mx-xl-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+ }
+
+ .mx-xl-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+ }
+
+ .mx-xl-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+ }
+
+ .mx-xl-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+ }
+
+ .my-xl-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .my-xl-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+ }
+
+ .my-xl-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+ }
+
+ .my-xl-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+ }
+
+ .my-xl-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+ }
+
+ .my-xl-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+ }
+
+ .my-xl-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ }
+
+ .mt-xl-0 {
+ margin-top: 0 !important;
+ }
+
+ .mt-xl-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mt-xl-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mt-xl-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mt-xl-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mt-xl-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mt-xl-auto {
+ margin-top: auto !important;
+ }
+
+ .me-xl-0 {
+ margin-right: 0 !important;
+ }
+
+ .me-xl-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .me-xl-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .me-xl-3 {
+ margin-right: 1rem !important;
+ }
+
+ .me-xl-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .me-xl-5 {
+ margin-right: 3rem !important;
+ }
+
+ .me-xl-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-xl-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .mb-xl-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .mb-xl-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .mb-xl-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .mb-xl-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .mb-xl-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .mb-xl-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ms-xl-0 {
+ margin-left: 0 !important;
+ }
+
+ .ms-xl-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .ms-xl-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .ms-xl-3 {
+ margin-left: 1rem !important;
+ }
+
+ .ms-xl-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .ms-xl-5 {
+ margin-left: 3rem !important;
+ }
+
+ .ms-xl-auto {
+ margin-left: auto !important;
+ }
+
+ .p-xl-0 {
+ padding: 0 !important;
+ }
+
+ .p-xl-1 {
+ padding: 0.25rem !important;
+ }
+
+ .p-xl-2 {
+ padding: 0.5rem !important;
+ }
+
+ .p-xl-3 {
+ padding: 1rem !important;
+ }
+
+ .p-xl-4 {
+ padding: 1.5rem !important;
+ }
+
+ .p-xl-5 {
+ padding: 3rem !important;
+ }
+
+ .px-xl-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+ }
+
+ .px-xl-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+ }
+
+ .px-xl-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+ }
+
+ .px-xl-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+ }
+
+ .px-xl-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+ }
+
+ .px-xl-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+ }
+
+ .py-xl-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ }
+
+ .py-xl-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+ }
+
+ .py-xl-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+ }
+
+ .py-xl-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+ }
+
+ .py-xl-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+ }
+
+ .py-xl-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+ }
+
+ .pt-xl-0 {
+ padding-top: 0 !important;
+ }
+
+ .pt-xl-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pt-xl-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pt-xl-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pt-xl-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pt-xl-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pe-xl-0 {
+ padding-right: 0 !important;
+ }
+
+ .pe-xl-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pe-xl-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pe-xl-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pe-xl-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pe-xl-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-xl-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pb-xl-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pb-xl-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pb-xl-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pb-xl-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pb-xl-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .ps-xl-0 {
+ padding-left: 0 !important;
+ }
+
+ .ps-xl-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .ps-xl-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .ps-xl-3 {
+ padding-left: 1rem !important;
+ }
+
+ .ps-xl-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .ps-xl-5 {
+ padding-left: 3rem !important;
+ }
+
+ .gap-xl-0 {
+ gap: 0 !important;
+ }
+
+ .gap-xl-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-xl-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-xl-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-xl-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-xl-5 {
+ gap: 3rem !important;
+ }
+
+ .row-gap-xl-0 {
+ row-gap: 0 !important;
+ }
+
+ .row-gap-xl-1 {
+ row-gap: 0.25rem !important;
+ }
+
+ .row-gap-xl-2 {
+ row-gap: 0.5rem !important;
+ }
+
+ .row-gap-xl-3 {
+ row-gap: 1rem !important;
+ }
+
+ .row-gap-xl-4 {
+ row-gap: 1.5rem !important;
+ }
+
+ .row-gap-xl-5 {
+ row-gap: 3rem !important;
+ }
+
+ .column-gap-xl-0 {
+ column-gap: 0 !important;
+ }
+
+ .column-gap-xl-1 {
+ column-gap: 0.25rem !important;
+ }
+
+ .column-gap-xl-2 {
+ column-gap: 0.5rem !important;
+ }
+
+ .column-gap-xl-3 {
+ column-gap: 1rem !important;
+ }
+
+ .column-gap-xl-4 {
+ column-gap: 1.5rem !important;
+ }
+
+ .column-gap-xl-5 {
+ column-gap: 3rem !important;
+ }
+
+ .text-xl-start {
+ text-align: left !important;
+ }
+
+ .text-xl-end {
+ text-align: right !important;
+ }
+
+ .text-xl-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 1400px) {
+ .float-xxl-start {
+ float: left !important;
+ }
+
+ .float-xxl-end {
+ float: right !important;
+ }
+
+ .float-xxl-none {
+ float: none !important;
+ }
+
+ .object-fit-xxl-contain {
+ object-fit: contain !important;
+ }
+
+ .object-fit-xxl-cover {
+ object-fit: cover !important;
+ }
+
+ .object-fit-xxl-fill {
+ object-fit: fill !important;
+ }
+
+ .object-fit-xxl-scale {
+ object-fit: scale-down !important;
+ }
+
+ .object-fit-xxl-none {
+ object-fit: none !important;
+ }
+
+ .d-xxl-inline {
+ display: inline !important;
+ }
+
+ .d-xxl-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-xxl-block {
+ display: block !important;
+ }
+
+ .d-xxl-grid {
+ display: grid !important;
+ }
+
+ .d-xxl-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-xxl-table {
+ display: table !important;
+ }
+
+ .d-xxl-table-row {
+ display: table-row !important;
+ }
+
+ .d-xxl-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-xxl-flex {
+ display: flex !important;
+ }
+
+ .d-xxl-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-xxl-none {
+ display: none !important;
+ }
+
+ .flex-xxl-fill {
+ flex: 1 1 auto !important;
+ }
+
+ .flex-xxl-row {
+ flex-direction: row !important;
+ }
+
+ .flex-xxl-column {
+ flex-direction: column !important;
+ }
+
+ .flex-xxl-row-reverse {
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-xxl-column-reverse {
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-xxl-grow-0 {
+ flex-grow: 0 !important;
+ }
+
+ .flex-xxl-grow-1 {
+ flex-grow: 1 !important;
+ }
+
+ .flex-xxl-shrink-0 {
+ flex-shrink: 0 !important;
+ }
+
+ .flex-xxl-shrink-1 {
+ flex-shrink: 1 !important;
+ }
+
+ .flex-xxl-wrap {
+ flex-wrap: wrap !important;
+ }
+
+ .flex-xxl-nowrap {
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-xxl-wrap-reverse {
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .justify-content-xxl-start {
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-xxl-end {
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-xxl-center {
+ justify-content: center !important;
+ }
+
+ .justify-content-xxl-between {
+ justify-content: space-between !important;
+ }
+
+ .justify-content-xxl-around {
+ justify-content: space-around !important;
+ }
+
+ .justify-content-xxl-evenly {
+ justify-content: space-evenly !important;
+ }
+
+ .align-items-xxl-start {
+ align-items: flex-start !important;
+ }
+
+ .align-items-xxl-end {
+ align-items: flex-end !important;
+ }
+
+ .align-items-xxl-center {
+ align-items: center !important;
+ }
+
+ .align-items-xxl-baseline {
+ align-items: baseline !important;
+ }
+
+ .align-items-xxl-stretch {
+ align-items: stretch !important;
+ }
+
+ .align-content-xxl-start {
+ align-content: flex-start !important;
+ }
+
+ .align-content-xxl-end {
+ align-content: flex-end !important;
+ }
+
+ .align-content-xxl-center {
+ align-content: center !important;
+ }
+
+ .align-content-xxl-between {
+ align-content: space-between !important;
+ }
+
+ .align-content-xxl-around {
+ align-content: space-around !important;
+ }
+
+ .align-content-xxl-stretch {
+ align-content: stretch !important;
+ }
+
+ .align-self-xxl-auto {
+ align-self: auto !important;
+ }
+
+ .align-self-xxl-start {
+ align-self: flex-start !important;
+ }
+
+ .align-self-xxl-end {
+ align-self: flex-end !important;
+ }
+
+ .align-self-xxl-center {
+ align-self: center !important;
+ }
+
+ .align-self-xxl-baseline {
+ align-self: baseline !important;
+ }
+
+ .align-self-xxl-stretch {
+ align-self: stretch !important;
+ }
+
+ .order-xxl-first {
+ order: -1 !important;
+ }
+
+ .order-xxl-0 {
+ order: 0 !important;
+ }
+
+ .order-xxl-1 {
+ order: 1 !important;
+ }
+
+ .order-xxl-2 {
+ order: 2 !important;
+ }
+
+ .order-xxl-3 {
+ order: 3 !important;
+ }
+
+ .order-xxl-4 {
+ order: 4 !important;
+ }
+
+ .order-xxl-5 {
+ order: 5 !important;
+ }
+
+ .order-xxl-last {
+ order: 6 !important;
+ }
+
+ .m-xxl-0 {
+ margin: 0 !important;
+ }
+
+ .m-xxl-1 {
+ margin: 0.25rem !important;
+ }
+
+ .m-xxl-2 {
+ margin: 0.5rem !important;
+ }
+
+ .m-xxl-3 {
+ margin: 1rem !important;
+ }
+
+ .m-xxl-4 {
+ margin: 1.5rem !important;
+ }
+
+ .m-xxl-5 {
+ margin: 3rem !important;
+ }
+
+ .m-xxl-auto {
+ margin: auto !important;
+ }
+
+ .mx-xxl-0 {
+ margin-right: 0 !important;
+ margin-left: 0 !important;
+ }
+
+ .mx-xxl-1 {
+ margin-right: 0.25rem !important;
+ margin-left: 0.25rem !important;
+ }
+
+ .mx-xxl-2 {
+ margin-right: 0.5rem !important;
+ margin-left: 0.5rem !important;
+ }
+
+ .mx-xxl-3 {
+ margin-right: 1rem !important;
+ margin-left: 1rem !important;
+ }
+
+ .mx-xxl-4 {
+ margin-right: 1.5rem !important;
+ margin-left: 1.5rem !important;
+ }
+
+ .mx-xxl-5 {
+ margin-right: 3rem !important;
+ margin-left: 3rem !important;
+ }
+
+ .mx-xxl-auto {
+ margin-right: auto !important;
+ margin-left: auto !important;
+ }
+
+ .my-xxl-0 {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .my-xxl-1 {
+ margin-top: 0.25rem !important;
+ margin-bottom: 0.25rem !important;
+ }
+
+ .my-xxl-2 {
+ margin-top: 0.5rem !important;
+ margin-bottom: 0.5rem !important;
+ }
+
+ .my-xxl-3 {
+ margin-top: 1rem !important;
+ margin-bottom: 1rem !important;
+ }
+
+ .my-xxl-4 {
+ margin-top: 1.5rem !important;
+ margin-bottom: 1.5rem !important;
+ }
+
+ .my-xxl-5 {
+ margin-top: 3rem !important;
+ margin-bottom: 3rem !important;
+ }
+
+ .my-xxl-auto {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ }
+
+ .mt-xxl-0 {
+ margin-top: 0 !important;
+ }
+
+ .mt-xxl-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mt-xxl-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mt-xxl-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mt-xxl-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mt-xxl-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mt-xxl-auto {
+ margin-top: auto !important;
+ }
+
+ .me-xxl-0 {
+ margin-right: 0 !important;
+ }
+
+ .me-xxl-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .me-xxl-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .me-xxl-3 {
+ margin-right: 1rem !important;
+ }
+
+ .me-xxl-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .me-xxl-5 {
+ margin-right: 3rem !important;
+ }
+
+ .me-xxl-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-xxl-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .mb-xxl-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .mb-xxl-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .mb-xxl-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .mb-xxl-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .mb-xxl-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .mb-xxl-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ms-xxl-0 {
+ margin-left: 0 !important;
+ }
+
+ .ms-xxl-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .ms-xxl-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .ms-xxl-3 {
+ margin-left: 1rem !important;
+ }
+
+ .ms-xxl-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .ms-xxl-5 {
+ margin-left: 3rem !important;
+ }
+
+ .ms-xxl-auto {
+ margin-left: auto !important;
+ }
+
+ .p-xxl-0 {
+ padding: 0 !important;
+ }
+
+ .p-xxl-1 {
+ padding: 0.25rem !important;
+ }
+
+ .p-xxl-2 {
+ padding: 0.5rem !important;
+ }
+
+ .p-xxl-3 {
+ padding: 1rem !important;
+ }
+
+ .p-xxl-4 {
+ padding: 1.5rem !important;
+ }
+
+ .p-xxl-5 {
+ padding: 3rem !important;
+ }
+
+ .px-xxl-0 {
+ padding-right: 0 !important;
+ padding-left: 0 !important;
+ }
+
+ .px-xxl-1 {
+ padding-right: 0.25rem !important;
+ padding-left: 0.25rem !important;
+ }
+
+ .px-xxl-2 {
+ padding-right: 0.5rem !important;
+ padding-left: 0.5rem !important;
+ }
+
+ .px-xxl-3 {
+ padding-right: 1rem !important;
+ padding-left: 1rem !important;
+ }
+
+ .px-xxl-4 {
+ padding-right: 1.5rem !important;
+ padding-left: 1.5rem !important;
+ }
+
+ .px-xxl-5 {
+ padding-right: 3rem !important;
+ padding-left: 3rem !important;
+ }
+
+ .py-xxl-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ }
+
+ .py-xxl-1 {
+ padding-top: 0.25rem !important;
+ padding-bottom: 0.25rem !important;
+ }
+
+ .py-xxl-2 {
+ padding-top: 0.5rem !important;
+ padding-bottom: 0.5rem !important;
+ }
+
+ .py-xxl-3 {
+ padding-top: 1rem !important;
+ padding-bottom: 1rem !important;
+ }
+
+ .py-xxl-4 {
+ padding-top: 1.5rem !important;
+ padding-bottom: 1.5rem !important;
+ }
+
+ .py-xxl-5 {
+ padding-top: 3rem !important;
+ padding-bottom: 3rem !important;
+ }
+
+ .pt-xxl-0 {
+ padding-top: 0 !important;
+ }
+
+ .pt-xxl-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pt-xxl-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pt-xxl-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pt-xxl-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pt-xxl-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pe-xxl-0 {
+ padding-right: 0 !important;
+ }
+
+ .pe-xxl-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pe-xxl-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pe-xxl-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pe-xxl-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pe-xxl-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-xxl-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pb-xxl-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pb-xxl-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pb-xxl-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pb-xxl-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pb-xxl-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .ps-xxl-0 {
+ padding-left: 0 !important;
+ }
+
+ .ps-xxl-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .ps-xxl-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .ps-xxl-3 {
+ padding-left: 1rem !important;
+ }
+
+ .ps-xxl-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .ps-xxl-5 {
+ padding-left: 3rem !important;
+ }
+
+ .gap-xxl-0 {
+ gap: 0 !important;
+ }
+
+ .gap-xxl-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-xxl-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-xxl-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-xxl-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-xxl-5 {
+ gap: 3rem !important;
+ }
+
+ .row-gap-xxl-0 {
+ row-gap: 0 !important;
+ }
+
+ .row-gap-xxl-1 {
+ row-gap: 0.25rem !important;
+ }
+
+ .row-gap-xxl-2 {
+ row-gap: 0.5rem !important;
+ }
+
+ .row-gap-xxl-3 {
+ row-gap: 1rem !important;
+ }
+
+ .row-gap-xxl-4 {
+ row-gap: 1.5rem !important;
+ }
+
+ .row-gap-xxl-5 {
+ row-gap: 3rem !important;
+ }
+
+ .column-gap-xxl-0 {
+ column-gap: 0 !important;
+ }
+
+ .column-gap-xxl-1 {
+ column-gap: 0.25rem !important;
+ }
+
+ .column-gap-xxl-2 {
+ column-gap: 0.5rem !important;
+ }
+
+ .column-gap-xxl-3 {
+ column-gap: 1rem !important;
+ }
+
+ .column-gap-xxl-4 {
+ column-gap: 1.5rem !important;
+ }
+
+ .column-gap-xxl-5 {
+ column-gap: 3rem !important;
+ }
+
+ .text-xxl-start {
+ text-align: left !important;
+ }
+
+ .text-xxl-end {
+ text-align: right !important;
+ }
+
+ .text-xxl-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .fs-1 {
+ font-size: 2.5rem !important;
+ }
+
+ .fs-2 {
+ font-size: 2rem !important;
+ }
+
+ .fs-3 {
+ font-size: 1.75rem !important;
+ }
+
+ .fs-4 {
+ font-size: 1.5rem !important;
+ }
+}
+
+@media print {
+ .d-print-inline {
+ display: inline !important;
+ }
+
+ .d-print-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-print-block {
+ display: block !important;
+ }
+
+ .d-print-grid {
+ display: grid !important;
+ }
+
+ .d-print-inline-grid {
+ display: inline-grid !important;
+ }
+
+ .d-print-table {
+ display: table !important;
+ }
+
+ .d-print-table-row {
+ display: table-row !important;
+ }
+
+ .d-print-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-print-flex {
+ display: flex !important;
+ }
+
+ .d-print-inline-flex {
+ display: inline-flex !important;
+ }
+
+ .d-print-none {
+ display: none !important;
+ }
+}
+
+/*
+ TALAWA SCSS
+ -----------
+ This file is used to import all partial scss files in the project.
+ It is used to compile the final CSS file to the CSS folder as main.css .
+
+ ========= Table of Contents =========
+ 1. Components
+ 2. Content
+ 3. Forms
+ 4. Utilities
+ 5. General
+ 6. Colors
+
+ */
+/*
+
+ 1. COMPONENTS
+
+ */
+.btn-primary,
+.btn-secondary,
+.btn-success,
+.btn-warning,
+.btn-info {
+ color: #fff;
+}
+
+.btn-primary:hover,
+.btn-primary:active,
+.btn-secondary:hover,
+.btn-secondary:active,
+.btn-success:hover,
+.btn-success:active,
+.btn-warning:hover,
+.btn-warning:active,
+.btn-info:hover,
+.btn-info:active {
+ color: #fff !important;
+}
+
+.btn-outline-primary:hover,
+.btn-outline-primary:active,
+.btn-outline-secondary:hover,
+.btn-outline-secondary:active,
+.btn-outline-success:hover,
+.btn-outline-success:active,
+.btn-outline-warning:hover,
+.btn-outline-warning:active,
+.btn-outline-info:hover,
+.btn-outline-info:active {
+ color: #fff !important;
+}
+
+@keyframes progress-bar-stripes {
+ 0% {
+ background-position-x: 1rem;
+ }
+}
+
+@keyframes spinner-border {
+ to {
+ transform: rotate(360deg) /* rtl:ignore */;
+ }
+}
+
+@keyframes spinner-grow {
+ 0% {
+ transform: scale(0);
+ }
+
+ 50% {
+ opacity: 1;
+ transform: none;
+ }
+}
+
+/*
+
+ 2. CONTENT
+
+ */
+/*
+ DISPLAY SASS VARIABLES
+ */
+/*
+ DISPLAY SASS VARIABLES
+ */
+/*
+
+ 3. FORMS
+
+ */
+/*
+
+ 4. UTILITIES
+
+ */
+/*
+
+ 5. General
+
+ */
+:root {
+ --bs-body-font-family: Arial, Helvetica, sans-serif;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ overflow-x: hidden;
+}
+
+body {
+ background-color: var(--bs-body-bg);
+}
+
+#root {
+ min-height: 100vh;
+ background-color: #f2f7ff;
+}
+
+input[type='checkbox'] {
+ transform: scale(1.5);
+}
+
+.form-switch {
+ padding-left: 3rem;
+}
+
+input[type='file']::file-selector-button {
+ background: var(--bs-gray-400);
+}
+
+.shimmer {
+ animation-duration: 2.2s;
+ animation-fill-mode: forwards;
+ animation-iteration-count: infinite;
+ animation-name: shimmer;
+ animation-timing-function: linear;
+ background: var(--bs-gray-200);
+ background: linear-gradient(to right, #f6f6f6 8%, #f0f0f0 18%, #f6f6f6 33%);
+ background-size: 1200px 100%;
+}
+
+@-webkit-keyframes shimmer {
+ 0% {
+ background-position: -100% 0;
+ }
+
+ 100% {
+ background-position: 100% 0;
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -1200px 0;
+ }
+
+ 100% {
+ background-position: 1200px 0;
+ }
+}
+
+/*
+
+ 6. COLORS
+
+*/
+
+/*# sourceMappingURL=app.css.map */
diff --git a/src/assets/css/app.css.map b/src/assets/css/app.css.map
new file mode 100644
index 0000000000..00a8ba5443
--- /dev/null
+++ b/src/assets/css/app.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../node_modules/bootstrap/scss/_root.scss","../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../node_modules/bootstrap/scss/_reboot.scss","../../../node_modules/bootstrap/scss/_variables.scss","../scss/_variables.scss","../../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../../../node_modules/bootstrap/scss/_type.scss","../../../node_modules/bootstrap/scss/mixins/_lists.scss","../../../node_modules/bootstrap/scss/_images.scss","../../../node_modules/bootstrap/scss/mixins/_image.scss","../../../node_modules/bootstrap/scss/_containers.scss","../../../node_modules/bootstrap/scss/mixins/_container.scss","../../../node_modules/bootstrap/scss/mixins/_breakpoints.scss","../../../node_modules/bootstrap/scss/_grid.scss","../../../node_modules/bootstrap/scss/mixins/_grid.scss","../../../node_modules/bootstrap/scss/_tables.scss","../../../node_modules/bootstrap/scss/mixins/_table-variants.scss","../../../node_modules/bootstrap/scss/forms/_labels.scss","../../../node_modules/bootstrap/scss/forms/_form-text.scss","../../../node_modules/bootstrap/scss/forms/_form-control.scss","../../../node_modules/bootstrap/scss/mixins/_transition.scss","../../../node_modules/bootstrap/scss/mixins/_gradients.scss","../../../node_modules/bootstrap/scss/forms/_form-select.scss","../../../node_modules/bootstrap/scss/forms/_form-check.scss","../../../node_modules/bootstrap/scss/forms/_form-range.scss","../../../node_modules/bootstrap/scss/forms/_floating-labels.scss","../../../node_modules/bootstrap/scss/forms/_input-group.scss","../../../node_modules/bootstrap/scss/mixins/_forms.scss","../../../node_modules/bootstrap/scss/_buttons.scss","../../../node_modules/bootstrap/scss/mixins/_buttons.scss","../../../node_modules/bootstrap/scss/_transitions.scss","../../../node_modules/bootstrap/scss/_dropdown.scss","../../../node_modules/bootstrap/scss/mixins/_caret.scss","../../../node_modules/bootstrap/scss/_button-group.scss","../../../node_modules/bootstrap/scss/_nav.scss","../../../node_modules/bootstrap/scss/_navbar.scss","../../../node_modules/bootstrap/scss/_card.scss","../../../node_modules/bootstrap/scss/_accordion.scss","../../../node_modules/bootstrap/scss/_breadcrumb.scss","../../../node_modules/bootstrap/scss/_pagination.scss","../../../node_modules/bootstrap/scss/mixins/_pagination.scss","../../../node_modules/bootstrap/scss/_badge.scss","../../../node_modules/bootstrap/scss/_alert.scss","../../../node_modules/bootstrap/scss/_progress.scss","../../../node_modules/bootstrap/scss/_list-group.scss","../../../node_modules/bootstrap/scss/_close.scss","../../../node_modules/bootstrap/scss/_toasts.scss","../../../node_modules/bootstrap/scss/_modal.scss","../../../node_modules/bootstrap/scss/mixins/_backdrop.scss","../../../node_modules/bootstrap/scss/_tooltip.scss","../../../node_modules/bootstrap/scss/mixins/_reset-text.scss","../../../node_modules/bootstrap/scss/_popover.scss","../../../node_modules/bootstrap/scss/_carousel.scss","../../../node_modules/bootstrap/scss/mixins/_clearfix.scss","../../../node_modules/bootstrap/scss/_spinners.scss","../../../node_modules/bootstrap/scss/_offcanvas.scss","../../../node_modules/bootstrap/scss/_placeholders.scss","../../../node_modules/bootstrap/scss/helpers/_color-bg.scss","../../../node_modules/bootstrap/scss/helpers/_colored-links.scss","../../../node_modules/bootstrap/scss/helpers/_focus-ring.scss","../../../node_modules/bootstrap/scss/helpers/_icon-link.scss","../../../node_modules/bootstrap/scss/helpers/_ratio.scss","../../../node_modules/bootstrap/scss/helpers/_position.scss","../../../node_modules/bootstrap/scss/helpers/_stacks.scss","../../../node_modules/bootstrap/scss/helpers/_visually-hidden.scss","../../../node_modules/bootstrap/scss/mixins/_visually-hidden.scss","../../../node_modules/bootstrap/scss/helpers/_stretched-link.scss","../../../node_modules/bootstrap/scss/helpers/_text-truncation.scss","../../../node_modules/bootstrap/scss/mixins/_text-truncate.scss","../../../node_modules/bootstrap/scss/helpers/_vr.scss","../../../node_modules/bootstrap/scss/mixins/_utilities.scss","../../../node_modules/bootstrap/scss/utilities/_api.scss","../scss/_talawa.scss","../scss/components/_buttons.scss","../scss/components/_progress.scss","../scss/components/_spinners.scss","../scss/content/_typography.scss","../scss/_general.scss"],"names":[],"mappings":";AACE;AAAA;AAAA;AAAA;AAAA;ACDF;AAAA;EASI;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAGF;EACA;EAMA;EACA;EACA;EAOA;EC2OI,qBALI;EDpOR;EACA;EAKA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EAGA;EAEA;EACA;EACA;EAEA;EACA;EAMA;EACA;EAGA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EAIA;EACA;EACA;EAIA;EACA;EACA;EACA;;;AE/GE;EFqHA;EAGA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EAGE;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAIA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAGF;EAEA;EACA;EACA;EACA;EAEA;EAEA;EACA;EAEA;EACA;EACA;EACA;;;AGrKJ;AAAA;AAAA;EAGE;;;AAeE;EANJ;IAOM;;;;AAcN;EACE;EACA;EF6OI,WALI;EEtOR;EACA;EACA;EACA;EACA;EACA;EACA;;;AASF;EACE;EACA,OCmnB4B;EDlnB5B;EACA;EACA,SCynB4B;;;AD/mB9B;EACE;EACA,eCwjB4B;EDrjB5B,aCwjB4B;EDvjB5B,aCwjB4B;EDvjB5B;;;AAGF;EFuMQ;;AA5JJ;EE3CJ;IF8MQ;;;;AEzMR;EFkMQ;;AA5JJ;EEtCJ;IFyMQ;;;;AEpMR;EF6LQ;;AA5JJ;EEjCJ;IFoMQ;;;;AE/LR;EFwLQ;;AA5JJ;EE5BJ;IF+LQ;;;;AE1LR;EF+KM,WALI;;;AErKV;EF0KM,WALI;;;AE1JV;EACE;EACA,eCwV0B;;;AD9U5B;EACE;EACA;EACA;;;AAMF;EACE;EACA;EACA;;;AAMF;AAAA;EAEE;;;AAGF;AAAA;AAAA;EAGE;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;;;AAGF;EACE,aC6b4B;;;ADxb9B;EACE;EACA;;;AAMF;EACE;;;AAQF;AAAA;EAEE,aCsa4B;;;AD9Z9B;EF6EM,WALI;;;AEjEV;EACE,SCif4B;EDhf5B;;;AASF;AAAA;EAEE;EFyDI,WALI;EElDR;EACA;;;AAGF;EAAM;;;AACN;EAAM;;;AAKN;EACE;EACA,iBE9NgB;;AFgOhB;EACE;;;AAWF;EAEE;EACA;;;AAOJ;AAAA;AAAA;AAAA;EAIE,aCiV4B;EHlUxB,WALI;;;AEFV;EACE;EACA;EACA;EACA;EFGI,WALI;;AEOR;EFFI,WALI;EESN;EACA;;;AAIJ;EFTM,WALI;EEgBR;EACA;;AAGA;EACE;;;AAIJ;EACE;EFrBI,WALI;EE4BR,OCo5CkC;EDn5ClC,kBCo5CkC;EExrDhC;;AHuSF;EACE;EF5BE,WALI;;;AE4CV;EACE;;;AAMF;AAAA;EAEE;;;AAQF;EACE;EACA;;;AAGF;EACE,aCwX4B;EDvX5B,gBCuX4B;EDtX5B,OCwZ4B;EDvZ5B;;;AAOF;EAEE;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;EACA;EACA;;;AAQF;EACE;;;AAMF;EAEE;;;AAQF;EACE;;;AAKF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;EF3HI,WALI;EEkIR;;;AAIF;AAAA;EAEE;;;AAKF;EACE;;;AAGF;EAGE;;AAGA;EACE;;;AAOJ;EACE;;;AAQF;AAAA;AAAA;AAAA;EAIE;;AAGE;AAAA;AAAA;AAAA;EACE;;;AAON;EACE;EACA;;;AAKF;EACE;;;AAUF;EACE;EACA;EACA;EACA;;;AAQF;EACE;EACA;EACA;EACA,eCgN4B;EHhatB;EEmNN;;AF/WE;EEwWJ;IFrMQ;;;AE8MN;EACE;;;AAOJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAOE;;;AAGF;EACE;;;AASF;EACE;EACA;;;AAQF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA;EACE;;;AAKF;EACE;;;AAOF;EACE;EACA;;;AAKF;EACE;;;AAKF;EACE;;;AAOF;EACE;EACA;;;AAQF;EACE;;;AAQF;EACE;;;AIpkBF;ENmQM,WALI;EM5PR,aHwoB4B;;;AGnoB5B;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AMvQN;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AMvQN;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AMvQN;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AMvQN;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AMvQN;ENgQM;EM5PJ,aHynBkB;EGxnBlB,aHwmB0B;;AHzgB1B;EMpGF;INuQM;;;;AM/OR;ECvDE;EACA;;;AD2DF;EC5DE;EACA;;;AD8DF;EACE;;AAEA;EACE,cHkoB0B;;;AGxnB9B;EN8MM,WALI;EMvMR;;;AAIF;EACE,eHiUO;EH1HH,WALI;;AM/LR;EACE;;;AAIJ;EACE;EACA,eHuTO;EH1HH,WALI;EMtLR,OHtFS;;AGwFT;EACE;;;AEhGJ;ECIE;EAGA;;;ADDF;EACE,SLyjDkC;EKxjDlC,kBLyjDkC;EKxjDlC;EHGE;EIRF;EAGA;;;ADcF;EAEE;;;AAGF;EACE;EACA;;;AAGF;ERyPM,WALI;EQlPR,OL4iDkC;;;AO9kDlC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECHA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACsDE;EF5CE;IACE,WPkee;;;ASvbnB;EF5CE;IACE,WPkee;;;ASvbnB;EF5CE;IACE,WPkee;;;ASvbnB;EF5CE;IACE,WPkee;;;ASvbnB;EF5CE;IACE,WPkee;;;AUlfvB;EAEI;EAAA;EAAA;EAAA;EAAA;EAAA;;;AAKF;ECNA;EACA;EACA;EACA;EAEA;EACA;EACA;;ADEE;ECOF;EACA;EACA;EACA;EACA;EACA;;;AA+CI;EACE;;;AAGF;EApCJ;EACA;;;AAcA;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AA+BE;EAhDJ;EACA;;;AAqDQ;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AA+DM;EAhEN;EACA;;;AAuEQ;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAwDU;EAxDV;;;AAmEM;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAPF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAPF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAPF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAPF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAPF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AF1DN;EEUE;IACE;;EAGF;IApCJ;IACA;;EAcA;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EA+BE;IAhDJ;IACA;;EAqDQ;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EAuEQ;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAmEM;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;;AF1DN;EEUE;IACE;;EAGF;IApCJ;IACA;;EAcA;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EA+BE;IAhDJ;IACA;;EAqDQ;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EAuEQ;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAmEM;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;;AF1DN;EEUE;IACE;;EAGF;IApCJ;IACA;;EAcA;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EA+BE;IAhDJ;IACA;;EAqDQ;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EAuEQ;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAmEM;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;;AF1DN;EEUE;IACE;;EAGF;IApCJ;IACA;;EAcA;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EA+BE;IAhDJ;IACA;;EAqDQ;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EAuEQ;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAmEM;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;;AF1DN;EEUE;IACE;;EAGF;IApCJ;IACA;;EAcA;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EAFF;IACE;IACA;;EA+BE;IAhDJ;IACA;;EAqDQ;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EA+DM;IAhEN;IACA;;EAuEQ;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAwDU;IAxDV;;EAmEM;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;EAPF;AAAA;IAEE;;EAGF;AAAA;IAEE;;;ACrHV;EAEE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA,eZkYO;EYjYP,gBZksB4B;EYjsB5B;;AAOA;EACE;EAEA;EACA;EACA,qBZ0sB0B;EYzsB1B;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAOF;EACE;;;AAUA;EACE;;;AAeF;EACE;;AAGA;EACE;;;AAOJ;EACE;;AAGF;EACE;;;AAUF;EACE;EACA;;;AAMF;EACE;EACA;;;AAQJ;EACE;EACA;;;AAQA;EACE;EACA;;;AC5IF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAlBF;EAOE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;ADiJA;EACE;EACA;;;AH3FF;EGyFA;IACE;IACA;;;AH3FF;EGyFA;IACE;IACA;;;AH3FF;EGyFA;IACE;IACA;;;AH3FF;EGyFA;IACE;IACA;;;AH3FF;EGyFA;IACE;IACA;;;AEnKN;EACE,edi2BsC;;;Acx1BxC;EACE;EACA;EACA;EjB8QI,WALI;EiBrQR,ad+lB4B;;;Ac3lB9B;EACE;EACA;EjBoQI,WALI;;;AiB3PV;EACE;EACA;EjB8PI,WALI;;;AkBtRV;EACE,Yfy1BsC;EH/jBlC,WALI;EkBjRR,Ofy1BsC;;;AgB91BxC;EACE;EACA;EACA;EnBwRI,WALI;EmBhRR,ahBkmB4B;EgBjmB5B,ahBymB4B;EgBxmB5B,OhBs3BsC;EgBr3BtC,kBfIe;EeHf;EACA;EACA;EdGE;EeHE,YDMJ;;ACFI;EDhBN;ICiBQ;;;ADGN;EACE;;AAEA;EACE;;AAKJ;EACE,OhBg2BoC;EgB/1BpC,kBflBa;EemBb,chBw2BoC;EgBv2BpC;EAKE,YhBkhBkB;;AgB9gBtB;EAME;EAMA;EAKA;;AAKF;EACE;EACA;;AAIF;EACE,OhBs0BoC;EgBp0BpC;;AAQF;EAEE,kBhBwyBoC;EgBryBpC;;AAIF;EACE;EACA;EACA,mBfpEkB;EeqElB,OhBgyBoC;EkB93BtC,kBlB+hCgC;EgB/7B9B;EACA;EACA;EACA;EACA,yBf9EiB;Ee+EjB;ECzFE,YD0FF;;ACtFE;ED0EJ;ICzEM;;;ADwFN;EACE,kBhBs7B8B;;;AgB76BlC;EACE;EACA;EACA;EACA;EACA,ahBwf4B;EgBvf5B,OhBqxBsC;EgBpxBtC;EACA;EACA;;AAEA;EACE;;AAGF;EAEE;EACA;;;AAWJ;EACE,YhBswBsC;EgBrwBtC;EnByII,WALI;EKvQN;;AcuIF;EACE;EACA;EACA,mBhB+nB0B;;;AgB3nB9B;EACE,YhB0vBsC;EgBzvBtC;EnB4HI,WALI;EKvQN;;AcoJF;EACE;EACA;EACA,mBhBsnB0B;;;AgB9mB5B;EACE,YhBuuBoC;;AgBpuBtC;EACE,YhBouBoC;;AgBjuBtC;EACE,YhBiuBoC;;;AgB5tBxC;EACE,OhB+tBsC;EgB9tBtC,QhBwtBsC;EgBvtBtC,SfvKoB;;AeyKpB;EACE;;AAGF;EACE;EdvLA;;Ac2LF;EACE;Ed5LA;;AcgMF;EAAoB,QhBwsBkB;;AgBvsBtC;EAAoB,QhBwsBkB;;;AmBv5BxC;EACE;EAEA;EACA;EACA;EtBqRI,WALI;EsB7QR,anB+lB4B;EmB9lB5B,anBsmB4B;EmBrmB5B,OnBm3BsC;EmBl3BtC,kBlBCe;EkBAf;EACA;EACA,qBnB09BkC;EmBz9BlC,iBnB09BkC;EmBz9BlC;EjBFE;EeHE,YEQJ;EACA;;AFLI;EEfN;IFgBQ;;;AEMN;EACE,cnBg3BoC;EmB/2BpC;EAKE,YnB29B4B;;AmBv9BhC;EAEE,elBXkB;EkBYlB;;AAGF;EAEE,kBnBi1BoC;;AmB50BtC;EACE;EACA;;;AAIJ;EACE,anBiuB4B;EmBhuB5B,gBnBguB4B;EmB/tB5B,cnBguB4B;EH7fxB,WALI;EKvQN;;;AiB8CJ;EACE,anB6tB4B;EmB5tB5B,gBnB4tB4B;EmB3tB5B,cnB4tB4B;EHjgBxB,WALI;EKvQN;;;AiBwDA;EACE;;;ACxEN;EACE;EACA,YpB+5BwC;EoB95BxC,cpB+5BwC;EoB95BxC,epB+5BwC;;AoB75BxC;EACE;EACA;;;AAIJ;EACE,epBq5BwC;EoBp5BxC;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EAEA,OpBq4BwC;EoBp4BxC,QpBo4BwC;EoBn4BxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QpBu4BwC;EoBt4BxC;EACA;;AAGA;ElB1BE;;AkB8BF;EAEE,epB83BsC;;AoB33BxC;EACE,QpBq3BsC;;AoBl3BxC;EACE,cpBi1BoC;EoBh1BpC;EACA,YpB+foB;;AoB5ftB;EACE,kBnB/DM;EmBgEN,cnBhEM;;AmBkEN;EAII;;AAIJ;EAII;;AAKN;EACE,kBnBpFM;EmBqFN,cnBrFM;EmB0FJ;;AAIJ;EACE;EACA;EACA,SpB61BuC;;AoBt1BvC;EACE;EACA,SpBo1BqC;;;AoBt0B3C;EACE,cpB+0BgC;;AoB70BhC;EACE;EAEA,OpBy0B8B;EoBx0B9B;EACA;EACA;ElBhHA;EeHE,YGqHF;;AHjHE;EGyGJ;IHxGM;;;AGkHJ;EACE;;AAGF;EACE,qBpBw0B4B;EoBn0B1B;;AAKN;EACE,epBmzB8B;EoBlzB9B;;AAEA;EACE;EACA;;;AAKN;EACE;EACA,cpBiyBgC;;;AoB9xBlC;EACE;EACA;EACA;;AAIE;EACE;EACA;EACA,SpBkpBwB;;;AoB3oB1B;EACE;;;AClLN;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIA;EAA0B,YrBwgCa;;AqBvgCvC;EAA0B,YrBugCa;;AqBpgCzC;EACE;;AAGF;EACE,OrBy/BuC;EqBx/BvC,QrBw/BuC;EqBv/BvC;EHzBF,kBjBFQ;EoB6BN,QrBw/BuC;EEpgCvC;EeHE,YIkBF;EACA;;AJfE;EIMJ;IJLM;;;AIgBJ;EHjCF,kBlBwhCyC;;AqBl/BzC;EACE,OrBk+B8B;EqBj+B9B,QrBk+B8B;EqBj+B9B;EACA,QrBi+B8B;EqBh+B9B,kBrBi+B8B;EqBh+B9B;EnB7BA;;AmBkCF;EACE,OrB89BuC;EqB79BvC,QrB69BuC;EkBhhCzC,kBjBFQ;EoBuDN,QrB89BuC;EEpgCvC;EeHE,YI4CF;EACA;;AJzCE;EIiCJ;IJhCM;;;AI0CJ;EH3DF,kBlBwhCyC;;AqBx9BzC;EACE,OrBw8B8B;EqBv8B9B,QrBw8B8B;EqBv8B9B;EACA,QrBu8B8B;EqBt8B9B,kBrBu8B8B;EqBt8B9B;EnBvDA;;AmB4DF;EACE;;AAEA;EACE,kBrB08BqC;;AqBv8BvC;EACE,kBrBs8BqC;;;AsB7hC3C;EACE;;AAEA;AAAA;AAAA;EAGE,QtBkiCoC;EsBjiCpC,YtBiiCoC;EsBhiCpC,atBiiCoC;;AsB9hCtC;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;ELRE,YKSF;;ALLE;EKTJ;ILUM;;;AKON;AAAA;EAEE;;AAEA;AAAA;EACE;;AAGF;AAAA;AAAA;EAEE,atBsgCkC;EsBrgClC,gBtBsgCkC;;AsBngCpC;AAAA;EACE,atBigCkC;EsBhgClC,gBtBigCkC;;AsB7/BtC;EACE,atB2/BoC;EsB1/BpC,gBtB2/BoC;;AsBp/BpC;AAAA;AAAA;AAAA;EACE;EACA,WtBq/BkC;;AsBn/BlC;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA,QtB6+BgC;EsB5+BhC;EACA,kBrBlDS;ECEb;;AoBuDA;EACE;EACA,WtBo+BkC;;AsB/9BpC;EACE;;AAIJ;EACE,OtBzEO;;AsB2EP;EACE,kBtBqyBkC;;;AuB33BxC;EACE;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;EAGE;EACA;EACA;EACA;;AAIF;AAAA;AAAA;EAGE;;AAMF;EACE;EACA;;AAEA;EACE;;;AAWN;EACE;EACA;EACA;E1B8OI,WALI;E0BvOR,avByjB4B;EuBxjB5B,avBgkB4B;EuB/jB5B,OvB60BsC;EuB50BtC;EACA;EACA,kBvBo6BsC;EuBn6BtC;ErBtCE;;;AqBgDJ;AAAA;AAAA;AAAA;EAIE;E1BwNI,WALI;EKvQN;;;AqByDJ;AAAA;AAAA;AAAA;EAIE;E1B+MI,WALI;EKvQN;;;AqBkEJ;AAAA;EAEE;;;AAaE;AAAA;AAAA;AAAA;ErBjEA;EACA;;AqByEA;AAAA;AAAA;AAAA;ErB1EA;EACA;;AqBsFF;EACE;ErB1EA;EACA;;AqB6EF;AAAA;ErB9EE;EACA;;;AsBxBF;EACE;EACA;EACA,YxBi0BoC;EH/jBlC,WALI;E2B1PN,OxB4iCqB;;;AwBziCvB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;E3BqPE,WALI;E2B7ON,OxB+hCqB;EwB9hCrB,kBxB8hCqB;EEzjCrB;;;AsBgCA;AAAA;AAAA;AAAA;EAEE;;;AA/CF;EAqDE,cxBihCmB;EwB9gCjB,exBw1BgC;EwBv1BhC;EACA;EACA;EACA;;AAGF;EACE,cxBsgCiB;EwBrgCjB,YxBqgCiB;;;AwBtkCrB;EA0EI,exBs0BgC;EwBr0BhC;;;AA3EJ;EAkFE,cxBo/BmB;;AwBj/BjB;EAEE;EACA,exBo5B8B;EwBn5B9B;EACA;;AAIJ;EACE,cxBu+BiB;EwBt+BjB,YxBs+BiB;;;AwBtkCrB;EAwGI;;;AAxGJ;EA+GE,cxBu9BmB;;AwBr9BnB;EACE,kBxBo9BiB;;AwBj9BnB;EACE,YxBg9BiB;;AwB78BnB;EACE,OxB48BiB;;;AwBv8BrB;EACE;;;AAhIF;AAAA;AAAA;AAAA;AAAA;EA0IM;;;AAtHR;EACE;EACA;EACA,YxBi0BoC;EH/jBlC,WALI;E2B1PN,OxB4iCqB;;;AwBziCvB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;E3BqPE,WALI;E2B7ON,OxB+hCqB;EwB9hCrB,kBxB8hCqB;EEzjCrB;;;AsBgCA;AAAA;AAAA;AAAA;EAEE;;;AA/CF;EAqDE,cxBihCmB;EwB9gCjB,exBw1BgC;EwBv1BhC;EACA;EACA;EACA;;AAGF;EACE,cxBsgCiB;EwBrgCjB,YxBqgCiB;;;AwBtkCrB;EA0EI,exBs0BgC;EwBr0BhC;;;AA3EJ;EAkFE,cxBo/BmB;;AwBj/BjB;EAEE;EACA,exBo5B8B;EwBn5B9B;EACA;;AAIJ;EACE,cxBu+BiB;EwBt+BjB,YxBs+BiB;;;AwBtkCrB;EAwGI;;;AAxGJ;EA+GE,cxBu9BmB;;AwBr9BnB;EACE,kBxBo9BiB;;AwBj9BnB;EACE,YxBg9BiB;;AwB78BnB;EACE,OxB48BiB;;;AwBv8BrB;EACE;;;AAhIF;AAAA;AAAA;AAAA;AAAA;EA4IM;;;AC9IV;EAEE;EACA;EACA;E5BuRI,oBALI;E4BhRR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;E5BsQI,WALI;E4B/PR;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EvBjBE;EgBfF,kBOkCqB;ERtBjB,YQwBJ;;ARpBI;EQhBN;IRiBQ;;;AQqBN;EACE;EAEA;EACA;;AAGF;EAEE;EACA;EACA;;AAGF;EACE;EPrDF,kBOsDuB;EACrB;EACA;EAKE;;AAIJ;EACE;EACA;EAKE;;AAIJ;EAKE;EACA;EAGA;;AAGA;EAKI;;AAKN;EAGE;EACA;EACA;EAEA;EACA;;;AAYF;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADyFA;ECtGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADmHA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AD0FA;ECvGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ADsGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,iBxBxJgB;;AwBkKhB;EACE;;AAGF;EACE;;;AAWJ;ECxIE;EACA;E7B8NI,oBALI;E6BvNR;;;ADyIF;EC5IE;EACA;E7B8NI,oBALI;E6BvNR;;;ACnEF;EVgBM,YUfJ;;AVmBI;EUpBN;IVqBQ;;;AUlBN;EACE;;;AAMF;EACE;;;AAIJ;EACE;EACA;EVDI,YUEJ;;AVEI;EULN;IVMQ;;;AUDN;EACE;EACA;EVNE,YUOF;;AVHE;EUAJ;IVCM;;;;AWpBR;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;;AAGF;EACE;;ACwBE;EACE;EACA,a7B6hBwB;E6B5hBxB,gB7B2hBwB;E6B1hBxB;EArCJ;EACA;EACA;EACA;;AA0DE;EACE;;;AD9CN;EAEE;EACA;EACA;EACA;EACA;E/BuQI,yBALI;E+BhQR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;E/B0OI,WALI;E+BnOR;EACA;EACA;EACA;EACA;EACA;E1BzCE;;A0B6CF;EACE;EACA;EACA;;;AAwBA;EACE;;AAEA;EACE;EACA;;;AAIJ;EACE;;AAEA;EACE;EACA;;;AnB1CJ;EmB4BA;IACE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;AnB1CJ;EmB4BA;IACE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;AnB1CJ;EmB4BA;IACE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;AnB1CJ;EmB4BA;IACE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;AnB1CJ;EmB4BA;IACE;;EAEA;IACE;IACA;;EAIJ;IACE;;EAEA;IACE;IACA;;;AAUN;EACE;EACA;EACA;EACA;;ACpFA;EACE;EACA,a7B6hBwB;E6B5hBxB,gB7B2hBwB;E6B1hBxB;EA9BJ;EACA;EACA;EACA;;AAmDE;EACE;;;ADgEJ;EACE;EACA;EACA;EACA;EACA;;AClGA;EACE;EACA,a7B6hBwB;E6B5hBxB,gB7B2hBwB;E6B1hBxB;EAvBJ;EACA;EACA;EACA;;AA4CE;EACE;;AD0EF;EACE;;;AAMJ;EACE;EACA;EACA;EACA;EACA;;ACnHA;EACE;EACA,a7B6hBwB;E6B5hBxB,gB7B2hBwB;E6B1hBxB;;AAWA;EACE;;AAGF;EACE;EACA,c7B0gBsB;E6BzgBtB,gB7BwgBsB;E6BvgBtB;EAnCN;EACA;EACA;;AAsCE;EACE;;AD2FF;EACE;;;AAON;EACE;EACA;EACA;EACA;EACA;;;AAMF;EACE;EACA;EACA;EACA;EACA,a5Byb4B;E4Bxb5B;EACA;EAEA;EACA;EACA;E1BtKE;;A0ByKF;EAEE;EV1LF,kBU4LuB;;AAGvB;EAEE;EACA;EVlMF,kBUmMuB;;AAGvB;EAEE;EACA;EACA;;;AAMJ;EACE;;;AAIF;EACE;EACA;EACA;E/BmEI,WALI;E+B5DR;EACA;;;AAIF;EACE;EACA;EACA;;;AAIF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AEtPF;AAAA;EAEE;EACA;EACA;;AAEA;AAAA;EACE;EACA;;AAKF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;;AAKJ;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;E5BhBI;;A4BoBF;AAAA;EAEE;;AAIF;AAAA;AAAA;E5BVE;EACA;;A4BmBF;AAAA;AAAA;E5BNE;EACA;;;A4BwBJ;EACE;EACA;;AAEA;EAGE;;AAGF;EACE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAoBF;EACE;EACA;EACA;;AAEA;AAAA;EAEE;;AAGF;AAAA;EAEE;;AAIF;AAAA;E5B1FE;EACA;;A4B8FF;AAAA;E5B7GE;EACA;;;A6BxBJ;EAEE;EACA;EAEA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;ElCsQI,WALI;EkC/PR;EACA;EAEA;EACA;EdfI,YcgBJ;;AdZI;EcGN;IdFQ;;;AcaN;EAEE;;AAIF;EACE;EACA,Y/BkhBoB;;A+B9gBtB;EACE;EACA;EACA;;;AAQJ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;;AAEA;EACE;EACA;E7B5CA;EACA;;A6B8CA;EAGE;EACA;;AAGF;EAEE;EACA;EACA;;AAIJ;AAAA;EAEE;EACA;EACA;;AAGF;EAEE;E7BvEA;EACA;;;A6BiFJ;EAEE;EACA;EACA;;AAGA;E7BlGE;;A6BqGA;EACE;EACA;EACA;;AAIJ;AAAA;EAEE;Eb7HF,kBa8HuB;;;AASzB;EAEE;EACA;EACA;EAGA;;AAEA;EACE;EACA;EACA;;AAEA;EAEE;;AAIJ;AAAA;EAEE,a/B8c0B;E+B7c1B;EACA;;;AAUF;AAAA;EAEE;EACA;;;AAKF;AAAA;EAEE;EACA;EACA;;;AAMF;AAAA;EACE;;;AAUF;EACE;;AAEF;EACE;;;ACzMJ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AAMA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;EACA;EACA;EACA;;AAoBJ;EACE;EACA;EACA;EnC4NI,WALI;EmCrNR;EAEA;;AAEA;EAEE;;;AAUJ;EAEE;EACA;EAEA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;;AAGE;EAEE;;AAIJ;EACE;;;AASJ;EACE,ahCwgCkC;EgCvgClC,gBhCugCkC;EgCtgClC;;AAEA;AAAA;AAAA;EAGE;;;AAaJ;EACE;EACA;EAGA;;;AAIF;EACE;EnCyII,WALI;EmClIR;EACA;EACA;EACA;E9BxIE;EeHE,Ye6IJ;;AfzII;EeiIN;IfhIQ;;;Ae0IN;EACE;;AAGF;EACE;EACA;EACA;;;AAMJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AvB1HE;EuBsIA;IAEI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE;IACA;;EAIJ;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;If9NJ,YegOI;;EAGA;IACE;;EAGF;IACE;IACA;IACA;IACA;;;AvB5LR;EuBsIA;IAEI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE;IACA;;EAIJ;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;If9NJ,YegOI;;EAGA;IACE;;EAGF;IACE;IACA;IACA;IACA;;;AvB5LR;EuBsIA;IAEI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE;IACA;;EAIJ;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;If9NJ,YegOI;;EAGA;IACE;;EAGF;IACE;IACA;IACA;IACA;;;AvB5LR;EuBsIA;IAEI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE;IACA;;EAIJ;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;If9NJ,YegOI;;EAGA;IACE;;EAGF;IACE;IACA;IACA;IACA;;;AvB5LR;EuBsIA;IAEI;IACA;;EAEA;IACE;;EAEA;IACE;;EAGF;IACE;IACA;;EAIJ;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IAEE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;If9NJ,YegOI;;EAGA;IACE;;EAGF;IACE;IACA;IACA;IACA;;;AAtDR;EAEI;EACA;;AAEA;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;Ef9NJ,YegOI;;AAGA;EACE;;AAGF;EACE;EACA;EACA;EACA;;;AAiBZ;AAAA;EAGE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAME;EACE;;;ACzRN;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;E/BjBE;;A+BqBF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;E/BtBF;EACA;;A+ByBA;EACE;E/BbF;EACA;;A+BmBF;AAAA;EAEE;;;AAIJ;EAGE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAQA;EACE;;;AAQJ;EACE;EACA;EACA;EACA;EACA;;AAEA;E/B7FE;;;A+BkGJ;EACE;EACA;EACA;EACA;;AAEA;E/BxGE;;;A+BkHJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;E/B1IE;;;A+B8IJ;AAAA;AAAA;EAGE;;;AAGF;AAAA;E/B3II;EACA;;;A+B+IJ;AAAA;E/BlII;EACA;;;A+B8IF;EACE;;AxB3HA;EwBuHJ;IAQI;IACA;;EAGA;IAEE;IACA;;EAEA;IACE;IACA;;EAKA;I/B3KJ;IACA;;E+B6KM;AAAA;IAGE;;EAEF;AAAA;IAGE;;EAIJ;I/B5KJ;IACA;;E+B8KM;AAAA;IAGE;;EAEF;AAAA;IAGE;;;;ACpOZ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIF;EACE;EACA;EACA;EACA;EACA;ErC2PI,WALI;EqCpPR;EACA;EACA;EACA;EhCtBE;EgCwBF;EjB3BI,YiB4BJ;;AjBxBI;EiBWN;IjBVQ;;;AiByBN;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EjBlDE,YiBmDF;;AjB/CE;EiBsCJ;IjBrCM;;;AiBiDN;EACE;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EhC/DE;EACA;;AgCiEA;EhClEA;EACA;;AgCsEF;EACE;;AAIF;EhC9DE;EACA;;AgCiEE;EhClEF;EACA;;AgCsEA;EhCvEA;EACA;;;AgC4EJ;EACE;;;AASA;EACE;;AAGF;EACE;EACA;EhCpHA;;AgCuHA;EAAgB;;AAChB;EAAe;;AAGb;EhC3HF;;;AgCqIA;EACE;EACA;;;AC1JN;EAEE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EtC+QI,WALI;EsCxQR;EACA;EjCAE;;;AiCMF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;;;ACrCJ;EAEE;EACA;EvC4RI,2BALI;EuCrRR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EhCpBA;EACA;;;AgCuBF;EACE;EACA;EACA;EvCgQI,WALI;EuCzPR;EAEA;EACA;EnBpBI,YmBqBJ;;AnBjBI;EmBQN;InBPQ;;;AmBkBN;EACE;EACA;EAEA;EACA;;AAGF;EACE;EACA;EACA;EACA,SpCouCgC;EoCnuChC;;AAGF;EAEE;EACA;ElBtDF,kBkBuDuB;EACrB;;AAGF;EAEE;EACA;EACA;EACA;;;AAKF;EACE,apCusCgC;;AoClsC9B;ElC9BF;EACA;;AkCmCE;ElClDF;EACA;;;AkCkEJ;EClGE;EACA;ExC0RI,2BALI;EwCnRR;;;ADmGF;ECtGE;EACA;ExC0RI,2BALI;EwCnRR;;;ACFF;EAEE;EACA;EzCuRI,sBALI;EyChRR;EACA;EACA;EAGA;EACA;EzC+QI,WALI;EyCxQR;EACA;EACA;EACA;EACA;EACA;EpCJE;;AoCSF;EACE;;;AAKJ;EACE;EACA;;;AChCF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;ErCHE;;;AqCQJ;EAEE;;;AAIF;EACE,avC6kB4B;EuC5kB5B;;;AAQF;EACE,evCg+C8B;;AuC79C9B;EACE;EACA;EACA;EACA;EACA;;;AAQF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AAJF;EACE;EACA;EACA;EACA;;;AC5DF;EACE;IAAK,uBxCmhD2B;;;AwC9gDpC;AAAA;EAGE;E3CkRI,yBALI;E2C3QR;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;E3CsQI,WALI;E2C/PR;EtCRE;;;AsCaJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EvBxBI,YuByBJ;;AvBrBI;EuBYN;IvBXQ;;;;AuBuBR;EtBAE;EsBEA;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;;AAGE;EAJJ;IAKM;;;;AC3DR;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EAGA;EACA;EvCXE;;;AuCeJ;EACE;EACA;;AAEA;EAEE;EACA;;;AASJ;EACE;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACA;;AAGF;EACE;EACA;;;AAQJ;EACE;EACA;EACA;EACA;EAEA;EACA;;AAEA;EvCvDE;EACA;;AuC0DF;EvC7CE;EACA;;AuCgDF;EAEE;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAIF;EACE;;AAEA;EACE;EACA;;;AAaF;EACE;;AAGE;EvCvDJ;EAZA;;AuCwEI;EvCxEJ;EAYA;;AuCiEI;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AhCtFR;EgC8DA;IACE;;EAGE;IvCvDJ;IAZA;;EuCwEI;IvCxEJ;IAYA;;EuCiEI;IACE;;EAGF;IACE;IACA;;EAEA;IACE;IACA;;;AhCtFR;EgC8DA;IACE;;EAGE;IvCvDJ;IAZA;;EuCwEI;IvCxEJ;IAYA;;EuCiEI;IACE;;EAGF;IACE;IACA;;EAEA;IACE;IACA;;;AhCtFR;EgC8DA;IACE;;EAGE;IvCvDJ;IAZA;;EuCwEI;IvCxEJ;IAYA;;EuCiEI;IACE;;EAGF;IACE;IACA;;EAEA;IACE;IACA;;;AhCtFR;EgC8DA;IACE;;EAGE;IvCvDJ;IAZA;;EuCwEI;IvCxEJ;IAYA;;EuCiEI;IACE;;EAGF;IACE;IACA;;EAEA;IACE;IACA;;;AhCtFR;EgC8DA;IACE;;EAGE;IvCvDJ;IAZA;;EuCwEI;IvCxEJ;IAYA;;EuCiEI;IACE;;EAGF;IACE;IACA;;EAEA;IACE;IACA;;;AAcZ;EvChJI;;AuCmJF;EACE;;AAEA;EACE;;;AAaJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAVF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AC5LJ;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA,O1C+oD2B;E0C9oD3B,Q1C8oD2B;E0C7oD3B;EACA;EACA;EACA;ExCJE;EwCMF;;AAGA;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EAEE;EACA;EACA;;;AAQJ;EAHE;;;AASE;EATF;;;ACjDF;EAEE;EACA;EACA;EACA;EACA;E9CyRI,sBALI;E8ClRR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;E9C2QI,WALI;E8CpQR;EACA;EACA;EACA;EACA;EACA;EzCRE;;AyCWF;EACE;;AAGF;EACE;;;AAIJ;EACE;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EzChCE;EACA;;AyCkCF;EACE;EACA;;;AAIJ;EACE;EACA;;;AC9DF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;;;AAOF;EACE;EACA;EACA;EAEA;;AAGA;E3B5CI,Y2B6CF;EACA,W5Cy7CgC;;AiBn+C9B;E2BwCJ;I3BvCM;;;A2B2CN;EACE,W5Cu7CgC;;A4Cn7ClC;EACE,W5Co7CgC;;;A4Ch7CpC;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;;AAIF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;E1CrFE;E0CyFF;;;AAIF;EAEE;EACA;EACA;EClHA;EACA;EACA;EACA,SDkH0B;ECjH1B;EACA;EACA,kBD+G4D;;AC5G5D;EAAS;;AACT;EAAS,SD2GiF;;;AAK5F;EACE;EACA;EACA;EACA;EACA;EACA;E1CtGE;EACA;;A0CwGF;EACE;EACA;;;AAKJ;EACE;EACA;;;AAKF;EACE;EAGA;EACA;;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;E1C1HE;EACA;;A0C+HF;EACE;;;AnC5GA;EmCkHF;IACE;IACA;;EAIF;IACE;IACA;IACA;;EAGF;IACE;;;AnC/HA;EmCoIF;AAAA;IAEE;;;AnCtIA;EmC2IF;IACE;;;AAUA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;E1C1MJ;;A0C8ME;AAAA;E1C9MF;;A0CmNE;EACE;;;AnC3JJ;EmCyIA;IACE;IACA;IACA;IACA;;EAEA;IACE;IACA;I1C1MJ;;E0C8ME;AAAA;I1C9MF;;E0CmNE;IACE;;;AnC3JJ;EmCyIA;IACE;IACA;IACA;IACA;;EAEA;IACE;IACA;I1C1MJ;;E0C8ME;AAAA;I1C9MF;;E0CmNE;IACE;;;AnC3JJ;EmCyIA;IACE;IACA;IACA;IACA;;EAEA;IACE;IACA;I1C1MJ;;E0C8ME;AAAA;I1C9MF;;E0CmNE;IACE;;;AnC3JJ;EmCyIA;IACE;IACA;IACA;IACA;;EAEA;IACE;IACA;I1C1MJ;;E0C8ME;AAAA;I1C9MF;;E0CmNE;IACE;;;AnC3JJ;EmCyIA;IACE;IACA;IACA;IACA;;EAEA;IACE;IACA;I1C1MJ;;E0C8ME;AAAA;I1C9MF;;E0CmNE;IACE;;;AEtOR;EAEE;EACA;EACA;EACA;EACA;EjDwRI,wBALI;EiDjRR;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EClBA,a/C+lB4B;E+C7lB5B;EACA,a/CwmB4B;E+CvmB5B,a/C+mB4B;E+C9mB5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;ElDgRI,WALI;EiDhQR;EACA;;AAEA;EAAS;;AAET;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;;AAKN;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;AACA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;AAEA;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;AACA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;AAkBA;EACE;EACA;EACA;EACA;EACA;E5CjGE;;;A8CnBJ;EAEE;EACA;EnD4RI,wBALI;EmDrRR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EnDmRI,+BALI;EmD5QR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EDzBA,a/C+lB4B;E+C7lB5B;EACA,a/CwmB4B;E+CvmB5B,a/C+mB4B;E+C9mB5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;ElDgRI,WALI;EmD1PR;EACA;EACA;EACA;E9ChBE;;A8CoBF;EACE;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAMJ;EACE;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AAEE;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AAGE;EACE;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;AAEE;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKN;AAkBA;EACE;EACA;EnD2GI,WALI;EmDpGR;EACA;EACA;E9C5JE;EACA;;A8C8JF;EACE;;;AAIJ;EACE;EACA;;;ACrLF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;ACtBA;EACE;EACA;EACA;;;ADuBJ;EACE;EACA;EACA;EACA;EACA;EACA;EhClBI,YgCmBJ;;AhCfI;EgCQN;IhCPQ;;;;AgCiBR;AAAA;AAAA;EAGE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AASA;EACE;EACA;EACA;;AAGF;AAAA;AAAA;EAGE;EACA;;AAGF;AAAA;EAEE;EACA;EhC5DE,YgC6DF;;AhCzDE;EgCqDJ;AAAA;IhCpDM;;;;AgCiER;AAAA;EAEE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA,OjD4gDmC;EiD3gDnC;EACA,OjD1FS;EiD2FT;EACA;EACA;EACA,SjDugDmC;EiB7lD/B,YgCuFJ;;AhCnFI;EgCkEN;AAAA;IhCjEQ;;;AgCqFN;AAAA;AAAA;EAEE,OjDpGO;EiDqGP;EACA;EACA,SjD+/CiC;;;AiD5/CrC;EACE;;;AAGF;EACE;;;AAKF;AAAA;EAEE;EACA,OjDggDmC;EiD//CnC,QjD+/CmC;EiD9/CnC;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;EACE;;;AAEF;EACE;;;AAQF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,cjDw8CmC;EiDv8CnC;EACA,ajDs8CmC;;AiDp8CnC;EACE;EACA;EACA,OjDs8CiC;EiDr8CjC,QjDs8CiC;EiDr8CjC;EACA,cjDs8CiC;EiDr8CjC,ajDq8CiC;EiDp8CjC;EACA;EACA,kBjD1KO;EiD2KP;EACA;EAEA;EACA;EACA,SjD67CiC;EiBrmD/B,YgCyKF;;AhCrKE;EgCoJJ;IhCnJM;;;AgCuKN;EACE,SjD07CiC;;;AiDj7CrC;EACE;EACA;EACA,QjDo7CmC;EiDn7CnC;EACA,ajDi7CmC;EiDh7CnC,gBjDg7CmC;EiD/6CnC,OjDrMS;EiDsMT;;;AAMA;AAAA;EAEE,QjDq7CiC;;AiDl7CnC;EACE,kBjDxMO;;AiD2MT;EACE,OjD5MO;;;AiDkMT;AAAA;AAAA;EAEE,QjDq7CiC;;AiDl7CnC;EACE,kBjDxMO;;AiD2MT;EACE,OjD5MO;;;AmDdX;AAAA;EAEE;EACA;EACA;EACA;EAEA;EACA;;;AAIF;EACE;IAAK;;;AAIP;EAEE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;;;AAGF;EAEE;EACA;EACA;;;AASF;EACE;IACE;;EAEF;IACE;IACA;;;AAKJ;EAEE;EACA;EACA;EACA;EACA;EAGA;EACA;;;AAGF;EACE;EACA;;;AAIA;EACE;AAAA;IAEE;;;AC/EN;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;A3C6DE;E2C5CF;IAEI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;InC5BA,YmC8BA;;;AnC1BA;EmCYJ;InCXM;;;ARuDJ;E2C5BE;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;;EAGF;IAEE;;EAGF;IAGE;;;A3C5BJ;E2C/BF;IAiEM;IACA;IACA;;EAEA;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;;;;A3CnCN;E2C5CF;IAEI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;InC5BA,YmC8BA;;;AnC1BA;EmCYJ;InCXM;;;ARuDJ;E2C5BE;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;;EAGF;IAEE;;EAGF;IAGE;;;A3C5BJ;E2C/BF;IAiEM;IACA;IACA;;EAEA;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;;;;A3CnCN;E2C5CF;IAEI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;InC5BA,YmC8BA;;;AnC1BA;EmCYJ;InCXM;;;ARuDJ;E2C5BE;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;;EAGF;IAEE;;EAGF;IAGE;;;A3C5BJ;E2C/BF;IAiEM;IACA;IACA;;EAEA;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;;;;A3CnCN;E2C5CF;IAEI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;InC5BA,YmC8BA;;;AnC1BA;EmCYJ;InCXM;;;ARuDJ;E2C5BE;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;;EAGF;IAEE;;EAGF;IAGE;;;A3C5BJ;E2C/BF;IAiEM;IACA;IACA;;EAEA;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;;;;A3CnCN;E2C5CF;IAEI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;InC5BA,YmC8BA;;;AnC1BA;EmCYJ;InCXM;;;ARuDJ;E2C5BE;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;;EAGF;IAEE;;EAGF;IAGE;;;A3C5BJ;E2C/BF;IAiEM;IACA;IACA;;EAEA;IACE;;EAGF;IACE;IACA;IACA;IACA;IAEA;;;;AA/ER;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EnC5BA,YmC8BA;;AnC1BA;EmCYJ;InCXM;;;AmC2BF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;;AAGF;EAGE;;;AA2BR;EPpHE;EACA;EACA;EACA,S7ComCkC;E6CnmClC;EACA;EACA,kB7CUS;;A6CPT;EAAS;;AACT;EAAS,S7C09CyB;;;AoD52CpC;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AChJF;EACE;EACA;EACA;EACA;EACA;EACA,SrDyyCkC;;AqDvyClC;EACE;EACA;;;AAKJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAKA;EACE;;;AAIJ;EACE;IACE,SrD4wCgC;;;AqDxwCpC;EACE;EACA;EACA;;;AAGF;EACE;IACE;;;AH9CF;EACE;EACA;EACA;;;AIFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;AAFF;EACE;EACA;;;ACHF;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AATN;EACE;EACA;;AAGE;EAGE;EACA;;;AAOR;EACE;EACA;;AAGE;EAEE;EACA;;;AC1BN;EACE;EAEA;;;ACHF;EACE;EACA,KzD6c4B;EyD5c5B;EACA;EACA,uBzD2c4B;EyD1c5B;;AAEA;EACE;EACA,OzDuc0B;EyDtc1B,QzDsc0B;EyDrc1B;ExCIE,YwCHF;;AxCOE;EwCZJ;IxCaM;;;;AwCDJ;EACE;;;ACnBN;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAKF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;ACrBJ;EACE;EACA;EACA;EACA;EACA,S3DimCkC;;;A2D9lCpC;EACE;EACA;EACA;EACA;EACA,S3DylCkC;;;A2DjlChC;EACE;EACA;EACA,S3D6kC8B;;;A2D1kChC;EACE;EACA;EACA,S3DukC8B;;;ASxiChC;EkDxCA;IACE;IACA;IACA,S3D6kC8B;;E2D1kChC;IACE;IACA;IACA,S3DukC8B;;;ASxiChC;EkDxCA;IACE;IACA;IACA,S3D6kC8B;;E2D1kChC;IACE;IACA;IACA,S3DukC8B;;;ASxiChC;EkDxCA;IACE;IACA;IACA,S3D6kC8B;;E2D1kChC;IACE;IACA;IACA,S3DukC8B;;;ASxiChC;EkDxCA;IACE;IACA;IACA,S3D6kC8B;;E2D1kChC;IACE;IACA;IACA,S3DukC8B;;;ASxiChC;EkDxCA;IACE;IACA;IACA,S3D6kC8B;;E2D1kChC;IACE;IACA;IACA,S3DukC8B;;;A4DtmCpC;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;ACRF;AAAA;ECIE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;AAAA;EACE;;;ACdF;EACE;EACA;EACA;EACA;EACA;EACA,S/DgcsC;E+D/btC;;;ACRJ;ECAE;EACA;EACA;;;ACNF;EACE;EACA;EACA;EACA;EACA;EACA,SlE2rB4B;;;AmE/nBtB;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAjBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AASF;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAjBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AASF;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AArBJ;AAcA;EAOI;EAAA;;;AAmBJ;AA1BA;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAjBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AASF;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAjBJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AAIJ;EAOI;;;AAKF;EAOI;;;AAnBN;EAOI;;;AAKF;EAOI;;;AAnBN;EAOI;;;AAKF;EAOI;;;AAnBN;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAjBJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AANJ;EACE;;;AAIA;EACE;;;AAIJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAPJ;EAIQ;EAGJ;;;AAjBJ;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AADF;EACE;;;AASF;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;EAAA;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;AAPJ;EAOI;;;A1DVR;E0DGI;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;A1DVR;E0DGI;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;A1DVR;E0DGI;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;A1DVR;E0DGI;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;A1DVR;E0DGI;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;IAAA;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;ACtDZ;ED+CQ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;ACnCZ;ED4BQ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;EAPJ;IAOI;;;AEzEZ;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAgBA;;AAAA;;AAAA;ACmCA;AAAA;AAAA;AAAA;AAAA;EAKE,OtEhDS;;AsEiDT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;;;AASF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAEE;;;AC3DF;EACE;IACE,uBAbY;;;ACUlB;EACE;IACE;;;AAGJ;EACE;IACE;;EAEF;IACE;IACA;;;AHuDJ;;AAAA;;AAAA;AI5EA;AAAA;AAAA;AAkBA;AAAA;AAAA;AJsEA;;AAAA;;AAAA;AA2BA;;AAAA;;AAAA;AAQA;;AAAA;;AAAA;AK3HA;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAGF;IACE;;;ALsEJ;;AAAA;;AAAA","file":"app.css"}
\ No newline at end of file
diff --git a/src/assets/css/scrollStyles.css b/src/assets/css/scrollStyles.css
new file mode 100644
index 0000000000..71248ac1b6
--- /dev/null
+++ b/src/assets/css/scrollStyles.css
@@ -0,0 +1,18 @@
+.customScroll {
+ overflow-y: scroll;
+}
+.customScroll::-webkit-scrollbar {
+ width: 5px;
+}
+.customScroll::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 6px;
+}
+.customScroll::-webkit-scrollbar-thumb {
+ background: var(--bs-gray-500);
+ border-radius: 6px;
+}
+.customScroll::-webkit-scrollbar-thumb:hover {
+ background: var(--bs-gray-600);
+ border-radius: 6px;
+}
diff --git a/src/assets/images/blank.png b/src/assets/images/blank.png
new file mode 100644
index 0000000000..ae6f25f049
Binary files /dev/null and b/src/assets/images/blank.png differ
diff --git a/src/assets/images/bronze.png b/src/assets/images/bronze.png
new file mode 100644
index 0000000000..e5b748a572
Binary files /dev/null and b/src/assets/images/bronze.png differ
diff --git a/src/assets/images/defaultImg.png b/src/assets/images/defaultImg.png
new file mode 100644
index 0000000000..310a79c130
Binary files /dev/null and b/src/assets/images/defaultImg.png differ
diff --git a/src/assets/images/gold.png b/src/assets/images/gold.png
new file mode 100644
index 0000000000..cdbb0efdc4
Binary files /dev/null and b/src/assets/images/gold.png differ
diff --git a/src/assets/images/palisadoes_logo.png b/src/assets/images/palisadoes_logo.png
new file mode 100644
index 0000000000..2e91c02233
Binary files /dev/null and b/src/assets/images/palisadoes_logo.png differ
diff --git a/src/assets/images/silver.png b/src/assets/images/silver.png
new file mode 100644
index 0000000000..b13d604de5
Binary files /dev/null and b/src/assets/images/silver.png differ
diff --git a/src/assets/images/talawa-logo-600x600.png b/src/assets/images/talawa-logo-600x600.png
new file mode 100644
index 0000000000..9498132600
Binary files /dev/null and b/src/assets/images/talawa-logo-600x600.png differ
diff --git a/src/assets/talawa-logo-dark-200x200.png b/src/assets/images/talawa-logo-dark-200x200.png
similarity index 100%
rename from src/assets/talawa-logo-dark-200x200.png
rename to src/assets/images/talawa-logo-dark-200x200.png
diff --git a/src/assets/images/talawa-logo-lite-200x200.png b/src/assets/images/talawa-logo-lite-200x200.png
new file mode 100644
index 0000000000..a8d0a8fa7b
Binary files /dev/null and b/src/assets/images/talawa-logo-lite-200x200.png differ
diff --git a/src/App.css b/src/assets/scss/_colors.scss
similarity index 100%
rename from src/App.css
rename to src/assets/scss/_colors.scss
diff --git a/src/assets/scss/_general.scss b/src/assets/scss/_general.scss
new file mode 100644
index 0000000000..f2309504ce
--- /dev/null
+++ b/src/assets/scss/_general.scss
@@ -0,0 +1,63 @@
+:root {
+ --bs-body-font-family: Arial, Helvetica, sans-serif;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ overflow-x: hidden;
+}
+
+body {
+ background-color: var(--bs-body-bg);
+}
+
+#root {
+ min-height: 100vh;
+ background-color: #f2f7ff;
+}
+
+input[type='checkbox'] {
+ transform: scale(1.5);
+}
+.form-switch {
+ padding-left: 3rem;
+}
+input[type='file']::file-selector-button {
+ background: var(--bs-gray-400);
+}
+
+.shimmer {
+ animation-duration: 2.2s;
+ animation-fill-mode: forwards;
+ animation-iteration-count: infinite;
+ animation-name: shimmer;
+ animation-timing-function: linear;
+ background: var(--bs-gray-200);
+ background: linear-gradient(to right, #f6f6f6 8%, #f0f0f0 18%, #f6f6f6 33%);
+ background-size: 1200px 100%;
+}
+
+@-webkit-keyframes shimmer {
+ 0% {
+ background-position: -100% 0;
+ }
+
+ 100% {
+ background-position: 100% 0;
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -1200px 0;
+ }
+
+ 100% {
+ background-position: 1200px 0;
+ }
+}
diff --git a/src/assets/scss/_talawa.scss b/src/assets/scss/_talawa.scss
new file mode 100644
index 0000000000..2537c2c682
--- /dev/null
+++ b/src/assets/scss/_talawa.scss
@@ -0,0 +1,136 @@
+/*
+ TALAWA SCSS
+ -----------
+ This file is used to import all partial scss files in the project.
+ It is used to compile the final CSS file to the CSS folder as main.css .
+
+========= Table of Contents =========
+1. Components
+2. Content
+3. Forms
+4. Utilities
+5. General
+6. Colors
+
+*/
+
+/*
+
+ 1. COMPONENTS
+
+*/
+
+// 1.1. Accordion
+@import './components/accordion';
+
+// 1.2. Alert
+@import './components/alert';
+
+// 1.3. Badge
+@import './components/badge';
+
+// 1.4. Breadcrumb
+@import './components/breadcrumb';
+
+// 1.5. Button
+@import './components/buttons';
+
+// 1.6. Card
+@import './components/card';
+
+// 1.7. Carousel
+@import './components/carousel';
+
+// 1.8. Close
+@import './components/close';
+
+// 1.9. Dropdown
+@import './components/dropdown';
+
+// 1.10. List Group
+@import './components/list-group';
+
+// 1.11. Modal
+@import './components/modal';
+
+// 1.12. Navbar
+@import './components/navbar';
+
+// 1.13. Nav and Nav Tabs
+@import './components/nav';
+
+// 1.14 Offcanvas
+@import './components/offcanvas';
+
+// 1.15 Pagination
+@import './components/pagination';
+
+// 1.16 Placeholder
+@import './components/placeholder';
+
+// 1.17 Progress
+@import './components/progress';
+
+// 1.18 Spinners
+@import './components/spinners';
+
+/*
+
+ 2. CONTENT
+
+*/
+
+// 2.1. Table
+@import './content/table';
+
+// 2.2. Typography
+@import './content/typography';
+
+/*
+
+ 3. FORMS
+
+*/
+
+// 3.1. Checkbox & Radio
+@import './forms/check-radios';
+
+// 3.2. Floating Labels
+@import './forms/floating-label';
+
+// 3.3. Form Controls
+@import './forms/form-control';
+
+// 3.4. Input Group
+@import './forms/input-group';
+
+// 3.5. Range
+@import './forms/range';
+
+// 3.6. Select
+@import './forms/select';
+
+// 3.7. Validation
+@import './forms/validation';
+
+/*
+
+ 4. UTILITIES
+
+*/
+
+@import './utilities';
+
+/*
+
+ 5. General
+
+*/
+@import './general';
+
+/*
+
+ 6. COLORS
+
+*/
+@import './colors';
diff --git a/src/assets/scss/_utilities.scss b/src/assets/scss/_utilities.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss
new file mode 100644
index 0000000000..2871cb9df2
--- /dev/null
+++ b/src/assets/scss/_variables.scss
@@ -0,0 +1,30 @@
+// Colors
+
+$primary: #31bb6b;
+$secondary: #707070;
+$success: #31bb6b;
+$warning: #febc59;
+
+$blue: #0d6efd;
+$indigo: #6610f2;
+$purple: #6f42c1;
+$pink: #d63384;
+$red: #dc3545;
+$orange: #fd7e14;
+$yellow: #ffc107;
+$green: #198754;
+$teal: #20c997;
+$cyan: #0dcaf0;
+$placeholder-bg: #f2f2f2;
+// Colors
+
+// Links
+$link-color: $blue !default;
+$link-decoration: none !default;
+
+// Inputs and buttons
+$input-bg: $placeholder-bg;
+$input-border-width: 0;
+
+$input-btn-padding-y: 0.7rem;
+$input-btn-padding-x: 1rem;
diff --git a/src/assets/scss/app.scss b/src/assets/scss/app.scss
new file mode 100644
index 0000000000..8685a742db
--- /dev/null
+++ b/src/assets/scss/app.scss
@@ -0,0 +1,14 @@
+// Importing Bootstrap SCSS Functions and Mixins
+@import '../../../node_modules/bootstrap/scss/functions';
+@import '../../../node_modules/bootstrap/scss/mixins';
+
+// Importing Our Bootstrap SCSS Variables
+@import './variables';
+
+// Importing Bootstrap Variables and SCSS
+@import '../../../node_modules/bootstrap/scss/variables';
+@import '../../../node_modules/bootstrap/scss/variables-dark';
+@import '../../../node_modules/bootstrap/scss/bootstrap.scss';
+
+// Importing Our Bootstrap SCSS Overrides
+@import './talawa';
diff --git a/src/assets/scss/components/_accordion.scss b/src/assets/scss/components/_accordion.scss
new file mode 100644
index 0000000000..64eb323b55
--- /dev/null
+++ b/src/assets/scss/components/_accordion.scss
@@ -0,0 +1,36 @@
+$accordion-padding-y: 1.25rem;
+$accordion-padding-x: 1.5rem;
+$accordion-color: var(--#{$prefix}body-color);
+$accordion-bg: var(--#{$prefix}body-bg);
+$accordion-border-width: var(--#{$prefix}border-width);
+$accordion-border-color: var(--#{$prefix}border-color);
+$accordion-border-radius: var(--#{$prefix}border-radius);
+$accordion-inner-border-radius: subtract(
+ $accordion-border-radius,
+ $accordion-border-width
+);
+
+$accordion-body-padding-y: $accordion-padding-y;
+$accordion-body-padding-x: $accordion-padding-x;
+
+$accordion-button-padding-y: $accordion-padding-y;
+$accordion-button-padding-x: $accordion-padding-x;
+$accordion-button-color: var(--#{$prefix}body-color);
+$accordion-button-bg: var(--#{$prefix}accordion-bg);
+$accordion-transition:
+ $btn-transition,
+ border-radius 0.15s ease;
+$accordion-button-active-bg: var(--#{$prefix}primary-bg-subtle);
+$accordion-button-active-color: var(--#{$prefix}primary-text-emphasis);
+
+$accordion-button-focus-border-color: $input-focus-border-color;
+$accordion-button-focus-box-shadow: $btn-focus-box-shadow;
+
+$accordion-icon-width: 1.25rem;
+$accordion-icon-color: $body-color;
+$accordion-icon-active-color: $primary-text-emphasis;
+$accordion-icon-transition: transform 0.2s ease-in-out;
+$accordion-icon-transform: rotate(-180deg);
+
+$accordion-button-icon: url("data:image/svg+xml, ");
+$accordion-button-active-icon: url("data:image/svg+xml, ");
diff --git a/src/assets/scss/components/_alert.scss b/src/assets/scss/components/_alert.scss
new file mode 100644
index 0000000000..64f29bf3c0
--- /dev/null
+++ b/src/assets/scss/components/_alert.scss
@@ -0,0 +1,10 @@
+$alert-padding-y: $spacer;
+$alert-padding-x: $spacer;
+$alert-margin-bottom: 1rem;
+$alert-border-radius: var(--#{$prefix}border-radius);
+$alert-link-font-weight: $font-weight-bold;
+$alert-border-width: var(--#{$prefix}border-width);
+$alert-bg-scale: -80%;
+$alert-border-scale: -70%;
+$alert-color-scale: 40%;
+$alert-dismissible-padding-r: $alert-padding-x * 3; // 3x covers width of x plus default padding on either side
diff --git a/src/assets/scss/components/_badge.scss b/src/assets/scss/components/_badge.scss
new file mode 100644
index 0000000000..9b0c6a8a3a
--- /dev/null
+++ b/src/assets/scss/components/_badge.scss
@@ -0,0 +1,6 @@
+$badge-font-size: 0.75em;
+$badge-font-weight: $font-weight-bold;
+$badge-color: $white;
+$badge-padding-y: 0.35em;
+$badge-padding-x: 0.65em;
+$badge-border-radius: var(--#{$prefix}border-radius);
diff --git a/src/assets/scss/components/_breadcrumb.scss b/src/assets/scss/components/_breadcrumb.scss
new file mode 100644
index 0000000000..73fb6bd692
--- /dev/null
+++ b/src/assets/scss/components/_breadcrumb.scss
@@ -0,0 +1,11 @@
+$breadcrumb-font-size: null;
+$breadcrumb-padding-y: 0;
+$breadcrumb-padding-x: 0;
+$breadcrumb-item-padding-x: 0.5rem;
+$breadcrumb-margin-bottom: 1rem;
+$breadcrumb-bg: null;
+$breadcrumb-divider-color: var(--#{$prefix}secondary-color);
+$breadcrumb-active-color: var(--#{$prefix}secondary-color);
+$breadcrumb-divider: quote('/');
+$breadcrumb-divider-flipped: $breadcrumb-divider;
+$breadcrumb-border-radius: null;
diff --git a/src/assets/scss/components/_buttons.scss b/src/assets/scss/components/_buttons.scss
new file mode 100644
index 0000000000..c0400fbeae
--- /dev/null
+++ b/src/assets/scss/components/_buttons.scss
@@ -0,0 +1,73 @@
+$btn-color: $white;
+$btn-padding-y: $input-btn-padding-y;
+$btn-padding-x: $input-btn-padding-x;
+$btn-font-family: $input-btn-font-family;
+$btn-font-size: $input-btn-font-size;
+$btn-line-height: $input-btn-line-height;
+$btn-white-space: null; // Set to `nowrap` to prevent text wrapping
+
+$btn-padding-y-sm: $input-btn-padding-y-sm;
+$btn-padding-x-sm: $input-btn-padding-x-sm;
+$btn-font-size-sm: $input-btn-font-size-sm;
+
+$btn-padding-y-lg: $input-btn-padding-y-lg;
+$btn-padding-x-lg: $input-btn-padding-x-lg;
+$btn-font-size-lg: $input-btn-font-size-lg;
+
+$btn-border-width: $input-btn-border-width;
+
+$btn-font-weight: $font-weight-normal;
+$btn-box-shadow:
+ inset 0 1px 0 rgba($white, 0.15),
+ 0 1px 1px rgba($black, 0.075);
+$btn-focus-width: $input-btn-focus-width;
+$btn-focus-box-shadow: $input-btn-focus-box-shadow;
+$btn-disabled-opacity: 0.65;
+$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125);
+
+$btn-link-color: var(--#{$prefix}link-color);
+$btn-link-hover-color: var(--#{$prefix}link-hover-color);
+$btn-link-disabled-color: $gray-600;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius: var(--#{$prefix}border-radius);
+$btn-border-radius-sm: var(--#{$prefix}border-radius-sm);
+$btn-border-radius-lg: var(--#{$prefix}border-radius-lg);
+
+$btn-transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+
+$btn-hover-bg-shade-amount: 15%;
+$btn-hover-bg-tint-amount: 15%;
+$btn-hover-border-shade-amount: 20%;
+$btn-hover-border-tint-amount: 10%;
+$btn-active-bg-shade-amount: 20%;
+$btn-active-bg-tint-amount: 20%;
+$btn-active-border-shade-amount: 25%;
+$btn-active-border-tint-amount: 10%;
+
+.btn-primary,
+.btn-secondary,
+.btn-success,
+.btn-warning,
+.btn-info {
+ color: $white;
+ &:hover,
+ &:active {
+ color: $white !important;
+ }
+}
+
+.btn-outline-primary,
+.btn-outline-secondary,
+.btn-outline-success,
+.btn-outline-warning,
+.btn-outline-info {
+ &:hover,
+ &:active {
+ color: $white !important;
+ }
+}
diff --git a/src/assets/scss/components/_card.scss b/src/assets/scss/components/_card.scss
new file mode 100644
index 0000000000..5fa5abb4ee
--- /dev/null
+++ b/src/assets/scss/components/_card.scss
@@ -0,0 +1,19 @@
+$card-spacer-y: $spacer;
+$card-spacer-x: $spacer;
+$card-title-spacer-y: $spacer * 0.5;
+$card-title-color: null;
+$card-subtitle-color: null;
+$card-border-width: var(--#{$prefix}border-width);
+$card-border-color: var(--#{$prefix}border-color-translucent);
+$card-border-radius: var(--#{$prefix}border-radius);
+$card-box-shadow: null;
+$card-inner-border-radius: subtract($card-border-radius, $card-border-width);
+$card-cap-padding-y: $card-spacer-y * 0.5;
+$card-cap-padding-x: $card-spacer-x;
+$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), 0.03);
+$card-cap-color: null;
+$card-height: null;
+$card-color: null;
+$card-bg: var(--#{$prefix}body-bg);
+$card-img-overlay-padding: $spacer;
+$card-group-margin: $grid-gutter-width * 0.5;
diff --git a/src/assets/scss/components/_carousel.scss b/src/assets/scss/components/_carousel.scss
new file mode 100644
index 0000000000..5f7d77e4c6
--- /dev/null
+++ b/src/assets/scss/components/_carousel.scss
@@ -0,0 +1,27 @@
+$carousel-control-color: $white;
+$carousel-control-width: 15%;
+$carousel-control-opacity: 0.5;
+$carousel-control-hover-opacity: 0.9;
+$carousel-control-transition: opacity 0.15s ease;
+
+$carousel-indicator-width: 30px;
+$carousel-indicator-height: 3px;
+$carousel-indicator-hit-area-height: 10px;
+$carousel-indicator-spacer: 3px;
+$carousel-indicator-opacity: 0.5;
+$carousel-indicator-active-bg: $white;
+$carousel-indicator-active-opacity: 1;
+$carousel-indicator-transition: opacity 0.6s ease;
+
+$carousel-caption-width: 70%;
+$carousel-caption-color: $white;
+$carousel-caption-padding-y: 1.25rem;
+$carousel-caption-spacer: 1.25rem;
+
+$carousel-control-icon-width: 2rem;
+
+$carousel-control-prev-icon-bg: url("data:image/svg+xml, ");
+$carousel-control-next-icon-bg: url("data:image/svg+xml, ");
+
+$carousel-transition-duration: 0.6s;
+$carousel-transition: transform $carousel-transition-duration ease-in-out; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
diff --git a/src/assets/scss/components/_close.scss b/src/assets/scss/components/_close.scss
new file mode 100644
index 0000000000..2ae25d5353
--- /dev/null
+++ b/src/assets/scss/components/_close.scss
@@ -0,0 +1,12 @@
+$btn-close-width: 1em;
+$btn-close-height: $btn-close-width;
+$btn-close-padding-x: 0.25em;
+$btn-close-padding-y: $btn-close-padding-x;
+$btn-close-color: $black;
+$btn-close-bg: url("data:image/svg+xml, ");
+$btn-close-focus-shadow: $focus-ring-box-shadow;
+$btn-close-opacity: 0.5;
+$btn-close-hover-opacity: 0.75;
+$btn-close-focus-opacity: 1;
+$btn-close-disabled-opacity: 0.25;
+$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
diff --git a/src/assets/scss/components/_dropdown.scss b/src/assets/scss/components/_dropdown.scss
new file mode 100644
index 0000000000..1e1f1fa040
--- /dev/null
+++ b/src/assets/scss/components/_dropdown.scss
@@ -0,0 +1,35 @@
+$dropdown-min-width: 10rem;
+$dropdown-padding-x: 0;
+$dropdown-padding-y: 0.5rem;
+$dropdown-spacer: 0.125rem;
+$dropdown-font-size: $font-size-base;
+$dropdown-color: var(--#{$prefix}body-color);
+$dropdown-bg: var(--#{$prefix}body-bg);
+$dropdown-border-color: var(--#{$prefix}border-color-translucent);
+$dropdown-border-radius: var(--#{$prefix}border-radius);
+$dropdown-border-width: var(--#{$prefix}border-width);
+$dropdown-inner-border-radius: calc(
+ #{$dropdown-border-radius} - #{$dropdown-border-width}
+); // stylelint-disable-line function-disallowed-list
+$dropdown-divider-bg: $dropdown-border-color;
+$dropdown-divider-margin-y: $spacer * 0.5;
+$dropdown-box-shadow: $box-shadow;
+
+$dropdown-link-color: var(--#{$prefix}body-color);
+$dropdown-link-hover-color: $dropdown-link-color;
+$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg);
+
+$dropdown-link-active-color: $component-active-color;
+$dropdown-link-active-bg: $component-active-bg;
+
+$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color);
+
+$dropdown-item-padding-y: $spacer * 0.25;
+$dropdown-item-padding-x: $spacer;
+
+$dropdown-header-color: $gray-600;
+$dropdown-header-padding-x: $dropdown-item-padding-x;
+$dropdown-header-padding-y: $dropdown-padding-y;
+// fusv-disable
+$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x; // Deprecated in v5.2.0
+// fusv-enable
diff --git a/src/assets/scss/components/_list-group.scss b/src/assets/scss/components/_list-group.scss
new file mode 100644
index 0000000000..2579f2e823
--- /dev/null
+++ b/src/assets/scss/components/_list-group.scss
@@ -0,0 +1,26 @@
+$list-group-color: var(--#{$prefix}body-color);
+$list-group-bg: var(--#{$prefix}body-bg);
+$list-group-border-color: var(--#{$prefix}border-color);
+$list-group-border-width: var(--#{$prefix}border-width);
+$list-group-border-radius: var(--#{$prefix}border-radius);
+
+$list-group-item-padding-y: $spacer * 0.5;
+$list-group-item-padding-x: $spacer;
+// fusv-disable
+$list-group-item-bg-scale: -80%; // Deprecated in v5.3.0
+$list-group-item-color-scale: 40%; // Deprecated in v5.3.0
+// fusv-enable
+
+$list-group-hover-bg: var(--#{$prefix}tertiary-bg);
+$list-group-active-color: $component-active-color;
+$list-group-active-bg: $component-active-bg;
+$list-group-active-border-color: $list-group-active-bg;
+
+$list-group-disabled-color: var(--#{$prefix}secondary-color);
+$list-group-disabled-bg: $list-group-bg;
+
+$list-group-action-color: var(--#{$prefix}secondary-color);
+$list-group-action-hover-color: var(--#{$prefix}emphasis-color);
+
+$list-group-action-active-color: var(--#{$prefix}body-color);
+$list-group-action-active-bg: var(--#{$prefix}secondary-bg);
diff --git a/src/assets/scss/components/_modal.scss b/src/assets/scss/components/_modal.scss
new file mode 100644
index 0000000000..b491a19f1c
--- /dev/null
+++ b/src/assets/scss/components/_modal.scss
@@ -0,0 +1,43 @@
+$modal-inner-padding: $spacer;
+
+$modal-footer-margin-between: 0.5rem;
+
+$modal-dialog-margin: 0.5rem;
+$modal-dialog-margin-y-sm-up: 1.75rem;
+
+$modal-title-line-height: $line-height-base;
+
+$modal-content-color: null;
+$modal-content-bg: var(--#{$prefix}body-bg);
+$modal-content-border-color: var(--#{$prefix}border-color-translucent);
+$modal-content-border-width: var(--#{$prefix}border-width);
+$modal-content-border-radius: var(--#{$prefix}border-radius-lg);
+$modal-content-inner-border-radius: subtract(
+ $modal-content-border-radius,
+ $modal-content-border-width
+);
+$modal-content-box-shadow-xs: $box-shadow-sm;
+$modal-content-box-shadow-sm-up: $box-shadow;
+
+$modal-backdrop-bg: $black;
+$modal-backdrop-opacity: 0.5;
+
+$modal-header-border-color: var(--#{$prefix}border-color);
+$modal-header-border-width: 0; // We dont want border here
+$modal-header-padding-y: $modal-inner-padding * 0.8;
+$modal-header-padding-x: $modal-inner-padding;
+$modal-header-padding: $modal-header-padding-y $modal-header-padding-x; // Keep this for backwards compatibility
+
+$modal-footer-bg: null;
+$modal-footer-border-color: $modal-header-border-color;
+$modal-footer-border-width: 0; // We don't want border here
+
+$modal-sm: 300px;
+$modal-md: 500px;
+$modal-lg: 800px;
+$modal-xl: 1140px;
+
+$modal-fade-transform: translate(0, -50px);
+$modal-show-transform: none;
+$modal-transition: transform 0.3s ease-out;
+$modal-scale-transform: scale(1.02);
diff --git a/src/assets/scss/components/_nav.scss b/src/assets/scss/components/_nav.scss
new file mode 100644
index 0000000000..84aeab343c
--- /dev/null
+++ b/src/assets/scss/components/_nav.scss
@@ -0,0 +1,30 @@
+$nav-link-padding-y: 0.5rem;
+$nav-link-padding-x: 1rem;
+$nav-link-font-size: null;
+$nav-link-font-weight: null;
+$nav-link-color: var(--#{$prefix}link-color);
+$nav-link-hover-color: var(--#{$prefix}link-hover-color);
+$nav-link-transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out;
+$nav-link-disabled-color: var(--#{$prefix}secondary-color);
+$nav-link-focus-box-shadow: $focus-ring-box-shadow;
+
+$nav-tabs-border-color: var(--#{$prefix}border-color);
+$nav-tabs-border-width: var(--#{$prefix}border-width);
+$nav-tabs-border-radius: var(--#{$prefix}border-radius);
+$nav-tabs-link-hover-border-color: var(--#{$prefix}secondary-bg)
+ var(--#{$prefix}secondary-bg) $nav-tabs-border-color;
+$nav-tabs-link-active-color: var(--#{$prefix}emphasis-color);
+$nav-tabs-link-active-bg: var(--#{$prefix}body-bg);
+$nav-tabs-link-active-border-color: var(--#{$prefix}border-color)
+ var(--#{$prefix}border-color) $nav-tabs-link-active-bg;
+
+$nav-pills-border-radius: var(--#{$prefix}border-radius);
+$nav-pills-link-active-color: $component-active-color;
+$nav-pills-link-active-bg: $component-active-bg;
+
+$nav-underline-gap: 1rem;
+$nav-underline-border-width: 0.125rem;
+$nav-underline-link-active-color: var(--#{$prefix}emphasis-color);
diff --git a/src/assets/scss/components/_navbar.scss b/src/assets/scss/components/_navbar.scss
new file mode 100644
index 0000000000..aaf27bc8b2
--- /dev/null
+++ b/src/assets/scss/components/_navbar.scss
@@ -0,0 +1,31 @@
+$navbar-padding-y: $spacer * 0.5;
+$navbar-padding-x: null;
+
+$navbar-nav-link-padding-x: 0.5rem;
+
+$navbar-brand-font-size: $font-size-lg;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2;
+$navbar-brand-height: $navbar-brand-font-size * $line-height-base;
+$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * 0.5;
+$navbar-brand-margin-end: 1rem;
+
+$navbar-toggler-padding-y: 0.25rem;
+$navbar-toggler-padding-x: 0.75rem;
+$navbar-toggler-font-size: $font-size-lg;
+$navbar-toggler-border-radius: $btn-border-radius;
+$navbar-toggler-focus-width: $btn-focus-width;
+$navbar-toggler-transition: box-shadow 0.15s ease-in-out;
+
+$navbar-light-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.65);
+$navbar-light-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.8);
+$navbar-light-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 1);
+$navbar-light-disabled-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.3);
+$navbar-light-icon-color: rgba($body-color, 0.75);
+$navbar-light-toggler-icon-bg: url("data:image/svg+xml, ");
+$navbar-light-toggler-border-color: rgba(
+ var(--#{$prefix}emphasis-color-rgb),
+ 0.15
+);
+$navbar-light-brand-color: $navbar-light-active-color;
+$navbar-light-brand-hover-color: $navbar-light-active-color;
diff --git a/src/assets/scss/components/_offcanvas.scss b/src/assets/scss/components/_offcanvas.scss
new file mode 100644
index 0000000000..3226f970f4
--- /dev/null
+++ b/src/assets/scss/components/_offcanvas.scss
@@ -0,0 +1,13 @@
+$offcanvas-padding-y: $modal-inner-padding;
+$offcanvas-padding-x: $modal-inner-padding;
+$offcanvas-horizontal-width: 400px;
+$offcanvas-vertical-height: 30vh;
+$offcanvas-transition-duration: 0.3s;
+$offcanvas-border-color: $modal-content-border-color;
+$offcanvas-border-width: $modal-content-border-width;
+$offcanvas-title-line-height: $modal-title-line-height;
+$offcanvas-bg-color: var(--#{$prefix}body-bg);
+$offcanvas-color: var(--#{$prefix}body-color);
+$offcanvas-box-shadow: $modal-content-box-shadow-xs;
+$offcanvas-backdrop-bg: $modal-backdrop-bg;
+$offcanvas-backdrop-opacity: $modal-backdrop-opacity;
diff --git a/src/assets/scss/components/_pagination.scss b/src/assets/scss/components/_pagination.scss
new file mode 100644
index 0000000000..830c140492
--- /dev/null
+++ b/src/assets/scss/components/_pagination.scss
@@ -0,0 +1,44 @@
+$pagination-padding-y: 0.375rem;
+$pagination-padding-x: 0.75rem;
+$pagination-padding-y-sm: 0.25rem;
+$pagination-padding-x-sm: 0.5rem;
+$pagination-padding-y-lg: 0.75rem;
+$pagination-padding-x-lg: 1.5rem;
+
+$pagination-font-size: $font-size-base;
+
+$pagination-color: var(--#{$prefix}link-color);
+$pagination-bg: var(--#{$prefix}body-bg);
+$pagination-border-radius: var(--#{$prefix}border-radius);
+$pagination-border-width: var(--#{$prefix}border-width);
+$pagination-margin-start: calc(
+ #{$pagination-border-width} * -1
+); // stylelint-disable-line function-disallowed-list
+$pagination-border-color: var(--#{$prefix}border-color);
+
+$pagination-focus-color: var(--#{$prefix}link-hover-color);
+$pagination-focus-bg: var(--#{$prefix}secondary-bg);
+$pagination-focus-box-shadow: $focus-ring-box-shadow;
+$pagination-focus-outline: 0;
+
+$pagination-hover-color: var(--#{$prefix}link-hover-color);
+$pagination-hover-bg: var(--#{$prefix}tertiary-bg);
+$pagination-hover-border-color: var(
+ --#{$prefix}border-color
+); // Todo in v6: remove this?
+
+$pagination-active-color: $component-active-color;
+$pagination-active-bg: $component-active-bg;
+$pagination-active-border-color: $component-active-bg;
+
+$pagination-disabled-color: var(--#{$prefix}secondary-color);
+$pagination-disabled-bg: var(--#{$prefix}secondary-bg);
+$pagination-disabled-border-color: var(--#{$prefix}border-color);
+
+$pagination-transition:
+ color 0.15s ease-in-out,
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out;
+
+$pagination-border-radius-sm: var(--#{$prefix}border-radius-sm);
+$pagination-border-radius-lg: var(--#{$prefix}border-radius-lg);
diff --git a/src/assets/scss/components/_placeholder.scss b/src/assets/scss/components/_placeholder.scss
new file mode 100644
index 0000000000..8e7b77d0a4
--- /dev/null
+++ b/src/assets/scss/components/_placeholder.scss
@@ -0,0 +1,2 @@
+$placeholder-opacity-max: 0.5;
+$placeholder-opacity-min: 0.2;
diff --git a/src/assets/scss/components/_progress.scss b/src/assets/scss/components/_progress.scss
new file mode 100644
index 0000000000..a40fa4676e
--- /dev/null
+++ b/src/assets/scss/components/_progress.scss
@@ -0,0 +1,17 @@
+$progress-height: 1rem;
+$progress-font-size: $font-size-base * 0.75;
+$progress-bg: var(--#{$prefix}secondary-bg);
+$progress-border-radius: var(--#{$prefix}border-radius);
+$progress-box-shadow: var(--#{$prefix}box-shadow-inset);
+$progress-bar-color: $white;
+$progress-bar-bg: $primary;
+$progress-bar-animation-timing: 1s linear infinite;
+$progress-bar-transition: width 0.6s ease;
+
+@if $enable-transitions {
+ @keyframes progress-bar-stripes {
+ 0% {
+ background-position-x: $progress-height;
+ }
+ }
+}
diff --git a/src/assets/scss/components/_spinners.scss b/src/assets/scss/components/_spinners.scss
new file mode 100644
index 0000000000..08f4c51c90
--- /dev/null
+++ b/src/assets/scss/components/_spinners.scss
@@ -0,0 +1,24 @@
+$spinner-width: 2rem;
+$spinner-height: $spinner-width;
+$spinner-vertical-align: -0.125em;
+$spinner-border-width: 0.25em;
+$spinner-animation-speed: 0.75s;
+
+$spinner-width-sm: 1rem;
+$spinner-height-sm: $spinner-width-sm;
+$spinner-border-width-sm: 0.2em;
+
+@keyframes spinner-border {
+ to {
+ transform: rotate(360deg) #{'/* rtl:ignore */'};
+ }
+}
+@keyframes spinner-grow {
+ 0% {
+ transform: scale(0);
+ }
+ 50% {
+ opacity: 1;
+ transform: none;
+ }
+}
diff --git a/src/assets/scss/content/_table.scss b/src/assets/scss/content/_table.scss
new file mode 100644
index 0000000000..f5c9c608fb
--- /dev/null
+++ b/src/assets/scss/content/_table.scss
@@ -0,0 +1,37 @@
+$table-cell-padding-y: 0.5rem;
+$table-cell-padding-x: 0.5rem;
+$table-cell-padding-y-sm: 0.25rem;
+$table-cell-padding-x-sm: 0.25rem;
+
+$table-cell-vertical-align: top;
+
+$table-color: var(--#{$prefix}body-color);
+$table-bg: var(--#{$prefix}body-bg);
+$table-accent-bg: transparent;
+
+$table-th-font-weight: null;
+
+$table-striped-color: $table-color;
+$table-striped-bg-factor: 0.05;
+$table-striped-bg: rgba($black, $table-striped-bg-factor);
+
+$table-active-color: $table-color;
+$table-active-bg-factor: 0.1;
+$table-active-bg: rgba($black, $table-active-bg-factor);
+
+$table-hover-color: $table-color;
+$table-hover-bg-factor: 0.075;
+$table-hover-bg: rgba($black, $table-hover-bg-factor);
+
+$table-border-factor: 0.1;
+$table-border-width: var(--#{$prefix}border-width);
+$table-border-color: var(--#{$prefix}border-color);
+
+$table-striped-order: odd;
+$table-striped-columns-order: even;
+
+$table-group-separator-color: currentcolor;
+
+$table-caption-color: var(--#{$prefix}secondary-color);
+
+$table-bg-scale: -80%;
diff --git a/src/assets/scss/content/_typography.scss b/src/assets/scss/content/_typography.scss
new file mode 100644
index 0000000000..6336f8554e
--- /dev/null
+++ b/src/assets/scss/content/_typography.scss
@@ -0,0 +1,69 @@
+/*
+ DISPLAY SASS VARIABLES
+*/
+
+$display-font-sizes: (
+ 1: 5rem,
+ 2: 4.5rem,
+ 3: 4rem,
+ 4: 3.5rem,
+ 5: 3rem,
+ 6: 2.5rem,
+);
+
+$display-font-family: null;
+$display-font-style: null;
+$display-font-weight: 300;
+$display-line-height: $headings-line-height;
+
+/*
+ DISPLAY SASS VARIABLES
+*/
+
+$lead-font-size: $font-size-base * 1.25;
+$lead-font-weight: 300;
+
+$small-font-size: 0.875em;
+
+$sub-sup-font-size: 0.75em;
+
+// fusv-disable
+$text-muted: var(--#{$prefix}secondary-color); // Deprecated in 5.3.0
+// fusv-enable
+
+$headings-margin-bottom: $spacer * 0.5;
+$headings-font-family: null;
+$headings-font-style: null;
+$headings-font-weight: 500;
+$headings-line-height: 1.2;
+$headings-color: inherit;
+
+$initialism-font-size: $small-font-size;
+
+$blockquote-margin-y: $spacer;
+$blockquote-font-size: $font-size-base * 1.25;
+$blockquote-footer-color: $gray-600;
+$blockquote-footer-font-size: $small-font-size;
+
+$hr-margin-y: $spacer;
+$hr-color: inherit;
+
+// fusv-disable
+$hr-bg-color: null; // Deprecated in v5.2.0
+$hr-height: null; // Deprecated in v5.2.0
+// fusv-enable
+
+$hr-border-color: null; // Allows for inherited colors
+$hr-border-width: var(--#{$prefix}border-width);
+$hr-opacity: 0.25;
+
+$legend-margin-bottom: 0.5rem;
+$legend-font-size: 1.5rem;
+$legend-font-weight: null;
+
+$dt-font-weight: $font-weight-bold;
+
+$list-inline-padding: 0.5rem;
+
+$mark-padding: 0.1875em;
+$mark-bg: $yellow-100;
diff --git a/src/assets/scss/forms/_check-radios.scss b/src/assets/scss/forms/_check-radios.scss
new file mode 100644
index 0000000000..1ed4d32fd0
--- /dev/null
+++ b/src/assets/scss/forms/_check-radios.scss
@@ -0,0 +1,34 @@
+$form-check-input-width: 1em;
+$form-check-min-height: $font-size-base * $line-height-base;
+$form-check-padding-start: $form-check-input-width + 0.5em;
+$form-check-margin-bottom: 0.125rem;
+$form-check-label-color: null;
+$form-check-label-cursor: null;
+$form-check-transition: null;
+
+$form-check-input-active-filter: brightness(90%);
+
+$form-check-input-bg: $input-bg;
+$form-check-input-border: var(--#{$prefix}border-width) solid
+ var(--#{$prefix}border-color);
+$form-check-input-border-radius: 0.25em;
+$form-check-radio-border-radius: 50%;
+$form-check-input-focus-border: $input-focus-border-color;
+$form-check-input-focus-box-shadow: $focus-ring-box-shadow;
+
+$form-check-input-checked-color: $component-active-color;
+$form-check-input-checked-bg-color: $component-active-bg;
+$form-check-input-checked-border-color: $form-check-input-checked-bg-color;
+$form-check-input-checked-bg-image: url("data:image/svg+xml, ");
+$form-check-radio-checked-bg-image: url("data:image/svg+xml, ");
+
+$form-check-input-indeterminate-color: $component-active-color;
+$form-check-input-indeterminate-bg-color: $component-active-bg;
+$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color;
+$form-check-input-indeterminate-bg-image: url("data:image/svg+xml, ");
+
+$form-check-input-disabled-opacity: 0.5;
+$form-check-label-disabled-opacity: $form-check-input-disabled-opacity;
+$form-check-btn-check-disabled-opacity: $btn-disabled-opacity;
+
+$form-check-inline-margin-end: 1rem;
diff --git a/src/assets/scss/forms/_floating-label.scss b/src/assets/scss/forms/_floating-label.scss
new file mode 100644
index 0000000000..8ab47c4a25
--- /dev/null
+++ b/src/assets/scss/forms/_floating-label.scss
@@ -0,0 +1,14 @@
+$form-floating-height: add(3.5rem, $input-height-border);
+$form-floating-line-height: 1.25;
+$form-floating-padding-x: $input-padding-x;
+$form-floating-padding-y: 1rem;
+$form-floating-input-padding-t: 1.625rem;
+$form-floating-input-padding-b: 0.625rem;
+$form-floating-label-height: 1.5em;
+$form-floating-label-opacity: 0.65;
+$form-floating-label-transform: scale(0.85) translateY(-0.5rem)
+ translateX(0.15rem);
+$form-floating-label-disabled-color: $gray-600;
+$form-floating-transition:
+ opacity 0.1s ease-in-out,
+ transform 0.1s ease-in-out;
diff --git a/src/assets/scss/forms/_form-control.scss b/src/assets/scss/forms/_form-control.scss
new file mode 100644
index 0000000000..230b3ec76b
--- /dev/null
+++ b/src/assets/scss/forms/_form-control.scss
@@ -0,0 +1,108 @@
+$input-btn-padding-y: 0.375rem;
+$input-btn-padding-x: 0.75rem;
+$input-btn-font-family: null;
+$input-btn-font-size: $font-size-base;
+$input-btn-line-height: $line-height-base;
+
+$input-btn-focus-width: $focus-ring-width;
+$input-btn-focus-color-opacity: $focus-ring-opacity;
+$input-btn-focus-color: $focus-ring-color;
+$input-btn-focus-blur: $focus-ring-blur;
+$input-btn-focus-box-shadow: $focus-ring-box-shadow;
+
+$input-btn-padding-y-sm: 0.25rem;
+$input-btn-padding-x-sm: 0.5rem;
+$input-btn-font-size-sm: $font-size-sm;
+
+$input-btn-padding-y-lg: 0.5rem;
+$input-btn-padding-x-lg: 1rem;
+$input-btn-font-size-lg: $font-size-lg;
+
+$input-btn-border-width: var(--#{$prefix}border-width);
+
+$input-padding-y: $input-btn-padding-y;
+$input-padding-x: $input-btn-padding-x;
+$input-font-family: $input-btn-font-family;
+$input-font-size: $input-btn-font-size;
+$input-font-weight: $font-weight-base;
+$input-line-height: $input-btn-line-height;
+
+$input-padding-y-sm: $input-btn-padding-y-sm;
+$input-padding-x-sm: $input-btn-padding-x-sm;
+$input-font-size-sm: $input-btn-font-size-sm;
+
+$input-padding-y-lg: $input-btn-padding-y-lg;
+$input-padding-x-lg: $input-btn-padding-x-lg;
+$input-font-size-lg: $input-btn-font-size-lg;
+
+$input-bg: var(--#{$prefix}body-bg);
+$input-disabled-color: null;
+$input-disabled-bg: var(--#{$prefix}secondary-bg);
+$input-disabled-border-color: null;
+
+$input-color: var(--#{$prefix}body-color);
+$input-border-color: var(--#{$prefix}border-color);
+$input-border-width: $input-btn-border-width;
+$input-box-shadow: $box-shadow-inset;
+
+$input-border-radius: var(--#{$prefix}border-radius);
+$input-border-radius-sm: var(--#{$prefix}border-radius-sm);
+$input-border-radius-lg: var(--#{$prefix}border-radius-lg);
+
+$input-focus-bg: $input-bg;
+$input-focus-border-color: tint-color($component-active-bg, 50%);
+$input-focus-color: $input-color;
+$input-focus-width: $input-btn-focus-width;
+$input-focus-box-shadow: $input-btn-focus-box-shadow;
+
+$input-placeholder-color: var(--#{$prefix}secondary-color);
+$input-plaintext-color: var(--#{$prefix}body-color);
+
+$input-height-border: calc(
+ #{$input-border-width} * 2
+); // stylelint-disable-line function-disallowed-list
+
+$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2);
+$input-height-inner-half: add($input-line-height * 0.5em, $input-padding-y);
+$input-height-inner-quarter: add(
+ $input-line-height * 0.25em,
+ $input-padding-y * 0.5
+);
+
+$input-height: add(
+ $input-line-height * 1em,
+ add($input-padding-y * 2, $input-height-border, false)
+);
+$input-height-sm: add(
+ $input-line-height * 1em,
+ add($input-padding-y-sm * 2, $input-height-border, false)
+);
+$input-height-lg: add(
+ $input-line-height * 1em,
+ add($input-padding-y-lg * 2, $input-height-border, false)
+);
+
+$input-transition:
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
+
+$form-color-width: 3rem;
+
+// Form Label Text
+$form-label-margin-bottom: 0.5rem;
+$form-label-font-size: null;
+$form-label-font-style: null;
+$form-label-font-weight: null;
+$form-label-color: null;
+
+// Form Text
+$form-text-margin-top: 0.25rem;
+$form-text-font-size: $small-font-size;
+$form-text-font-style: null;
+$form-text-font-weight: null;
+$form-text-color: var(--#{$prefix}secondary-color);
+
+// Form File Button
+$form-file-button-color: $input-color;
+$form-file-button-bg: var(--#{$prefix}tertiary-bg);
+$form-file-button-hover-bg: var(--#{$prefix}secondary-bg);
diff --git a/src/assets/scss/forms/_input-group.scss b/src/assets/scss/forms/_input-group.scss
new file mode 100644
index 0000000000..cd50a59126
--- /dev/null
+++ b/src/assets/scss/forms/_input-group.scss
@@ -0,0 +1,6 @@
+$input-group-addon-padding-y: $input-padding-y;
+$input-group-addon-padding-x: $input-padding-x;
+$input-group-addon-font-weight: $input-font-weight;
+$input-group-addon-color: $input-color;
+$input-group-addon-bg: var(--#{$prefix}tertiary-bg);
+$input-group-addon-border-color: $input-border-color;
diff --git a/src/assets/scss/forms/_range.scss b/src/assets/scss/forms/_range.scss
new file mode 100644
index 0000000000..c0cdc61171
--- /dev/null
+++ b/src/assets/scss/forms/_range.scss
@@ -0,0 +1,23 @@
+$form-range-track-width: 100%;
+$form-range-track-height: 0.5rem;
+$form-range-track-cursor: pointer;
+$form-range-track-bg: var(--#{$prefix}tertiary-bg);
+$form-range-track-border-radius: 1rem;
+$form-range-track-box-shadow: $box-shadow-inset;
+
+$form-range-thumb-width: 1rem;
+$form-range-thumb-height: $form-range-thumb-width;
+$form-range-thumb-bg: $component-active-bg;
+$form-range-thumb-border: 0;
+$form-range-thumb-border-radius: 1rem;
+$form-range-thumb-box-shadow: 0 0.1rem 0.25rem rgba($black, 0.1);
+$form-range-thumb-focus-box-shadow:
+ 0 0 0 1px $body-bg,
+ $input-focus-box-shadow;
+$form-range-thumb-focus-box-shadow-width: $input-focus-width; // For focus box shadow issue in Edge
+$form-range-thumb-active-bg: tint-color($component-active-bg, 70%);
+$form-range-thumb-disabled-bg: var(--#{$prefix}secondary-color);
+$form-range-thumb-transition:
+ background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out,
+ box-shadow 0.15s ease-in-out;
diff --git a/src/assets/scss/forms/_select.scss b/src/assets/scss/forms/_select.scss
new file mode 100644
index 0000000000..4d5c428636
--- /dev/null
+++ b/src/assets/scss/forms/_select.scss
@@ -0,0 +1,44 @@
+$form-select-padding-y: $input-padding-y;
+$form-select-padding-x: $input-padding-x;
+$form-select-font-family: $input-font-family;
+$form-select-font-size: $input-font-size;
+$form-select-indicator-padding: $form-select-padding-x * 3; // Extra padding for background-image
+$form-select-font-weight: $input-font-weight;
+$form-select-line-height: $input-line-height;
+$form-select-color: $input-color;
+$form-select-bg: $input-bg;
+$form-select-disabled-color: null;
+$form-select-disabled-bg: $input-disabled-bg;
+$form-select-disabled-border-color: $input-disabled-border-color;
+$form-select-bg-position: right $form-select-padding-x center;
+$form-select-bg-size: 16px 12px; // In pixels because image dimensions
+$form-select-indicator-color: $gray-800;
+$form-select-indicator: url("data:image/svg+xml, ");
+
+$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 +
+ $form-select-indicator-padding;
+$form-select-feedback-icon-position: center right $form-select-indicator-padding;
+$form-select-feedback-icon-size: $input-height-inner-half
+ $input-height-inner-half;
+
+$form-select-border-width: $input-border-width;
+$form-select-border-color: $input-border-color;
+$form-select-border-radius: $input-border-radius;
+$form-select-box-shadow: $box-shadow-inset;
+
+$form-select-focus-border-color: $input-focus-border-color;
+$form-select-focus-width: $input-focus-width;
+$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width
+ $input-btn-focus-color;
+
+$form-select-padding-y-sm: $input-padding-y-sm;
+$form-select-padding-x-sm: $input-padding-x-sm;
+$form-select-font-size-sm: $input-font-size-sm;
+$form-select-border-radius-sm: $input-border-radius-sm;
+
+$form-select-padding-y-lg: $input-padding-y-lg;
+$form-select-padding-x-lg: $input-padding-x-lg;
+$form-select-font-size-lg: $input-font-size-lg;
+$form-select-border-radius-lg: $input-border-radius-lg;
+
+$form-select-transition: $input-transition;
diff --git a/src/assets/scss/forms/_validation.scss b/src/assets/scss/forms/_validation.scss
new file mode 100644
index 0000000000..9877dfe327
--- /dev/null
+++ b/src/assets/scss/forms/_validation.scss
@@ -0,0 +1,20 @@
+$form-feedback-margin-top: $form-text-margin-top;
+$form-feedback-font-size: $form-text-font-size;
+$form-feedback-font-style: $form-text-font-style;
+$form-feedback-valid-color: $success;
+$form-feedback-invalid-color: $danger;
+
+$form-feedback-icon-valid-color: $form-feedback-valid-color;
+$form-feedback-icon-valid: url("data:image/svg+xml, ");
+$form-feedback-icon-invalid-color: $form-feedback-invalid-color;
+$form-feedback-icon-invalid: url("data:image/svg+xml, ");
+
+$form-valid-color: $form-feedback-valid-color;
+$form-valid-border-color: $form-feedback-valid-color;
+$form-invalid-color: $form-feedback-invalid-color;
+$form-invalid-border-color: $form-feedback-invalid-color;
+
+$form-valid-color-dark: $green-300;
+$form-valid-border-color-dark: $green-300;
+$form-invalid-color-dark: $red-300;
+$form-invalid-border-color-dark: $red-300;
diff --git a/src/assets/svgs/Attendance.svg b/src/assets/svgs/Attendance.svg
new file mode 100644
index 0000000000..8dbc7b07ce
--- /dev/null
+++ b/src/assets/svgs/Attendance.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/actionItem.svg b/src/assets/svgs/actionItem.svg
new file mode 100644
index 0000000000..d660b378cd
--- /dev/null
+++ b/src/assets/svgs/actionItem.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/assets/svgs/admin.svg b/src/assets/svgs/admin.svg
new file mode 100644
index 0000000000..8ee42f611d
--- /dev/null
+++ b/src/assets/svgs/admin.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/assets/svgs/agenda-category-icon.svg b/src/assets/svgs/agenda-category-icon.svg
new file mode 100644
index 0000000000..8e3d4562f3
--- /dev/null
+++ b/src/assets/svgs/agenda-category-icon.svg
@@ -0,0 +1 @@
+checklist
\ No newline at end of file
diff --git a/src/assets/svgs/agenda-items.svg b/src/assets/svgs/agenda-items.svg
new file mode 100644
index 0000000000..343d1808b4
--- /dev/null
+++ b/src/assets/svgs/agenda-items.svg
@@ -0,0 +1 @@
+notebook
diff --git a/src/assets/svgs/angleRight.svg b/src/assets/svgs/angleRight.svg
new file mode 100644
index 0000000000..4a3a498877
--- /dev/null
+++ b/src/assets/svgs/angleRight.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/article.svg b/src/assets/svgs/article.svg
new file mode 100644
index 0000000000..e828aa40fc
--- /dev/null
+++ b/src/assets/svgs/article.svg
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/svgs/blockUser.svg b/src/assets/svgs/blockUser.svg
new file mode 100644
index 0000000000..f9aef51775
--- /dev/null
+++ b/src/assets/svgs/blockUser.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/blockedUser.svg b/src/assets/svgs/blockedUser.svg
new file mode 100644
index 0000000000..bbe0a51f84
--- /dev/null
+++ b/src/assets/svgs/blockedUser.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/cardItemDate.svg b/src/assets/svgs/cardItemDate.svg
new file mode 100644
index 0000000000..e3e738a3dc
--- /dev/null
+++ b/src/assets/svgs/cardItemDate.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/cardItemEvent.svg b/src/assets/svgs/cardItemEvent.svg
new file mode 100644
index 0000000000..e37a084018
--- /dev/null
+++ b/src/assets/svgs/cardItemEvent.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/cardItemLocation.svg b/src/assets/svgs/cardItemLocation.svg
new file mode 100644
index 0000000000..1518f97a75
--- /dev/null
+++ b/src/assets/svgs/cardItemLocation.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/chat.svg b/src/assets/svgs/chat.svg
new file mode 100644
index 0000000000..72787fab78
--- /dev/null
+++ b/src/assets/svgs/chat.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/checkInRegistrants.svg b/src/assets/svgs/checkInRegistrants.svg
new file mode 100644
index 0000000000..0a663e3876
--- /dev/null
+++ b/src/assets/svgs/checkInRegistrants.svg
@@ -0,0 +1 @@
+check_in Created with Sketch.
\ No newline at end of file
diff --git a/src/assets/svgs/dashboard.svg b/src/assets/svgs/dashboard.svg
new file mode 100644
index 0000000000..12e8b0fe63
--- /dev/null
+++ b/src/assets/svgs/dashboard.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/date.svg b/src/assets/svgs/date.svg
new file mode 100644
index 0000000000..9baf0768c4
--- /dev/null
+++ b/src/assets/svgs/date.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/event.svg b/src/assets/svgs/event.svg
new file mode 100644
index 0000000000..3c73e7b04e
--- /dev/null
+++ b/src/assets/svgs/event.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/events.svg b/src/assets/svgs/events.svg
new file mode 100644
index 0000000000..95b8a3b587
--- /dev/null
+++ b/src/assets/svgs/events.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/flask.svg b/src/assets/svgs/flask.svg
new file mode 100644
index 0000000000..599220a9d2
--- /dev/null
+++ b/src/assets/svgs/flask.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/funds.svg b/src/assets/svgs/funds.svg
new file mode 100644
index 0000000000..372a258577
--- /dev/null
+++ b/src/assets/svgs/funds.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/key.svg b/src/assets/svgs/key.svg
new file mode 100644
index 0000000000..a1f47615e8
--- /dev/null
+++ b/src/assets/svgs/key.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/listEventRegistrants.svg b/src/assets/svgs/listEventRegistrants.svg
new file mode 100644
index 0000000000..4d2874d641
--- /dev/null
+++ b/src/assets/svgs/listEventRegistrants.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/location.svg b/src/assets/svgs/location.svg
new file mode 100644
index 0000000000..b75f616dd6
--- /dev/null
+++ b/src/assets/svgs/location.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/logout.svg b/src/assets/svgs/logout.svg
new file mode 100644
index 0000000000..e71a973f0d
--- /dev/null
+++ b/src/assets/svgs/logout.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/media.svg b/src/assets/svgs/media.svg
new file mode 100644
index 0000000000..956c1e9a19
--- /dev/null
+++ b/src/assets/svgs/media.svg
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/svgs/newChat.svg b/src/assets/svgs/newChat.svg
new file mode 100644
index 0000000000..e3609e2895
--- /dev/null
+++ b/src/assets/svgs/newChat.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/organizations.svg b/src/assets/svgs/organizations.svg
new file mode 100644
index 0000000000..5c616655d2
--- /dev/null
+++ b/src/assets/svgs/organizations.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/assets/svgs/palisadoes.svg b/src/assets/svgs/palisadoes.svg
new file mode 100644
index 0000000000..dc57b69a42
--- /dev/null
+++ b/src/assets/svgs/palisadoes.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/people.svg b/src/assets/svgs/people.svg
new file mode 100644
index 0000000000..cd8134aa67
--- /dev/null
+++ b/src/assets/svgs/people.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/plugins.svg b/src/assets/svgs/plugins.svg
new file mode 100644
index 0000000000..7da1701dc1
--- /dev/null
+++ b/src/assets/svgs/plugins.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/post.svg b/src/assets/svgs/post.svg
new file mode 100644
index 0000000000..34e468523b
--- /dev/null
+++ b/src/assets/svgs/post.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/posts.svg b/src/assets/svgs/posts.svg
new file mode 100644
index 0000000000..181e25855a
--- /dev/null
+++ b/src/assets/svgs/posts.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/requests.svg b/src/assets/svgs/requests.svg
new file mode 100644
index 0000000000..ccf700e5fe
--- /dev/null
+++ b/src/assets/svgs/requests.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/svgs/roles.svg b/src/assets/svgs/roles.svg
new file mode 100644
index 0000000000..bc301784f9
--- /dev/null
+++ b/src/assets/svgs/roles.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/settings.svg b/src/assets/svgs/settings.svg
new file mode 100644
index 0000000000..fc111cfc08
--- /dev/null
+++ b/src/assets/svgs/settings.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Facebook-Logo.svg b/src/assets/svgs/social-icons/Facebook-Logo.svg
new file mode 100644
index 0000000000..a70f112dc7
--- /dev/null
+++ b/src/assets/svgs/social-icons/Facebook-Logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Github-Logo.svg b/src/assets/svgs/social-icons/Github-Logo.svg
new file mode 100644
index 0000000000..7679af859c
--- /dev/null
+++ b/src/assets/svgs/social-icons/Github-Logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Instagram-Logo.svg b/src/assets/svgs/social-icons/Instagram-Logo.svg
new file mode 100644
index 0000000000..cbf278ae75
--- /dev/null
+++ b/src/assets/svgs/social-icons/Instagram-Logo.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Linkedin-Logo.svg b/src/assets/svgs/social-icons/Linkedin-Logo.svg
new file mode 100644
index 0000000000..998dd1b826
--- /dev/null
+++ b/src/assets/svgs/social-icons/Linkedin-Logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Reddit-Logo.svg b/src/assets/svgs/social-icons/Reddit-Logo.svg
new file mode 100644
index 0000000000..8111415030
--- /dev/null
+++ b/src/assets/svgs/social-icons/Reddit-Logo.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Slack-Logo.svg b/src/assets/svgs/social-icons/Slack-Logo.svg
new file mode 100644
index 0000000000..29a9087d21
--- /dev/null
+++ b/src/assets/svgs/social-icons/Slack-Logo.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/social-icons/X-Logo.svg b/src/assets/svgs/social-icons/X-Logo.svg
new file mode 100644
index 0000000000..efa659339d
--- /dev/null
+++ b/src/assets/svgs/social-icons/X-Logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/Youtube-Logo.svg b/src/assets/svgs/social-icons/Youtube-Logo.svg
new file mode 100644
index 0000000000..112b9c2d3b
--- /dev/null
+++ b/src/assets/svgs/social-icons/Youtube-Logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/social-icons/index.tsx b/src/assets/svgs/social-icons/index.tsx
new file mode 100644
index 0000000000..20544051e5
--- /dev/null
+++ b/src/assets/svgs/social-icons/index.tsx
@@ -0,0 +1,19 @@
+import FacebookLogo from './Facebook-Logo.svg';
+import GithubLogo from './Github-Logo.svg';
+import InstagramLogo from './Instagram-Logo.svg';
+import LinkedInLogo from './Linkedin-Logo.svg';
+import SlackLogo from './Slack-Logo.svg';
+import XLogo from './X-Logo.svg';
+import YoutubeLogo from './Youtube-Logo.svg';
+import RedditLogo from './Reddit-Logo.svg';
+
+export {
+ FacebookLogo,
+ GithubLogo,
+ InstagramLogo,
+ LinkedInLogo,
+ SlackLogo,
+ XLogo,
+ YoutubeLogo,
+ RedditLogo,
+};
diff --git a/src/assets/svgs/tag.svg b/src/assets/svgs/tag.svg
new file mode 100644
index 0000000000..e3646993be
--- /dev/null
+++ b/src/assets/svgs/tag.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/tags.svg b/src/assets/svgs/tags.svg
new file mode 100644
index 0000000000..32cb76851a
--- /dev/null
+++ b/src/assets/svgs/tags.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/svgs/talawa.svg b/src/assets/svgs/talawa.svg
new file mode 100644
index 0000000000..0c89afbcca
--- /dev/null
+++ b/src/assets/svgs/talawa.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/assets/svgs/user.svg b/src/assets/svgs/user.svg
new file mode 100644
index 0000000000..b23b34481e
--- /dev/null
+++ b/src/assets/svgs/user.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svgs/userEvent.svg b/src/assets/svgs/userEvent.svg
new file mode 100644
index 0000000000..1623ca2e07
--- /dev/null
+++ b/src/assets/svgs/userEvent.svg
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/svgs/users.svg b/src/assets/svgs/users.svg
new file mode 100644
index 0000000000..a1a474206d
--- /dev/null
+++ b/src/assets/svgs/users.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/svgs/venues.svg b/src/assets/svgs/venues.svg
new file mode 100644
index 0000000000..c8cd52f4e7
--- /dev/null
+++ b/src/assets/svgs/venues.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/talawa-logo-lite-200x200.png b/src/assets/talawa-logo-lite-200x200.png
deleted file mode 100644
index 9d34137661..0000000000
Binary files a/src/assets/talawa-logo-lite-200x200.png and /dev/null differ
diff --git a/src/components/AddOn/AddOn.module.css b/src/components/AddOn/AddOn.module.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/components/AddOn/AddOn.test.tsx b/src/components/AddOn/AddOn.test.tsx
new file mode 100644
index 0000000000..68475e8196
--- /dev/null
+++ b/src/components/AddOn/AddOn.test.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+
+import { store } from 'state/store';
+import AddOn from './AddOn';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+const link = new StaticMockLink([], true);
+describe('Testing Addon component', () => {
+ const props = {
+ children: 'This is a dummy text',
+ };
+
+ test('should render with default props', () => {
+ const { getByTestId } = render( );
+ const container = getByTestId('pluginContainer');
+ expect(container).toBeInTheDocument();
+ expect(container).toHaveClass('plugin-container');
+ expect(container).toHaveTextContent('Default text');
+ });
+
+ test('should render props and text elements test for the page component', () => {
+ const { getByTestId, getByText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(getByTestId('pluginContainer')).toBeInTheDocument();
+ expect(getByText(props.children)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/AddOn/AddOn.tsx b/src/components/AddOn/AddOn.tsx
new file mode 100644
index 0000000000..850cc05ee4
--- /dev/null
+++ b/src/components/AddOn/AddOn.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+interface InterfaceAddOnProps {
+ extras?: any;
+ name?: string;
+ children?: React.ReactNode;
+}
+
+/**
+ * The AddOn component is used to wrap children within a plugin container.
+ * It also accepts additional properties (`extras` and `name`) to allow for
+ * extensibility and custom naming.
+ *
+ * @param props - The props for the AddOn component.
+ * @param extras - Additional properties for the AddOn component.
+ * @param name - The name for the AddOn component.
+ * @param children - The child elements to be rendered within the AddOn component.
+ *
+ * @returns The JSX element representing the AddOn component.
+ */
+function AddOn({
+ children = 'Default text',
+ extras = {},
+ name = '',
+}: InterfaceAddOnProps): JSX.Element {
+ return (
+
+ {children}
+
+ );
+}
+
+// PropTypes validation for the AddOn component
+AddOn.propTypes = {
+ extras: PropTypes.shape({
+ components: PropTypes.shape({}),
+ actions: PropTypes.shape({}),
+ }),
+ name: PropTypes.string,
+ children: PropTypes.node,
+};
+
+export default AddOn;
diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css b/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css
new file mode 100644
index 0000000000..1f1ea89996
--- /dev/null
+++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.module.css
@@ -0,0 +1,20 @@
+.entrytoggle {
+ margin: 24px 24px 0 auto;
+ width: fit-content;
+}
+
+.entryaction {
+ margin-left: auto;
+ display: flex !important;
+ align-items: center;
+}
+
+.entryaction i {
+ margin-right: 8px;
+}
+
+.entryaction .spinner-grow {
+ height: 1rem;
+ width: 1rem;
+ margin-right: 8px;
+}
diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx b/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx
new file mode 100644
index 0000000000..3d800eb59f
--- /dev/null
+++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx
@@ -0,0 +1,236 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+import AddOnEntry from './AddOnEntry';
+import {
+ ApolloClient,
+ ApolloProvider,
+ InMemoryCache,
+ ApolloLink,
+ HttpLink,
+} from '@apollo/client';
+
+import type { NormalizedCacheObject } from '@apollo/client';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { BACKEND_URL } from 'Constant/constant';
+import i18nForTest from 'utils/i18nForTest';
+import { I18nextProvider } from 'react-i18next';
+import userEvent from '@testing-library/user-event';
+import { MockedProvider, wait } from '@apollo/react-testing';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { ADD_ON_ENTRY_MOCK } from './AddOnEntryMocks';
+import { ToastContainer } from 'react-toastify';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { getItem } = useLocalStorage();
+
+const link = new StaticMockLink(ADD_ON_ENTRY_MOCK, true);
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+console.error = jest.fn();
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+let mockID: string | undefined = '1';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockID }),
+}));
+
+describe('Testing AddOnEntry', () => {
+ const props = {
+ id: 'string',
+ enabled: true,
+ title: 'string',
+ description: 'string',
+ createdBy: 'string',
+ component: 'string',
+ installed: true,
+ configurable: true,
+ modified: true,
+ isInstalled: true,
+ getInstalledPlugins: (): { sample: string } => {
+ return { sample: 'sample' };
+ },
+ };
+
+ test('should render modal and take info to add plugin for registered organization', () => {
+ const { getByTestId } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+ expect(getByTestId('AddOnEntry')).toBeInTheDocument();
+ });
+
+ test('uses default values for title and description when not provided', () => {
+ // Render the component with only required parameters
+ const mockGetInstalledPlugins = jest.fn();
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const titleElement = screen.getByText('No title provided'); // This will check for the default empty string in the title
+ const descriptionElement = screen.getByText('Description not available'); // This will check for the default empty string in the description
+ expect(titleElement).toBeInTheDocument(); // Ensure the title element with default value exists
+ expect(descriptionElement).toBeInTheDocument(); // Ensure the description element with default value exists
+ });
+
+ it('renders correctly', () => {
+ const props = {
+ id: '1',
+ title: 'Test Addon',
+ description: 'Test addon description',
+ createdBy: 'Test User',
+ component: 'string',
+ installed: true,
+ configurable: true,
+ modified: true,
+ isInstalled: true,
+ uninstalledOrgs: [],
+ enabled: true,
+ getInstalledPlugins: (): { sample: string } => {
+ return { sample: 'sample' };
+ },
+ };
+
+ const { getByText } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+
+ expect(getByText('Test Addon')).toBeInTheDocument();
+ expect(getByText('Test addon description')).toBeInTheDocument();
+ expect(getByText('Test User')).toBeInTheDocument();
+ });
+
+ it('Uninstall Button works correctly', async () => {
+ const props = {
+ id: '1',
+ title: 'Test Addon',
+ description: 'Test addon description',
+ createdBy: 'Test User',
+ component: 'string',
+ installed: true,
+ configurable: true,
+ modified: true,
+ isInstalled: true,
+ uninstalledOrgs: [],
+ enabled: true,
+ getInstalledPlugins: (): { sample: string } => {
+ return { sample: 'sample' };
+ },
+ };
+ mockID = 'undefined';
+ const { findByText, getByTestId } = render(
+
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+ await wait(100);
+ const btn = getByTestId('AddOnEntry_btn_install');
+ await userEvent.click(btn);
+ await wait(100);
+ expect(btn.innerHTML).toMatch(/Install/i);
+ expect(
+ await findByText('This feature is now removed from your organization'),
+ ).toBeInTheDocument();
+ await userEvent.click(btn);
+ await wait(100);
+
+ expect(btn.innerHTML).toMatch(/Uninstall/i);
+ expect(
+ await findByText('This feature is now enabled in your organization'),
+ ).toBeInTheDocument();
+ });
+
+ it('Check if uninstalled orgs includes current org', async () => {
+ const props = {
+ id: '1',
+ title: 'Test Addon',
+ description: 'Test addon description',
+ createdBy: 'Test User',
+ component: 'string',
+ installed: true,
+ configurable: true,
+ modified: true,
+ isInstalled: true,
+ uninstalledOrgs: ['undefined'],
+ enabled: true,
+ getInstalledPlugins: (): { sample: string } => {
+ return { sample: 'sample' };
+ },
+ };
+
+ const { getByTestId } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+ await wait(100);
+ const btn = getByTestId('AddOnEntry_btn_install');
+ expect(btn.innerHTML).toMatch(/install/i);
+ });
+ test('should be redirected to /orglist if orgId is undefined', async () => {
+ mockID = undefined;
+ render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+ await wait(100);
+ expect(window.location.pathname).toEqual('/orglist');
+ });
+});
diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx
new file mode 100644
index 0000000000..257917e2c2
--- /dev/null
+++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx
@@ -0,0 +1,152 @@
+import React, { useState } from 'react';
+import styles from './AddOnEntry.module.css';
+import { Button, Card, Spinner } from 'react-bootstrap';
+import { UPDATE_INSTALL_STATUS_PLUGIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import { Navigate, useParams } from 'react-router-dom';
+
+/**
+ * Props for the `addOnEntry` component.
+ */
+interface InterfaceAddOnEntryProps {
+ id: string;
+ enabled?: boolean; // Optional props
+ title?: string; // Optional props
+ description?: string; // Optional props
+ createdBy: string;
+ component?: string; // Optional props
+ modified?: any; // Optional props
+ uninstalledOrgs: string[];
+ getInstalledPlugins: () => any;
+}
+
+/**
+ * A React component that represents an add-on entry, displaying its details and allowing installation or uninstallation.
+ *
+ * @param props - The properties for the component.
+ * @returns A JSX element containing the add-on entry.
+ *
+ * @example
+ * ```tsx
+ * {}}
+ * />
+ * ```
+ */
+function addOnEntry({
+ id,
+ title = 'No title provided', // Default parameter
+ description = 'Description not available', // Default parameter
+ createdBy,
+ uninstalledOrgs,
+ getInstalledPlugins,
+ // enabled = false, // Default parameter
+ // component = '', // Default parameter
+ // modified = null, // Default parameter
+}: InterfaceAddOnEntryProps): JSX.Element {
+ // Translation hook with namespace 'addOnEntry'
+ const { t } = useTranslation('translation', { keyPrefix: 'addOnEntry' });
+
+ // Getting orgId from URL parameters
+ const { orgId: currentOrg } = useParams();
+ if (!currentOrg) {
+ // If orgId is not present in the URL, navigate to the org list page
+ return ;
+ }
+
+ // State to manage button loading state
+ const [buttonLoading, setButtonLoading] = useState(false);
+ // State to manage local installation status of the add-on
+ const [isInstalledLocal, setIsInstalledLocal] = useState(
+ uninstalledOrgs.includes(currentOrg),
+ );
+
+ // Mutation hook for updating the install status of the plugin
+ const [addOrgAsUninstalled] = useMutation(
+ UPDATE_INSTALL_STATUS_PLUGIN_MUTATION,
+ );
+
+ /**
+ * Function to toggle the installation status of the plugin.
+ */
+ const togglePluginInstall = async (): Promise => {
+ setButtonLoading(true);
+ await addOrgAsUninstalled({
+ variables: {
+ id: id.toString(),
+ orgId: currentOrg.toString(),
+ },
+ });
+
+ // Toggle the local installation status
+ setIsInstalledLocal(!isInstalledLocal);
+ setButtonLoading(false);
+
+ // Display a success message based on the new installation status
+ const dialog: string = isInstalledLocal
+ ? t('installMsg')
+ : t('uninstallMsg');
+ toast.success(dialog);
+ };
+
+ return (
+ <>
+
+ {/* {uninstalledOrgs.includes(currentOrg) && (
+ {}}
+ disabled={switchInProgress}
+ checked={enabled}
+ />
+ )} */}
+
+ {title}
+
+ {createdBy}
+
+ {description}
+ {
+ togglePluginInstall();
+ getInstalledPlugins();
+ }}
+ >
+ {buttonLoading ? (
+
+ ) : (
+
+ )}
+ {/* {installed ? 'Remove' : configurable ? 'Installed' : 'Install'} */}
+ {uninstalledOrgs.includes(currentOrg)
+ ? t('install')
+ : t('uninstall')}
+
+
+
+
+ >
+ );
+}
+
+export default addOnEntry;
diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntryMocks.ts b/src/components/AddOn/core/AddOnEntry/AddOnEntryMocks.ts
new file mode 100644
index 0000000000..3967a0963a
--- /dev/null
+++ b/src/components/AddOn/core/AddOnEntry/AddOnEntryMocks.ts
@@ -0,0 +1,25 @@
+import { UPDATE_INSTALL_STATUS_PLUGIN_MUTATION } from 'GraphQl/Mutations/mutations';
+
+// Mock data representing the response structure of the mutation
+const updatePluginStatus = {
+ _id: '123',
+ pluginName: 'Sample Plugin',
+ pluginCreatedBy: 'John Doe',
+ pluginDesc: 'This is a sample plugin description.',
+ uninstalledOrgs: [],
+};
+
+// Creating the mock entry
+export const ADD_ON_ENTRY_MOCK = [
+ {
+ request: {
+ query: UPDATE_INSTALL_STATUS_PLUGIN_MUTATION,
+ variables: { id: '1', orgId: 'undefined' },
+ },
+ result: {
+ data: {
+ updatePluginStatus: updatePluginStatus,
+ },
+ },
+ },
+];
diff --git a/src/components/AddOn/core/AddOnRegister/AddOnRegister.module.css b/src/components/AddOn/core/AddOnRegister/AddOnRegister.module.css
new file mode 100644
index 0000000000..c122d386fa
--- /dev/null
+++ b/src/components/AddOn/core/AddOnRegister/AddOnRegister.module.css
@@ -0,0 +1,9 @@
+.modalbtn {
+ display: flex !important;
+ margin-left: auto;
+ align-items: center;
+}
+
+.modalbtn i {
+ margin-right: 8px;
+}
diff --git a/src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx b/src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx
new file mode 100644
index 0000000000..dc6a7c2091
--- /dev/null
+++ b/src/components/AddOn/core/AddOnRegister/AddOnRegister.test.tsx
@@ -0,0 +1,207 @@
+import React, { act } from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { MockedProvider } from '@apollo/react-testing';
+import { ADD_PLUGIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import AddOnRegister from './AddOnRegister';
+import {
+ ApolloClient,
+ ApolloProvider,
+ InMemoryCache,
+ ApolloLink,
+ HttpLink,
+} from '@apollo/client';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { BrowserRouter } from 'react-router-dom';
+import { BACKEND_URL } from 'Constant/constant';
+import i18nForTest from 'utils/i18nForTest';
+import { I18nextProvider } from 'react-i18next';
+import { toast } from 'react-toastify';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { getItem } = useLocalStorage();
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+
+const mocks = [
+ {
+ request: {
+ query: ADD_PLUGIN_MUTATION,
+ variables: {
+ pluginName: 'Test Plugin',
+ pluginCreatedBy: 'AdminTest Creator',
+ pluginDesc: 'Test Description',
+ pluginInstallStatus: false,
+ installedOrgs: ['id'],
+ },
+ },
+ result: {
+ data: {
+ createPlugin: {
+ _id: '1',
+ pluginName: 'Test Plugin',
+ pluginCreatedBy: 'AdminTest Creator',
+ pluginDesc: 'Test Description',
+ },
+ },
+ },
+ },
+];
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const pluginData = {
+ pluginName: 'Test Plugin',
+ pluginCreatedBy: 'Test Creator',
+ pluginDesc: 'Test Description',
+};
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ },
+}));
+
+const mockNavigate = jest.fn();
+let mockId: string | undefined = 'id';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockId }),
+ useNavigate: () => mockNavigate,
+}));
+
+describe('Testing AddOnRegister', () => {
+ const props = {
+ id: '6234d8bf6ud937ddk70ecc5c9',
+ };
+
+ test('should render modal and take info to add plugin for registered organization', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ // Wait for the button to be in the document
+ await waitFor(() =>
+ expect(
+ screen.getByRole('button', { name: /Add New/i }),
+ ).toBeInTheDocument(),
+ );
+
+ // Simulate user interactions
+ userEvent.click(screen.getByRole('button', { name: /Add New/i }));
+ userEvent.type(screen.getByPlaceholderText(/Ex: Donations/i), 'myplugin');
+ userEvent.type(
+ screen.getByPlaceholderText(/This Plugin enables UI for/i),
+ 'test description',
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(/Ex: john Doe/i),
+ 'test creator',
+ );
+ });
+
+ test('Expect toast.success to be called on successful plugin addition', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await waitFor(() => new Promise((resolve) => setTimeout(resolve, 0)));
+
+ userEvent.click(screen.getByRole('button', { name: /Add New/i }));
+ await wait(100);
+ expect(screen.getByTestId('addonregisterBtn')).toBeInTheDocument();
+ userEvent.type(screen.getByTestId('pluginName'), pluginData.pluginName);
+ userEvent.type(
+ screen.getByTestId('pluginCreatedBy'),
+ pluginData.pluginCreatedBy,
+ );
+ userEvent.type(screen.getByTestId('pluginDesc'), pluginData.pluginDesc);
+ userEvent.click(screen.getByTestId('addonregisterBtn'));
+
+ await wait(100);
+ expect(toast.success).toHaveBeenCalledWith('Plugin added Successfully');
+ });
+
+ test('Expect the window to reload after successful plugin addition', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await waitFor(() => new Promise((resolve) => setTimeout(resolve, 0)));
+
+ userEvent.click(screen.getByRole('button', { name: /Add New/i }));
+ await wait(100);
+ expect(screen.getByTestId('addonregisterBtn')).toBeInTheDocument();
+ userEvent.type(screen.getByTestId('pluginName'), pluginData.pluginName);
+ userEvent.type(
+ screen.getByTestId('pluginCreatedBy'),
+ pluginData.pluginCreatedBy,
+ );
+ userEvent.type(screen.getByTestId('pluginDesc'), pluginData.pluginDesc);
+ userEvent.click(screen.getByTestId('addonregisterBtn'));
+
+ await wait(3000); // Waiting for 3 seconds to reload the page as timeout is set to 2 seconds in the component
+ expect(mockNavigate).toHaveBeenCalledWith(0);
+ });
+
+ test('should be redirected to /orglist if orgId is undefined', async () => {
+ mockId = undefined;
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ expect(window.location.pathname).toEqual('/orglist');
+ });
+});
diff --git a/src/components/AddOn/core/AddOnRegister/AddOnRegister.tsx b/src/components/AddOn/core/AddOnRegister/AddOnRegister.tsx
new file mode 100644
index 0000000000..6023e74ee1
--- /dev/null
+++ b/src/components/AddOn/core/AddOnRegister/AddOnRegister.tsx
@@ -0,0 +1,202 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import styles from './AddOnRegister.module.css';
+import { Button, Form, Modal } from 'react-bootstrap';
+import { useMutation } from '@apollo/client';
+import { ADD_PLUGIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import { Navigate, useNavigate, useParams } from 'react-router-dom';
+
+/**
+ * Interface defining the form state for the `addOnRegister` component.
+ */
+interface InterfaceFormStateTypes {
+ pluginName: string;
+ pluginCreatedBy: string;
+ pluginDesc: string;
+ pluginInstallStatus: boolean;
+ installedOrgs: [string] | [];
+}
+
+interface InterfaceAddOnRegisterProps {
+ createdBy?: string;
+}
+
+/**
+ * A React component for registering a new add-on plugin.
+ *
+ * This component:
+ * - Displays a button to open a modal for plugin registration.
+ * - Contains a form in the modal for entering plugin details.
+ * - Uses GraphQL mutation to register the plugin.
+ * - Uses `react-i18next` for localization and `react-toastify` for notifications.
+ * - Redirects to the organization list page if no `orgId` is found in the URL.
+ *
+ * @returns A JSX element containing the button and modal for plugin registration.
+ */
+function addOnRegister({
+ createdBy = 'Admin',
+}: InterfaceAddOnRegisterProps): JSX.Element {
+ // Translation hook for the 'addOnRegister' namespace
+ const { t } = useTranslation('translation', { keyPrefix: 'addOnRegister' });
+ // Translation hook for the 'common' namespace
+ const { t: tCommon } = useTranslation('common');
+
+ // Get the organization ID from the URL parameters
+ const { orgId: currentUrl } = useParams();
+ const navigate = useNavigate();
+
+ // Redirect to the organization list if no organization ID is found
+ if (!currentUrl) {
+ return ;
+ }
+
+ // State to manage the visibility of the modal
+ const [show, setShow] = useState(false);
+
+ // Functions to show and hide the modal
+ const handleClose = (): void => setShow(false);
+ const handleShow = (): void => setShow(true);
+
+ // GraphQL mutation hook for adding a plugin
+ const [create] = useMutation(ADD_PLUGIN_MUTATION);
+
+ // Initial form state
+ const [formState, setFormState] = useState({
+ pluginName: '',
+ pluginCreatedBy: createdBy, // Using the default value here
+ pluginDesc: '',
+ pluginInstallStatus: false,
+ installedOrgs: [currentUrl],
+ });
+
+ /**
+ * Handles the registration of the plugin.
+ * Sends the form data to the GraphQL mutation and displays a success message.
+ */
+ const handleRegister = async (): Promise => {
+ const { data } = await create({
+ variables: {
+ pluginName: formState.pluginName,
+ pluginCreatedBy: formState.pluginCreatedBy,
+ pluginDesc: formState.pluginDesc,
+ pluginInstallStatus: formState.pluginInstallStatus,
+ installedOrgs: formState.installedOrgs,
+ },
+ });
+
+ if (data) {
+ // Show a success message when the plugin is added
+ toast.success(tCommon('addedSuccessfully', { item: 'Plugin' }) as string);
+ // Refresh the page after 2 seconds
+ setTimeout(() => {
+ navigate(0);
+ }, 2000);
+ }
+ };
+
+ return (
+ <>
+ {/* Button to open the modal */}
+
+
+ {t('addNew')}
+
+
+ {/* Modal for plugin registration */}
+
+
+ {t('addPlugin')}
+
+
+
+ {t('pluginName')}
+ {
+ setFormState({
+ ...formState,
+ pluginName: e.target.value,
+ });
+ }}
+ />
+
+
+ {t('creatorName')}
+ {
+ setFormState({
+ ...formState,
+ pluginCreatedBy: e.target.value,
+ });
+ }}
+ />
+
+ {/* Form group for plugin description */}
+
+ {t('pluginDesc')}
+ {
+ setFormState({
+ ...formState,
+ pluginDesc: e.target.value,
+ });
+ }}
+ />
+
+
+
+
+ {/* Button to close the modal */}
+
+ {tCommon('close')}
+
+ {/* Button to register the plugin */}
+
+ {tCommon('register')}
+
+
+
+ >
+ );
+}
+
+// Prop types validation for the component
+addOnRegister.propTypes = {
+ createdBy: PropTypes.string,
+};
+
+export default addOnRegister;
diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.module.css b/src/components/AddOn/core/AddOnStore/AddOnStore.module.css
new file mode 100644
index 0000000000..8a34c03be5
--- /dev/null
+++ b/src/components/AddOn/core/AddOnStore/AddOnStore.module.css
@@ -0,0 +1,31 @@
+.container {
+ display: flex;
+}
+
+.logintitle {
+ color: #707070;
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 30px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid #31bb6b;
+ width: 15%;
+}
+
+.actioninput {
+ text-decoration: none;
+ margin-bottom: 50px;
+ border-color: #e8e5e5;
+ width: 80%;
+ border-radius: 7px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ box-shadow: none;
+}
+
+.actionradio input {
+ width: fit-content;
+ margin: inherit;
+}
diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx b/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx
new file mode 100644
index 0000000000..e76e2a7b73
--- /dev/null
+++ b/src/components/AddOn/core/AddOnStore/AddOnStore.test.tsx
@@ -0,0 +1,394 @@
+import React, { act } from 'react';
+import 'jest-location-mock';
+import { fireEvent, render, screen } from '@testing-library/react';
+import {
+ ApolloClient,
+ ApolloProvider,
+ InMemoryCache,
+ ApolloLink,
+ HttpLink,
+} from '@apollo/client';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { BrowserRouter } from 'react-router-dom';
+import AddOnStore from './AddOnStore';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { BACKEND_URL } from 'Constant/constant';
+import i18nForTest from 'utils/i18nForTest';
+import { I18nextProvider } from 'react-i18next';
+import { ORGANIZATIONS_LIST, PLUGIN_GET } from 'GraphQl/Queries/Queries';
+import userEvent from '@testing-library/user-event';
+import useLocalStorage from 'utils/useLocalstorage';
+import { MockedProvider } from '@apollo/react-testing';
+
+const { getItem } = useLocalStorage();
+
+jest.mock('components/AddOn/support/services/Plugin.helper', () => ({
+ __esModule: true,
+ default: jest.fn().mockImplementation(() => ({
+ fetchStore: jest.fn().mockResolvedValue([
+ {
+ _id: '1',
+ pluginName: 'Plugin 1',
+ pluginDesc: 'Description 1',
+ pluginCreatedBy: 'User 1',
+ pluginInstallStatus: true,
+ },
+ {
+ _id: '2',
+ pluginName: 'Plugin 2',
+ pluginDesc: 'Description 2',
+ pluginCreatedBy: 'User 2',
+ pluginInstallStatus: false,
+ },
+ // Add more mock data as needed
+ ]),
+ fetchInstalled: jest.fn().mockResolvedValue([
+ {
+ _id: '1',
+ pluginName: 'Installed Plugin 1',
+ pluginDesc: 'Installed Description 1',
+ pluginCreatedBy: 'User 3',
+ pluginInstallStatus: true,
+ },
+ {
+ _id: '3',
+ pluginName: 'Installed Plugin 3',
+ pluginDesc: 'Installed Description 3',
+ pluginCreatedBy: 'User 4',
+ pluginInstallStatus: true,
+ },
+ // Add more mock data as needed
+ ]),
+ generateLinks: jest.fn().mockImplementation((plugins) => {
+ return plugins
+ .filter((plugin: { enabled: any }) => plugin.enabled)
+ .map((installedPlugin: { pluginName: any; component: string }) => {
+ return {
+ name: installedPlugin.pluginName,
+ url: `/plugin/${installedPlugin.component.toLowerCase()}`,
+ };
+ });
+ }),
+ })),
+}));
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+
+jest.mock('components/AddOn/support/services/Plugin.helper', () => ({
+ __esModule: true,
+ default: jest.fn().mockImplementation(() => ({
+ fetchInstalled: jest.fn().mockResolvedValue([]),
+ fetchStore: jest.fn().mockResolvedValue([]),
+ })),
+}));
+
+const today = new Date();
+const tomorrow = today;
+tomorrow.setDate(today.getDate() + 1);
+
+const PLUGIN_GET_MOCK = {
+ request: {
+ query: PLUGIN_GET,
+ },
+ result: {
+ data: {
+ getPlugins: [
+ {
+ _id: '6581be50e88e74003aab436c',
+ pluginName: 'Plugin 1',
+ pluginCreatedBy: 'Talawa Team',
+ pluginDesc:
+ 'User can share messages with other users in a chat user interface.',
+ uninstalledOrgs: [
+ '62ccfccd3eb7fd2a30f41601',
+ '62ccfccd3eb7fd2a30f41601',
+ ],
+ __typename: 'Plugin',
+ },
+ {
+ _id: '6581be50e88e74003aab436d',
+ pluginName: 'Plugin 2',
+ pluginCreatedBy: 'Talawa Team',
+ pluginDesc:
+ 'User can share messages with other users in a chat user interface.',
+ uninstalledOrgs: ['6537904485008f171cf29924'],
+ __typename: 'Plugin',
+ },
+ {
+ _id: '6581be50e88e74003aab436e',
+ pluginName: 'Plugin 3',
+ pluginCreatedBy: 'Talawa Team',
+ pluginDesc:
+ 'User can share messages with other users in a chat user interface.',
+ uninstalledOrgs: [
+ '62ccfccd3eb7fd2a30f41601',
+ '62ccfccd3eb7fd2a30f41601',
+ ],
+ __typename: 'Plugin',
+ },
+ ],
+ },
+ loading: false,
+ },
+};
+
+const PLUGIN_LOADING_MOCK = {
+ request: {
+ query: PLUGIN_GET,
+ },
+ result: {
+ data: {
+ getPlugins: [],
+ },
+ loading: true,
+ },
+};
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: 'undefined' }),
+}));
+const ORGANIZATIONS_LIST_MOCK = {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: {
+ id: 'undefined',
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: 'undefined',
+ image: '',
+ creator: {
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ name: 'name',
+ description: 'description',
+ userRegistrationRequired: true,
+
+ visibleInSearch: true,
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ members: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ admins: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ membershipRequests: {
+ _id: 'id',
+ user: {
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ },
+ blockedUsers: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ },
+ ],
+ },
+ },
+};
+
+describe('Testing AddOnStore Component', () => {
+ test('for the working of the tabs', async () => {
+ const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText('Installed'));
+
+ await wait();
+ userEvent.click(screen.getByText('Available'));
+ });
+
+ test('check the working search bar when on Available tab', async () => {
+ const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText('Available'));
+
+ await wait();
+ let searchText = '';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+ expect(screen.getAllByText('Plugin 1').length).toBeGreaterThanOrEqual(1);
+ expect(screen.getAllByText('Plugin 2').length).toBeGreaterThanOrEqual(1);
+ expect(screen.getAllByText('Plugin 3').length).toBeGreaterThanOrEqual(1);
+
+ searchText = 'Plugin 1';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+ const plugin1Elements = screen.queryAllByText('Plugin 1');
+ expect(plugin1Elements.length).toBeGreaterThan(1);
+
+ searchText = 'Test Plugin';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+
+ const message = screen.getAllByText('Plugin does not exists');
+ expect(message.length).toBeGreaterThanOrEqual(1);
+ });
+
+ test('check filters enabled and disabled under Installed tab', async () => {
+ const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK];
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText('Installed'));
+
+ expect(screen.getByText('Filters')).toBeInTheDocument();
+ expect(screen.getByLabelText('Enabled')).toBeInTheDocument();
+ expect(screen.getByLabelText('Disabled')).toBeInTheDocument();
+
+ fireEvent.click(screen.getByLabelText('Enabled'));
+ expect(screen.getByLabelText('Enabled')).toBeChecked();
+ fireEvent.click(screen.getByLabelText('Disabled'));
+ expect(screen.getByLabelText('Disabled')).toBeChecked();
+ });
+
+ test('check the working search bar when on Installed tab', async () => {
+ const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_GET_MOCK];
+
+ const { container } = render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByText('Installed'));
+
+ await wait();
+ let searchText = '';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+ expect(container).toHaveTextContent('Plugin 1');
+ expect(container).toHaveTextContent('Plugin 3');
+
+ searchText = 'Plugin 1';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+ const plugin1Elements = screen.queryAllByText('Plugin 1');
+ expect(plugin1Elements.length).toBeGreaterThan(1);
+
+ searchText = 'Test Plugin';
+ fireEvent.change(screen.getByPlaceholderText('Ex: Donations'), {
+ target: { value: searchText },
+ });
+ const message = screen.getAllByText('Plugin does not exists');
+ expect(message.length).toBeGreaterThanOrEqual(1);
+ });
+
+ test('AddOnStore loading test', async () => {
+ expect(true).toBe(true);
+ const mocks = [ORGANIZATIONS_LIST_MOCK, PLUGIN_LOADING_MOCK];
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByTestId('AddOnEntryStore')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.tsx b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx
new file mode 100644
index 0000000000..878ad64e31
--- /dev/null
+++ b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx
@@ -0,0 +1,317 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import React, { useState } from 'react';
+// import PropTypes from 'react';
+import styles from './AddOnStore.module.css';
+import AddOnEntry from '../AddOnEntry/AddOnEntry';
+import Action from '../../support/components/Action/Action';
+import { useQuery } from '@apollo/client';
+import { PLUGIN_GET } from 'GraphQl/Queries/Queries'; // GraphQL query for fetching plugins
+import { Col, Form, Row, Tab, Tabs } from 'react-bootstrap';
+import PluginHelper from 'components/AddOn/support/services/Plugin.helper';
+import { store } from './../../../../state/store';
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+
+/**
+ * Component for managing and displaying plugins in the store.
+ *
+ * This component:
+ * - Displays a search input and filter options.
+ * - Uses tabs to switch between available and installed plugins.
+ * - Fetches plugins from a GraphQL endpoint and filters them based on search criteria.
+ * - Utilizes Redux store to manage plugin data.
+ *
+ * @returns A JSX element containing the UI for the add-on store.
+ */
+function addOnStore(): JSX.Element {
+ const { t } = useTranslation('translation', { keyPrefix: 'addOnStore' });
+ document.title = t('title');
+
+ const [isStore, setIsStore] = useState(true);
+ const [showEnabled, setShowEnabled] = useState(true);
+ const [searchText, setSearchText] = useState('');
+ const [, setDataList] = useState([]);
+
+ // type plugData = { pluginName: String, plug };
+ const { data, loading } = useQuery(PLUGIN_GET);
+
+ const { orgId } = useParams();
+
+ /**
+ * Fetches store plugins and updates the Redux store with the plugin data.
+ */
+ /* istanbul ignore next */
+ const getStorePlugins = async (): Promise => {
+ let plugins = await new PluginHelper().fetchStore();
+ const installIds = (await new PluginHelper().fetchInstalled()).map(
+ (plugin: any) => plugin.id,
+ );
+ plugins = plugins.map((plugin: any) => {
+ plugin.installed = installIds.includes(plugin.id);
+ return plugin;
+ });
+ store.dispatch({ type: 'UPDATE_STORE', payload: plugins });
+ };
+
+ /**
+ * Sets the list of installed plugins in the component's state.
+ */
+ /* istanbul ignore next */
+ const getInstalledPlugins: () => any = () => {
+ setDataList(data);
+ };
+
+ /**
+ * Updates the currently selected tab and fetches the relevant plugin data.
+ *
+ * @param tab - The key of the selected tab (either 'available' or 'installed').
+ */
+ const updateSelectedTab = (tab: any): void => {
+ setIsStore(tab === 'available');
+ /* istanbul ignore next */
+ isStore ? getStorePlugins() : getInstalledPlugins();
+ };
+
+ /**
+ * Handles changes in the filter options.
+ *
+ * @param ev - The event object from the filter change.
+ */
+ const filterChange = (ev: any): void => {
+ setShowEnabled(ev.target.value === 'enabled');
+ };
+
+ // Show a loader while the data is being fetched
+ /* istanbul ignore next */
+ if (loading) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+
+
+ setSearchText(e.target.value)}
+ />
+
+ {!isStore && (
+
+
+
+ )}
+
+
+
+
{t('pHeading')}
+ {searchText ? (
+
+ Search results for {searchText}
+
+ ) : null}
+
+
+
+ {data.getPlugins.filter(
+ (val: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ pluginInstallStatus: boolean | undefined;
+ getInstalledPlugins: () => any;
+ }) => {
+ if (searchText == '') {
+ return val;
+ } else if (
+ val.pluginName
+ ?.toLowerCase()
+ .includes(searchText.toLowerCase())
+ ) {
+ return val;
+ }
+ },
+ ).length === 0 ? (
+ {t('pMessage')}
+ ) : (
+ data.getPlugins
+ .filter(
+ (val: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ pluginInstallStatus: boolean | undefined;
+ getInstalledPlugins: () => any;
+ }) => {
+ if (searchText == '') {
+ return val;
+ } else if (
+ val.pluginName
+ ?.toLowerCase()
+ .includes(searchText.toLowerCase())
+ ) {
+ return val;
+ }
+ },
+ )
+ .map(
+ (
+ plug: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ uninstalledOrgs: string[];
+ getInstalledPlugins: () => any;
+ },
+ i: React.Key | null | undefined,
+ ): JSX.Element => (
+
+ ),
+ )
+ )}
+
+
+ {data.getPlugins
+ .filter(
+ (plugin: any) => !plugin.uninstalledOrgs.includes(orgId),
+ )
+ .filter(
+ (val: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ pluginInstallStatus: boolean | undefined;
+ getInstalledPlugins: () => any;
+ }) => {
+ if (searchText == '') {
+ return val;
+ } else if (
+ val.pluginName
+ ?.toLowerCase()
+ .includes(searchText.toLowerCase())
+ ) {
+ return val;
+ }
+ },
+ ).length === 0 ? (
+ {t('pMessage')}
+ ) : (
+ data.getPlugins
+ .filter(
+ (plugin: any) => !plugin.uninstalledOrgs.includes(orgId),
+ )
+ .filter(
+ (val: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ pluginInstallStatus: boolean | undefined;
+ getInstalledPlugins: () => any;
+ }) => {
+ if (searchText == '') {
+ return val;
+ } else if (
+ val.pluginName
+ ?.toLowerCase()
+ .includes(searchText.toLowerCase())
+ ) {
+ return val;
+ }
+ },
+ )
+ .map(
+ (
+ plug: {
+ _id: string;
+ pluginName: string | undefined;
+ pluginDesc: string | undefined;
+ pluginCreatedBy: string;
+ uninstalledOrgs: string[];
+ pluginInstallStatus: boolean | undefined;
+ getInstalledPlugins: () => any;
+ },
+ i: React.Key | null | undefined,
+ ): JSX.Element => (
+
+ ),
+ )
+ )}
+
+
+
+
+
+ >
+ );
+}
+
+export default addOnStore;
diff --git a/src/components/AddOn/support/components/Action/Action.module.css b/src/components/AddOn/support/components/Action/Action.module.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/components/AddOn/support/components/Action/Action.test.tsx b/src/components/AddOn/support/components/Action/Action.test.tsx
new file mode 100644
index 0000000000..ce6cd633b9
--- /dev/null
+++ b/src/components/AddOn/support/components/Action/Action.test.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { Provider } from 'react-redux';
+
+import { store } from 'state/store';
+import Action from './Action';
+
+describe('Testing Action Component', () => {
+ const props = {
+ children: 'dummy children',
+ label: 'dummy label',
+ };
+
+ test('should render props and text elements test for the page component', () => {
+ const { getByText } = render(
+
+
+ ,
+ );
+
+ expect(getByText(props.label)).toBeInTheDocument();
+ expect(getByText(props.children)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/AddOn/support/components/Action/Action.tsx b/src/components/AddOn/support/components/Action/Action.tsx
new file mode 100644
index 0000000000..f29d8492d3
--- /dev/null
+++ b/src/components/AddOn/support/components/Action/Action.tsx
@@ -0,0 +1,40 @@
+import React, { useRef } from 'react';
+
+/**
+ * Props for the `action` component.
+ */
+interface InterfaceActionProps {
+ /**
+ * The child elements to be rendered inside the action component.
+ */
+ children: any;
+
+ /**
+ * The label to be displayed above the child elements.
+ */
+ label: string;
+}
+
+/**
+ * A React component that renders a labeled container for embedded actions.
+ *
+ * @param props - The properties for the component.
+ * @returns A JSX element containing the label and child elements.
+ *
+ * @example
+ *
+ * Click Me
+ *
+ */
+function action(props: InterfaceActionProps): JSX.Element {
+ const actionRef = useRef(null);
+
+ return (
+
+
{props.label}
+ {props.children}
+
+ );
+}
+
+export default action;
diff --git a/src/components/AddOn/support/components/MainContent/MainContent.module.css b/src/components/AddOn/support/components/MainContent/MainContent.module.css
new file mode 100644
index 0000000000..3321cf6fd7
--- /dev/null
+++ b/src/components/AddOn/support/components/MainContent/MainContent.module.css
@@ -0,0 +1,4 @@
+.maincontainer {
+ width: 70vw;
+ margin-right: 2rem;
+}
diff --git a/src/components/AddOn/support/components/MainContent/MainContent.test.tsx b/src/components/AddOn/support/components/MainContent/MainContent.test.tsx
new file mode 100644
index 0000000000..81adbc916e
--- /dev/null
+++ b/src/components/AddOn/support/components/MainContent/MainContent.test.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+
+import { store } from 'state/store';
+import MainContent from './MainContent';
+
+describe('Testing MainContent component', () => {
+ const props = {
+ children: 'This is a dummy text',
+ };
+
+ test('should render props and children for the Main Content', () => {
+ const { getByTestId, getByText } = render(
+
+
+
+
+ ,
+ );
+
+ expect(getByTestId('mainContentCheck')).toBeInTheDocument();
+ expect(getByText(props.children)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/AddOn/support/components/MainContent/MainContent.tsx b/src/components/AddOn/support/components/MainContent/MainContent.tsx
new file mode 100644
index 0000000000..eddc1b993d
--- /dev/null
+++ b/src/components/AddOn/support/components/MainContent/MainContent.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import styles from './MainContent.module.css';
+
+/**
+ * Props for the `mainContent` component.
+ */
+interface InterfaceMainContentProps {
+ /**
+ * The child elements to be rendered inside the main content container.
+ */
+ children: any;
+}
+
+/**
+ * A React component that renders a main content container with additional styles.
+ *
+ * @param props - The properties for the component.
+ * @returns A JSX element containing the main content container with the provided child elements.
+ *
+ * @example
+ *
+ * Main content goes here
+ *
+ */
+function mainContent({ children }: InterfaceMainContentProps): JSX.Element {
+ return (
+
+ {children}
+
+ );
+}
+
+export default mainContent;
diff --git a/src/components/AddOn/support/components/SidePanel/SidePanel.module.css b/src/components/AddOn/support/components/SidePanel/SidePanel.module.css
new file mode 100644
index 0000000000..f337eeea1b
--- /dev/null
+++ b/src/components/AddOn/support/components/SidePanel/SidePanel.module.css
@@ -0,0 +1,12 @@
+.sidebarcontainer {
+ width: 30vw;
+ justify-content: center;
+ display: flex;
+ flex-direction: column;
+ padding: 2rem;
+ height: fit-content;
+}
+
+.sidebarcollapsed {
+ display: none;
+}
diff --git a/src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx b/src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx
new file mode 100644
index 0000000000..d929278d0e
--- /dev/null
+++ b/src/components/AddOn/support/components/SidePanel/SidePanel.test.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import SidePanel from './SidePanel';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
+import { BACKEND_URL } from 'Constant/constant';
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ uri: BACKEND_URL,
+});
+
+describe('Testing Contribution Stats', () => {
+ const props = {
+ collapse: true,
+ children: '234',
+ };
+
+ test('should render props and text elements test for the SidePanel component', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.getByTestId('SidePanel')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/AddOn/support/components/SidePanel/SidePanel.tsx b/src/components/AddOn/support/components/SidePanel/SidePanel.tsx
new file mode 100644
index 0000000000..d4c0d65966
--- /dev/null
+++ b/src/components/AddOn/support/components/SidePanel/SidePanel.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import styles from './SidePanel.module.css';
+
+/**
+ * Props for the `sidePanel` component.
+ */
+interface InterfaceSidePanelProps {
+ /**
+ * Whether the side panel should be collapsed.
+ */
+ collapse?: boolean;
+
+ /**
+ * The child elements to be rendered inside the side panel.
+ */
+ children: any;
+}
+
+/**
+ * A React component that renders a side panel with an optional collapse state.
+ *
+ * @param props - The properties for the component.
+ * @returns A JSX element containing the side panel with the provided child elements.
+ *
+ * @example
+ *
+ * Side panel content
+ *
+ */
+function sidePanel({
+ collapse,
+ children,
+}: InterfaceSidePanelProps): JSX.Element {
+ return (
+
+ {children}
+
+ );
+}
+
+export default sidePanel;
diff --git a/src/components/AddOn/support/services/Plugin.helper.test.ts b/src/components/AddOn/support/services/Plugin.helper.test.ts
new file mode 100644
index 0000000000..e024734247
--- /dev/null
+++ b/src/components/AddOn/support/services/Plugin.helper.test.ts
@@ -0,0 +1,46 @@
+import PluginHelper from './Plugin.helper';
+
+describe('Testing src/components/AddOn/support/services/Plugin.helper.ts', () => {
+ test('Class should contain the required method definitions', () => {
+ const pluginHelper = new PluginHelper();
+ expect(pluginHelper).toHaveProperty('fetchStore');
+ expect(pluginHelper).toHaveProperty('fetchInstalled');
+ expect(pluginHelper).toHaveProperty('generateLinks');
+ expect(pluginHelper).toHaveProperty('generateLinks');
+ });
+ test('generateLinks should return proper objects', () => {
+ const obj = { enabled: true, name: 'demo', component: 'samplecomponent' };
+ const objToMatch = { name: 'demo', url: '/plugin/samplecomponent' };
+ const pluginHelper = new PluginHelper();
+ const val = pluginHelper.generateLinks([obj]);
+ expect(val).toMatchObject([objToMatch]);
+ });
+ it('fetchStore should return expected JSON', async () => {
+ const helper = new PluginHelper();
+ const spy = jest.spyOn(global, 'fetch').mockImplementation(() => {
+ const response = new Response();
+ response.json = jest
+ .fn()
+ .mockReturnValue(Promise.resolve({ data: 'mock data' }));
+ return Promise.resolve(response);
+ });
+
+ const result = await helper.fetchStore();
+ expect(result).toEqual({ data: 'mock data' });
+ spy.mockRestore();
+ });
+ it('fetchInstalled() should return expected JSON', async () => {
+ const pluginHelper = new PluginHelper();
+ const mockResponse = [
+ { name: 'plugin1', component: 'Component1', enabled: true },
+ { name: 'plugin2', component: 'Component2', enabled: false },
+ ];
+ jest.spyOn(global, 'fetch').mockImplementation(() => {
+ const response = new Response();
+ response.json = jest.fn().mockReturnValue(Promise.resolve(mockResponse));
+ return Promise.resolve(response);
+ }) as jest.Mock;
+ const result = await pluginHelper.fetchInstalled();
+ expect(result).toEqual(mockResponse);
+ });
+});
diff --git a/src/components/AddOn/support/services/Plugin.helper.ts b/src/components/AddOn/support/services/Plugin.helper.ts
new file mode 100644
index 0000000000..1ebefc351f
--- /dev/null
+++ b/src/components/AddOn/support/services/Plugin.helper.ts
@@ -0,0 +1,44 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/**
+ * Helper class for managing plugin-related tasks such as fetching store data, installed plugins, and generating plugin links.
+ */
+class PluginHelper {
+ /**
+ * Fetches the store data from a local server.
+ *
+ * @returns A promise that resolves to the store data in JSON format.
+ */
+ fetchStore = async (): Promise => {
+ const result = await fetch(`http://localhost:${process.env.PORT}/store`);
+ return await result.json();
+ };
+
+ /**
+ * Fetches the list of installed plugins from a local server.
+ *
+ * @returns A promise that resolves to the installed plugins data in JSON format.
+ */
+ fetchInstalled = async (): Promise => {
+ const result = await fetch(`http://localhost:3005/installed`);
+ return await result.json();
+ };
+
+ /**
+ * Generates an array of links for the enabled plugins.
+ *
+ * @param plugins - An array of plugin objects.
+ * @returns An array of objects containing the name and URL of each enabled plugin.
+ */
+ generateLinks = (plugins: any[]): { name: string; url: string }[] => {
+ return plugins
+ .filter((plugin: any) => plugin.enabled)
+ .map((installedPlugin: any) => {
+ return {
+ name: installedPlugin.name,
+ url: `/plugin/${installedPlugin.component.toLowerCase()}`,
+ };
+ });
+ };
+}
+
+export default PluginHelper;
diff --git a/src/components/AddOn/support/services/Render.helper.ts b/src/components/AddOn/support/services/Render.helper.ts
new file mode 100644
index 0000000000..21202dc7af
--- /dev/null
+++ b/src/components/AddOn/support/services/Render.helper.ts
@@ -0,0 +1,3 @@
+class RenderHelper {}
+
+export default RenderHelper;
diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.module.css b/src/components/AddPeopleToTag/AddPeopleToTag.module.css
new file mode 100644
index 0000000000..5dd04ffed5
--- /dev/null
+++ b/src/components/AddPeopleToTag/AddPeopleToTag.module.css
@@ -0,0 +1,54 @@
+.errorContainer {
+ min-height: 100vh;
+}
+
+.errorMessage {
+ margin-top: 25%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.errorIcon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.tableHeader {
+ background-color: var(--bs-primary);
+ color: var(--bs-white);
+ font-size: 1rem;
+}
+
+.rowBackground {
+ background-color: var(--bs-white);
+ max-height: 120px;
+}
+
+.scrollContainer {
+ height: 100px;
+ overflow-y: auto;
+ margin-bottom: 1rem;
+}
+
+.memberBadge {
+ display: flex;
+ align-items: center;
+ padding: 5px 10px;
+ border-radius: 12px;
+ box-shadow: 0 1px 3px var(--bs-gray-400);
+ max-width: calc(100% - 2rem);
+}
+
+.removeFilterIcon {
+ cursor: pointer;
+}
+
+.loadingDiv {
+ min-height: 300px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx b/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx
new file mode 100644
index 0000000000..824bc25654
--- /dev/null
+++ b/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx
@@ -0,0 +1,316 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ waitFor,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MemoryRouter, Route, Routes } from 'react-router-dom';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast } from 'react-toastify';
+import { InMemoryCache, type ApolloLink } from '@apollo/client';
+import type { InterfaceAddPeopleToTagProps } from './AddPeopleToTag';
+import AddPeopleToTag from './AddPeopleToTag';
+import i18n from 'utils/i18nForTest';
+import { MOCKS, MOCKS_ERROR } from './AddPeopleToTagsMocks';
+import type { TFunction } from 'i18next';
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR, true);
+
+async function wait(): Promise {
+ await waitFor(() => {
+ // The waitFor utility automatically uses optimal timing
+ return Promise.resolve();
+ });
+}
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(i18n.getDataByLanguage('en')?.translation.manageTag ?? {}),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const props: InterfaceAddPeopleToTagProps = {
+ addPeopleToTagModalIsOpen: true,
+ hideAddPeopleToTagModal: () => {},
+ refetchAssignedMembersData: () => {},
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+};
+
+const cache = new InMemoryCache({
+ typePolicies: {
+ Query: {
+ fields: {
+ getUserTag: {
+ merge(existing = {}, incoming) {
+ const merged = {
+ ...existing,
+ ...incoming,
+ usersToAssignTo: {
+ ...existing.usersToAssignTo,
+ ...incoming.usersToAssignTo,
+ edges: [
+ ...(existing.usersToAssignTo?.edges || []),
+ ...(incoming.usersToAssignTo?.edges || []),
+ ],
+ },
+ };
+
+ return merged;
+ },
+ },
+ },
+ },
+ },
+});
+
+const renderAddPeopleToTagModal = (
+ props: InterfaceAddPeopleToTagProps,
+ link: ApolloLink,
+): RenderResult => {
+ return render(
+
+
+
+
+
+ }
+ />
+
+
+
+
+ ,
+ );
+};
+
+describe('Organisation Tags Page', () => {
+ beforeEach(() => {
+ jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: 'orgId' }),
+ }));
+ cache.reset();
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('Component loads correctly', async () => {
+ const { getByText } = renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.addPeople)).toBeInTheDocument();
+ });
+ });
+
+ test('Renders error component when when query is unsuccessful', async () => {
+ const { queryByText } = renderAddPeopleToTagModal(props, link2);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(queryByText(translations.addPeople)).not.toBeInTheDocument();
+ });
+ });
+
+ test('Selects and deselects members to assign to', async () => {
+ renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('selectMemberBtn')[0]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('selectMemberBtn')[0]);
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('selectMemberBtn')[1]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('selectMemberBtn')[1]);
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('clearSelectedMember')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('clearSelectedMember')[0]);
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('deselectMemberBtn')[0]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('deselectMemberBtn')[0]);
+ });
+
+ test('searchs for tags where the firstName matches the provided firstName search input', async () => {
+ renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getByPlaceholderText(translations.firstName),
+ ).toBeInTheDocument();
+ });
+ const input = screen.getByPlaceholderText(translations.firstName);
+ fireEvent.change(input, { target: { value: 'usersToAssignTo' } });
+
+ // should render the two users from the mock data
+ // where firstName starts with "usersToAssignTo"
+ await waitFor(() => {
+ const members = screen.getAllByTestId('memberName');
+ expect(members.length).toEqual(2);
+ });
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('memberName')[0]).toHaveTextContent(
+ 'usersToAssignTo user1',
+ );
+ });
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('memberName')[1]).toHaveTextContent(
+ 'usersToAssignTo user2',
+ );
+ });
+ });
+
+ test('searchs for tags where the lastName matches the provided lastName search input', async () => {
+ renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getByPlaceholderText(translations.lastName),
+ ).toBeInTheDocument();
+ });
+ const input = screen.getByPlaceholderText(translations.lastName);
+ fireEvent.change(input, { target: { value: 'userToAssignTo' } });
+
+ // should render the two users from the mock data
+ // where lastName starts with "usersToAssignTo"
+ await waitFor(() => {
+ const members = screen.getAllByTestId('memberName');
+ expect(members.length).toEqual(2);
+ });
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('memberName')[0]).toHaveTextContent(
+ 'first userToAssignTo',
+ );
+ });
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('memberName')[1]).toHaveTextContent(
+ 'second userToAssignTo',
+ );
+ });
+ });
+
+ test('Renders more members with infinite scroll', async () => {
+ const { getByText } = renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.addPeople)).toBeInTheDocument();
+ });
+
+ // Find the infinite scroll div by test ID or another selector
+ const addPeopleToTagScrollableDiv = screen.getByTestId(
+ 'addPeopleToTagScrollableDiv',
+ );
+
+ const initialMemberDataLength = screen.getAllByTestId('memberName').length;
+
+ // Set scroll position to the bottom
+ fireEvent.scroll(addPeopleToTagScrollableDiv, {
+ target: { scrollY: addPeopleToTagScrollableDiv.scrollHeight },
+ });
+
+ await waitFor(() => {
+ const finalMemberDataLength = screen.getAllByTestId('memberName').length;
+ expect(finalMemberDataLength).toBeGreaterThan(initialMemberDataLength);
+
+ expect(getByText(translations.addPeople)).toBeInTheDocument();
+ });
+ });
+
+ test('Toasts error when no one is selected while assigning', async () => {
+ renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('assignPeopleBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('assignPeopleBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(translations.noOneSelected);
+ });
+ });
+
+ test('Assigns tag to multiple people', async () => {
+ renderAddPeopleToTagModal(props, link);
+
+ await wait();
+
+ // select members and assign them
+ await waitFor(() => {
+ expect(screen.getAllByTestId('selectMemberBtn')[0]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('selectMemberBtn')[0]);
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('selectMemberBtn')[1]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('selectMemberBtn')[1]);
+
+ await waitFor(() => {
+ expect(screen.getAllByTestId('selectMemberBtn')[2]).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('selectMemberBtn')[2]);
+
+ userEvent.click(screen.getByTestId('assignPeopleBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfullyAssignedToPeople,
+ );
+ });
+ });
+});
diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.tsx b/src/components/AddPeopleToTag/AddPeopleToTag.tsx
new file mode 100644
index 0000000000..51d21a6a1c
--- /dev/null
+++ b/src/components/AddPeopleToTag/AddPeopleToTag.tsx
@@ -0,0 +1,419 @@
+import { useMutation, useQuery } from '@apollo/client';
+import type { GridCellParams, GridColDef } from '@mui/x-data-grid';
+import { DataGrid } from '@mui/x-data-grid';
+import { USER_TAGS_MEMBERS_TO_ASSIGN_TO } from 'GraphQl/Queries/userTagQueries';
+import type { ChangeEvent } from 'react';
+import React, { useEffect, useState } from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import { useParams } from 'react-router-dom';
+import type { InterfaceQueryUserTagsMembersToAssignTo } from 'utils/interfaces';
+import styles from './AddPeopleToTag.module.css';
+import type { InterfaceTagUsersToAssignToQuery } from 'utils/organizationTagsUtils';
+import {
+ TAGS_QUERY_DATA_CHUNK_SIZE,
+ dataGridStyle,
+} from 'utils/organizationTagsUtils';
+import { Stack } from '@mui/material';
+import { toast } from 'react-toastify';
+import { ADD_PEOPLE_TO_TAG } from 'GraphQl/Mutations/TagMutations';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import { WarningAmberRounded } from '@mui/icons-material';
+import { useTranslation } from 'react-i18next';
+import InfiniteScrollLoader from 'components/InfiniteScrollLoader/InfiniteScrollLoader';
+import type { TFunction } from 'i18next';
+
+/**
+ * Props for the `AddPeopleToTag` component.
+ */
+export interface InterfaceAddPeopleToTagProps {
+ addPeopleToTagModalIsOpen: boolean;
+ hideAddPeopleToTagModal: () => void;
+ refetchAssignedMembersData: () => void;
+ t: TFunction<'translation', 'manageTag'>;
+ tCommon: TFunction<'common', undefined>;
+}
+
+interface InterfaceMemberData {
+ _id: string;
+ firstName: string;
+ lastName: string;
+}
+
+const AddPeopleToTag: React.FC = ({
+ addPeopleToTagModalIsOpen,
+ hideAddPeopleToTagModal,
+ refetchAssignedMembersData,
+ t,
+ tCommon,
+}) => {
+ const { tagId: currentTagId } = useParams();
+
+ const { t: tErrors } = useTranslation('error');
+
+ const [assignToMembers, setAssignToMembers] = useState(
+ [],
+ );
+
+ const [memberToAssignToSearchFirstName, setMemberToAssignToSearchFirstName] =
+ useState('');
+ const [memberToAssignToSearchLastName, setMemberToAssignToSearchLastName] =
+ useState('');
+
+ const {
+ data: userTagsMembersToAssignToData,
+ loading: userTagsMembersToAssignToLoading,
+ error: userTagsMembersToAssignToError,
+ refetch: userTagsMembersToAssignToRefetch,
+ fetchMore: fetchMoreMembersToAssignTo,
+ }: InterfaceTagUsersToAssignToQuery = useQuery(
+ USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ {
+ variables: {
+ id: currentTagId,
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: {
+ firstName: { starts_with: memberToAssignToSearchFirstName },
+ lastName: { starts_with: memberToAssignToSearchLastName },
+ },
+ },
+ skip: !addPeopleToTagModalIsOpen,
+ },
+ );
+
+ useEffect(() => {
+ setMemberToAssignToSearchFirstName('');
+ setMemberToAssignToSearchLastName('');
+ userTagsMembersToAssignToRefetch();
+ }, [addPeopleToTagModalIsOpen]);
+
+ const loadMoreMembersToAssignTo = (): void => {
+ fetchMoreMembersToAssignTo({
+ variables: {
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after:
+ userTagsMembersToAssignToData?.getUsersToAssignTo.usersToAssignTo
+ .pageInfo.endCursor, // Fetch after the last loaded cursor
+ },
+ updateQuery: (
+ prevResult: {
+ getUsersToAssignTo: InterfaceQueryUserTagsMembersToAssignTo;
+ },
+ {
+ fetchMoreResult,
+ }: {
+ fetchMoreResult: {
+ getUsersToAssignTo: InterfaceQueryUserTagsMembersToAssignTo;
+ };
+ },
+ ) => {
+ if (!fetchMoreResult) /* istanbul ignore next */ return prevResult;
+
+ return {
+ getUsersToAssignTo: {
+ ...fetchMoreResult.getUsersToAssignTo,
+ usersToAssignTo: {
+ ...fetchMoreResult.getUsersToAssignTo.usersToAssignTo,
+ edges: [
+ ...prevResult.getUsersToAssignTo.usersToAssignTo.edges,
+ ...fetchMoreResult.getUsersToAssignTo.usersToAssignTo.edges,
+ ],
+ },
+ },
+ };
+ },
+ });
+ };
+
+ const userTagMembersToAssignTo =
+ userTagsMembersToAssignToData?.getUsersToAssignTo.usersToAssignTo.edges.map(
+ (edge) => edge.node,
+ ) ?? /* istanbul ignore next */ [];
+
+ const handleAddOrRemoveMember = (member: InterfaceMemberData): void => {
+ setAssignToMembers((prevMembers) => {
+ const isAssigned = prevMembers.some((m) => m._id === member._id);
+ if (isAssigned) {
+ return prevMembers.filter((m) => m._id !== member._id);
+ } else {
+ return [...prevMembers, member];
+ }
+ });
+ };
+
+ const removeMember = (id: string): void => {
+ setAssignToMembers((prevMembers) =>
+ prevMembers.filter((m) => m._id !== id),
+ );
+ };
+
+ const [addPeople, { loading: addPeopleToTagLoading }] =
+ useMutation(ADD_PEOPLE_TO_TAG);
+
+ const addPeopleToCurrentTag = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+
+ if (!assignToMembers.length) {
+ toast.error(t('noOneSelected'));
+ return;
+ }
+
+ try {
+ const { data } = await addPeople({
+ variables: {
+ tagId: currentTagId,
+ userIds: assignToMembers.map((member) => member._id),
+ },
+ });
+
+ if (data) {
+ toast.success(t('successfullyAssignedToPeople'));
+ refetchAssignedMembersData();
+ hideAddPeopleToTagModal();
+ setAssignToMembers([]);
+ }
+ } catch (error: unknown) /* istanbul ignore next */ {
+ const errorMessage =
+ error instanceof Error ? error.message : tErrors('unknownError');
+ toast.error(errorMessage);
+ }
+ };
+
+ if (userTagsMembersToAssignToError) {
+ return (
+
+
+
+
+ {t('errorOccurredWhileLoadingMembers')}
+
+ {userTagsMembersToAssignToError.message}
+
+
+
+ );
+ }
+
+ const columns: GridColDef[] = [
+ {
+ field: 'id',
+ headerName: '#',
+ minWidth: 100,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return {params.row.id}
;
+ },
+ },
+ {
+ field: 'userName',
+ headerName: t('userName'),
+ flex: 2,
+ minWidth: 100,
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ {params.row.firstName + ' ' + params.row.lastName}
+
+ );
+ },
+ },
+ {
+ field: 'actions',
+ headerName: t('actions'),
+ flex: 1,
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ const isToBeAssigned = assignToMembers.some(
+ (member) => member._id === params.row._id,
+ );
+
+ return (
+ handleAddOrRemoveMember(params.row)}
+ data-testid={
+ isToBeAssigned ? 'deselectMemberBtn' : 'selectMemberBtn'
+ }
+ variant={!isToBeAssigned ? 'primary' : 'danger'}
+ >
+ {isToBeAssigned ? 'x' : '+'}
+
+ );
+ },
+ },
+ ];
+
+ return (
+ <>
+
+
+ {t('addPeople')}
+
+
+
+ >
+ );
+};
+
+export default AddPeopleToTag;
diff --git a/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts b/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts
new file mode 100644
index 0000000000..fbaf812186
--- /dev/null
+++ b/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts
@@ -0,0 +1,292 @@
+import { ADD_PEOPLE_TO_TAG } from 'GraphQl/Mutations/TagMutations';
+import { USER_TAGS_MEMBERS_TO_ASSIGN_TO } from 'GraphQl/Queries/userTagQueries';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+
+export const MOCKS = [
+ {
+ request: {
+ query: USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: {
+ firstName: { starts_with: '' },
+ lastName: { starts_with: '' },
+ },
+ },
+ },
+ result: {
+ data: {
+ getUsersToAssignTo: {
+ name: 'tag1',
+ usersToAssignTo: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ firstName: 'member',
+ lastName: '1',
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ firstName: 'member',
+ lastName: '2',
+ },
+ cursor: '2',
+ },
+ {
+ node: {
+ _id: '3',
+ firstName: 'member',
+ lastName: '3',
+ },
+ cursor: '3',
+ },
+ {
+ node: {
+ _id: '4',
+ firstName: 'member',
+ lastName: '4',
+ },
+ cursor: '4',
+ },
+ {
+ node: {
+ _id: '5',
+ firstName: 'member',
+ lastName: '5',
+ },
+ cursor: '5',
+ },
+ {
+ node: {
+ _id: '6',
+ firstName: 'member',
+ lastName: '6',
+ },
+ cursor: '6',
+ },
+ {
+ node: {
+ _id: '7',
+ firstName: 'member',
+ lastName: '7',
+ },
+ cursor: '7',
+ },
+ {
+ node: {
+ _id: '8',
+ firstName: 'member',
+ lastName: '8',
+ },
+ cursor: '8',
+ },
+ {
+ node: {
+ _id: '9',
+ firstName: 'member',
+ lastName: '9',
+ },
+ cursor: '9',
+ },
+ {
+ node: {
+ _id: '10',
+ firstName: 'member',
+ lastName: '10',
+ },
+ cursor: '10',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '10',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 12,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: '10',
+ where: {
+ firstName: { starts_with: '' },
+ lastName: { starts_with: '' },
+ },
+ },
+ },
+ result: {
+ data: {
+ getUsersToAssignTo: {
+ name: 'tag1',
+ usersToAssignTo: {
+ edges: [
+ {
+ node: {
+ _id: '11',
+ firstName: 'member',
+ lastName: '11',
+ },
+ cursor: '11',
+ },
+ {
+ node: {
+ _id: '12',
+ firstName: 'member',
+ lastName: '12',
+ },
+ cursor: '12',
+ },
+ ],
+ pageInfo: {
+ startCursor: '11',
+ endCursor: '12',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 12,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: {
+ firstName: { starts_with: 'usersToAssignTo' },
+ lastName: { starts_with: '' },
+ },
+ },
+ },
+ result: {
+ data: {
+ getUsersToAssignTo: {
+ name: 'tag1',
+ usersToAssignTo: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ firstName: 'usersToAssignTo',
+ lastName: 'user1',
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ firstName: 'usersToAssignTo',
+ lastName: 'user2',
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: {
+ firstName: { starts_with: '' },
+ lastName: { starts_with: 'userToAssignTo' },
+ },
+ },
+ },
+ result: {
+ data: {
+ getUsersToAssignTo: {
+ name: 'tag1',
+ usersToAssignTo: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ firstName: 'first',
+ lastName: 'userToAssignTo',
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ firstName: 'second',
+ lastName: 'userToAssignTo',
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: ADD_PEOPLE_TO_TAG,
+ variables: {
+ tagId: '1',
+ userIds: ['1', '3', '5'],
+ },
+ },
+ result: {
+ data: {
+ addPeopleToUserTag: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR = [
+ {
+ request: {
+ query: USER_TAGS_MEMBERS_TO_ASSIGN_TO,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: {
+ firstName: { starts_with: '' },
+ lastName: { starts_with: '' },
+ },
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
diff --git a/src/components/Advertisements/Advertisements.module.css b/src/components/Advertisements/Advertisements.module.css
new file mode 100644
index 0000000000..8a34c03be5
--- /dev/null
+++ b/src/components/Advertisements/Advertisements.module.css
@@ -0,0 +1,31 @@
+.container {
+ display: flex;
+}
+
+.logintitle {
+ color: #707070;
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 30px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid #31bb6b;
+ width: 15%;
+}
+
+.actioninput {
+ text-decoration: none;
+ margin-bottom: 50px;
+ border-color: #e8e5e5;
+ width: 80%;
+ border-radius: 7px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ box-shadow: none;
+}
+
+.actionradio input {
+ width: fit-content;
+ margin: inherit;
+}
diff --git a/src/components/Advertisements/Advertisements.test.tsx b/src/components/Advertisements/Advertisements.test.tsx
new file mode 100644
index 0000000000..c0992a1012
--- /dev/null
+++ b/src/components/Advertisements/Advertisements.test.tsx
@@ -0,0 +1,730 @@
+import React, { act } from 'react';
+import {
+ ApolloClient,
+ ApolloLink,
+ ApolloProvider,
+ HttpLink,
+ InMemoryCache,
+} from '@apollo/client';
+import { MockedProvider } from '@apollo/client/testing';
+import { fireEvent, render, screen } from '@testing-library/react';
+import 'jest-location-mock';
+
+import type { DocumentNode, NormalizedCacheObject } from '@apollo/client';
+import userEvent from '@testing-library/user-event';
+import { BACKEND_URL } from 'Constant/constant';
+import { ADD_ADVERTISEMENT_MUTATION } from 'GraphQl/Mutations/mutations';
+import {
+ ORGANIZATIONS_LIST,
+ ORGANIZATION_ADVERTISEMENT_LIST,
+ PLUGIN_GET,
+} from 'GraphQl/Queries/Queries';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { ToastContainer } from 'react-toastify';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import useLocalStorage from 'utils/useLocalstorage';
+import Advertisement from './Advertisements';
+
+const { getItem } = useLocalStorage();
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+
+jest.mock('components/AddOn/support/services/Plugin.helper', () => ({
+ __esModule: true,
+ default: jest.fn().mockImplementation(() => ({
+ fetchInstalled: jest.fn().mockResolvedValue([]),
+ fetchStore: jest.fn().mockResolvedValue([]),
+ })),
+}));
+let mockID: string | undefined = '1';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockID }),
+}));
+
+const today = new Date();
+const tomorrow = today;
+tomorrow.setDate(today.getDate() + 1);
+
+const ADVERTISEMENTS_LIST_MOCK: {
+ request:
+ | {
+ query: DocumentNode;
+ variables: { id: string; first: number; after: null };
+ }
+ | {
+ query: DocumentNode;
+ variables: {
+ id: string;
+ first: number;
+ after: null;
+ before: null;
+ last: null;
+ };
+ };
+ result:
+ | {
+ data: {
+ organizations: {
+ _id: string;
+ advertisements: {
+ edges: {
+ node: {
+ _id: string;
+ name: string;
+ startDate: string;
+ endDate: string;
+ mediaUrl: string;
+ };
+ cursor: string;
+ }[];
+ pageInfo: {
+ startCursor: string;
+ endCursor: string;
+ hasNextPage: boolean;
+ hasPreviousPage: boolean;
+ };
+ totalCount: number;
+ };
+ }[];
+ };
+ }
+ | {
+ data: {
+ organizations: {
+ _id: string;
+ advertisements: {
+ edges: {
+ node: {
+ _id: string;
+ name: string;
+ startDate: string;
+ endDate: string;
+ mediaUrl: string;
+ };
+ cursor: string;
+ }[];
+ pageInfo: {
+ startCursor: string;
+ endCursor: string;
+ hasNextPage: boolean;
+ hasPreviousPage: boolean;
+ };
+ totalCount: number;
+ };
+ }[];
+ };
+ };
+}[] = [];
+
+for (let i = 0; i < 4; i++) {
+ ADVERTISEMENTS_LIST_MOCK.push({
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 6,
+ after: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor2',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'cursor1',
+ endCursor: 'cursor2',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ });
+ ADVERTISEMENTS_LIST_MOCK.push({
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 6,
+ after: null,
+ before: null,
+ last: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor2',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'cursor1',
+ endCursor: 'cursor2',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ });
+}
+
+const PLUGIN_GET_MOCK = {
+ request: {
+ query: PLUGIN_GET,
+ },
+ result: {
+ data: {
+ getPlugins: [
+ {
+ _id: '6581be50e88e74003aab436c',
+ pluginName: 'Chats',
+ pluginCreatedBy: 'Talawa Team',
+ pluginDesc:
+ 'User can share messages with other users in a chat user interface.',
+ uninstalledOrgs: [
+ '62ccfccd3eb7fd2a30f41601',
+ '62ccfccd3eb7fd2a30f41601',
+ ],
+ pluginInstallStatus: true,
+ __typename: 'Plugin',
+ },
+ ],
+ },
+ loading: false,
+ },
+};
+
+const ADD_ADVERTISEMENT_MUTATION_MOCK = {
+ request: {
+ query: ADD_ADVERTISEMENT_MUTATION,
+ variables: {
+ organizationId: '1',
+ name: 'Cookie Shop',
+ file: 'data:image/png;base64,bWVkaWEgY29udGVudA==',
+ type: 'POPUP',
+ startDate: '2023-01-01',
+ endDate: '2023-02-02',
+ },
+ },
+ result: {
+ data: {
+ createAdvertisement: {
+ _id: '65844efc814dd4003db811c4',
+ advertisement: null,
+ __typename: 'Advertisement',
+ },
+ },
+ },
+};
+
+const ORGANIZATIONS_LIST_MOCK = {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ image: '',
+ creator: {
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ name: 'name',
+ description: 'description',
+ userRegistrationRequired: true,
+
+ visibleInSearch: true,
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ members: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ admins: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ membershipRequests: {
+ _id: 'id',
+ user: {
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ },
+ blockedUsers: {
+ _id: 'id',
+ firstName: 'firstName',
+ lastName: 'lastName',
+ email: 'email',
+ },
+ },
+ ],
+ },
+ },
+};
+
+describe('Testing Advertisement Component', () => {
+ test('for creating new Advertisements', async () => {
+ const mocks = [
+ ORGANIZATIONS_LIST_MOCK,
+ PLUGIN_GET_MOCK,
+ ADD_ADVERTISEMENT_MUTATION_MOCK,
+ ...ADVERTISEMENTS_LIST_MOCK,
+ ];
+
+ render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByText('Create Advertisement'));
+ userEvent.type(
+ screen.getByLabelText('Enter name of Advertisement'),
+ 'Cookie Shop',
+ );
+ const mediaFile = new File(['media content'], 'test.png', {
+ type: 'image/png',
+ });
+
+ const mediaInput = screen.getByTestId('advertisementMedia');
+ fireEvent.change(mediaInput, {
+ target: {
+ files: [mediaFile],
+ },
+ });
+ const mediaPreview = await screen.findByTestId('mediaPreview');
+ expect(mediaPreview).toBeInTheDocument();
+ userEvent.selectOptions(
+ screen.getByLabelText('Select type of Advertisement'),
+ 'POPUP',
+ );
+ userEvent.type(screen.getByLabelText('Select Start Date'), '2023-01-01');
+ userEvent.type(screen.getByLabelText('Select End Date'), '2023-02-02');
+
+ userEvent.click(screen.getByTestId('addonregister'));
+ expect(
+ await screen.findByText('Advertisement created successfully.'),
+ ).toBeInTheDocument();
+ });
+
+ test('for the working of the tabs', async () => {
+ const mocks = [
+ ORGANIZATIONS_LIST_MOCK,
+ PLUGIN_GET_MOCK,
+ ADD_ADVERTISEMENT_MUTATION_MOCK,
+ ...ADVERTISEMENTS_LIST_MOCK,
+ ];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText('Active Campaigns'));
+
+ await wait();
+ userEvent.click(screen.getByText('Completed Campaigns'));
+ });
+
+ test('if the component renders correctly and ads are correctly categorized date wise', async () => {
+ mockID = '1';
+ const mocks = [...ADVERTISEMENTS_LIST_MOCK];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const date = await screen.findAllByTestId('Ad_end_date');
+ const dateString = date[1].innerHTML;
+ const dateMatch = dateString.match(
+ /\b(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d{1,2})\s+(\d{4})\b/,
+ );
+ let dateObject = new Date();
+
+ if (dateMatch) {
+ const monthName = dateMatch[1];
+ const day = parseInt(dateMatch[2], 10);
+ const year = parseInt(dateMatch[3], 10);
+
+ const monthIndex =
+ 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(monthName) / 3;
+
+ dateObject = new Date(year, monthIndex, day);
+ }
+
+ expect(dateObject.getTime()).toBeLessThan(new Date().getTime());
+ });
+
+ test('delete ad', async () => {
+ mockID = '1';
+ const mocks = [...ADVERTISEMENTS_LIST_MOCK];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const moreiconbtn = await screen.findAllByTestId('moreiconbtn');
+ fireEvent.click(moreiconbtn[1]);
+ const deleteBtn = await screen.findByTestId('deletebtn');
+ expect(deleteBtn).toBeInTheDocument();
+ fireEvent.click(deleteBtn);
+ });
+
+ test('infinite scroll', async () => {
+ mockID = '1';
+ const mocks = [
+ ...ADVERTISEMENTS_LIST_MOCK,
+ {
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 2,
+ after: null,
+ last: null,
+ before: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor2',
+ },
+ {
+ node: {
+ _id: '3',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor3',
+ },
+ {
+ node: {
+ _id: '4',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor4',
+ },
+ {
+ node: {
+ _id: '5',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor5',
+ },
+ {
+ node: {
+ _id: '6',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor6',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'cursor1',
+ endCursor: 'cursor6',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 8,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 6,
+ after: 'cursor6',
+ last: null,
+ before: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '7',
+ name: 'Advertisement7',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: '5rdiyruyu3hkjkjiwfhwaify',
+ },
+ {
+ node: {
+ _id: '8',
+ name: 'Advertisement8',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: '5rdiyrhgkjkjjyg3iwfhwaify',
+ },
+ ],
+ pageInfo: {
+ startCursor: '5rdiyruyu3hkjkjiwfhwaify',
+ endCursor: '5rdiyrhgkjkjjyg3iwfhwaify',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 8,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 6,
+ after: 'cursor2',
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '7',
+ name: 'Advertisement7',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: '5rdiyruyu3hkjkjiwfhwaify',
+ },
+ {
+ node: {
+ _id: '8',
+ name: 'Advertisement8',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: '5rdiyrhgkjkjjyg3iwfhwaify',
+ },
+ ],
+ pageInfo: {
+ startCursor: '5rdiyruyu3hkjkjiwfhwaify',
+ endCursor: '5rdiyrhgkjkjjyg3iwfhwaify',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 8,
+ },
+ },
+ ],
+ },
+ },
+ },
+ ];
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ let moreiconbtn = await screen.findAllByTestId('moreiconbtn');
+ console.log('before scroll', moreiconbtn);
+ fireEvent.scroll(window, { target: { scrollY: 500 } });
+ moreiconbtn = await screen.findAllByTestId('moreiconbtn');
+ console.log('after scroll', moreiconbtn);
+ });
+});
diff --git a/src/components/Advertisements/Advertisements.tsx b/src/components/Advertisements/Advertisements.tsx
new file mode 100644
index 0000000000..f20c2a7d8e
--- /dev/null
+++ b/src/components/Advertisements/Advertisements.tsx
@@ -0,0 +1,273 @@
+import React, { useEffect, useState } from 'react';
+import styles from './Advertisements.module.css';
+import { useQuery } from '@apollo/client';
+import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/Queries';
+import { Col, Row, Tab, Tabs } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import AdvertisementEntry from './core/AdvertisementEntry/AdvertisementEntry';
+import AdvertisementRegister from './core/AdvertisementRegister/AdvertisementRegister';
+import { useParams } from 'react-router-dom';
+import type { InterfaceQueryOrganizationAdvertisementListItem } from 'utils/interfaces';
+import InfiniteScroll from 'react-infinite-scroll-component';
+
+/**
+ * The `Advertisements` component displays a list of advertisements for a specific organization.
+ * It uses a tab-based interface to toggle between active and archived advertisements.
+ *
+ * The component utilizes the `useQuery` hook from Apollo Client to fetch advertisements data
+ * and implements infinite scrolling to load more advertisements as the user scrolls.
+ *
+ * @example
+ * return (
+ *
+ * )
+ *
+ */
+
+export default function Advertisements(): JSX.Element {
+ // Retrieve the organization ID from URL parameters
+ const { orgId: currentOrgId } = useParams();
+ // Translation hook for internationalization
+ const { t } = useTranslation('translation', { keyPrefix: 'advertisement' });
+ const { t: tCommon } = useTranslation('common');
+
+ // Set the document title based on the translation
+ document.title = t('title');
+
+ // State to manage pagination cursor for infinite scrolling
+ const [after, setAfter] = useState(null);
+
+ // Type definition for an advertisement object
+ type Ad = {
+ _id: string;
+ name: string;
+ type: 'BANNER' | 'MENU' | 'POPUP';
+ mediaUrl: string;
+ endDate: string; // Assuming it's a string in the format 'yyyy-MM-dd'
+ startDate: string; // Assuming it's a string in the format 'yyyy-MM-dd'
+ };
+
+ // GraphQL query to fetch the list of advertisements
+ const {
+ data: orgAdvertisementListData,
+ refetch,
+ }: {
+ data?: {
+ organizations: InterfaceQueryOrganizationAdvertisementListItem[];
+ };
+ refetch: () => void;
+ } = useQuery(ORGANIZATION_ADVERTISEMENT_LIST, {
+ variables: {
+ id: currentOrgId,
+ after: after,
+ first: 6,
+ },
+ });
+
+ // State to manage the list of advertisements
+ const [advertisements, setAdvertisements] = useState(
+ orgAdvertisementListData?.organizations[0].advertisements?.edges.map(
+ (edge: { node: Ad }) => edge.node,
+ ) || [],
+ );
+
+ // Effect hook to update advertisements list when data changes or pagination cursor changes
+ useEffect(() => {
+ if (orgAdvertisementListData && orgAdvertisementListData.organizations) {
+ const ads: Ad[] =
+ orgAdvertisementListData.organizations[0].advertisements?.edges.map(
+ (edge) => edge.node,
+ ) || [];
+ setAdvertisements(after ? [...advertisements, ...ads] : ads);
+ }
+ }, [orgAdvertisementListData, after]);
+
+ /**
+ * Fetches more advertisements for infinite scrolling.
+ */
+ async function loadMoreAdvertisements(): Promise {
+ await refetch();
+
+ if (orgAdvertisementListData && orgAdvertisementListData.organizations) {
+ setAfter(
+ orgAdvertisementListData?.organizations[0]?.advertisements.pageInfo
+ .endCursor,
+ );
+ }
+ }
+
+ return (
+ <>
+
+
+
+ {/* Component for registering a new advertisement */}
+
+
+ {/* Tabs for active and archived advertisements */}
+
+ {/* Tab for active advertisements */}
+
+
+ {/* Skeleton loader while fetching more advertisements */}
+ {[...Array(6)].map((_, index) => (
+
+ ))}
+ >
+ }
+ hasMore={
+ orgAdvertisementListData?.organizations[0].advertisements
+ .pageInfo.hasNextPage ?? false
+ }
+ className={styles.listBox}
+ data-testid="organizations-list"
+ endMessage={
+ advertisements.filter(
+ (ad: Ad) => new Date(ad.endDate) > new Date(),
+ ).length !== 0 && (
+
+
{tCommon('endOfResults')}
+
+ )
+ }
+ >
+ {advertisements.filter(
+ (ad: Ad) => new Date(ad.endDate) > new Date(),
+ ).length === 0 ? (
+ {t('pMessage')}
+ ) : (
+ advertisements
+ .filter((ad: Ad) => new Date(ad.endDate) > new Date())
+ .map(
+ (
+ ad: {
+ _id: string;
+ name: string | undefined;
+ type: string | undefined;
+ mediaUrl: string;
+ endDate: string;
+ startDate: string;
+ },
+ i: React.Key | null | undefined,
+ ): JSX.Element => (
+
+ ),
+ )
+ )}
+
+
+
+ {/* Tab for archived advertisements */}
+
+
+ {/* Skeleton loader while fetching more advertisements */}
+ {[...Array(6)].map((_, index) => (
+
+ ))}
+ >
+ }
+ hasMore={
+ orgAdvertisementListData?.organizations[0].advertisements
+ .pageInfo.hasNextPage ?? false
+ }
+ className={styles.listBox}
+ data-testid="organizations-list"
+ endMessage={
+ advertisements.filter(
+ (ad: Ad) => new Date(ad.endDate) < new Date(),
+ ).length !== 0 && (
+
+
{tCommon('endOfResults')}
+
+ )
+ }
+ >
+ {advertisements.filter(
+ (ad: Ad) => new Date(ad.endDate) < new Date(),
+ ).length === 0 ? (
+ {t('pMessage')}
+ ) : (
+ advertisements
+ .filter((ad: Ad) => new Date(ad.endDate) < new Date())
+ .map(
+ (
+ ad: {
+ _id: string;
+ name: string | undefined;
+ type: string | undefined;
+ mediaUrl: string;
+ endDate: string;
+ startDate: string;
+ },
+ i: React.Key | null | undefined,
+ ): JSX.Element => (
+
+ ),
+ )
+ )}
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css
new file mode 100644
index 0000000000..879d96a0a0
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.module.css
@@ -0,0 +1,75 @@
+.entrytoggle {
+ margin: 24px 24px 0 auto;
+ width: fit-content;
+}
+
+.entryaction {
+ display: flex !important;
+}
+
+.entryaction i {
+ margin-right: 8px;
+ margin-top: 4px;
+}
+
+.entryaction .spinner-grow {
+ height: 1rem;
+ width: 1rem;
+ margin-right: 8px;
+}
+
+.admedia {
+ object-fit: cover;
+ height: 20rem;
+}
+
+.buttons {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.dropdownButton {
+ background-color: transparent;
+ color: #000;
+ border: none;
+ cursor: pointer;
+ display: flex;
+ width: 100%;
+ justify-content: flex-end;
+ padding: 8px 10px;
+}
+
+.dropdownContainer {
+ position: relative;
+ display: inline-block;
+}
+
+.dropdownmenu {
+ display: none;
+ position: absolute;
+ z-index: 1;
+ background-color: white;
+ width: 120px;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
+ padding: 5px 0;
+ margin: 0;
+ list-style-type: none;
+ right: 0;
+ top: 100%;
+}
+
+.dropdownmenu li {
+ cursor: pointer;
+ padding: 8px 16px;
+ text-decoration: none;
+ display: block;
+ color: #333;
+}
+
+.dropdownmenu li:hover {
+ background-color: #f1f1f1;
+}
+
+.dropdownContainer:hover .dropdownmenu {
+ display: block;
+}
diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.test.tsx b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.test.tsx
new file mode 100644
index 0000000000..dbd6f88cc3
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.test.tsx
@@ -0,0 +1,637 @@
+import React from 'react';
+import { render, fireEvent, waitFor, screen } from '@testing-library/react';
+import {
+ ApolloClient,
+ ApolloProvider,
+ InMemoryCache,
+ ApolloLink,
+ HttpLink,
+} from '@apollo/client';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { BrowserRouter } from 'react-router-dom';
+import AdvertisementEntry from './AdvertisementEntry';
+import AdvertisementRegister from '../AdvertisementRegister/AdvertisementRegister';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { BACKEND_URL } from 'Constant/constant';
+import i18nForTest from 'utils/i18nForTest';
+import { I18nextProvider } from 'react-i18next';
+import dayjs from 'dayjs';
+import useLocalStorage from 'utils/useLocalstorage';
+import { MockedProvider } from '@apollo/client/testing';
+import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/OrganizationQueries';
+import { DELETE_ADVERTISEMENT_BY_ID } from 'GraphQl/Mutations/mutations';
+
+const { getItem } = useLocalStorage();
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+
+const translations = JSON.parse(
+ JSON.stringify(
+ i18nForTest.getDataByLanguage('en')?.translation?.advertisement ?? null,
+ ),
+);
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+
+const mockUseMutation = jest.fn();
+jest.mock('@apollo/client', () => {
+ const originalModule = jest.requireActual('@apollo/client');
+ return {
+ ...originalModule,
+ useMutation: () => mockUseMutation(),
+ };
+});
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: '1' }),
+}));
+
+describe('Testing Advertisement Entry Component', () => {
+ test('Testing rendering and deleting of advertisement', async () => {
+ const deleteAdByIdMock = jest.fn();
+ mockUseMutation.mockReturnValue([deleteAdByIdMock]);
+ const { getByTestId, getAllByText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ //Testing rendering
+ expect(getByTestId('AdEntry')).toBeInTheDocument();
+ expect(getAllByText('POPUP')[0]).toBeInTheDocument();
+ expect(getAllByText('Advert1')[0]).toBeInTheDocument();
+ expect(screen.getByTestId('media')).toBeInTheDocument();
+
+ //Testing successful deletion
+ fireEvent.click(getByTestId('moreiconbtn'));
+ fireEvent.click(getByTestId('deletebtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('delete_title')).toBeInTheDocument();
+ expect(screen.getByTestId('delete_body')).toBeInTheDocument();
+ });
+
+ fireEvent.click(getByTestId('delete_yes'));
+
+ await waitFor(() => {
+ expect(deleteAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ },
+ });
+ const deletedMessage = screen.queryByText('Advertisement Deleted');
+ expect(deletedMessage).toBeNull();
+ });
+
+ //Testing unsuccessful deletion
+ deleteAdByIdMock.mockRejectedValueOnce(new Error('Deletion Failed'));
+
+ fireEvent.click(getByTestId('moreiconbtn'));
+
+ fireEvent.click(getByTestId('delete_yes'));
+
+ await waitFor(() => {
+ expect(deleteAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ },
+ });
+ const deletionFailedText = screen.queryByText((content, element) => {
+ return (
+ element?.textContent === 'Deletion Failed' &&
+ element.tagName.toLowerCase() === 'div'
+ );
+ });
+ expect(deletionFailedText).toBeNull();
+ });
+ });
+ it('should use default props when none are provided', () => {
+ render(
+ ,
+ ): void {
+ throw new Error('Function not implemented.');
+ }}
+ />,
+ );
+
+ //Check if component renders with default ''(empty string)
+ const elements = screen.getAllByText(''); // This will return an array of matching elements
+ elements.forEach((element) => expect(element).toBeInTheDocument());
+
+ // Check that the component renders with default `mediaUrl` (empty string)
+ const mediaElement = screen.getByTestId('media');
+ expect(mediaElement).toHaveAttribute('src', '');
+
+ // Check that the component renders with default `endDate`
+ const defaultEndDate = new Date().toDateString();
+ expect(screen.getByText(`Ends on ${defaultEndDate}`)).toBeInTheDocument();
+
+ // Check that the component renders with default `startDate`
+ const defaultStartDate = new Date().toDateString();
+ console.log(screen.getByText);
+ expect(screen.getByText(`Ends on ${defaultStartDate}`)).toBeInTheDocument(); //fix text "Ends on"?
+ });
+ it('should correctly override default props when values are provided', () => {
+ const mockName = 'Test Ad';
+ const mockType = 'Banner';
+ const mockMediaUrl = 'https://example.com/media.png';
+ const mockEndDate = new Date(2025, 11, 31);
+ const mockStartDate = new Date(2024, 0, 1);
+ const mockOrganizationId = 'org123';
+
+ const { getByText } = render(
+ ,
+ ): void {
+ throw new Error('Function not implemented.');
+ }}
+ />,
+ );
+
+ // Check that the component renders with provided values
+ expect(getByText(mockName)).toBeInTheDocument();
+ // Add more checks based on how each prop affects rendering
+ });
+ it('should open and close the dropdown when options button is clicked', () => {
+ const { getByTestId, queryByText, getAllByText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Test initial rendering
+ expect(getByTestId('AdEntry')).toBeInTheDocument();
+ expect(getAllByText('POPUP')[0]).toBeInTheDocument();
+ expect(getAllByText('Advert1')[0]).toBeInTheDocument();
+
+ // Test dropdown functionality
+ const optionsButton = getByTestId('moreiconbtn');
+
+ // Initially, the dropdown should not be visible
+ expect(queryByText('Edit')).toBeNull();
+
+ // Click to open the dropdown
+ fireEvent.click(optionsButton);
+
+ // After clicking the button, the dropdown should be visible
+ expect(queryByText('Edit')).toBeInTheDocument();
+
+ // Click again to close the dropdown
+ fireEvent.click(optionsButton);
+
+ // After the second click, the dropdown should be hidden again
+ expect(queryByText('Edit')).toBeNull();
+ });
+
+ test('Updates the advertisement and shows success toast on successful update', async () => {
+ const updateAdByIdMock = jest.fn().mockResolvedValue({
+ data: {
+ updateAdvertisement: {
+ advertisement: {
+ _id: '1',
+ name: 'Updated Advertisement',
+ mediaUrl: '',
+ startDate: dayjs(new Date()).add(1, 'day').format('YYYY-MM-DD'),
+ endDate: dayjs(new Date()).add(2, 'days').format('YYYY-MM-DD'),
+ type: 'BANNER',
+ },
+ },
+ },
+ });
+
+ mockUseMutation.mockReturnValue([updateAdByIdMock]);
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const optionsButton = screen.getByTestId('moreiconbtn');
+ fireEvent.click(optionsButton);
+ fireEvent.click(screen.getByTestId('editBtn'));
+
+ fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), {
+ target: { value: 'Updated Advertisement' },
+ });
+
+ expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue(
+ 'Updated Advertisement',
+ );
+
+ fireEvent.change(screen.getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+ expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+
+ fireEvent.change(screen.getByLabelText(translations.RstartDate), {
+ target: { value: dayjs().add(1, 'day').format('YYYY-MM-DD') },
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.RendDate), {
+ target: { value: dayjs().add(2, 'days').format('YYYY-MM-DD') },
+ });
+
+ fireEvent.click(screen.getByTestId('addonupdate'));
+
+ expect(updateAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ name: 'Updated Advertisement',
+ type: 'BANNER',
+ startDate: dayjs().add(1, 'day').format('YYYY-MM-DD'),
+ endDate: dayjs().add(2, 'days').format('YYYY-MM-DD'),
+ },
+ });
+ });
+
+ test('Simulating if the mutation doesnt have data variable while updating', async () => {
+ const updateAdByIdMock = jest.fn().mockResolvedValue({
+ updateAdvertisement: {
+ _id: '1',
+ name: 'Updated Advertisement',
+ type: 'BANNER',
+ },
+ });
+
+ mockUseMutation.mockReturnValue([updateAdByIdMock]);
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const optionsButton = screen.getByTestId('moreiconbtn');
+ fireEvent.click(optionsButton);
+ fireEvent.click(screen.getByTestId('editBtn'));
+
+ fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), {
+ target: { value: 'Updated Advertisement' },
+ });
+
+ expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue(
+ 'Updated Advertisement',
+ );
+
+ fireEvent.change(screen.getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+ expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+
+ fireEvent.click(screen.getByTestId('addonupdate'));
+
+ expect(updateAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ name: 'Updated Advertisement',
+ type: 'BANNER',
+ },
+ });
+ });
+
+ test('Simulating if the mutation does not have data variable while registering', async () => {
+ Object.defineProperty(window, 'location', {
+ configurable: true,
+ value: {
+ reload: jest.fn(),
+ href: 'https://example.com/page/id=1',
+ },
+ });
+ const createAdByIdMock = jest.fn().mockResolvedValue({
+ data1: {
+ createAdvertisement: {
+ _id: '1',
+ },
+ },
+ });
+
+ mockUseMutation.mockReturnValue([createAdByIdMock]);
+
+ render(
+
+
+
+
+ {
+
+ }
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId('createAdvertisement'));
+
+ fireEvent.change(screen.getByLabelText('Enter name of Advertisement'), {
+ target: { value: 'Updated Advertisement' },
+ });
+
+ expect(screen.getByLabelText('Enter name of Advertisement')).toHaveValue(
+ 'Updated Advertisement',
+ );
+
+ fireEvent.change(screen.getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+ expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+
+ fireEvent.change(screen.getByLabelText(translations.RstartDate), {
+ target: { value: '2023-01-01' },
+ });
+ expect(screen.getByLabelText(translations.RstartDate)).toHaveValue(
+ '2023-01-01',
+ );
+
+ fireEvent.change(screen.getByLabelText(translations.RendDate), {
+ target: { value: '2023-02-01' },
+ });
+ expect(screen.getByLabelText(translations.RendDate)).toHaveValue(
+ '2023-02-01',
+ );
+
+ fireEvent.click(screen.getByTestId('addonregister'));
+
+ expect(createAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ organizationId: '1',
+ name: 'Updated Advertisement',
+ file: '',
+ type: 'BANNER',
+ startDate: dayjs(new Date('2023-01-01')).format('YYYY-MM-DD'),
+ endDate: dayjs(new Date('2023-02-01')).format('YYYY-MM-DD'),
+ },
+ });
+ });
+ test('delet advertisement', async () => {
+ const deleteAdByIdMock = jest.fn();
+ const mocks = [
+ {
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 2,
+ after: null,
+ last: null,
+ before: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor2',
+ },
+ {
+ node: {
+ _id: '3',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor3',
+ },
+ {
+ node: {
+ _id: '4',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor4',
+ },
+ {
+ node: {
+ _id: '5',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: 'cursor5',
+ },
+ {
+ node: {
+ _id: '6',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: 'cursor6',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'cursor1',
+ endCursor: 'cursor6',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 8,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_ADVERTISEMENT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ advertisements: {
+ _id: null,
+ },
+ },
+ },
+ },
+ ];
+ mockUseMutation.mockReturnValue([deleteAdByIdMock]);
+ const { getByTestId, getAllByText } = render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ //Testing rendering
+ expect(getByTestId('AdEntry')).toBeInTheDocument();
+ expect(getAllByText('POPUP')[0]).toBeInTheDocument();
+ expect(getAllByText('Advert1')[0]).toBeInTheDocument();
+ expect(screen.getByTestId('media')).toBeInTheDocument();
+
+ //Testing successful deletion
+ fireEvent.click(getByTestId('moreiconbtn'));
+ fireEvent.click(getByTestId('deletebtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('delete_title')).toBeInTheDocument();
+ expect(screen.getByTestId('delete_body')).toBeInTheDocument();
+ });
+
+ fireEvent.click(getByTestId('delete_yes'));
+
+ await waitFor(() => {
+ expect(deleteAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ },
+ });
+ const deletedMessage = screen.queryByText('Advertisement Deleted');
+ expect(deletedMessage).toBeNull();
+ });
+
+ //Testing unsuccessful deletion
+ deleteAdByIdMock.mockRejectedValueOnce(new Error('Deletion Failed'));
+
+ fireEvent.click(getByTestId('moreiconbtn'));
+
+ fireEvent.click(getByTestId('delete_yes'));
+
+ await waitFor(() => {
+ expect(deleteAdByIdMock).toHaveBeenCalledWith({
+ variables: {
+ id: '1',
+ },
+ });
+ const deletionFailedText = screen.queryByText((content, element) => {
+ return (
+ element?.textContent === 'Deletion Failed' &&
+ element.tagName.toLowerCase() === 'div'
+ );
+ });
+ expect(deletionFailedText).toBeNull();
+ });
+ });
+});
diff --git a/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx
new file mode 100644
index 0000000000..7368ded68e
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementEntry/AdvertisementEntry.tsx
@@ -0,0 +1,221 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import styles from './AdvertisementEntry.module.css';
+import { Button, Card, Col, Row, Spinner, Modal } from 'react-bootstrap';
+import { DELETE_ADVERTISEMENT_BY_ID } from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import { useTranslation } from 'react-i18next';
+import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/Queries';
+import AdvertisementRegister from '../AdvertisementRegister/AdvertisementRegister';
+import MoreVertIcon from '@mui/icons-material/MoreVert';
+import { toast } from 'react-toastify';
+
+interface InterfaceAddOnEntryProps {
+ id: string;
+ name?: string;
+ mediaUrl?: string;
+ type?: string;
+ organizationId?: string;
+ startDate?: Date;
+ endDate?: Date;
+ setAfter: React.Dispatch>;
+}
+
+/**
+ * Component for displaying an advertisement entry.
+ * Allows viewing, editing, and deleting of the advertisement.
+ *
+ * @param props - Component properties
+ * @returns The rendered component
+ */
+function AdvertisementEntry({
+ id,
+ name = '',
+ type = '',
+ mediaUrl = '',
+ endDate = new Date(),
+ organizationId = '',
+ startDate = new Date(),
+ setAfter,
+}: InterfaceAddOnEntryProps): JSX.Element {
+ const { t } = useTranslation('translation', { keyPrefix: 'advertisement' });
+ const { t: tCommon } = useTranslation('common');
+
+ // State for loading button
+ const [buttonLoading, setButtonLoading] = useState(false);
+ // State for dropdown menu visibility
+ const [dropdown, setDropdown] = useState(false);
+ // State for delete confirmation modal visibility
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+
+ // Mutation hook for deleting an advertisement
+ const [deleteAdById] = useMutation(DELETE_ADVERTISEMENT_BY_ID, {
+ refetchQueries: [
+ {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: { first: 6, after: null, id: organizationId },
+ },
+ ],
+ });
+
+ /**
+ * Toggles the visibility of the delete confirmation modal.
+ */
+ const toggleShowDeleteModal = (): void => setShowDeleteModal((prev) => !prev);
+
+ /**
+ * Handles advertisement deletion.
+ * Displays a success or error message based on the result.
+ */
+ const onDelete = async (): Promise => {
+ setButtonLoading(true);
+ try {
+ await deleteAdById({
+ variables: {
+ id: id.toString(),
+ },
+ });
+ toast.success(t('advertisementDeleted') as string);
+ setButtonLoading(false);
+ setAfter?.(null);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ setButtonLoading(false);
+ }
+ };
+
+ /**
+ * Toggles the visibility of the dropdown menu.
+ */
+ const handleOptionsClick = (): void => {
+ setDropdown(!dropdown);
+ };
+
+ return (
+ <>
+
+ {Array.from({ length: 1 }).map((_, idx) => (
+
+
+
+
+
+
+ {dropdown && (
+
+
+
+
+
+ {tCommon('delete')}
+
+
+ )}
+
+ {mediaUrl?.includes('videos') ? (
+
+
+
+ ) : (
+
+ )}
+
+ {name}
+
+ Ends on {endDate?.toDateString()}
+
+
+ {type}
+
+
+
+ {buttonLoading ? (
+
+ ) : (
+
+ )}
+ {t('view')}
+
+
+
+
+
+ {t('deleteAdvertisement')}
+
+
+
+
+
+
+ {t('deleteAdvertisementMsg')}
+
+
+
+ {tCommon('no')}
+
+ {
+ onDelete();
+ }}
+ data-testid="delete_yes"
+ >
+ {tCommon('yes')}
+
+
+
+
+
+
+ ))}
+
+
+ >
+ );
+}
+
+AdvertisementEntry.propTypes = {
+ name: PropTypes.string,
+ type: PropTypes.string,
+ organizationId: PropTypes.string,
+ mediaUrl: PropTypes.string,
+ endDate: PropTypes.instanceOf(Date),
+ startDate: PropTypes.instanceOf(Date),
+};
+
+export default AdvertisementEntry;
diff --git a/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.module.css b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.module.css
new file mode 100644
index 0000000000..0c5678676b
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.module.css
@@ -0,0 +1,56 @@
+.modalbtn {
+ margin-top: 1rem;
+ display: flex !important;
+ margin-left: auto;
+ align-items: center;
+}
+
+.modalbtn i,
+.button i {
+ margin-right: 8px;
+}
+
+.preview {
+ display: flex;
+ position: relative;
+ width: 100%;
+ margin-top: 10px;
+ justify-content: center;
+}
+.preview img {
+ width: 400px;
+ height: auto;
+}
+.preview video {
+ width: 400px;
+ height: auto;
+}
+
+.closeButton {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ background: transparent;
+ transform: scale(1.2);
+ cursor: pointer;
+ border: none;
+ color: #707070;
+ font-weight: 600;
+ font-size: 16px;
+ cursor: pointer;
+}
+
+.button {
+ min-width: 102px;
+}
+
+.editHeader {
+ background-color: #31bb6b;
+ color: white;
+}
+
+.link_check {
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+}
diff --git a/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx
new file mode 100644
index 0000000000..0646a94819
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.test.tsx
@@ -0,0 +1,641 @@
+import React, { act } from 'react';
+import { render, fireEvent, waitFor, screen } from '@testing-library/react';
+
+import {
+ ApolloClient,
+ ApolloProvider,
+ InMemoryCache,
+ ApolloLink,
+ HttpLink,
+} from '@apollo/client';
+
+import type { NormalizedCacheObject } from '@apollo/client';
+import { BrowserRouter } from 'react-router-dom';
+import AdvertisementRegister from './AdvertisementRegister';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { BACKEND_URL } from 'Constant/constant';
+// import i18nForTest from 'utils/i18nForTest';
+import { I18nextProvider } from 'react-i18next';
+import { MockedProvider } from '@apollo/client/testing';
+import i18n from 'utils/i18nForTest';
+import { toast } from 'react-toastify';
+import { ADD_ADVERTISEMENT_MUTATION } from 'GraphQl/Mutations/mutations';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import userEvent from '@testing-library/user-event';
+import useLocalStorage from 'utils/useLocalstorage';
+import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/Queries';
+
+const { getItem } = useLocalStorage();
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: ADD_ADVERTISEMENT_MUTATION,
+ variables: {
+ organizationId: '1',
+ name: 'Ad1',
+ type: 'BANNER',
+ startDate: '2023-01-01',
+ endDate: '2023-02-01',
+ file: 'data:image/png;base64,bWVkaWEgY29udGVudA==',
+ },
+ },
+ result: {
+ data: {
+ createAdvertisement: {
+ _id: '1',
+ advertisement: null,
+ __typename: 'Advertisement',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: {
+ id: '1',
+ first: 6,
+ after: null,
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ advertisements: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'Advertisement1',
+ startDate: '2022-01-01',
+ endDate: '2023-01-01',
+ mediaUrl: 'http://example1.com',
+ },
+ cursor: '5rdiyr3iwfhwaify',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'Advertisement2',
+ startDate: '2024-02-01',
+ endDate: '2025-02-01',
+ mediaUrl: 'http://example2.com',
+ },
+ cursor: '5rdiyr3iwfhwaify',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'erdftgyhujkerty',
+ endCursor: 'edrftgyhujikl',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+const httpLink = new HttpLink({
+ uri: BACKEND_URL,
+ headers: {
+ authorization: 'Bearer ' + getItem('token') || '',
+ },
+});
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: ApolloLink.from([httpLink]),
+});
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.advertisement ?? {},
+ ),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: '1' }),
+}));
+describe('Testing Advertisement Register Component', () => {
+ test('AdvertismentRegister component loads correctly in register mode', async () => {
+ const { getByText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await waitFor(() => {
+ expect(getByText(translations.createAdvertisement)).toBeInTheDocument();
+ });
+ });
+
+ test('create advertisement', async () => {
+ jest.useFakeTimers();
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ expect(
+ screen.getByText(translations.createAdvertisement),
+ ).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.createAdvertisement));
+ });
+
+ expect(screen.queryByText(translations.addNew)).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.change(screen.getByLabelText(translations.Rname), {
+ target: { value: 'Ad1' },
+ });
+
+ const mediaFile = new File(['media content'], 'test.png', {
+ type: 'image/png',
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.Rmedia), {
+ target: {
+ files: [mediaFile],
+ },
+ });
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('mediaPreview')).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ fireEvent.change(screen.getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.RstartDate), {
+ target: { value: '2023-01-01' },
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.RendDate), {
+ target: { value: '2023-02-01' },
+ });
+ });
+
+ expect(screen.getByLabelText(translations.Rname)).toHaveValue('Ad1');
+ expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+ expect(screen.getByLabelText(translations.RstartDate)).toHaveValue(
+ '2023-01-01',
+ );
+ expect(screen.getByLabelText(translations.RendDate)).toHaveValue(
+ '2023-02-01',
+ );
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.register));
+ });
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ 'Advertisement created successfully.',
+ );
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ });
+ jest.useRealTimers();
+ });
+
+ test('update advertisement', async () => {
+ jest.useFakeTimers();
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText(translations.edit)).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.edit));
+ });
+
+ await act(async () => {
+ fireEvent.change(screen.getByLabelText(translations.Rname), {
+ target: { value: 'Ad1' },
+ });
+
+ const mediaFile = new File(['media content'], 'test.png', {
+ type: 'image/png',
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.Rmedia), {
+ target: {
+ files: [mediaFile],
+ },
+ });
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('mediaPreview')).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ fireEvent.change(screen.getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.RstartDate), {
+ target: { value: '2023-01-01' },
+ });
+
+ fireEvent.change(screen.getByLabelText(translations.RendDate), {
+ target: { value: '2023-02-01' },
+ });
+ });
+
+ expect(screen.getByLabelText(translations.Rname)).toHaveValue('Ad1');
+ expect(screen.getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+ expect(screen.getByLabelText(translations.RstartDate)).toHaveValue(
+ '2023-01-01',
+ );
+ expect(screen.getByLabelText(translations.RendDate)).toHaveValue(
+ '2023-02-01',
+ );
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.saveChanges));
+ });
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ 'Advertisement created successfully.',
+ );
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ });
+
+ jest.useRealTimers();
+ });
+
+ test('Logs error to the console and shows error toast when advertisement creation fails', async () => {
+ jest.useFakeTimers();
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
+ const toastErrorSpy = jest.spyOn(toast, 'error');
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ expect(
+ screen.getByText(translations.createAdvertisement),
+ ).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.createAdvertisement));
+ });
+
+ expect(screen.queryByText(translations.addNew)).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(screen.getByText(translations.register));
+ });
+
+ await waitFor(() => {
+ expect(toastErrorSpy).toHaveBeenCalledWith(
+ `An error occurred. Couldn't create advertisement`,
+ );
+ });
+
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ jest.useRealTimers();
+ });
+
+ test('Throws error when the end date is less than the start date', async () => {
+ jest.useFakeTimers();
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
+ const { getByText, queryByText, getByLabelText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(getByText(translations.createAdvertisement)).toBeInTheDocument();
+
+ fireEvent.click(getByText(translations.createAdvertisement));
+ expect(queryByText(translations.addNew)).toBeInTheDocument();
+
+ fireEvent.change(getByLabelText(translations.Rname), {
+ target: { value: 'Ad1' },
+ });
+ expect(getByLabelText(translations.Rname)).toHaveValue('Ad1');
+
+ const mediaFile = new File(['media content'], 'test.png', {
+ type: 'image/png',
+ });
+
+ const mediaInput = getByLabelText(translations.Rmedia);
+ fireEvent.change(mediaInput, {
+ target: {
+ files: [mediaFile],
+ },
+ });
+
+ const mediaPreview = await screen.findByTestId('mediaPreview');
+ expect(mediaPreview).toBeInTheDocument();
+
+ fireEvent.change(getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+ expect(getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+
+ fireEvent.change(getByLabelText(translations.RstartDate), {
+ target: { value: '2023-01-01' },
+ });
+ expect(getByLabelText(translations.RstartDate)).toHaveValue('2023-01-01');
+
+ fireEvent.change(getByLabelText(translations.RendDate), {
+ target: { value: '2022-02-01' },
+ });
+ expect(getByLabelText(translations.RendDate)).toHaveValue('2022-02-01');
+
+ await waitFor(() => {
+ fireEvent.click(getByText(translations.register));
+ });
+ expect(toast.error).toHaveBeenCalledWith(
+ 'End Date should be greater than or equal to Start Date',
+ );
+ expect(setTimeoutSpy).toHaveBeenCalled();
+ jest.useRealTimers();
+ });
+
+ test('AdvertismentRegister component loads correctly in edit mode', async () => {
+ jest.useFakeTimers();
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await waitFor(() => {
+ expect(screen.getByTestId('editBtn')).toBeInTheDocument();
+ });
+ jest.useRealTimers();
+ });
+
+ test('Opens and closes modals on button click', async () => {
+ jest.useFakeTimers();
+ const { getByText, queryByText } = render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ fireEvent.click(getByText(translations.createAdvertisement));
+ await waitFor(() => {
+ expect(queryByText(translations.addNew)).toBeInTheDocument();
+ });
+ fireEvent.click(getByText(translations.close));
+ await waitFor(() => {
+ expect(queryByText(translations.close)).not.toBeInTheDocument();
+ });
+ jest.useRealTimers();
+ });
+
+ test('Throws error when the end date is less than the start date while editing the advertisement', async () => {
+ jest.useFakeTimers();
+ const { getByText, getByLabelText, queryByText } = render(
+
+
+
+
+ {
+
+ }
+
+
+
+ ,
+ );
+
+ fireEvent.click(getByText(translations.edit));
+ expect(queryByText(translations.editAdvertisement)).toBeInTheDocument();
+ fireEvent.change(getByLabelText(translations.Rname), {
+ target: { value: 'Test Advertisement' },
+ });
+ expect(getByLabelText(translations.Rname)).toHaveValue(
+ 'Test Advertisement',
+ );
+
+ const mediaFile = new File(['video content'], 'test.mp4', {
+ type: 'video/mp4',
+ });
+ const mediaInput = screen.getByTestId('advertisementMedia');
+ userEvent.upload(mediaInput, mediaFile);
+
+ const mediaPreview = await screen.findByTestId('mediaPreview');
+ expect(mediaPreview).toBeInTheDocument();
+
+ fireEvent.change(getByLabelText(translations.Rtype), {
+ target: { value: 'BANNER' },
+ });
+ expect(getByLabelText(translations.Rtype)).toHaveValue('BANNER');
+
+ fireEvent.change(getByLabelText(translations.RstartDate), {
+ target: { value: '2023-02-02' },
+ });
+ expect(getByLabelText(translations.RstartDate)).toHaveValue('2023-02-02');
+
+ fireEvent.change(getByLabelText(translations.RendDate), {
+ target: { value: '2023-01-01' },
+ });
+ expect(getByLabelText(translations.RendDate)).toHaveValue('2023-01-01');
+
+ fireEvent.click(getByText(translations.saveChanges));
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(
+ 'End Date should be greater than or equal to Start Date',
+ );
+ });
+ jest.useRealTimers();
+ });
+
+ test('Media preview renders correctly', async () => {
+ jest.useFakeTimers();
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByText(translations.createAdvertisement));
+ await screen.findByText(translations.addNew);
+
+ const mediaFile = new File(['video content'], 'test.mp4', {
+ type: 'video/mp4',
+ });
+ const mediaInput = screen.getByTestId('advertisementMedia');
+ userEvent.upload(mediaInput, mediaFile);
+
+ const mediaPreview = await screen.findByTestId('mediaPreview');
+ expect(mediaPreview).toBeInTheDocument();
+
+ const closeButton = await screen.findByTestId('closePreview');
+ fireEvent.click(closeButton);
+ expect(mediaPreview).not.toBeInTheDocument();
+ });
+ jest.useRealTimers();
+});
diff --git a/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.tsx b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.tsx
new file mode 100644
index 0000000000..1d5c51ce84
--- /dev/null
+++ b/src/components/Advertisements/core/AdvertisementRegister/AdvertisementRegister.tsx
@@ -0,0 +1,445 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import styles from './AdvertisementRegister.module.css';
+import { Button, Form, Modal } from 'react-bootstrap';
+import {
+ ADD_ADVERTISEMENT_MUTATION,
+ UPDATE_ADVERTISEMENT_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import dayjs from 'dayjs';
+import convertToBase64 from 'utils/convertToBase64';
+import { ORGANIZATION_ADVERTISEMENT_LIST } from 'GraphQl/Queries/Queries';
+import { useParams } from 'react-router-dom';
+
+/**
+ * Props for the `advertisementRegister` component.
+ */
+interface InterfaceAddOnRegisterProps {
+ id?: string; // Optional organization ID
+ createdBy?: string; // Optional user who created the advertisement
+ formStatus?: string; // Determines if the form is in register or edit mode
+ idEdit?: string; // ID of the advertisement to edit
+ nameEdit?: string; // Name of the advertisement to edit
+ typeEdit?: string; // Type of the advertisement to edit
+ orgIdEdit?: string; // Organization ID associated with the advertisement
+ advertisementMediaEdit?: string; // Media URL of the advertisement to edit
+ endDateEdit?: Date; // End date of the advertisement to edit
+ startDateEdit?: Date; // Start date of the advertisement to edit
+ setAfter: React.Dispatch>; // Function to update parent state
+}
+
+/**
+ * State for the advertisement form.
+ */
+interface InterfaceFormStateTypes {
+ name: string; // Name of the advertisement
+ advertisementMedia: string; // Base64-encoded media of the advertisement
+ type: string; // Type of advertisement (e.g., BANNER, POPUP)
+ startDate: Date; // Start date of the advertisement
+ endDate: Date; // End date of the advertisement
+ organizationId: string | undefined; // Organization ID
+}
+
+/**
+ * Component for registering or editing an advertisement.
+ *
+ * @param props - Contains form status, advertisement details, and a function to update parent state.
+ * @returns A JSX element that renders a form inside a modal for creating or editing an advertisement.
+ *
+ * @example
+ * ```tsx
+ * console.log(value)}
+ * />
+ * ```
+ */
+function advertisementRegister({
+ formStatus = 'register',
+ idEdit,
+ nameEdit = '',
+ typeEdit = 'BANNER',
+ advertisementMediaEdit = '',
+ endDateEdit = new Date(),
+ startDateEdit = new Date(),
+ setAfter,
+}: InterfaceAddOnRegisterProps): JSX.Element {
+ const { t } = useTranslation('translation', { keyPrefix: 'advertisement' });
+ const { t: tCommon } = useTranslation('common');
+ const { t: tErrors } = useTranslation('errors');
+
+ const { orgId: currentOrg } = useParams();
+
+ const [show, setShow] = useState(false);
+ const handleClose = (): void => setShow(false); // Closes the modal
+ const handleShow = (): void => setShow(true); // Shows the modal
+
+ const [create] = useMutation(ADD_ADVERTISEMENT_MUTATION, {
+ refetchQueries: [
+ {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: { first: 6, after: null, id: currentOrg },
+ },
+ ],
+ });
+
+ const [updateAdvertisement] = useMutation(UPDATE_ADVERTISEMENT_MUTATION, {
+ refetchQueries: [
+ {
+ query: ORGANIZATION_ADVERTISEMENT_LIST,
+ variables: { first: 6, after: null, id: currentOrg },
+ },
+ ],
+ });
+
+ // Initialize form state
+ const [formState, setFormState] = useState({
+ name: '',
+ advertisementMedia: '',
+ type: 'BANNER',
+ startDate: new Date(),
+ endDate: new Date(),
+ organizationId: currentOrg,
+ });
+
+ // Set form state if editing
+ useEffect(() => {
+ if (formStatus === 'edit') {
+ setFormState((prevState) => ({
+ ...prevState,
+ name: nameEdit || '',
+ advertisementMedia: advertisementMediaEdit || '',
+ type: typeEdit || 'BANNER',
+ startDate: startDateEdit || new Date(),
+ endDate: endDateEdit || new Date(),
+ orgId: currentOrg,
+ }));
+ }
+ }, [
+ formStatus,
+ nameEdit,
+ advertisementMediaEdit,
+ typeEdit,
+ startDateEdit,
+ endDateEdit,
+ currentOrg,
+ ]);
+ /**
+ * Handles advertisement registration.
+ * Validates the date range and performs the mutation to create an advertisement.
+ */
+ const handleRegister = async (): Promise => {
+ try {
+ console.log('At handle register', formState);
+ if (formState.endDate < formState.startDate) {
+ toast.error(t('endDateGreaterOrEqual') as string);
+ return;
+ }
+ const { data } = await create({
+ variables: {
+ organizationId: currentOrg,
+ name: formState.name as string,
+ type: formState.type as string,
+ startDate: dayjs(formState.startDate).format('YYYY-MM-DD'),
+ endDate: dayjs(formState.endDate).format('YYYY-MM-DD'),
+ file: formState.advertisementMedia as string,
+ },
+ });
+
+ if (data) {
+ toast.success(t('advertisementCreated') as string);
+ setFormState({
+ name: '',
+ advertisementMedia: '',
+ type: 'BANNER',
+ startDate: new Date(),
+ endDate: new Date(),
+ organizationId: currentOrg,
+ });
+ }
+ setAfter(null);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(
+ tErrors('errorOccurredCouldntCreate', {
+ entity: 'advertisement',
+ }) as string,
+ );
+ console.log('error occured', error.message);
+ }
+ }
+ };
+
+ /**
+ * Handles advertisement update.
+ * Validates the date range and performs the mutation to update the advertisement.
+ */
+ const handleUpdate = async (): Promise => {
+ try {
+ const updatedFields: Partial = {};
+
+ // Only include the fields which are updated
+ if (formState.name !== nameEdit) {
+ updatedFields.name = formState.name;
+ }
+ if (formState.advertisementMedia !== advertisementMediaEdit) {
+ updatedFields.advertisementMedia = formState.advertisementMedia;
+ }
+ if (formState.type !== typeEdit) {
+ updatedFields.type = formState.type;
+ }
+ if (formState.endDate < formState.startDate) {
+ toast.error(t('endDateGreaterOrEqual') as string);
+ return;
+ }
+ const startDateFormattedString = dayjs(formState.startDate).format(
+ 'YYYY-MM-DD',
+ );
+ const endDateFormattedString = dayjs(formState.endDate).format(
+ 'YYYY-MM-DD',
+ );
+
+ const startDateDate = dayjs(
+ startDateFormattedString,
+ 'YYYY-MM-DD',
+ ).toDate();
+ const endDateDate = dayjs(endDateFormattedString, 'YYYY-MM-DD').toDate();
+
+ if (!dayjs(startDateDate).isSame(startDateEdit, 'day')) {
+ updatedFields.startDate = startDateDate;
+ }
+ if (!dayjs(endDateDate).isSame(endDateEdit, 'day')) {
+ updatedFields.endDate = endDateDate;
+ }
+
+ console.log('At handle update', updatedFields);
+ const { data } = await updateAdvertisement({
+ variables: {
+ id: idEdit,
+ ...(updatedFields.name && { name: updatedFields.name }),
+ ...(updatedFields.advertisementMedia && {
+ file: updatedFields.advertisementMedia,
+ }),
+ ...(updatedFields.type && { type: updatedFields.type }),
+ ...(updatedFields.startDate && {
+ startDate: startDateFormattedString,
+ }),
+ ...(updatedFields.endDate && { endDate: endDateFormattedString }),
+ },
+ });
+
+ if (data) {
+ toast.success(
+ tCommon('updatedSuccessfully', { item: 'Advertisement' }) as string,
+ );
+ handleClose();
+ setAfter(null);
+ }
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+ return (
+ //If register show register button else show edit button
+ <>
+ {formStatus === 'register' ? (
+
+
+ {t('createAdvertisement')}
+
+ ) : (
+
+ {tCommon('edit')}
+
+ )}
+
+
+ {formStatus === 'register' ? (
+ {t('addNew')}
+ ) : (
+ {t('editAdvertisement')}
+ )}
+
+
+
+ {t('Rname')}
+ {
+ setFormState({
+ ...formState,
+ name: e.target.value,
+ });
+ }}
+ />
+
+
+
+ {t('Rmedia')}
+
+ ,
+ ): Promise => {
+ const target = e.target as HTMLInputElement;
+ const file = target.files && target.files[0];
+ if (file) {
+ const mediaBase64 = await convertToBase64(file);
+ setFormState({
+ ...formState,
+ advertisementMedia: mediaBase64,
+ });
+ }
+ }}
+ />
+ {formState.advertisementMedia && (
+
+ {formState.advertisementMedia.includes('video') ? (
+
+
+
+ ) : (
+
+ )}
+
{
+ e.preventDefault();
+ setFormState({
+ ...formState,
+ advertisementMedia: '',
+ });
+ const fileInput = document.getElementById(
+ 'advertisementMedia',
+ ) as HTMLInputElement;
+ if (fileInput) {
+ fileInput.value = '';
+ }
+ }}
+ data-testid="closePreview"
+ >
+
+
+
+ )}
+
+
+ {t('Rtype')}
+ {
+ setFormState({
+ ...formState,
+ type: e.target.value,
+ });
+ }}
+ >
+ Popup Ad
+ Banner Ad
+
+
+
+
+ {t('RstartDate')}
+ {
+ setFormState({
+ ...formState,
+ startDate: new Date(e.target.value),
+ });
+ }}
+ />
+
+
+
+ {t('RendDate')}
+ {
+ setFormState({
+ ...formState,
+ endDate: new Date(e.target.value),
+ });
+ }}
+ />
+
+
+
+
+
+ {tCommon('close')}
+
+ {formStatus === 'register' ? (
+
+ {tCommon('register')}
+
+ ) : (
+
+ {tCommon('saveChanges')}
+
+ )}
+
+
+ >
+ );
+}
+
+advertisementRegister.propTypes = {
+ name: PropTypes.string,
+ advertisementMedia: PropTypes.string,
+ type: PropTypes.string,
+ startDate: PropTypes.instanceOf(Date),
+ endDate: PropTypes.instanceOf(Date),
+ organizationId: PropTypes.string,
+ formStatus: PropTypes.string,
+};
+
+export default advertisementRegister;
diff --git a/src/components/AgendaCategory/AgendaCategoryContainer.module.css b/src/components/AgendaCategory/AgendaCategoryContainer.module.css
new file mode 100644
index 0000000000..7ad16b4c7c
--- /dev/null
+++ b/src/components/AgendaCategory/AgendaCategoryContainer.module.css
@@ -0,0 +1,20 @@
+.createModal {
+ margin-top: 20vh;
+ margin-left: 13vw;
+ max-width: 80vw;
+}
+
+.titlemodal {
+ color: var(--bs-gray-600);
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid var(--bs-primary);
+ width: 65%;
+}
+
+.agendaCategoryOptionsButton {
+ width: 24px;
+ height: 24px;
+}
diff --git a/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx b/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx
new file mode 100644
index 0000000000..d8e27c3cb2
--- /dev/null
+++ b/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx
@@ -0,0 +1,419 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ waitFor,
+ act,
+ waitForElementToBeRemoved,
+ fireEvent,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { MockedProvider } from '@apollo/client/testing';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import i18nForTest from 'utils/i18nForTest';
+import { toast } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+import AgendaCategoryContainer from './AgendaCategoryContainer';
+import { props, props2 } from './AgendaCategoryContainerProps';
+import { MOCKS, MOCKS_ERROR_MUTATIONS } from './AgendaCategoryContainerMocks';
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR_MUTATIONS, true);
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+async function wait(ms = 100): Promise {
+ await act(async () => {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ });
+}
+
+const translations = JSON.parse(
+ JSON.stringify(
+ i18nForTest.getDataByLanguage('en')?.translation.organizationAgendaCategory,
+ ),
+);
+
+describe('Testing Agenda Category Component', () => {
+ const formData = {
+ name: 'AgendaCategory 1 Edited',
+ description: 'AgendaCategory 1 Description Edited',
+ };
+
+ test('component loads correctly with categories', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.queryByText(translations.noAgendaCategories),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('component loads correctly with no agenda Categories', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.queryByText(translations.noAgendaCategories),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('opens and closes the update modal correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendCategoryModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('updateAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('updateAgendaCategoryModalCloseBtn'),
+ );
+ });
+
+ test('opens and closes the preview modal correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaCategoryModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('previewAgendaCategoryModalCloseBtn'),
+ );
+ });
+
+ test('opens and closes the update and delete modals through the preview modal', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteAgendaCategoryModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaCategoryCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('deleteAgendaCategoryCloseBtn'),
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('editAgendaCategoryPreviewModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('editAgendaCategoryPreviewModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('updateAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('updateAgendaCategoryModalCloseBtn'),
+ );
+ });
+
+ test('updates an agenda category and toasts success', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendCategoryModalBtn')[0]);
+
+ const name = screen.getByPlaceholderText(translations.name);
+ const description = screen.getByPlaceholderText(translations.description);
+
+ fireEvent.change(name, { target: { value: '' } });
+ userEvent.type(name, formData.name);
+
+ fireEvent.change(description, { target: { value: '' } });
+ userEvent.type(description, formData.description);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('editAgendaCategoryBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('editAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.agendaCategoryUpdated,
+ );
+ });
+ });
+
+ test('toasts error on unsuccessful updation', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendCategoryModalBtn')[0]);
+
+ const nameInput = screen.getByLabelText(translations.name);
+ const descriptionInput = screen.getByLabelText(translations.description);
+ fireEvent.change(nameInput, { target: { value: '' } });
+ fireEvent.change(descriptionInput, {
+ target: { value: '' },
+ });
+ userEvent.type(nameInput, formData.name);
+ userEvent.type(descriptionInput, formData.description);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('editAgendaCategoryBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('editAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ test('deletes the agenda category and toasts success', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteAgendaCategoryModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaCategoryCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.agendaCategoryDeleted,
+ );
+ });
+ });
+
+ test('toasts error on unsuccessful deletion', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaCategoryModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteAgendaCategoryModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaCategoryCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/AgendaCategory/AgendaCategoryContainer.tsx b/src/components/AgendaCategory/AgendaCategoryContainer.tsx
new file mode 100644
index 0000000000..7b4c5cf8f4
--- /dev/null
+++ b/src/components/AgendaCategory/AgendaCategoryContainer.tsx
@@ -0,0 +1,324 @@
+import React, { useState } from 'react';
+import type { ChangeEvent } from 'react';
+import { Button, Col, Row } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import { useMutation } from '@apollo/client';
+
+import {
+ DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+ UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import type { InterfaceAgendaItemCategoryInfo } from 'utils/interfaces';
+import styles from './AgendaCategoryContainer.module.css';
+
+import AgendaCategoryDeleteModal from 'components/OrgSettings/AgendaItemCategories/AgendaCategoryDeleteModal';
+import AgendaCategoryPreviewModal from 'components/OrgSettings/AgendaItemCategories/AgendaCategoryPreviewModal';
+import AgendaCategoryUpdateModal from 'components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal';
+
+/**
+ * Component for displaying and managing agenda item categories.
+ *
+ * @param props - Contains agenda category data and functions for data management.
+ * @returns A JSX element that renders agenda item categories with options to preview, edit, and delete.
+ *
+ * @example
+ * ```tsx
+ *
+ * ```
+ */
+function agendaCategoryContainer({
+ agendaCategoryConnection,
+ agendaCategoryData,
+ agendaCategoryRefetch,
+}: {
+ agendaCategoryConnection: 'Organization';
+ agendaCategoryData: InterfaceAgendaItemCategoryInfo[] | undefined;
+ agendaCategoryRefetch: () => void;
+}): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'organizationAgendaCategory',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // State management for modals and form data
+ const [
+ agendaCategoryPreviewModalIsOpen,
+ setAgendaCategoryPreviewModalIsOpen,
+ ] = useState(false);
+ const [agendaCategoryUpdateModalIsOpen, setAgendaCategoryUpdateModalIsOpen] =
+ useState(false);
+ const [agendaCategoryDeleteModalIsOpen, setAgendaCategoryDeleteModalIsOpen] =
+ useState(false);
+
+ const [agendaCategoryId, setAgendaCategoryId] = useState('');
+
+ const [formState, setFormState] = useState({
+ name: '',
+ description: '',
+ createdBy: '',
+ });
+
+ /**
+ * Opens the preview modal and sets the state for the selected agenda category.
+ *
+ * @param agendaItemCategory - The agenda category to preview.
+ */
+ const showPreviewModal = (
+ agendaItemCategory: InterfaceAgendaItemCategoryInfo,
+ ): void => {
+ setAgendaCategoryState(agendaItemCategory);
+ setAgendaCategoryPreviewModalIsOpen(true);
+ };
+
+ /**
+ * Closes the preview modal.
+ */
+ const hidePreviewModal = (): void => {
+ setAgendaCategoryPreviewModalIsOpen(false);
+ };
+
+ /**
+ * Toggles the visibility of the update modal.
+ */
+ const showUpdateModal = (): void => {
+ setAgendaCategoryUpdateModalIsOpen(!agendaCategoryUpdateModalIsOpen);
+ };
+
+ /**
+ * Toggles the visibility of the update modal.
+ */
+ const hideUpdateModal = (): void => {
+ setAgendaCategoryUpdateModalIsOpen(!agendaCategoryUpdateModalIsOpen);
+ };
+
+ /**
+ * Toggles the visibility of the delete modal.
+ */
+ const toggleDeleteModal = (): void => {
+ setAgendaCategoryDeleteModalIsOpen(!agendaCategoryDeleteModalIsOpen);
+ };
+
+ const [updateAgendaCategory] = useMutation(
+ UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ );
+
+ /**
+ * Handles the update of an agenda category.
+ *
+ * @param event - The form submit event.
+ */
+ const updateAgendaCategoryHandler = async (
+ event: ChangeEvent,
+ ): Promise => {
+ event.preventDefault();
+ try {
+ await updateAgendaCategory({
+ variables: {
+ updateAgendaCategoryId: agendaCategoryId,
+ input: {
+ name: formState.name,
+ description: formState.description,
+ },
+ },
+ });
+
+ agendaCategoryRefetch();
+ hideUpdateModal();
+ toast.success(t('agendaCategoryUpdated') as string);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(`Agenda Category Update Failed ${error.message}`);
+ }
+ }
+ };
+
+ const [deleteAgendaCategory] = useMutation(
+ DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+ );
+
+ /**
+ * Handles the deletion of an agenda category.
+ */
+ const deleteAgendaCategoryHandler = async (): Promise => {
+ try {
+ await deleteAgendaCategory({
+ variables: {
+ deleteAgendaCategoryId: agendaCategoryId,
+ },
+ });
+ agendaCategoryRefetch();
+ toggleDeleteModal();
+ toast.success(t('agendaCategoryDeleted') as string);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(`Agenda Category Delete Failed, ${error.message}`);
+ }
+ }
+ };
+
+ /**
+ * Prepares the form state and shows the update modal for the selected agenda category.
+ *
+ * @param agendaItemCategory - The agenda category to edit.
+ */
+ const handleEditClick = (
+ agendaItemCategory: InterfaceAgendaItemCategoryInfo,
+ ): void => {
+ setAgendaCategoryState(agendaItemCategory);
+ showUpdateModal();
+ };
+
+ /**
+ * Updates the form state with the selected agenda category's details.
+ *
+ * @param agendaItemCategory - The agenda category details.
+ */
+ const setAgendaCategoryState = (
+ agendaItemCategory: InterfaceAgendaItemCategoryInfo,
+ ): void => {
+ setFormState({
+ ...formState,
+ name: `${agendaItemCategory.name} `,
+ description: `${agendaItemCategory.description}`,
+ createdBy: `${agendaItemCategory.createdBy.firstName} ${agendaItemCategory.createdBy.lastName}`,
+ });
+ setAgendaCategoryId(agendaItemCategory._id);
+ };
+
+ return (
+ <>
+
+
+
+
+ {t('name')}
+
+
+ {t('description')}
+
+
+ {t('createdBy')}
+
+
+ {t('options')}
+
+
+
+
+ {agendaCategoryData?.map((agendaCategory, index) => (
+
+
+
+ {`${agendaCategory.name}`}
+
+
+ {agendaCategory.description}
+
+
+ {`${agendaCategory.createdBy.firstName} ${agendaCategory.createdBy.lastName}`}
+
+
+
+
+ showPreviewModal(agendaCategory)}
+ >
+
+
+ handleEditClick(agendaCategory)}
+ className={`${styles.agendaCategoryOptionsButton} d-flex align-items-center justify-content-center`}
+ variant="outline-secondary"
+ >
+
+
+
+
+
+
+ {index !== agendaCategoryData.length - 1 && (
+
+ )}
+
+ ))}
+ {agendaCategoryData?.length === 0 && (
+
+ {t('noAgendaCategories')}
+
+ )}
+
+
+
+ {/* Preview modal */}
+
+ {/* Update modal */}
+
+ {/* Delete modal */}
+
+ >
+ );
+}
+
+export default agendaCategoryContainer;
diff --git a/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts b/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts
new file mode 100644
index 0000000000..f5dee5cffd
--- /dev/null
+++ b/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts
@@ -0,0 +1,104 @@
+import {
+ UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+} from 'GraphQl/Mutations/AgendaCategoryMutations';
+
+export const MOCKS = [
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ updateAgendaCategoryId: 'agendaCategory1',
+ input: {
+ name: 'AgendaCategory 1 Edited',
+ description: 'AgendaCategory 1 Description Edited',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaCategory: {
+ _id: 'agendaCategory1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ updateAgendaCategoryId: 'agendaCategory1',
+ input: {
+ name: 'AgendaCategory 1',
+ description: 'AgendaCategory 1 Description',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaCategory: {
+ _id: 'agendaCategory1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ updateAgendaCategoryId: 'agendaCategory2',
+ input: {
+ name: 'AgendaCategory 2 edited',
+ description: 'AgendaCategory 2 Description',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaCategory: {
+ _id: 'agendaCategory2',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ deleteAgendaCategoryId: 'agendaCategory1',
+ },
+ },
+ result: {
+ data: {
+ deleteAgendaCategory: {
+ _id: 'agendaCategory1',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR_MUTATIONS = [
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ updateAgendaCategoryId: 'agendaCategory1',
+ input: {
+ name: 'AgendaCategory 1 Edited',
+ description: 'AgendaCategory 1 Description Edited',
+ },
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+ {
+ request: {
+ query: DELETE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ deleteAgendaCategoryId: 'agendaCategory1',
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
diff --git a/src/components/AgendaCategory/AgendaCategoryContainerProps.ts b/src/components/AgendaCategory/AgendaCategoryContainerProps.ts
new file mode 100644
index 0000000000..5181eec153
--- /dev/null
+++ b/src/components/AgendaCategory/AgendaCategoryContainerProps.ts
@@ -0,0 +1,34 @@
+type AgendaCategoryConnectionType = 'Organization';
+
+export const props = {
+ agendaCategoryConnection: 'Organization' as AgendaCategoryConnectionType,
+ agendaCategoryData: [
+ {
+ _id: 'agendaCategory1',
+ name: 'AgendaCategory 1',
+ description: 'AgendaCategory 1 Description',
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ {
+ _id: 'agendaCategory2',
+ name: 'AgendaCategory 2',
+ description: 'AgendaCategory 2 Description',
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ ],
+ agendaCategoryRefetch: jest.fn(),
+};
+
+export const props2 = {
+ agendaCategoryConnection: 'Organization' as AgendaCategoryConnectionType,
+ agendaCategoryData: [],
+ agendaCategoryRefetch: jest.fn(),
+};
diff --git a/src/components/AgendaItems/AgendaItemsContainer.module.css b/src/components/AgendaItems/AgendaItemsContainer.module.css
new file mode 100644
index 0000000000..f254e7aad7
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsContainer.module.css
@@ -0,0 +1,230 @@
+.createModal {
+ margin-top: 20vh;
+ margin-left: 13vw;
+ max-width: 80vw;
+}
+
+.titlemodal {
+ color: var(--bs-gray-600);
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid var(--bs-primary);
+ width: 65%;
+}
+
+.agendaItemsOptionsButton {
+ width: 24px;
+ height: 24px;
+}
+
+.agendaItemModal {
+ max-width: 80vw;
+ margin-top: 2vh;
+ margin-left: 13vw;
+}
+
+.iconContainer {
+ display: flex;
+ justify-content: flex-end;
+}
+.icon {
+ margin: 1px;
+}
+
+.errorIcon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.greenregbtn {
+ margin: 1rem 0 0;
+ margin-top: 15px;
+ border: 1px solid var(--bs-gray-300);
+ box-shadow: 0 2px 2px var(--bs-gray-300);
+ padding: 10px 10px;
+ border-radius: 5px;
+ background-color: var(--bs-primary);
+ width: 100%;
+ font-size: 16px;
+ color: var(--bs-white);
+ outline: none;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.2s,
+ box-shadow 0.2s;
+ width: 100%;
+}
+
+.preview {
+ display: flex;
+ flex-direction: row;
+ font-weight: 900;
+ font-size: 16px;
+ color: rgb(80, 80, 80);
+}
+
+.view {
+ margin-left: 2%;
+ font-weight: 600;
+ font-size: 16px;
+ color: var(--bs-gray-600);
+}
+
+.previewFile {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ margin-top: 10px;
+}
+
+.previewFile img,
+.previewFile video {
+ width: 100%;
+ max-width: 400px;
+ height: auto;
+ margin-bottom: 10px;
+}
+
+.attachmentPreview {
+ position: relative;
+ width: 100%;
+}
+
+.closeButtonFile {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ background: transparent;
+ transform: scale(1.2);
+ cursor: pointer;
+ border: none;
+ color: #707070;
+ font-weight: 600;
+ font-size: 16px;
+ cursor: pointer;
+}
+
+.tableHeader {
+ background-color: var(--bs-primary);
+ color: var(--bs-white);
+ font-size: 16px;
+}
+
+.noOutline input {
+ outline: none;
+}
+
+.categoryContainer {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ justify-content: center;
+}
+
+.categoryChip {
+ display: inline-flex;
+ align-items: center;
+ background-color: #e0e0e0;
+ border-radius: 16px;
+ padding: 0 12px;
+ font-size: 14px;
+ height: 32px;
+ margin: 5px;
+}
+
+.urlListItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 5px 0;
+}
+
+.urlIcon {
+ margin-right: 10px;
+}
+
+.deleteButton {
+ margin-left: auto;
+ padding: 2px 5px;
+}
+
+.urlListItem a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.urlListItem a:hover {
+ text-decoration: underline;
+}
+
+.agendaItemRow {
+ border: 1px solid #dee2e6;
+ border-radius: 4px;
+ transition: box-shadow 0.2s ease;
+ background-color: #fff;
+}
+.agendaItemRow:hover {
+ background-color: #f0f0f0;
+}
+
+.dragging {
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ z-index: 1000;
+ background-color: #f0f0f0;
+}
+
+.droppable {
+ background-color: #f9f9f9; /* Background color of droppable area */
+}
+
+.droppableDraggingOver {
+ background-color: #e6f7ff; /* Background color of droppable area while dragging over */
+}
+
+.tableHead {
+ background-color: #31bb6b !important;
+ color: white;
+ border-radius: 20px 20px 0px 0px !important;
+ padding: 20px;
+}
+
+@media (max-width: 768px) {
+ .createModal,
+ .agendaItemModal {
+ margin: 10vh auto;
+ max-width: 90%;
+ }
+
+ .titlemodal {
+ width: 90%;
+ }
+
+ .greenregbtn {
+ width: 90%;
+ }
+
+ /* Add more specific styles for smaller screens as needed */
+}
+
+@media (max-width: 576px) {
+ .createModal,
+ .agendaItemModal {
+ margin: 5vh auto;
+ max-width: 95%;
+ }
+
+ .titlemodal {
+ width: 100%;
+ }
+
+ .greenregbtn {
+ width: 100%;
+ }
+
+ /* Additional specific styles for even smaller screens */
+}
diff --git a/src/components/AgendaItems/AgendaItemsContainer.test.tsx b/src/components/AgendaItems/AgendaItemsContainer.test.tsx
new file mode 100644
index 0000000000..8b391a2073
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsContainer.test.tsx
@@ -0,0 +1,428 @@
+import React, { act } from 'react';
+import {
+ render,
+ screen,
+ waitFor,
+ waitForElementToBeRemoved,
+ fireEvent,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { MockedProvider } from '@apollo/client/testing';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import i18nForTest from 'utils/i18nForTest';
+import { toast } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+import { props, props2 } from './AgendaItemsContainerProps';
+import { MOCKS, MOCKS_ERROR } from './AgendaItemsContainerMocks';
+import AgendaItemsContainer from './AgendaItemsContainer';
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR, true);
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+//temporarily fixes react-beautiful-dnd droppable method's depreciation error
+//needs to be fixed in React 19
+jest.spyOn(console, 'error').mockImplementation((message) => {
+ if (message.includes('Support for defaultProps will be removed')) {
+ return;
+ }
+ console.error(message);
+});
+
+async function wait(ms = 100): Promise {
+ await act(async () => {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ });
+}
+
+const translations = JSON.parse(
+ JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.agendaItems),
+);
+
+describe('Testing Agenda Items components', () => {
+ const formData = {
+ title: 'AgendaItem 1 Edited',
+ description: 'AgendaItem 1 Description Edited',
+ };
+
+ test('component loads correctly with items', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.queryByText(translations.noAgendaItems),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('component loads correctly with no agenda items', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.queryByText(translations.noAgendaItems),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('opens and closes the update modal correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendaItemModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('updateAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaItemModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('updateAgendaItemModalCloseBtn'),
+ );
+ });
+
+ test('opens and closes the preview modal correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaItemModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaItemModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('previewAgendaItemModalCloseBtn'),
+ );
+ });
+
+ test('opens and closes the update and delete modals through the preview modal', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaItemModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('previewAgendaItemModalDeleteBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaItemModalDeleteBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaItemCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaItemCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('deleteAgendaItemCloseBtn'),
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('previewAgendaItemModalUpdateBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaItemModalUpdateBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('updateAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaItemModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('updateAgendaItemModalCloseBtn'),
+ );
+ });
+
+ test('updates an agenda Items and toasts success', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendaItemModalBtn')[0]);
+
+ const title = screen.getByPlaceholderText(translations.enterTitle);
+ const description = screen.getByPlaceholderText(
+ translations.enterDescription,
+ );
+
+ fireEvent.change(title, { target: { value: '' } });
+ userEvent.type(title, formData.title);
+
+ fireEvent.change(description, { target: { value: '' } });
+ userEvent.type(description, formData.description);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateAgendaItemBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaItemBtn'));
+
+ await waitFor(() => {
+ // expect(toast.success).toBeCalledWith(translations.agendaItemUpdated);
+ });
+ });
+
+ test('toasts error on unsuccessful updation', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editAgendaItemModalBtn')[0]);
+
+ const titleInput = screen.getByLabelText(translations.title);
+ const descriptionInput = screen.getByLabelText(translations.description);
+ fireEvent.change(titleInput, { target: { value: '' } });
+ fireEvent.change(descriptionInput, {
+ target: { value: '' },
+ });
+ userEvent.type(titleInput, formData.title);
+ userEvent.type(descriptionInput, formData.description);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateAgendaItemBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateAgendaItemBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ test('deletes the agenda item and toasts success', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaItemModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('previewAgendaItemModalDeleteBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaItemModalDeleteBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaItemCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('deleteAgendaItemBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.agendaItemDeleted,
+ );
+ });
+ });
+
+ test('toasts error on unsuccessful deletion', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('previewAgendaItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('previewAgendaItemModalBtn')[0]);
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('previewAgendaItemModalDeleteBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('previewAgendaItemModalDeleteBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('deleteAgendaItemCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteAgendaItemBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ // write test case for drag and drop line:- 172-202
+});
diff --git a/src/components/AgendaItems/AgendaItemsContainer.tsx b/src/components/AgendaItems/AgendaItemsContainer.tsx
new file mode 100644
index 0000000000..4e50cb5fe8
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsContainer.tsx
@@ -0,0 +1,456 @@
+import React, { useState } from 'react';
+import type { ChangeEvent } from 'react';
+import { Button, Col, Row } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import { useMutation } from '@apollo/client';
+import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
+import type { DropResult } from 'react-beautiful-dnd';
+
+import {
+ DELETE_AGENDA_ITEM_MUTATION,
+ UPDATE_AGENDA_ITEM_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import type {
+ InterfaceAgendaItemInfo,
+ InterfaceAgendaItemCategoryInfo,
+} from 'utils/interfaces';
+import styles from './AgendaItemsContainer.module.css';
+
+import AgendaItemsPreviewModal from 'components/AgendaItems/AgendaItemsPreviewModal';
+import AgendaItemsDeleteModal from 'components/AgendaItems/AgendaItemsDeleteModal';
+import AgendaItemsUpdateModal from 'components/AgendaItems/AgendaItemsUpdateModal';
+
+/**
+ * Component for displaying and managing agenda items.
+ * Supports drag-and-drop functionality, and includes modals for previewing,
+ * updating, and deleting agenda items.
+ *
+ * @param props - The props for the component.
+ * @returns JSX.Element
+ */
+function AgendaItemsContainer({
+ agendaItemConnection,
+ agendaItemData,
+ agendaItemRefetch,
+ agendaItemCategories,
+}: {
+ agendaItemConnection: 'Event';
+ agendaItemData: InterfaceAgendaItemInfo[] | undefined;
+ agendaItemRefetch: () => void;
+ agendaItemCategories: InterfaceAgendaItemCategoryInfo[] | undefined;
+}): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'agendaItems',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // State for modals
+ const [agendaItemPreviewModalIsOpen, setAgendaItemPreviewModalIsOpen] =
+ useState(false);
+ const [agendaItemUpdateModalIsOpen, setAgendaItemUpdateModalIsOpen] =
+ useState(false);
+ const [agendaItemDeleteModalIsOpen, setAgendaItemDeleteModalIsOpen] =
+ useState(false);
+
+ // State for current agenda item ID and form data
+ const [agendaItemId, setAgendaItemId] = useState('');
+
+ const [formState, setFormState] = useState<{
+ agendaItemCategoryIds: string[];
+ agendaItemCategoryNames: string[];
+ title: string;
+ description: string;
+ duration: string;
+ attachments: string[];
+ urls: string[];
+ createdBy: {
+ firstName: string;
+ lastName: string;
+ };
+ }>({
+ agendaItemCategoryIds: [],
+ agendaItemCategoryNames: [],
+ title: '',
+ description: '',
+ duration: '',
+ attachments: [],
+ urls: [],
+ createdBy: {
+ firstName: '',
+ lastName: '',
+ },
+ });
+
+ /**
+ * Shows the preview modal with the details of the selected agenda item.
+ * @param agendaItem - The agenda item to preview.
+ */
+ const showPreviewModal = (agendaItem: InterfaceAgendaItemInfo): void => {
+ setAgendaItemState(agendaItem);
+ setAgendaItemPreviewModalIsOpen(true);
+ };
+
+ /**
+ * Hides the preview modal.
+ */
+ const hidePreviewModal = (): void => {
+ setAgendaItemPreviewModalIsOpen(false);
+ };
+
+ /**
+ * Toggles the visibility of the update modal.
+ */
+ const showUpdateModal = (): void => {
+ setAgendaItemUpdateModalIsOpen(!agendaItemUpdateModalIsOpen);
+ };
+
+ /**
+ * Toggles the visibility of the update modal.
+ */
+ const hideUpdateModal = (): void => {
+ setAgendaItemUpdateModalIsOpen(!agendaItemUpdateModalIsOpen);
+ };
+
+ /**
+ * Toggles the visibility of the delete modal.
+ */
+ const toggleDeleteModal = (): void => {
+ setAgendaItemDeleteModalIsOpen(!agendaItemDeleteModalIsOpen);
+ };
+
+ const [updateAgendaItem] = useMutation(UPDATE_AGENDA_ITEM_MUTATION);
+
+ /**
+ * Handles updating an agenda item.
+ * @param e - The form submission event.
+ */
+ const updateAgendaItemHandler = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ await updateAgendaItem({
+ variables: {
+ updateAgendaItemId: agendaItemId,
+ input: {
+ title: formState.title,
+ description: formState.description,
+ duration: formState.duration,
+ categories: formState.agendaItemCategoryIds,
+ attachments: formState.attachments,
+ urls: formState.urls,
+ },
+ },
+ });
+ agendaItemRefetch();
+ hideUpdateModal();
+ toast.success(t('agendaItemUpdated') as string);
+ } catch (error) {
+ if (error instanceof Error) {
+ toast.error(`${error.message}`);
+ }
+ }
+ };
+
+ const [deleteAgendaItem] = useMutation(DELETE_AGENDA_ITEM_MUTATION);
+
+ /**
+ * Handles deleting an agenda item.
+ */
+ const deleteAgendaItemHandler = async (): Promise => {
+ try {
+ await deleteAgendaItem({
+ variables: {
+ removeAgendaItemId: agendaItemId,
+ },
+ });
+ agendaItemRefetch();
+ toggleDeleteModal();
+ toast.success(t('agendaItemDeleted') as string);
+ } catch (error) {
+ if (error instanceof Error) {
+ toast.error(`${error.message}`);
+ }
+ }
+ };
+
+ /**
+ * Handles click event to show the update modal for the selected agenda item.
+ * @param agendaItem - The agenda item to update.
+ */
+ const handleEditClick = (agendaItem: InterfaceAgendaItemInfo): void => {
+ setAgendaItemState(agendaItem);
+ showUpdateModal();
+ };
+
+ /**
+ * Sets the state for the selected agenda item.
+ * @param agendaItem - The agenda item to set in the state.
+ */
+ const setAgendaItemState = (agendaItem: InterfaceAgendaItemInfo): void => {
+ setFormState({
+ ...formState,
+ agendaItemCategoryIds: agendaItem.categories.map(
+ (category) => category._id,
+ ),
+ agendaItemCategoryNames: agendaItem.categories.map(
+ (category) => category.name,
+ ),
+ title: agendaItem.title,
+ description: agendaItem.description,
+ duration: agendaItem.duration,
+ attachments: agendaItem.attachments,
+ urls: agendaItem.urls,
+ createdBy: {
+ firstName: agendaItem.createdBy.firstName,
+ lastName: agendaItem.createdBy.lastName,
+ },
+ });
+ setAgendaItemId(agendaItem._id);
+ };
+
+ /**
+ * Handles the end of a drag-and-drop operation.
+ * @param result - The result of the drag-and-drop operation.
+ */
+ const onDragEnd = async (result: DropResult): Promise => {
+ if (!result.destination || !agendaItemData) {
+ return;
+ }
+
+ const reorderedAgendaItems = Array.from(agendaItemData);
+ const [removed] = reorderedAgendaItems.splice(result.source.index, 1);
+ reorderedAgendaItems.splice(result.destination.index, 0, removed);
+
+ try {
+ await Promise.all(
+ reorderedAgendaItems.map(async (item, index) => {
+ if (item.sequence !== index + 1) {
+ // Only update if the sequence has changed
+ await updateAgendaItem({
+ variables: {
+ updateAgendaItemId: item._id,
+ input: {
+ sequence: index + 1, // Update sequence based on new index
+ },
+ },
+ });
+ }
+ }),
+ );
+
+ // After updating all items, refetch data and notify success
+ agendaItemRefetch();
+ } catch (error) {
+ if (error instanceof Error) {
+ toast.error(`${error.message}`);
+ }
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+ {t('sequence')}
+
+
+ {t('title')}
+
+
+ {t('category')}
+
+
+ {t('description')}
+
+
+ {t('options')}
+
+
+
+
+
+ {(provided) => (
+
+ {agendaItemData &&
+ agendaItemData.map((agendaItem, index) => (
+
+ {(provided, snapshot) => (
+
+
+
+
+
+
+ {agendaItem.title}
+
+
+
+ {agendaItem.categories.length > 0 ? (
+ agendaItem.categories.map((category, idx) => (
+
+ {category.name}
+ {idx < agendaItem.categories.length - 1 &&
+ ', '}
+
+ ))
+ ) : (
+
+ No Category
+
+ )}
+
+ {' '}
+
+ {agendaItem.description}
+
+
+
+ showPreviewModal(agendaItem)}
+ >
+
+
+ handleEditClick(agendaItem)}
+ className={`${styles.agendaItemsOptionsButton} d-flex align-items-center justify-content-center`}
+ variant="outline-secondary"
+ >
+
+
+
+
+
+
+ )}
+
+ ))}
+ {agendaItemData?.length === 0 && (
+
+ {t('noAgendaItems')}
+
+ )}
+
+ )}
+
+
+
+ {/* Preview modal */}
+
+ {/* Delete modal */}
+
+ {/* Update modal */}
+
+ >
+ );
+}
+
+export default AgendaItemsContainer;
diff --git a/src/components/AgendaItems/AgendaItemsContainerMocks.ts b/src/components/AgendaItems/AgendaItemsContainerMocks.ts
new file mode 100644
index 0000000000..11cd8b254e
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsContainerMocks.ts
@@ -0,0 +1,119 @@
+import {
+ UPDATE_AGENDA_ITEM_MUTATION,
+ DELETE_AGENDA_ITEM_MUTATION,
+} from 'GraphQl/Mutations/AgendaItemMutations';
+
+export const MOCKS = [
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ updateAgendaItemId: 'agendaItem1',
+ input: {
+ title: 'AgendaItem 1 Edited',
+ description: 'AgendaItem 1 Description Edited',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaItem: {
+ _id: 'agendaItem1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ updateAgendaItemId: 'agendaItem1',
+ input: {
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaItem: {
+ _id: 'agendaItem1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ updateAgendaItemId: 'agendaItem2',
+ input: {
+ title: 'AgendaItem 2 edited',
+ description: 'AgendaItem 2 Description',
+ },
+ },
+ },
+ result: {
+ data: {
+ updateAgendaItem: {
+ _id: 'agendaItem2',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_AGENDA_ITEM_MUTATION,
+ variables: {
+ removeAgendaItemId: 'agendaItem1',
+ },
+ },
+ result: {
+ data: {
+ removeAgendaItem: {
+ _id: 'agendaItem1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_AGENDA_ITEM_MUTATION,
+ variables: {
+ removeAgendaItemId: 'agendaItem2',
+ },
+ },
+ result: {
+ data: {
+ removeAgendaItem: {
+ _id: 'agendaItem2',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR = [
+ {
+ request: {
+ query: UPDATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ updateAgendaItemId: 'agendaItem1',
+ input: {
+ title: 'AgendaItem 1 Edited',
+ description: 'AgendaItem 1 Description Edited',
+ },
+ },
+ },
+ error: new Error('An error occurred'),
+ },
+ {
+ request: {
+ query: DELETE_AGENDA_ITEM_MUTATION,
+ variables: {
+ removeAgendaItemId: 'agendaItem1',
+ },
+ },
+ error: new Error('An error occurred'),
+ },
+];
diff --git a/src/components/AgendaItems/AgendaItemsContainerProps.ts b/src/components/AgendaItems/AgendaItemsContainerProps.ts
new file mode 100644
index 0000000000..d6dcf3feca
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsContainerProps.ts
@@ -0,0 +1,101 @@
+type AgendaItemConnectionType = 'Event';
+
+export const props = {
+ agendaItemConnection: 'Event' as AgendaItemConnectionType,
+ agendaItemData: [
+ {
+ _id: 'agendaItem1',
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ duration: '2h',
+ attachments: ['attachment1'],
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ urls: [],
+ users: [],
+ sequence: 1,
+ categories: [
+ {
+ _id: 'category1',
+ name: 'Category 1',
+ },
+ ],
+ organization: {
+ _id: 'org1',
+ name: 'Unity Foundation',
+ },
+ relatedEvent: {
+ _id: 'event1',
+ title: 'Aerobics for Everyone',
+ },
+ },
+ {
+ _id: 'agendaItem2',
+ title: 'AgendaItem 2',
+ description: 'AgendaItem 2 Description',
+ duration: '1h',
+ attachments: ['attachment3'],
+ createdBy: {
+ _id: 'user1',
+ firstName: 'Jane',
+ lastName: 'Doe',
+ },
+ urls: ['http://example.com'],
+ users: [
+ {
+ _id: 'user2',
+ firstName: 'John',
+ lastName: 'Smith',
+ },
+ ],
+ sequence: 2,
+ categories: [
+ {
+ _id: 'category2',
+ name: 'Category 2',
+ },
+ ],
+ organization: {
+ _id: 'org2',
+ name: 'Health Organization',
+ },
+ relatedEvent: {
+ _id: 'event2',
+ title: 'Yoga for Beginners',
+ },
+ },
+ ],
+ agendaItemRefetch: jest.fn(),
+ agendaItemCategories: [
+ {
+ _id: 'agendaCategory1',
+ name: 'AgendaCategory 1',
+ description: 'AgendaCategory 1 Description',
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ {
+ _id: 'agendaCategory2',
+ name: 'AgendaCategory 2',
+ description: 'AgendaCategory 2 Description',
+ createdBy: {
+ _id: 'user1',
+ firstName: 'Jane',
+ lastName: 'Doe',
+ },
+ },
+ ],
+};
+
+export const props2 = {
+ agendaItemConnection: 'Event' as AgendaItemConnectionType,
+ agendaItemData: [],
+ agendaItemRefetch: jest.fn(),
+ agendaItemCategories: [],
+};
diff --git a/src/components/AgendaItems/AgendaItemsCreateModal.test.tsx b/src/components/AgendaItems/AgendaItemsCreateModal.test.tsx
new file mode 100644
index 0000000000..5b7339ad67
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsCreateModal.test.tsx
@@ -0,0 +1,368 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+ within,
+} from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import AgendaItemsCreateModal from './AgendaItemsCreateModal';
+import { toast } from 'react-toastify';
+import convertToBase64 from 'utils/convertToBase64';
+
+const mockFormState = {
+ title: 'Test Title',
+ description: 'Test Description',
+ duration: '20',
+ attachments: ['Test Attachment'],
+ urls: ['https://example.com'],
+ agendaItemCategoryIds: ['category'],
+};
+const mockHideCreateModal = jest.fn();
+const mockSetFormState = jest.fn();
+const mockCreateAgendaItemHandler = jest.fn();
+const mockT = (key: string): string => key;
+const mockAgendaItemCategories = [
+ {
+ _id: '1',
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: {
+ _id: '1',
+ firstName: 'Test',
+ lastName: 'User',
+ },
+ },
+ {
+ _id: '2',
+ name: 'Another Category',
+ description: 'Another Description',
+ createdBy: {
+ _id: '2',
+ firstName: 'Another',
+ lastName: 'Creator',
+ },
+ },
+ {
+ _id: '3',
+ name: 'Third Category',
+ description: 'Third Description',
+ createdBy: {
+ _id: '3',
+ firstName: 'Third',
+ lastName: 'User',
+ },
+ },
+];
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+jest.mock('utils/convertToBase64');
+const mockedConvertToBase64 = convertToBase64 as jest.MockedFunction<
+ typeof convertToBase64
+>;
+
+describe('AgendaItemsCreateModal', () => {
+ test('renders modal correctly', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('agendaItemDetails')).toBeInTheDocument();
+ expect(screen.getByTestId('createAgendaItemFormBtn')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('createAgendaItemModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+
+ test('tests the condition for formState', async () => {
+ const mockFormState = {
+ title: 'Test Title',
+ description: 'Test Description',
+ duration: '20',
+ attachments: ['Test Attachment'],
+ urls: ['https://example.com'],
+ agendaItemCategoryIds: ['1'],
+ };
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ fireEvent.change(screen.getByLabelText('title'), {
+ target: { value: 'New title' },
+ });
+
+ fireEvent.change(screen.getByLabelText('description'), {
+ target: { value: 'New description' },
+ });
+
+ fireEvent.change(screen.getByLabelText('duration'), {
+ target: { value: '30' },
+ });
+
+ fireEvent.click(screen.getByTestId('deleteUrl'));
+ fireEvent.click(screen.getByTestId('deleteAttachment'));
+
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ title: 'New title',
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ description: 'New description',
+ });
+
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ duration: '30',
+ });
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ urls: [],
+ });
+ });
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ attachments: [],
+ });
+ });
+ });
+ test('handleAddUrl correctly adds valid URL', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const urlInput = screen.getByTestId('urlInput');
+ const linkBtn = screen.getByTestId('linkBtn');
+
+ fireEvent.change(urlInput, { target: { value: 'https://example.com' } });
+ fireEvent.click(linkBtn);
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ urls: [...mockFormState.urls, 'https://example.com'],
+ });
+ });
+ });
+
+ test('shows error toast for invalid URL', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const urlInput = screen.getByTestId('urlInput');
+ const linkBtn = screen.getByTestId('linkBtn');
+
+ fireEvent.change(urlInput, { target: { value: 'invalid-url' } });
+ fireEvent.click(linkBtn);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('invalidUrl');
+ });
+ });
+
+ test('shows error toast for file size exceeding limit', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const fileInput = screen.getByTestId('attachment');
+ const largeFile = new File(
+ ['a'.repeat(11 * 1024 * 1024)],
+ 'large-file.jpg',
+ ); // 11 MB file
+
+ Object.defineProperty(fileInput, 'files', {
+ value: [largeFile],
+ });
+
+ fireEvent.change(fileInput);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('fileSizeExceedsLimit');
+ });
+ });
+
+ test('adds files correctly when within size limit', async () => {
+ mockedConvertToBase64.mockResolvedValue('base64-file');
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const fileInput = screen.getByTestId('attachment');
+ const smallFile = new File(['small-file-content'], 'small-file.jpg'); // Small file
+
+ Object.defineProperty(fileInput, 'files', {
+ value: [smallFile],
+ });
+
+ fireEvent.change(fileInput);
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ attachments: [...mockFormState.attachments, 'base64-file'],
+ });
+ });
+ });
+ test('renders autocomplete and selects categories correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const autocomplete = screen.getByTestId('categorySelect');
+ expect(autocomplete).toBeInTheDocument();
+
+ const input = within(autocomplete).getByRole('combobox');
+ fireEvent.mouseDown(input);
+
+ const options = screen.getAllByRole('option');
+ expect(options).toHaveLength(mockAgendaItemCategories.length);
+
+ fireEvent.click(options[0]);
+ fireEvent.click(options[1]);
+ });
+});
diff --git a/src/components/AgendaItems/AgendaItemsCreateModal.tsx b/src/components/AgendaItems/AgendaItemsCreateModal.tsx
new file mode 100644
index 0000000000..a37ab329a2
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsCreateModal.tsx
@@ -0,0 +1,330 @@
+import React, { useState, useEffect } from 'react';
+import { Modal, Form, Button, Row, Col } from 'react-bootstrap';
+import { Autocomplete, TextField } from '@mui/material';
+
+import { FaLink, FaTrash } from 'react-icons/fa';
+import { toast } from 'react-toastify';
+import styles from './AgendaItemsContainer.module.css';
+import type { ChangeEvent } from 'react';
+import type { InterfaceAgendaItemCategoryInfo } from 'utils/interfaces';
+import convertToBase64 from 'utils/convertToBase64';
+
+interface InterfaceFormStateType {
+ agendaItemCategoryIds: string[];
+ title: string;
+ description: string;
+ duration: string;
+ attachments: string[];
+ urls: string[];
+}
+
+interface InterfaceAgendaItemsCreateModalProps {
+ agendaItemCreateModalIsOpen: boolean;
+ hideCreateModal: () => void;
+ formState: InterfaceFormStateType;
+ setFormState: (state: React.SetStateAction) => void;
+ createAgendaItemHandler: (e: ChangeEvent) => Promise;
+ t: (key: string) => string;
+ agendaItemCategories: InterfaceAgendaItemCategoryInfo[] | undefined;
+}
+
+/**
+ * Component for creating a new agenda item.
+ * Displays a modal form where users can input details for a new agenda item, including title, description, duration, categories, URLs, and attachments.
+ *
+ * @param agendaItemCreateModalIsOpen - Boolean flag indicating if the modal is open.
+ * @param hideCreateModal - Function to close the modal.
+ * @param formState - Current state of the form fields.
+ * @param setFormState - Function to update the form state.
+ * @param createAgendaItemHandler - Function to handle form submission.
+ * @param t - Function for translating text based on keys.
+ * @param agendaItemCategories - List of agenda item categories for selection.
+ */
+const AgendaItemsCreateModal: React.FC<
+ InterfaceAgendaItemsCreateModalProps
+> = ({
+ agendaItemCreateModalIsOpen,
+ hideCreateModal,
+ formState,
+ setFormState,
+ createAgendaItemHandler,
+ t,
+ agendaItemCategories,
+}) => {
+ const [newUrl, setNewUrl] = useState('');
+
+ useEffect(() => {
+ // Ensure URLs and attachments do not have empty or invalid entries
+ setFormState((prevState) => ({
+ ...prevState,
+ urls: prevState.urls.filter((url) => url.trim() !== ''),
+ attachments: prevState.attachments.filter((att) => att !== ''),
+ }));
+ }, []);
+
+ /**
+ * Validates if a given URL is in a correct format.
+ *
+ * @param url - URL string to validate.
+ * @returns True if the URL is valid, false otherwise.
+ */
+ const isValidUrl = (url: string): boolean => {
+ // Regular expression for basic URL validation
+ const urlRegex = /^(ftp|http|https):\/\/[^ "]+$/;
+ return urlRegex.test(url);
+ };
+
+ /**
+ * Handles adding a new URL to the form state.
+ *
+ * Checks if the URL is valid before adding it.
+ */
+ const handleAddUrl = (): void => {
+ if (newUrl.trim() !== '' && isValidUrl(newUrl.trim())) {
+ setFormState({
+ ...formState,
+ urls: [...formState.urls.filter((url) => url.trim() !== ''), newUrl],
+ });
+ setNewUrl('');
+ } else {
+ toast.error(t('invalidUrl'));
+ }
+ };
+
+ /**
+ * Handles removing a URL from the form state.
+ *
+ * @param url - URL to remove.
+ */
+ const handleRemoveUrl = (url: string): void => {
+ setFormState({
+ ...formState,
+ urls: formState.urls.filter((item) => item !== url),
+ });
+ };
+
+ /**
+ * Handles file selection and converts files to base64 before updating the form state.
+ *
+ * @param e - File input change event.
+ */
+ const handleFileChange = async (
+ e: React.ChangeEvent,
+ ): Promise => {
+ const target = e.target as HTMLInputElement;
+ if (target.files) {
+ const files = Array.from(target.files);
+ let totalSize = 0;
+ files.forEach((file) => {
+ totalSize += file.size;
+ });
+ if (totalSize > 10 * 1024 * 1024) {
+ toast.error(t('fileSizeExceedsLimit'));
+ return;
+ }
+ const base64Files = await Promise.all(
+ files.map(async (file) => await convertToBase64(file)),
+ );
+ setFormState({
+ ...formState,
+ attachments: [...formState.attachments, ...base64Files],
+ });
+ }
+ };
+
+ /**
+ * Handles removing an attachment from the form state.
+ *
+ * @param attachment - Attachment to remove.
+ */
+ const handleRemoveAttachment = (attachment: string): void => {
+ setFormState({
+ ...formState,
+ attachments: formState.attachments.filter((item) => item !== attachment),
+ });
+ };
+
+ return (
+
+
+ {t('agendaItemDetails')}
+
+
+
+
+
+
+
+ formState.agendaItemCategoryIds.includes(category._id),
+ ) || []
+ }
+ filterSelectedOptions={true}
+ getOptionLabel={(
+ category: InterfaceAgendaItemCategoryInfo,
+ ): string => category.name}
+ onChange={(_, newCategories): void => {
+ setFormState({
+ ...formState,
+ agendaItemCategoryIds: newCategories.map(
+ (category) => category._id,
+ ),
+ });
+ }}
+ renderInput={(params) => (
+
+ )}
+ />
+
+
+
+
+ {t('title')}
+
+ setFormState({ ...formState, title: e.target.value })
+ }
+ />
+
+
+
+
+ {t('duration')}
+
+ setFormState({ ...formState, duration: e.target.value })
+ }
+ />
+
+
+
+
+ {t('description')}
+
+ setFormState({ ...formState, description: e.target.value })
+ }
+ />
+
+
+
+ {t('url')}
+
+
setNewUrl(e.target.value)}
+ />
+
+ {t('link')}
+
+
+
+ {formState.urls.map((url, index) => (
+
+
+
+ {url.length > 50 ? url.substring(0, 50) + '...' : url}
+
+ handleRemoveUrl(url)}
+ >
+
+
+
+ ))}
+
+
+ {t('attachments')}
+
+ {t('attachmentLimit')}
+
+ {formState.attachments && (
+
+ {formState.attachments.map((attachment, index) => (
+
+ {attachment.includes('video') ? (
+
+
+
+ ) : (
+
+ )}
+
{
+ e.preventDefault();
+ handleRemoveAttachment(attachment);
+ }}
+ data-testid="deleteAttachment"
+ >
+
+
+
+ ))}
+
+ )}
+
+ {t('createAgendaItem')}
+
+
+
+
+ );
+};
+
+export default AgendaItemsCreateModal;
diff --git a/src/components/AgendaItems/AgendaItemsDeleteModal.tsx b/src/components/AgendaItems/AgendaItemsDeleteModal.tsx
new file mode 100644
index 0000000000..cba3b29446
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsDeleteModal.tsx
@@ -0,0 +1,73 @@
+import React from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import styles from './AgendaItemsContainer.module.css';
+
+interface InterfaceAgendaItemsDeleteModalProps {
+ agendaItemDeleteModalIsOpen: boolean;
+ toggleDeleteModal: () => void;
+ deleteAgendaItemHandler: () => Promise;
+ t: (key: string) => string;
+ tCommon: (key: string) => string;
+}
+
+/**
+ * Modal component for confirming the deletion of an agenda item.
+ * Displays a confirmation dialog when a user attempts to delete an agenda item.
+ *
+ * @param agendaItemDeleteModalIsOpen - Boolean flag indicating if the modal is open.
+ * @param toggleDeleteModal - Function to toggle the visibility of the modal.
+ * @param deleteAgendaItemHandler - Function to handle the deletion of the agenda item.
+ * @param t - Function for translating text based on keys.
+ * @param tCommon - Function for translating common text keys.
+ */
+const AgendaItemsDeleteModal: React.FC<
+ InterfaceAgendaItemsDeleteModalProps
+> = ({
+ agendaItemDeleteModalIsOpen,
+ toggleDeleteModal,
+ deleteAgendaItemHandler,
+ t,
+ tCommon,
+}) => {
+ return (
+
+
+
+ {t('deleteAgendaItem')}
+
+
+
+ {t('deleteAgendaItemMsg')}
+
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+ );
+};
+
+export default AgendaItemsDeleteModal;
diff --git a/src/components/AgendaItems/AgendaItemsPreviewModal.test.tsx b/src/components/AgendaItems/AgendaItemsPreviewModal.test.tsx
new file mode 100644
index 0000000000..0a7b4646ba
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsPreviewModal.test.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import AgendaItemsPreviewModal from './AgendaItemsPreviewModal';
+
+const mockFormState = {
+ title: 'Test Title',
+ description: 'Test Description',
+ duration: '20',
+ attachments: [
+ 'https://example.com/video.mp4',
+ 'https://example.com/image.jpg',
+ ],
+ urls: [
+ 'https://example.com',
+ 'https://thisisaverylongurlthatexceedsfiftycharacters.com/very/long/path',
+ ],
+ agendaItemCategoryIds: ['category'],
+ agendaItemCategoryNames: ['category'],
+ createdBy: {
+ firstName: 'Test',
+ lastName: 'User',
+ },
+};
+
+const mockT = (key: string): string => key;
+
+describe('AgendaItemsPreviewModal', () => {
+ test('check url and attachment links', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('Test Title')).toBeInTheDocument();
+ expect(screen.getByText('Test Description')).toBeInTheDocument();
+ expect(screen.getByText('20')).toBeInTheDocument();
+ expect(screen.getByText('https://example.com')).toBeInTheDocument();
+
+ // Check attachments
+ const videoLink = screen.getByText(
+ (content, element) =>
+ element?.tagName.toLowerCase() === 'a' &&
+ (element as HTMLAnchorElement)?.href ===
+ 'https://example.com/video.mp4',
+ );
+ expect(videoLink).toBeInTheDocument();
+
+ const imageLink = screen.getByText(
+ (content, element) =>
+ element?.tagName.toLowerCase() === 'a' &&
+ (element as HTMLAnchorElement)?.href ===
+ 'https://example.com/image.jpg',
+ );
+ expect(imageLink).toBeInTheDocument();
+
+ // Check URLs
+ const shortUrlLink = screen.getByText(
+ (content, element) =>
+ element?.tagName.toLowerCase() === 'a' &&
+ (element as HTMLAnchorElement)?.href === 'https://example.com/',
+ );
+ expect(shortUrlLink).toBeInTheDocument();
+ expect(shortUrlLink).toHaveTextContent('https://example.com');
+
+ const longUrlLink = screen.getByText(
+ (content, element) =>
+ element?.tagName.toLowerCase() === 'a' &&
+ (element as HTMLAnchorElement)?.href ===
+ 'https://thisisaverylongurlthatexceedsfiftycharacters.com/very/long/path',
+ );
+ expect(longUrlLink).toBeInTheDocument();
+ expect(longUrlLink).toHaveTextContent(
+ 'https://thisisaverylongurlthatexceedsfiftycharacte...',
+ );
+ });
+});
diff --git a/src/components/AgendaItems/AgendaItemsPreviewModal.tsx b/src/components/AgendaItems/AgendaItemsPreviewModal.tsx
new file mode 100644
index 0000000000..299165656c
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsPreviewModal.tsx
@@ -0,0 +1,173 @@
+import React from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import styles from './AgendaItemsContainer.module.css';
+import { FaLink } from 'react-icons/fa';
+
+interface InterfaceFormStateType {
+ agendaItemCategoryIds: string[];
+ agendaItemCategoryNames: string[];
+ title: string;
+ description: string;
+ duration: string;
+ attachments: string[];
+ urls: string[];
+ createdBy: {
+ firstName: string;
+ lastName: string;
+ };
+}
+
+interface InterfaceAgendaItemsPreviewModalProps {
+ agendaItemPreviewModalIsOpen: boolean;
+ hidePreviewModal: () => void;
+ showUpdateModal: () => void;
+ toggleDeleteModal: () => void;
+ formState: InterfaceFormStateType;
+ t: (key: string) => string;
+}
+
+/**
+ * Modal component for previewing details of an agenda item.
+ * Displays the details of the selected agenda item, including its categories, title, description, duration, creator, URLs, and attachments.
+ * Also provides options to update or delete the agenda item.
+ *
+ * @param agendaItemPreviewModalIsOpen - Boolean flag indicating if the preview modal is open.
+ * @param hidePreviewModal - Function to hide the preview modal.
+ * @param showUpdateModal - Function to show the update modal.
+ * @param toggleDeleteModal - Function to toggle the delete modal.
+ * @param formState - The current state of the form containing agenda item details.
+ * @param t - Function for translating text based on keys.
+ */
+const AgendaItemsPreviewModal: React.FC<
+ InterfaceAgendaItemsPreviewModalProps
+> = ({
+ agendaItemPreviewModalIsOpen,
+ hidePreviewModal,
+ showUpdateModal,
+ toggleDeleteModal,
+ formState,
+ t,
+}) => {
+ /**
+ * Renders the attachments preview.
+ *
+ * @returns JSX elements for each attachment, displaying videos and images.
+ */
+ const renderAttachments = (): JSX.Element[] => {
+ return formState.attachments.map((attachment, index) => (
+
+ {attachment.includes('video') ? (
+
+
+
+
+
+ ) : (
+
+
+
+ )}
+
+ ));
+ };
+
+ /**
+ * Renders the URLs list.
+ *
+ * @returns JSX elements for each URL, displaying clickable links.
+ */
+ const renderUrls = (): JSX.Element[] => {
+ return formState.urls.map((url, index) => (
+
+
+
+ {url.length > 50 ? `${url.substring(0, 50)}...` : url}
+
+
+ ));
+ };
+
+ return (
+
+
+ {t('agendaItemDetails')}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AgendaItemsPreviewModal;
diff --git a/src/components/AgendaItems/AgendaItemsUpdateModal.test.tsx b/src/components/AgendaItems/AgendaItemsUpdateModal.test.tsx
new file mode 100644
index 0000000000..0f11ea31b2
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsUpdateModal.test.tsx
@@ -0,0 +1,369 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+ within,
+} from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import AgendaItemsUpdateModal from './AgendaItemsUpdateModal';
+import { toast } from 'react-toastify';
+import convertToBase64 from 'utils/convertToBase64';
+
+const mockFormState = {
+ title: 'Test Title',
+ description: 'Test Description',
+ duration: '20',
+ attachments: ['Test Attachment'],
+ urls: ['https://example.com'],
+ agendaItemCategoryIds: ['category'],
+ agendaItemCategoryNames: ['category'],
+ createdBy: {
+ firstName: 'Test',
+ lastName: 'User',
+ },
+};
+
+const mockAgendaItemCategories = [
+ {
+ _id: '1',
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: {
+ _id: '1',
+ firstName: 'Test',
+ lastName: 'User',
+ },
+ },
+ {
+ _id: '2',
+ name: 'Another Category',
+ description: 'Another Description',
+ createdBy: {
+ _id: '2',
+ firstName: 'Another',
+ lastName: 'Creator',
+ },
+ },
+ {
+ _id: '3',
+ name: 'Third Category',
+ description: 'Third Description',
+ createdBy: {
+ _id: '3',
+ firstName: 'Third',
+ lastName: 'User',
+ },
+ },
+];
+
+const mockHideUpdateModal = jest.fn();
+const mockSetFormState = jest.fn();
+const mockUpdateAgendaItemHandler = jest.fn();
+const mockT = (key: string): string => key;
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+jest.mock('utils/convertToBase64');
+const mockedConvertToBase64 = convertToBase64 as jest.MockedFunction<
+ typeof convertToBase64
+>;
+
+describe('AgendaItemsUpdateModal', () => {
+ test('renders modal correctly', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('updateAgendaItem')).toBeInTheDocument();
+ expect(screen.getByTestId('updateAgendaItemBtn')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('updateAgendaItemModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+
+ test('tests the condition for formState.title and formState.description', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ fireEvent.change(screen.getByLabelText('title'), {
+ target: { value: 'New title' },
+ });
+
+ fireEvent.change(screen.getByLabelText('description'), {
+ target: { value: 'New description' },
+ });
+
+ fireEvent.change(screen.getByLabelText('duration'), {
+ target: { value: '30' },
+ });
+
+ fireEvent.click(screen.getByTestId('deleteUrl'));
+ fireEvent.click(screen.getByTestId('deleteAttachment'));
+
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ title: 'New title',
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ description: 'New description',
+ });
+
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ duration: '30',
+ });
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ urls: [],
+ });
+ });
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ attachments: [],
+ });
+ });
+ });
+
+ test('handleAddUrl correctly adds valid URL', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const urlInput = screen.getByTestId('urlInput');
+ const linkBtn = screen.getByTestId('linkBtn');
+
+ fireEvent.change(urlInput, { target: { value: 'https://example.com' } });
+ fireEvent.click(linkBtn);
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ urls: [...mockFormState.urls, 'https://example.com'],
+ });
+ });
+ });
+
+ test('shows error toast for invalid URL', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const urlInput = screen.getByTestId('urlInput');
+ const linkBtn = screen.getByTestId('linkBtn');
+
+ fireEvent.change(urlInput, { target: { value: 'invalid-url' } });
+ fireEvent.click(linkBtn);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('invalidUrl');
+ });
+ });
+
+ test('shows error toast for file size exceeding limit', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const fileInput = screen.getByTestId('attachment');
+ const largeFile = new File(
+ ['a'.repeat(11 * 1024 * 1024)],
+ 'large-file.jpg',
+ ); // 11 MB file
+
+ Object.defineProperty(fileInput, 'files', {
+ value: [largeFile],
+ });
+
+ fireEvent.change(fileInput);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('fileSizeExceedsLimit');
+ });
+ });
+
+ test('adds files correctly when within size limit', async () => {
+ mockedConvertToBase64.mockResolvedValue('base64-file');
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const fileInput = screen.getByTestId('attachment');
+ const smallFile = new File(['small-file-content'], 'small-file.jpg'); // Small file
+
+ Object.defineProperty(fileInput, 'files', {
+ value: [smallFile],
+ });
+
+ fireEvent.change(fileInput);
+
+ await waitFor(() => {
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ attachments: [...mockFormState.attachments, 'base64-file'],
+ });
+ });
+ });
+ test('renders autocomplete and selects categories correctly', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const autocomplete = screen.getByTestId('categorySelect');
+ expect(autocomplete).toBeInTheDocument();
+
+ const input = within(autocomplete).getByRole('combobox');
+ fireEvent.mouseDown(input);
+
+ const options = screen.getAllByRole('option');
+ expect(options).toHaveLength(mockAgendaItemCategories.length);
+
+ fireEvent.click(options[0]);
+ fireEvent.click(options[1]);
+ });
+});
diff --git a/src/components/AgendaItems/AgendaItemsUpdateModal.tsx b/src/components/AgendaItems/AgendaItemsUpdateModal.tsx
new file mode 100644
index 0000000000..7761c912ca
--- /dev/null
+++ b/src/components/AgendaItems/AgendaItemsUpdateModal.tsx
@@ -0,0 +1,334 @@
+import React, { useState, useEffect } from 'react';
+import { Modal, Form, Button, Row, Col } from 'react-bootstrap';
+import type { ChangeEvent } from 'react';
+import { Autocomplete, TextField } from '@mui/material';
+import { FaLink, FaTrash } from 'react-icons/fa';
+import { toast } from 'react-toastify';
+import convertToBase64 from 'utils/convertToBase64';
+
+import styles from './AgendaItemsContainer.module.css';
+import type { InterfaceAgendaItemCategoryInfo } from 'utils/interfaces';
+
+interface InterfaceFormStateType {
+ agendaItemCategoryIds: string[];
+ agendaItemCategoryNames: string[];
+ title: string;
+ description: string;
+ duration: string;
+ attachments: string[];
+ urls: string[];
+ createdBy: {
+ firstName: string;
+ lastName: string;
+ };
+}
+
+interface InterfaceAgendaItemsUpdateModalProps {
+ agendaItemUpdateModalIsOpen: boolean;
+ hideUpdateModal: () => void;
+ formState: InterfaceFormStateType;
+ setFormState: (state: React.SetStateAction) => void;
+ updateAgendaItemHandler: (e: ChangeEvent) => Promise;
+ t: (key: string) => string;
+ agendaItemCategories: InterfaceAgendaItemCategoryInfo[] | undefined;
+}
+
+/**
+ * Modal component for updating details of an agenda item.
+ * Provides a form to update the agenda item's title, description, duration, categories, URLs, and attachments.
+ * Also includes functionality to add, remove URLs and attachments.
+ *
+ * @param agendaItemUpdateModalIsOpen - Boolean flag indicating if the update modal is open.
+ * @param hideUpdateModal - Function to hide the update modal.
+ * @param formState - The current state of the form containing agenda item details.
+ * @param setFormState - Function to update the form state.
+ * @param updateAgendaItemHandler - Handler function for submitting the form.
+ * @param t - Function for translating text based on keys.
+ * @param agendaItemCategories - List of agenda item categories for selection.
+ */
+const AgendaItemsUpdateModal: React.FC<
+ InterfaceAgendaItemsUpdateModalProps
+> = ({
+ agendaItemUpdateModalIsOpen,
+ hideUpdateModal,
+ formState,
+ setFormState,
+ updateAgendaItemHandler,
+ t,
+ agendaItemCategories,
+}) => {
+ const [newUrl, setNewUrl] = useState('');
+
+ useEffect(() => {
+ setFormState((prevState) => ({
+ ...prevState,
+ urls: prevState.urls.filter((url) => url.trim() !== ''),
+ attachments: prevState.attachments.filter((att) => att !== ''),
+ }));
+ }, []);
+
+ /**
+ * Validates if a given URL is in a correct format.
+ *
+ * @param url - The URL to validate.
+ * @returns True if the URL is valid, false otherwise.
+ */
+ const isValidUrl = (url: string): boolean => {
+ // Regular expression for basic URL validation
+ const urlRegex = /^(ftp|http|https):\/\/[^ "]+$/;
+ return urlRegex.test(url);
+ };
+
+ /**
+ * Handles adding a new URL to the form state.
+ * Displays an error toast if the URL is invalid.
+ */
+ const handleAddUrl = (): void => {
+ if (newUrl.trim() !== '' && isValidUrl(newUrl.trim())) {
+ setFormState({
+ ...formState,
+ urls: [...formState.urls.filter((url) => url.trim() !== ''), newUrl],
+ });
+ setNewUrl('');
+ } else {
+ toast.error(t('invalidUrl'));
+ }
+ };
+
+ /**
+ * Handles removing a URL from the form state.
+ *
+ * @param url - The URL to remove.
+ */
+ const handleRemoveUrl = (url: string): void => {
+ setFormState({
+ ...formState,
+ urls: formState.urls.filter((item) => item !== url),
+ });
+ };
+
+ /**
+ * Handles file input change event.
+ * Converts selected files to base64 format and updates the form state.
+ * Displays an error toast if the total file size exceeds the limit.
+ *
+ * @param e - The change event for file input.
+ */
+ const handleFileChange = async (
+ e: React.ChangeEvent,
+ ): Promise => {
+ const target = e.target as HTMLInputElement;
+ if (target.files) {
+ const files = Array.from(target.files);
+ let totalSize = 0;
+ files.forEach((file) => {
+ totalSize += file.size;
+ });
+ if (totalSize > 10 * 1024 * 1024) {
+ toast.error(t('fileSizeExceedsLimit'));
+ return;
+ }
+ const base64Files = await Promise.all(
+ files.map(async (file) => await convertToBase64(file)),
+ );
+ setFormState({
+ ...formState,
+ attachments: [...formState.attachments, ...base64Files],
+ });
+ }
+ };
+
+ /**
+ * Handles removing an attachment from the form state.
+ *
+ * @param attachment - The attachment to remove.
+ */
+ const handleRemoveAttachment = (attachment: string): void => {
+ setFormState({
+ ...formState,
+ attachments: formState.attachments.filter((item) => item !== attachment),
+ });
+ };
+
+ return (
+
+
+ {t('updateAgendaItem')}
+
+
+
+
+
+
+
+ formState.agendaItemCategoryIds.includes(category._id),
+ ) || []
+ }
+ filterSelectedOptions={true}
+ getOptionLabel={(
+ category: InterfaceAgendaItemCategoryInfo,
+ ): string => category.name}
+ onChange={(_, newCategories): void => {
+ setFormState({
+ ...formState,
+ agendaItemCategoryIds: newCategories.map(
+ (category) => category._id,
+ ),
+ });
+ }}
+ renderInput={(params) => (
+
+ )}
+ />
+
+
+
+
+
+ {t('title')}
+
+ setFormState({ ...formState, title: e.target.value })
+ }
+ />
+
+
+
+
+ {t('duration')}
+
+ setFormState({ ...formState, duration: e.target.value })
+ }
+ />
+
+
+
+
+
+ {t('description')}
+
+ setFormState({ ...formState, description: e.target.value })
+ }
+ />
+
+
+
+ {t('url')}
+
+
setNewUrl(e.target.value)}
+ />
+
+ {t('link')}
+
+
+
+ {formState.urls.map((url, index) => (
+
+
+
+ {url.length > 50 ? url.substring(0, 50) + '...' : url}
+
+ handleRemoveUrl(url)}
+ >
+
+
+
+ ))}
+
+
+ {t('attachments')}
+
+ {t('attachmentLimit')}
+
+ {formState.attachments && (
+
+ {formState.attachments.map((attachment, index) => (
+
+ {attachment.includes('video') ? (
+
+
+
+ ) : (
+
+ )}
+
{
+ e.preventDefault();
+ handleRemoveAttachment(attachment);
+ }}
+ data-testid="deleteAttachment"
+ >
+
+
+
+ ))}
+
+ )}
+
+ {t('update')}
+
+
+
+
+ );
+};
+
+export default AgendaItemsUpdateModal;
diff --git a/src/components/Avatar/Avatar.module.css b/src/components/Avatar/Avatar.module.css
new file mode 100644
index 0000000000..13fe1bbbc6
--- /dev/null
+++ b/src/components/Avatar/Avatar.module.css
@@ -0,0 +1,14 @@
+.imageContainer {
+ width: fit-content;
+ height: fit-content;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 100%;
+}
+.imageContainer img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 100%;
+}
diff --git a/src/components/Avatar/Avatar.test.tsx b/src/components/Avatar/Avatar.test.tsx
new file mode 100644
index 0000000000..d178c48a4a
--- /dev/null
+++ b/src/components/Avatar/Avatar.test.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import Avatar from './Avatar';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+
+describe('Avatar component', () => {
+ test('renders with name and alt attribute', () => {
+ const testName = 'John Doe';
+ const testAlt = 'Test Alt Text';
+ const testSize = 64;
+
+ const { getByAltText } = render(
+
+
+
+
+ ,
+ );
+ const avatarElement = getByAltText(testAlt);
+
+ expect(avatarElement).toBeInTheDocument();
+ expect(avatarElement.getAttribute('src')).toBeDefined();
+ });
+
+ test('renders with custom style and data-testid', () => {
+ const testName = 'Jane Doe';
+ const testStyle = 'custom-avatar-style';
+ const testDataTestId = 'custom-avatar-test-id';
+
+ const { getByAltText } = render(
+
+
+
+
+ ,
+ );
+ const avatarElement = getByAltText('Dummy Avatar');
+
+ expect(avatarElement).toBeInTheDocument();
+ expect(avatarElement.getAttribute('src')).toBeDefined();
+ expect(avatarElement.getAttribute('class')).toContain(testStyle);
+ expect(avatarElement.getAttribute('data-testid')).toBe(testDataTestId);
+ });
+});
diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx
new file mode 100644
index 0000000000..3e2f818e3f
--- /dev/null
+++ b/src/components/Avatar/Avatar.tsx
@@ -0,0 +1,61 @@
+import React, { useMemo } from 'react';
+import { createAvatar } from '@dicebear/core';
+import { initials } from '@dicebear/collection';
+import styles from 'components/Avatar/Avatar.module.css';
+
+interface InterfaceAvatarProps {
+ name: string;
+ alt?: string;
+ size?: number;
+ containerStyle?: string;
+ avatarStyle?: string;
+ dataTestId?: string;
+ radius?: number;
+}
+
+/**
+ * A component that generates and displays an avatar based on the provided name.
+ * The avatar is generated using the DiceBear library with the initials style.
+ *
+ * @param name - The name used to generate the avatar.
+ * @param alt - Alternative text for the avatar image.
+ * @param size - Size of the avatar image.
+ * @param avatarStyle - Custom CSS class for the avatar image.
+ * @param dataTestId - Data-testid attribute for testing purposes.
+ * @param radius - Radius of the avatar corners.
+ *
+ * @returns JSX.Element - The rendered avatar image component.
+ */
+const Avatar = ({
+ name,
+ alt = 'Dummy Avatar',
+ size,
+ avatarStyle,
+ containerStyle,
+ dataTestId,
+ radius,
+}: InterfaceAvatarProps): JSX.Element => {
+ // Memoize the avatar creation to avoid unnecessary recalculations
+ const avatar = useMemo(() => {
+ return createAvatar(initials, {
+ size: size || 128,
+ seed: name,
+ radius: radius || 0,
+ }).toDataUri();
+ }, [name, size]);
+
+ const svg = avatar?.toString();
+
+ return (
+
+
+
+ );
+};
+
+export default Avatar;
diff --git a/src/components/ChangeLanguageDropdown/ChangeLanguageDropDown.tsx b/src/components/ChangeLanguageDropdown/ChangeLanguageDropDown.tsx
new file mode 100644
index 0000000000..ac758be867
--- /dev/null
+++ b/src/components/ChangeLanguageDropdown/ChangeLanguageDropDown.tsx
@@ -0,0 +1,100 @@
+import React from 'react';
+import { Dropdown } from 'react-bootstrap';
+import i18next from 'i18next';
+import { languages } from 'utils/languages';
+import cookies from 'js-cookie';
+import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { getItem } = useLocalStorage();
+
+interface InterfaceChangeLanguageDropDownProps {
+ parentContainerStyle?: string;
+ btnStyle?: string;
+ btnTextStyle?: string;
+}
+
+/**
+ * A dropdown component that allows users to change the application's language.
+ * It updates the user's language preference in the backend and stores the selection in cookies.
+ *
+ * @param props - The properties for customizing the dropdown component.
+ * @param parentContainerStyle - Custom style for the dropdown container.
+ * @param btnStyle - Custom style for the dropdown button.
+ * @param btnTextStyle - Custom style for the button text.
+ *
+ * @returns JSX.Element - The rendered dropdown component for changing languages.
+ */
+const ChangeLanguageDropDown = (
+ props: InterfaceChangeLanguageDropDownProps,
+): JSX.Element => {
+ const currentLanguageCode = cookies.get('i18next') || 'en';
+ const userId = getItem('userId');
+ const [updateUser] = useMutation(UPDATE_USER_MUTATION);
+
+ /**
+ * Changes the application's language and updates the user's language preference.
+ *
+ * @param languageCode - The code of the language to switch to.
+ */
+ const changeLanguage = async (languageCode: string): Promise => {
+ if (userId) {
+ try {
+ await updateUser({
+ variables: {
+ appLanguageCode: languageCode,
+ },
+ });
+ await i18next.changeLanguage(languageCode);
+ cookies.set('i18next', languageCode);
+ } catch (error) {
+ console.log('Error in changing language', error);
+ }
+ }
+ };
+
+ return (
+
+
+ {languages.map((language, index: number) => (
+
+ {currentLanguageCode === language.code ? (
+
+
+ {language.name}
+
+ ) : null}
+
+ ))}
+
+
+ {languages.map((language, index: number) => (
+ => changeLanguage(language.code)}
+ disabled={currentLanguageCode === language.code}
+ data-testid={`change-language-btn-${language.code}`}
+ >
+
+ {language.name}
+
+ ))}
+
+
+ );
+};
+
+export default ChangeLanguageDropDown;
diff --git a/src/components/ChangeLanguageDropdown/ChangeLanguageDropdown.test.tsx b/src/components/ChangeLanguageDropdown/ChangeLanguageDropdown.test.tsx
new file mode 100644
index 0000000000..dc14f6ce17
--- /dev/null
+++ b/src/components/ChangeLanguageDropdown/ChangeLanguageDropdown.test.tsx
@@ -0,0 +1,158 @@
+import React, { act } from 'react';
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter } from 'react-router-dom';
+import i18nForTest from 'utils/i18nForTest';
+import { languages } from 'utils/languages';
+import ChangeLanguageDropDown from './ChangeLanguageDropDown';
+import cookies from 'js-cookie';
+import { MockedProvider } from '@apollo/react-testing';
+import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import useLocalStorage from 'utils/useLocalstorage';
+// import { Provider } from 'react-redux';
+// import { store } from 'state/store';
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const MOCKS = [
+ {
+ request: {
+ query: UPDATE_USER_MUTATION,
+ variables: {
+ appLanguageCode: 'fr',
+ },
+ },
+ result: {
+ data: {
+ updateUserProfile: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_USER_MUTATION,
+ variables: {
+ appLanguageCode: 'hi',
+ },
+ },
+ error: new Error('An error occurred'),
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+describe('Testing Change Language Dropdown', () => {
+ test('Component Should be rendered properly', async () => {
+ const { getByTestId } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(getByTestId('language-dropdown-container')).toBeInTheDocument();
+ expect(getByTestId('language-dropdown-btn')).toBeInTheDocument();
+ expect(getByTestId('dropdown-btn-0')).toBeInTheDocument();
+
+ getByTestId('language-dropdown-container').className.includes('');
+ getByTestId('language-dropdown-btn').className.includes('');
+ getByTestId('dropdown-btn-0').className.includes('');
+
+ userEvent.click(getByTestId('dropdown-btn-0'));
+ await wait();
+
+ languages.map((language) => {
+ expect(
+ getByTestId(`change-language-btn-${language.code}`),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('Component Should accept props properly', async () => {
+ const props = {
+ parentContainerStyle: 'parentContainerStyle',
+ btnStyle: 'btnStyle',
+ btnTextStyle: 'btnTextStyle',
+ };
+ const { getByTestId } = render(
+
+
+
+
+
+
+ ,
+ );
+ getByTestId('language-dropdown-container').className.includes(
+ props.parentContainerStyle,
+ );
+ getByTestId('language-dropdown-btn').className.includes(props.btnStyle);
+ getByTestId('dropdown-btn-0').className.includes(props.btnTextStyle);
+ });
+
+ test('Testing when language cookie is not set', async () => {
+ Object.defineProperty(window.document, 'cookie', {
+ writable: true,
+ value: 'i18next=',
+ });
+
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ expect(cookies.get('i18next')).toBe('');
+ });
+
+ test('Testing change language functionality', async () => {
+ Object.defineProperty(window.document, 'cookie', {
+ writable: true,
+ value: 'i18next=sp',
+ });
+ setItem('userId', '1');
+
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ userEvent.click(getByTestId('language-dropdown-btn'));
+ await wait();
+ const changeLanguageBtn = getByTestId(`change-language-btn-fr`);
+ await wait();
+ expect(changeLanguageBtn).toBeInTheDocument();
+ await wait();
+ userEvent.click(changeLanguageBtn);
+ await wait();
+ expect(cookies.get('i18next')).toBe('fr');
+ await wait();
+ userEvent.click(getByTestId('language-dropdown-btn'));
+ await wait();
+ const changeLanguageBtnHi = getByTestId(`change-language-btn-hi`);
+ await wait();
+ expect(changeLanguageBtnHi).toBeInTheDocument();
+ await wait();
+ userEvent.click(changeLanguageBtnHi);
+ await wait();
+ });
+});
diff --git a/src/components/CheckIn/CheckInModal.module.css b/src/components/CheckIn/CheckInModal.module.css
new file mode 100644
index 0000000000..0f78d81c01
--- /dev/null
+++ b/src/components/CheckIn/CheckInModal.module.css
@@ -0,0 +1,43 @@
+.loader,
+.loader:after {
+ border-radius: 50%;
+ width: 10em;
+ height: 10em;
+}
+.loader {
+ margin: 60px auto;
+ margin-top: 35vh !important;
+ font-size: 10px;
+ position: relative;
+ text-indent: -9999em;
+ border-top: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-right: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-left: 1.1em solid #febc59;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-animation: load8 1.1s infinite linear;
+ animation: load8 1.1s infinite linear;
+}
+
+@-webkit-keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/CheckIn/CheckInModal.test.tsx b/src/components/CheckIn/CheckInModal.test.tsx
new file mode 100644
index 0000000000..1660c7c4bb
--- /dev/null
+++ b/src/components/CheckIn/CheckInModal.test.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { CheckInModal } from './CheckInModal';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { checkInQueryMock } from './mocks';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const link = new StaticMockLink(checkInQueryMock, true);
+
+describe('Testing Check In Attendees Modal', () => {
+ const props = {
+ show: true,
+ eventId: 'event123',
+ handleClose: jest.fn(),
+ };
+
+ test('The modal should be rendered, and all the fetched users should be shown properly and user filtering should work', async () => {
+ const { queryByText, queryByLabelText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Event Check In Management')).toBeInTheDocument(),
+ );
+
+ await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());
+ await waitFor(() => expect(queryByText('John2 Doe2')).toBeInTheDocument());
+
+ // Tetst filtering of users
+ fireEvent.change(queryByLabelText('Search Attendees') as Element, {
+ target: { value: 'John Doe' },
+ });
+
+ await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());
+ await waitFor(() =>
+ expect(queryByText('John2 Doe2')).not.toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/CheckIn/CheckInModal.tsx b/src/components/CheckIn/CheckInModal.tsx
new file mode 100644
index 0000000000..8c75bab598
--- /dev/null
+++ b/src/components/CheckIn/CheckInModal.tsx
@@ -0,0 +1,147 @@
+import React, { useState, useEffect } from 'react';
+import { Modal } from 'react-bootstrap';
+import { useQuery } from '@apollo/client';
+import { EVENT_CHECKINS } from 'GraphQl/Queries/Queries';
+import styles from 'components/CheckIn/CheckInModal.module.css';
+import { TableRow } from './TableRow';
+import type {
+ InterfaceAttendeeCheckIn,
+ InterfaceModalProp,
+ InterfaceTableData,
+} from './types';
+import type { GridColDef, GridRowHeightReturnValue } from '@mui/x-data-grid';
+import { DataGrid } from '@mui/x-data-grid';
+import TextField from '@mui/material/TextField';
+
+/**
+ * Modal component for managing event check-ins. Displays a list of attendees
+ * and their check-in statuses, allowing for filtering by user name.
+ *
+ * @param show - Boolean indicating whether the modal is visible.
+ * @param eventId - ID of the event whose check-ins are to be displayed.
+ * @param handleClose - Function to call when the modal is closed.
+ *
+ * @returns JSX.Element - The rendered modal component.
+ */
+export const CheckInModal = ({
+ show,
+ eventId,
+ handleClose,
+}: InterfaceModalProp): JSX.Element => {
+ // State to hold the data for the table
+ const [tableData, setTableData] = useState([]);
+
+ // State for search filter input
+ const [userFilterQuery, setUserFilterQuery] = useState('');
+
+ // State for filter model used in DataGrid
+ const [filterQueryModel, setFilterQueryModel] = useState({
+ items: [{ field: 'userName', operator: 'contains', value: '' }],
+ });
+
+ // Query to get check-in data from the server
+ const {
+ data: checkInData,
+ loading: checkInLoading,
+ refetch: checkInRefetch,
+ } = useQuery(EVENT_CHECKINS, {
+ variables: { id: eventId },
+ });
+
+ // Effect runs whenever checkInData, eventId, or checkInLoading changes
+ useEffect(() => {
+ checkInRefetch(); // Refetch data when component mounts or updates
+ if (checkInLoading) {
+ setTableData([]); // Clear table data while loading
+ } else {
+ // Map the check-in data to table rows
+ setTableData(
+ checkInData.event.attendeesCheckInStatus.map(
+ (checkIn: InterfaceAttendeeCheckIn) => ({
+ userName: `${checkIn.user.firstName} ${checkIn.user.lastName}`,
+ id: checkIn._id,
+ checkInData: {
+ id: checkIn._id,
+ name: `${checkIn.user.firstName} ${checkIn.user.lastName}`,
+ userId: checkIn.user._id,
+ checkIn: checkIn.checkIn,
+ eventId,
+ },
+ }),
+ ),
+ );
+ }
+ }, [checkInData, eventId, checkInLoading]);
+
+ // Define columns for the DataGrid
+ const columns: GridColDef[] = [
+ { field: 'userName', headerName: 'User', width: 300 }, // Column for user names
+ {
+ field: 'checkInData',
+ headerName: 'Check In Status',
+ width: 400,
+ renderCell: (props) => (
+ // Render a custom row component for check-in status
+
+ ),
+ },
+ ];
+
+ // Show a loading indicator while data is loading
+ if (checkInLoading) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+
+
+ Event Check In Management
+
+
+
+
+ {
+ setUserFilterQuery(e.target.value);
+ setFilterQueryModel({
+ items: [
+ {
+ field: 'userName',
+ operator: 'contains',
+ value: e.target.value,
+ },
+ ],
+ });
+ }}
+ fullWidth
+ />
+
+
+ 'auto'}
+ columns={columns}
+ filterModel={filterQueryModel}
+ />
+
+
+
+ >
+ );
+};
diff --git a/src/components/CheckIn/CheckInWrapper.module.css b/src/components/CheckIn/CheckInWrapper.module.css
new file mode 100644
index 0000000000..f5f42546c3
--- /dev/null
+++ b/src/components/CheckIn/CheckInWrapper.module.css
@@ -0,0 +1,13 @@
+button .iconWrapper {
+ width: 32px;
+ padding-right: 4px;
+ margin-right: 4px;
+ transform: translateY(4px);
+}
+
+button .iconWrapperSm {
+ width: 32px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/CheckIn/CheckInWrapper.test.tsx b/src/components/CheckIn/CheckInWrapper.test.tsx
new file mode 100644
index 0000000000..81f53d0043
--- /dev/null
+++ b/src/components/CheckIn/CheckInWrapper.test.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { CheckInWrapper } from './CheckInWrapper';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { checkInQueryMock } from './mocks';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const link = new StaticMockLink(checkInQueryMock, true);
+
+describe('Testing CheckIn Wrapper', () => {
+ const props = {
+ eventId: 'event123',
+ };
+
+ test('The button to open and close the modal should work properly', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Open the modal
+ fireEvent.click(screen.getByLabelText('checkInRegistrants') as Element);
+
+ await waitFor(() =>
+ expect(screen.queryByTestId('modal-title')).toBeInTheDocument(),
+ );
+
+ // Close the modal
+ const closebtn = screen.getByLabelText('Close');
+
+ fireEvent.click(closebtn as Element);
+
+ await waitFor(() =>
+ expect(screen.queryByTestId('modal-title')).not.toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/CheckIn/CheckInWrapper.tsx b/src/components/CheckIn/CheckInWrapper.tsx
new file mode 100644
index 0000000000..859ae1f869
--- /dev/null
+++ b/src/components/CheckIn/CheckInWrapper.tsx
@@ -0,0 +1,48 @@
+import React, { useState } from 'react';
+import { CheckInModal } from './CheckInModal';
+import { Button } from 'react-bootstrap';
+import IconComponent from 'components/IconComponent/IconComponent';
+import styles from './CheckInWrapper.module.css';
+
+type PropType = {
+ eventId: string;
+};
+
+/**
+ * Wrapper component that displays a button to open the CheckInModal.
+ *
+ * @param eventId - The ID of the event for which check-in management is being handled.
+ *
+ * @returns JSX.Element - The rendered CheckInWrapper component.
+ */
+export const CheckInWrapper = ({ eventId }: PropType): JSX.Element => {
+ const [showModal, setShowModal] = useState(false);
+
+ return (
+ <>
+ {
+ setShowModal(true);
+ }}
+ >
+
+
+
+ Check In Registrants
+
+ {showModal && (
+ setShowModal(false)}
+ eventId={eventId}
+ />
+ )}
+ >
+ );
+};
diff --git a/src/components/CheckIn/TableRow.test.tsx b/src/components/CheckIn/TableRow.test.tsx
new file mode 100644
index 0000000000..1a08f40a3d
--- /dev/null
+++ b/src/components/CheckIn/TableRow.test.tsx
@@ -0,0 +1,174 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+import { TableRow } from './TableRow';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { MockedProvider } from '@apollo/react-testing';
+import { checkInMutationSuccess, checkInMutationUnsuccess } from './mocks';
+
+describe('Testing Table Row for CheckIn Table', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('If the user is not checked in, button to check in should be displayed, and the user should be able to check in successfully', async () => {
+ const props = {
+ data: {
+ id: `123`,
+ name: `John Doe`,
+ userId: `user123`,
+ checkIn: null,
+ eventId: `event123`,
+ },
+ refetch: jest.fn(),
+ };
+
+ const { findByText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(await findByText('Check In')).toBeInTheDocument();
+
+ fireEvent.click(await findByText('Check In'));
+
+ expect(await findByText('Checked in successfully')).toBeInTheDocument();
+ });
+
+ test('If the user is checked in, the option to download tag should be shown', async () => {
+ const props = {
+ data: {
+ id: '123',
+ name: 'John Doe',
+ userId: 'user123',
+ checkIn: {
+ _id: '123',
+ time: '12:00:00',
+ },
+ eventId: 'event123',
+ },
+ refetch: jest.fn(),
+ };
+
+ const { findByText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ global.URL.createObjectURL = jest.fn(() => 'mockURL');
+ global.window.open = jest.fn();
+
+ expect(await findByText('Checked In')).toBeInTheDocument();
+ expect(await findByText('Download Tag')).toBeInTheDocument();
+
+ fireEvent.click(await findByText('Download Tag'));
+
+ expect(await findByText('Generating pdf...')).toBeInTheDocument();
+ expect(await findByText('PDF generated successfully!')).toBeInTheDocument();
+
+ // Cleanup mocks
+ jest.clearAllMocks();
+ });
+
+ test('Upon failing of check in mutation, the appropriate error message should be shown', async () => {
+ const props = {
+ data: {
+ id: `123`,
+ name: `John Doe`,
+ userId: `user123`,
+ checkIn: null,
+ eventId: `event123`,
+ },
+ refetch: jest.fn(),
+ };
+
+ const { findByText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(await findByText('Check In')).toBeInTheDocument();
+
+ fireEvent.click(await findByText('Check In'));
+
+ expect(await findByText('Error checking in')).toBeInTheDocument();
+ expect(await findByText('Oops')).toBeInTheDocument();
+ });
+
+ test('If PDF generation fails, the error message should be shown', async () => {
+ const props = {
+ data: {
+ id: `123`,
+ name: '',
+ userId: `user123`,
+ checkIn: {
+ _id: '123',
+ time: '12:00:00',
+ },
+ eventId: `event123`,
+ },
+ refetch: jest.fn(),
+ };
+
+ const { findByText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Mocking the PDF generation function to throw an error
+ global.URL.createObjectURL = jest.fn(() => 'mockURL');
+ global.window.open = jest.fn();
+
+ fireEvent.click(await findByText('Download Tag'));
+
+ expect(
+ await findByText('Error generating pdf: Invalid or empty name provided'),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/CheckIn/TableRow.tsx b/src/components/CheckIn/TableRow.tsx
new file mode 100644
index 0000000000..b2bd6e11cb
--- /dev/null
+++ b/src/components/CheckIn/TableRow.tsx
@@ -0,0 +1,113 @@
+import React from 'react';
+import type { InterfaceTableCheckIn } from './types';
+import Button from '@mui/material/Button';
+import { useMutation } from '@apollo/client';
+import { MARK_CHECKIN } from 'GraphQl/Mutations/mutations';
+import { toast } from 'react-toastify';
+import { generate } from '@pdfme/generator';
+import { tagTemplate } from './tagTemplate';
+import { useTranslation } from 'react-i18next';
+/**
+ * Component that represents a single row in the check-in table.
+ * Allows users to mark themselves as checked in and download a tag if they are already checked in.
+ *
+ * @param data - The data for the current row, including user and event information.
+ * @param refetch - Function to refetch the check-in data after marking a check-in.
+ *
+ * @returns JSX.Element - The rendered TableRow component.
+ */
+export const TableRow = ({
+ data,
+ refetch,
+}: {
+ data: InterfaceTableCheckIn;
+ refetch: () => void;
+}): JSX.Element => {
+ const [checkInMutation] = useMutation(MARK_CHECKIN);
+ const { t } = useTranslation('translation', { keyPrefix: 'checkIn' });
+
+ /**
+ * Marks the user as checked in for the event.
+ * Displays success or error messages based on the result of the mutation.
+ */
+ const markCheckIn = (): void => {
+ // as we do not want to clutter the UI currently with the same (only provide the most basic of operations)
+ checkInMutation({
+ variables: {
+ userId: data.userId,
+ eventId: data.eventId,
+ },
+ })
+ .then(() => {
+ toast.success(t('checkedInSuccessfully') as string);
+ refetch();
+ })
+ .catch((err) => {
+ toast.error(t('errorCheckingIn') as string);
+ toast.error(err.message);
+ });
+ };
+ /**
+ * Triggers a notification while generating and downloading a PDF tag.
+ *
+ * @returns A promise that resolves when the PDF is generated and opened.
+ */
+ const notify = (): Promise =>
+ toast.promise(generateTag, {
+ pending: 'Generating pdf...',
+ success: 'PDF generated successfully!',
+ error: 'Error generating pdf!',
+ });
+
+ /**
+ * Generates a PDF tag based on the provided data and opens it in a new tab.
+ *
+ * @returns A promise that resolves when the PDF is successfully generated and opened.
+ */
+ const generateTag = async (): Promise => {
+ try {
+ const inputs = [];
+ if (typeof data.name !== 'string' || !data.name.trim()) {
+ throw new Error('Invalid or empty name provided');
+ }
+ inputs.push({ name: data.name.trim() });
+ const pdf = await generate({ template: tagTemplate, inputs });
+ // istanbul ignore next
+ const blob = new Blob([pdf.buffer], { type: 'application/pdf' });
+ // istanbul ignore next
+ const url = URL.createObjectURL(blob);
+ // istanbul ignore next
+ window.open(url);
+ // istanbul ignore next
+ toast.success('PDF generated successfully!');
+ } catch (error: unknown) {
+ const errorMessage =
+ error instanceof Error ? error.message : 'Unknown error';
+ toast.error(`Error generating pdf: ${errorMessage}`);
+ }
+ };
+
+ return (
+ <>
+ {data.checkIn !== null ? (
+
+
+ Checked In
+
+
+ Download Tag
+
+
+ ) : (
+
+ Check In
+
+ )}
+ >
+ );
+};
diff --git a/src/components/CheckIn/mocks.ts b/src/components/CheckIn/mocks.ts
new file mode 100644
index 0000000000..a4e78aa2fc
--- /dev/null
+++ b/src/components/CheckIn/mocks.ts
@@ -0,0 +1,76 @@
+import { EVENT_CHECKINS } from 'GraphQl/Queries/Queries';
+import { MARK_CHECKIN } from 'GraphQl/Mutations/mutations';
+import type { InterfaceAttendeeQueryResponse } from './types';
+
+const checkInQueryData: InterfaceAttendeeQueryResponse = {
+ event: {
+ _id: 'event123',
+ attendeesCheckInStatus: [
+ {
+ _id: 'eventAttendee1',
+ user: {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ checkIn: null,
+ },
+ {
+ _id: 'eventAttendee2',
+ user: {
+ _id: 'user2',
+ firstName: 'John2',
+ lastName: 'Doe2',
+ },
+ checkIn: {
+ _id: 'checkin1',
+ time: '08:00:00',
+ },
+ },
+ ],
+ },
+};
+
+export const checkInQueryMock = [
+ {
+ request: {
+ query: EVENT_CHECKINS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: checkInQueryData,
+ },
+ },
+];
+
+export const checkInMutationSuccess = [
+ {
+ request: {
+ query: MARK_CHECKIN,
+ variables: {
+ userId: 'user123',
+ eventId: 'event123',
+ },
+ },
+ result: {
+ data: {
+ checkIn: {
+ _id: '123',
+ },
+ },
+ },
+ },
+];
+
+export const checkInMutationUnsuccess = [
+ {
+ request: {
+ query: MARK_CHECKIN,
+ variables: {
+ userId: 'user123',
+ eventId: 'event123',
+ },
+ },
+ error: new Error('Oops'),
+ },
+];
diff --git a/src/components/CheckIn/tagTemplate.ts b/src/components/CheckIn/tagTemplate.ts
new file mode 100644
index 0000000000..acd35fca0d
--- /dev/null
+++ b/src/components/CheckIn/tagTemplate.ts
@@ -0,0 +1,22 @@
+import { Template } from '@pdfme/common';
+
+export const tagTemplate: Template = {
+ schemas: [
+ {
+ name: {
+ type: 'text',
+ position: { x: 14.91, y: 27.03 },
+ width: 58.55,
+ height: 5.67,
+ alignment: 'center',
+ fontSize: 16,
+ characterSpacing: 0,
+ lineHeight: 1,
+ fontName: 'Roboto',
+ fontColor: '#08780b',
+ },
+ },
+ ],
+ basePdf:
+ 'data:application/pdf;base64,JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovVmVyc2lvbiAvMS40Ci9QYWdlcyAyIDAgUgovU3RydWN0VHJlZVJvb3QgMyAwIFIKL01hcmtJbmZvIDQgMCBSCi9MYW5nIChlbikKL1ZpZXdlclByZWZlcmVuY2VzIDUgMCBSCj4+CmVuZG9iago2IDAgb2JqCjw8Ci9DcmVhdG9yIChDYW52YSkKL1Byb2R1Y2VyIChDYW52YSkKL0NyZWF0aW9uRGF0ZSAoRDoyMDIzMDYyMDA3MjgxMyswMCcwMCcpCi9Nb2REYXRlIChEOjIwMjMwNjIwMDcyODEzKzAwJzAwJykKL0tleXdvcmRzIChEQUZjMjhYSXViTSxCQUUycS01WEdhaykKL0F1dGhvciAoRXNoYWFuIEFnZ2Fyd2FsKQovVGl0bGUgKEJsYW5rIE5hbWUgVGFnIGluIEVtZXJhbGQgTWludCBHcmVlbiBBc3BpcmF0aW9uYWwgRWxlZ2FuY2UgU3R5bGUpCj4+CmVuZG9iagoyIDAgb2JqCjw8Ci9UeXBlIC9QYWdlcwovS2lkcyBbNyAwIFJdCi9Db3VudCAxCj4+CmVuZG9iagozIDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RUcmVlUm9vdAovUGFyZW50VHJlZSA4IDAgUgovUGFyZW50VHJlZU5leHRLZXkgMQovSyBbOSAwIFJdCi9JRFRyZWUgMTAgMCBSCj4+CmVuZG9iago0IDAgb2JqCjw8Ci9NYXJrZWQgdHJ1ZQovU3VzcGVjdHMgZmFsc2UKPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0Rpc3BsYXlEb2NUaXRsZSB0cnVlCj4+CmVuZG9iago3IDAgb2JqCjw8Ci9UeXBlIC9QYWdlCi9SZXNvdXJjZXMgMTEgMCBSCi9NZWRpYUJveCBbMC4wIDcuOTIwMDAyNSAyNTIuMCAxNTEuOTJdCi9Db250ZW50cyAxMiAwIFIKL1N0cnVjdFBhcmVudHMgMAovUGFyZW50IDIgMCBSCi9UYWJzIC9TCi9CbGVlZEJveCBbMC4wIDcuOTIwMDAyNSAyNTIuMCAxNTEuOTJdCi9UcmltQm94IFswLjAgNy45MjAwMDI1IDI1Mi4wIDE1MS45Ml0KL0Nyb3BCb3ggWzAuMCA3LjkyMDAwMjUgMjUyLjAgMTUxLjkyXQovUm90YXRlIDAKL0Fubm90cyBbXQo+PgplbmRvYmoKOCAwIG9iago8PAovTGltaXRzIFswIDBdCi9OdW1zIFswIFsxMyAwIFIgMTQgMCBSIDE1IDAgUiAxNiAwIFIgMTcgMCBSIDE4IDAgUiAxOSAwIFJdCl0KPj4KZW5kb2JqCjkgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RvY3VtZW50Ci9MYW5nIChlbikKL1AgMyAwIFIKL0sgWzIwIDAgUl0KL0lEIChub2RlMDAwMDE3MzgpCj4+CmVuZG9iagoxMCAwIG9iago8PAovTmFtZXMgWyhub2RlMDAwMDE3MzgpIDkgMCBSIChub2RlMDAwMDE3MzkpIDEzIDAgUiAobm9kZTAwMDAxNzQwKSAyMCAwIFIgKG5vZGUwMDAwMTc0MSkgMjEgMCBSIChub2RlMDAwMDE3NDIpIDIyIDAgUgoobm9kZTAwMDAxNzQzKSAyMyAwIFIgKG5vZGUwMDAwMTc0NCkgMjQgMCBSIChub2RlMDAwMDE3NDUpIDI1IDAgUiAobm9kZTAwMDAxNzQ2KSAyNiAwIFIgKG5vZGUwMDAwMTc0NykgMjcgMCBSCihub2RlMDAwMDE3NjEpIDI4IDAgUiAobm9kZTAwMDAxNzYyKSAyOSAwIFIgKG5vZGUwMDAwMTc2MykgMzAgMCBSIChub2RlMDAwMDE3NjQpIDMxIDAgUiAobm9kZTAwMDAxNzY1KSAzMiAwIFIKKG5vZGUwMDAwMTc2NikgMzMgMCBSIChub2RlMDAwMDE3NjcpIDE0IDAgUiAobm9kZTAwMDAxNzY4KSAzNCAwIFIgKG5vZGUwMDAwMTc2OSkgMzUgMCBSIChub2RlMDAwMDE3NzApIDM2IDAgUgoobm9kZTAwMDAxNzcxKSAxNSAwIFIgKG5vZGUwMDAwMTc3MikgMzcgMCBSIChub2RlMDAwMDE3NzMpIDM4IDAgUiAobm9kZTAwMDAxNzc0KSAzOSAwIFIgKG5vZGUwMDAwMTc3NSkgMTYgMCBSCihub2RlMDAwMDE3NzYpIDE3IDAgUiAobm9kZTAwMDAxNzc3KSA0MCAwIFIgKG5vZGUwMDAwMTc3OCkgMTggMCBSIChub2RlMDAwMDE3NzkpIDQxIDAgUiAobm9kZTAwMDAxNzgwKSA0MiAwIFIKKG5vZGUwMDAwMTc4MSkgNDMgMCBSIChub2RlMDAwMDE3ODIpIDQ0IDAgUiAobm9kZTAwMDAxNzgzKSAxOSAwIFJdCj4+CmVuZG9iagoxMSAwIG9iago8PAovUHJvY1NldCBbL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSV0KL0V4dEdTdGF0ZSA0NSAwIFIKL1hPYmplY3QgPDwKL1g1IDQ2IDAgUgo+PgovRm9udCA0NyAwIFIKPj4KZW5kb2JqCjEyIDAgb2JqCjw8Ci9MZW5ndGggOTc4Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlCj4+CnN0cmVhbQ0KeJytVk2PGzcMvc+v0LlAtCQl6gNYLODYmyCHAE1roL27SYDC2yDJ/wfyJM2MNFnvJpuuDdsyKZGPj0/SWHG5vgzh/cIOf1nZZsY4mdPd9HkqE5iUTHBsRc2X99Nfv5n/4HGW8b/4+whL2JT3H69NG3z5OF29dubj1xrJuWCEpET5ML3De0xA9AzROf+P6JY4B2N9/ZYMoMh03/iErJ005L0H5gEbO7HBc0AqCWw5M5uYyboCVbK2wTI5kI0hbCbfTSk7m1KKrhvPU6ZsvXDI3ZhFUJ8r86KzMTlKoy1l5ApsTlM3qkRMVEklYrd6h6nJmZ5EVSElwsSOpttOI/JuPk/sFYJ0muNgHihZM422FdNpw96C/7yxrpUOqVZGLvF5qkx/nsSmRS3z8FKripBS0YnNkj2+CZupCOmecSskSVyk8JPyjQTcg4RZyYpn55zxDmx4CqFpmlyGcMWpsaySm6a/N1YovkGxCgXhTLDJowPl73n683mkHXywpI68AfUM3OyxgbwNKaKEPIYM0VuOaTP1bsoo04vqaIUOA4SiScNgzTFZVcJMBR3klcJggwbEReWimG4VipbR2FBiditUHtnD2vOAOesoFNuKqNtOA/puPU9BE+SiRS2rtVPS8wy2FdFpIK+jPw/WXmfP0/m4xOfDwr7UqF8VNgNnSKOug2JjlkXYbFReUAGF0nwGw2F7zzRHvamkjqksXCIItn7KSSq8OenV7+b6+urt/s0Bi25uXh7209Xfag6fptu3+6fdDJtzA4LwPuhTt1XF9PI4wuIF1quInWtzyvXKPX7ADVcrLUcB2UDZlY0h5ng3XYOm/Y05/jsxFJdy9KFMPv5jrnEGH6oHLU7OMyQwO8SHxaGCDSarI3J1iM05Ek6TxUHK84oEghmofrjitiWP6AhWxNXB2hyF9dtjpf0ev9BZUhSD01jYeoer1P2S0rYEy0gwLgxpDzgjwUWYM6+H2HgVHKEx8VoylFsdCoyRUpBema8OZ5VY3Eo3025eoLh4ci21OUJzBBu4PF7p9xyhpxEezT9OvvT0EVodo33kcQV4bDEBxPgMqnUjqbIh03tLsV2JNJMKMnjn24cTCn2FX27jubS5gm0W/3CWFMuzRb0r5iwQ5SrMi9H04WiitkGWdX9p6WwRdIVaglf4YdfGeljgV3u17XfVz7v9pcawxbNJTOKkZh3+as4Ww1ILqsJRS/roE+62rPBIK5Kdj42lE3zbIBb45QOotRvu0Mrb9/Jq6fDVXzd3aym7rFnKTocWcz93N9NMzUwb5RJ3S8e76RuIroTkDQplbmRzdHJlYW0KZW5kb2JqCjEzIDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9GaWd1cmUKL1AgMzAgMCBSCi9LIFs0OCAwIFJdCi9JRCAobm9kZTAwMDAxNzM5KQo+PgplbmRvYmoKMTQgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL05vblN0cnVjdAovUCAzMyAwIFIKL0sgWzQ5IDAgUl0KL0lEIChub2RlMDAwMDE3NjcpCj4+CmVuZG9iagoxNSAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvTm9uU3RydWN0Ci9QIDM2IDAgUgovSyBbNTAgMCBSXQovSUQgKG5vZGUwMDAwMTc3MSkKPj4KZW5kb2JqCjE2IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9Ob25TdHJ1Y3QKL1AgMzkgMCBSCi9LIFs1MSAwIFJdCi9JRCAobm9kZTAwMDAxNzc1KQo+PgplbmRvYmoKMTcgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL05vblN0cnVjdAovUCAzOSAwIFIKL0sgWzUyIDAgUl0KL0lEIChub2RlMDAwMDE3NzYpCj4+CmVuZG9iagoxOCAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvTm9uU3RydWN0Ci9QIDQwIDAgUgovSyBbNTMgMCBSXQovSUQgKG5vZGUwMDAwMTc3OCkKPj4KZW5kb2JqCjE5IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9Ob25TdHJ1Y3QKL1AgNDQgMCBSCi9LIFs1NCAwIFJdCi9JRCAobm9kZTAwMDAxNzgzKQo+PgplbmRvYmoKMjAgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCA5IDAgUgovSyBbMjEgMCBSXQovSUQgKG5vZGUwMDAwMTc0MCkKPj4KZW5kb2JqCjIxIDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMjAgMCBSCi9LIFsyMiAwIFJdCi9JRCAobm9kZTAwMDAxNzQxKQo+PgplbmRvYmoKMjIgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAyMSAwIFIKL0sgWzIzIDAgUl0KL0lEIChub2RlMDAwMDE3NDIpCj4+CmVuZG9iagoyMyAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDIyIDAgUgovSyBbMjQgMCBSXQovSUQgKG5vZGUwMDAwMTc0MykKPj4KZW5kb2JqCjI0IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMjMgMCBSCi9LIFsyNSAwIFJdCi9JRCAobm9kZTAwMDAxNzQ0KQo+PgplbmRvYmoKMjUgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAyNCAwIFIKL0sgWzI2IDAgUl0KL0lEIChub2RlMDAwMDE3NDUpCj4+CmVuZG9iagoyNiAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDI1IDAgUgovSyBbMjcgMCBSXQovSUQgKG5vZGUwMDAwMTc0NikKPj4KZW5kb2JqCjI3IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMjYgMCBSCi9LIFsyOCAwIFIgMzEgMCBSIDM0IDAgUiAzNyAwIFIgNDEgMCBSXQovSUQgKG5vZGUwMDAwMTc0NykKPj4KZW5kb2JqCjI4IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMjcgMCBSCi9LIFsyOSAwIFJdCi9JRCAobm9kZTAwMDAxNzYxKQo+PgplbmRvYmoKMjkgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAyOCAwIFIKL0sgWzMwIDAgUl0KL0lEIChub2RlMDAwMDE3NjIpCj4+CmVuZG9iagozMCAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDI5IDAgUgovSyBbMTMgMCBSXQovSUQgKG5vZGUwMDAwMTc2MykKPj4KZW5kb2JqCjMxIDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMjcgMCBSCi9LIFszMiAwIFJdCi9JRCAobm9kZTAwMDAxNzY0KQo+PgplbmRvYmoKMzIgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAzMSAwIFIKL0sgWzMzIDAgUl0KL0lEIChub2RlMDAwMDE3NjUpCj4+CmVuZG9iagozMyAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvUAovUCAzMiAwIFIKL0sgWzE0IDAgUl0KL0lEIChub2RlMDAwMDE3NjYpCj4+CmVuZG9iagozNCAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDI3IDAgUgovSyBbMzUgMCBSXQovSUQgKG5vZGUwMDAwMTc2OCkKPj4KZW5kb2JqCjM1IDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgMzQgMCBSCi9LIFszNiAwIFJdCi9JRCAobm9kZTAwMDAxNzY5KQo+PgplbmRvYmoKMzYgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL1AKL1AgMzUgMCBSCi9LIFsxNSAwIFJdCi9JRCAobm9kZTAwMDAxNzcwKQo+PgplbmRvYmoKMzcgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAyNyAwIFIKL0sgWzM4IDAgUl0KL0lEIChub2RlMDAwMDE3NzIpCj4+CmVuZG9iagozOCAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDM3IDAgUgovSyBbMzkgMCBSIDQwIDAgUl0KL0lEIChub2RlMDAwMDE3NzMpCj4+CmVuZG9iagozOSAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvUAovUCAzOCAwIFIKL0sgWzE2IDAgUiAxNyAwIFJdCi9JRCAobm9kZTAwMDAxNzc0KQo+PgplbmRvYmoKNDAgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL1AKL1AgMzggMCBSCi9LIFsxOCAwIFJdCi9JRCAobm9kZTAwMDAxNzc3KQo+PgplbmRvYmoKNDEgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL0RpdgovUCAyNyAwIFIKL0sgWzQyIDAgUl0KL0lEIChub2RlMDAwMDE3NzkpCj4+CmVuZG9iago0MiAwIG9iago8PAovVHlwZSAvU3RydWN0RWxlbQovUyAvRGl2Ci9QIDQxIDAgUgovSyBbNDMgMCBSXQovSUQgKG5vZGUwMDAwMTc4MCkKPj4KZW5kb2JqCjQzIDAgb2JqCjw8Ci9UeXBlIC9TdHJ1Y3RFbGVtCi9TIC9EaXYKL1AgNDIgMCBSCi9LIFs0NCAwIFJdCi9JRCAobm9kZTAwMDAxNzgxKQo+PgplbmRvYmoKNDQgMCBvYmoKPDwKL1R5cGUgL1N0cnVjdEVsZW0KL1MgL1AKL1AgNDMgMCBSCi9LIFsxOSAwIFJdCi9JRCAobm9kZTAwMDAxNzgyKQo+PgplbmRvYmoKNDUgMCBvYmoKPDwKL0czIDU1IDAgUgovRzQgNTYgMCBSCj4+CmVuZG9iago0NiAwIG9iago8PAovTGVuZ3RoIDMwMTg4Ci9UeXBlIC9YT2JqZWN0Ci9TdWJ0eXBlIC9JbWFnZQovV2lkdGggMzQ3Ci9IZWlnaHQgMjMzCi9Db2xvclNwYWNlIC9EZXZpY2VSR0IKL1NNYXNrIDU3IDAgUgovQml0c1BlckNvbXBvbmVudCA4Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlCj4+CnN0cmVhbQ0KeJzsfQl4FNeVbgmpN6GuajDYY1tIIpPJm8dbJhkyb5LJTIaZJCaYRRvCwTEOjmNi42DAZjGLpNbGamOMgVgJGGMMxo3QvqKltQsBXuKQxFlmPEPGkziLd0BSd6vfuefcul29SOqWhBa7zldff6VWdS237vnv2Y8k6aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTp9Y8sI2T/KulrzFkrdL8l6SvM2St0ryOvCzGb+B79+UvG9JXu94365OOul0Ywi42/uO5H0RMcEueZ3I+7Q51E1848Tjf4ew8I6ODDrp9Ikixt20XWbM7noqpq/U5GoyulqNrgajq9roLjW6qozueqO7xeRymnpeiWEHX8Lj6VeO8X4GnXTSaZSI0MDzG6P7kqmvxfJeia2n2NpXJ/fUTu2ps/bWxPXWwKe1p8rqqlP6auT3q5XfOqTeCwb3a0bPRQMTHi7rcoJOOn1CqP/fTe7XpvbWTXO1Wl1Oub9N6b+g9F9S+i+qn2Lngq3/kq2/W3E1w2b1tFh7GhV3t4VByhUdFnTSaRITCQZuQIMm2dMoA5szfm9VPM2Kp0XxtA68tSj9zUp/u+xlEKG4Wmye8xYmJ8AJ3x7vp9JJJ52GRbSgey6Y+hoVb6sMUOB2Iho4ZU9zOJviaVI8nSA82NxVcn9nDLMqvDneT6WTTjoNi7ydzBjoaTN5Wm2w0LtBSAA2B5WhWRly8ziVfic7mO20yG62Y2Tehzd0rUEnnSYlkTOxv8rEVICLCkGBh/E7Y3MPlxbwM2BzqgcLQGiyepqM3B2pA4JOOk1C4oBQbfK2MlOAp0nmS3+YEoIfIMTpgKCTTpOaSGXwtpu87QgIPgkhHEDg6OFpZsYHTyPs6CqDTjpNYmKOQq/U/4aJWRQv+Hg8HEDoV82PbL9N9oDK0GqiwGaddNJpMhKJ9/1vmdwtSv/5yADBI+SEFuZ/dDWC7mCi3AeddNJpMhK5HfsvmNxNtv7OSFUG1fbYIgMgMJWhycQUEB0QdNJpchLjX7vUX2nyAhR0RGhUVCUETytICIqn0drfpEsIOuk0ickHCCj2e5rURT98QGhGQAAwAZWh3Ui5kDrppNNkJKEyuJqm9bfKPAIBDYaRAUKn0tcgM7ejLiHopNOkJWFUZF4Gpw8NIpUQvOcVb1Ocp9ZIdVR00kmnyUi8msEFs9dpY+EELb74wwgAoU3p71Lc9db+ciNFOumkk06TkRgg2BEQmqZzQAjbrugHCN2K+5zcr0sIOuk0mcknIbTI/hJCJF6GNsXbrXgb5P5G3Yagk06TmHjZtLdUlaE1cgkBAYFlStbH9bfqXgaddJrExCun/ZvJ23gTS3hsJc9jJIDQJHswD8JdbfXogKCTTpOZOCD8yvRRXRyTENowUwlDESKUEGyuBpnnMuiAoJNOk5Oo6JmnxOQttjIoaCcbghyB25EkhIuKq07xtOmAoNMkI2ZUPyR5a/VyoIy82diF4Uist3o6Uxk6IjcqIiB4LyneBmu/LiHoNKnIbpecdsQEKgd65dPeUMBL2w7F65jpBwgRSggMEKrlfqee/qzTZCIABNiuPGfoqzG5f2pkmEAdyuo/3bBQfou37hZWej2ShEc/G8JFpa9S8TSYWIGUtvF+Hp10Goq8KBj85ED0mw7LB0WyqyXuWvX0vs5Y9wUjb1VWi+3JPpWw4C23eStuo4DDYbodL9l6q6a56xAQnOP9PDrpNBQxu4Fdcr9kuFZtcXUrni7F3a30OpXrTpuryeJujiHdgZUU+/TZFrxnzaz2civWSGkapoTQW2F11eqAoNMNI+BK5zzpcoZUu17qvF9yZLBP2Idv4PsIedZ7AuuNlxi9Ttn7uq2/nenLzMvWzVjgWp2lp8ncWxfDbQvvsm4jnx5Y8J7CUqutsg8QIpUQLio9JYqrxqgDgk6jTMCGl+dIV+LZ5kW1H5Z2QAPavGgFoGMOrWYb/BnOWcvYRO2vNbGcvg7F3SwzaxgLxUFLWhdIC/Ifz8VeazP3UrcR76eoejCDwWNSf7uVAULjMCMVr5Va+ioMDBBqx/t5dPokERm+Ge9LMU1fNJ9bbC2+V675lnImTa5dbi1ZGVeTHOf4mqlhLjsG0GCeUxpKXGCsfQ4XQYfR7aTwG3UyEyw0M2nZ3YGwcDb2Wqe5902DryHyJx0WGCBUS5FWYg8AhKtnY3tLsOtr2Xg/j06fDEIoiP3tLUrrV5Tuv5Pr/kU6Nm/G0W/En1yUWJw66/TChNKM20+n3vbcHbfsveOW08nTX7x7ak6miiCDnpiaBaCs0e+09rdQ1j9Xlj3UfqgFZ3i34mpVepvl3xfHfdwd2/OWUQsLn1Rk8J5GTGiz+QAhIhsCGh8+LLa6ivX0Z51GiViIALMM3NL6pcSz82eVpyaWpc4qTU0oSU4sT02qTEusTEuqTE8sT0soSUkoTYH/JlakxjuSb87fZnl0L1coBiAqFMY2Z5IvEs8ZBAsUstuCybytcm+78k7D1Gvdlt6fmPp+GfMJhgXvYclbKHk7sHlT+BKCaNTSong7FW/9NE8lGmGOjPfz6PQJILQGxFUsSHrpm0nV6bMbliXVpCdWpAEaJJSlJpSmJpbSZ0oCg4I0+C87pm7prOLFclHaEIBAaOBlQXRuvgLK/aJDmTMAFpAp2lCJaFGutVqvVVh6z1tc/6YqEZ+scCb2RKfY+Hg6ZB8gNIdRIMXJG0HCiHnbZW+FKobpNgSdRk6184GjlfI7QQAAfmcgALIBsD+AQBmAgNgQE1BIgAOSapfCAfLJ5QwNjtw/0Lm1gOCb8MT7TtFwxCczwKe7Gc1rHYqnQ3G3KT0t1utV5r43zK7/MnzCwpk4ygEgtCkRAoKipkGxvOnLDvVUl8f7kXT6BFDnl0BfUM59fVYJcj1jfPwsSyHxQN3ovyn0L9AjZpWlysfuYxrHvvUDndsPEJwaCSFAMHaqqrFaQIwpEQALXQowi7tdBmmhp1rpbTe7umIonOkdp/RWt+SczI42zsUOf5VhKEDwa+yIlVU+wSqVTuNAAAggIZz7WlLxksSKVGYrQEMBSQgcHEBrYPsp6r9SEqvTAR+UiADBX0IIYSjDZoW+JmUUrg+/6lQ8rYrrPAtn+qDC1FNvcmE4E6DBK07pslNyTE5pQUgI3nZbBIDQrKgF29mRbvh81eh16ICg0yjRkSUMEF5ckliyGAEhlVsO6TNgI0AA5aImPZEBwjKmMuy7Z6Bz+wDBKZHZcMicPnUF1MACOuhZgnCr7OqUe5vkD2pN7zab3m1nsABoUPuS5DwVZljEBCIOCKzxawRGxQAJwQ079XrrZ51Gj/bNl+zzlKMLE4qSmS2xUgWEsoEBoTSF2RCYyrAMfjsYIPi8DPOwcqCs0X/DUpNFLWIGCy0Y4thO4UzW90tMvz9n+l0TUyIADS5PtsRqXom91ejtikRC0AJCKwCCVe8Fr9NoEgKCfGThrKL0xMqhAKFMBYS6pfGlqfILKxgrHlo90LnFRO1voN7lspB4hzSdaSe/sK6LcCYP6BGd8vUm69ullv8os/zHWZ/YPFlYg4/MFRPgW3+3iFSMEBBgQJpNei94nUaNVJUh4cwiBAQWbzCEyoCAwLwMZ1ewMgeOgQEB5vwvMZeh0+RuUnihMCd3MkaACaFggZ3hvOJpswIs/PHsjGutsb1v+EU5TnAGEYDg7bBpchnCBAQ8slVxN9n0XvA6jSZhIIFyJjUJcKAqLbEqdTBA8EkIGYmlqUpNGgtqcg4YhwDkbUADQqMJhFtmHiT3otMXkDB8WCD3ZTMur23Kdaf8h6qpPW/Euv7LNClggXdzu2TyACB0hRupqCpTiKjtICEong69XJJOo0cICPKZtFlnkhOq0gEThrQhwGfSuYxZpclKzXwEhHmDnJ4BwiUGCK4mW/8lHpfYL2Ah7LphPl4IgAVyRmBTM3er7bpz2tVzM3p+OrX3v9Tg505pYob18n6vNSZvq6ZAypDWFfHgLQQIMIx662edRo8IEF5Ove1McmJ1elLV4CpDKnoZUpPqMxLKlsi1YQACBhF56ox9LWg6a1fcFKvMZ7if5TASw4J6vBoR7W7EkmJdirtN/rh52rv1067/NLbvpzzInyVdVk4sWOApHsdNzO3Yyc2JQ0sIzbIPEDoUF+zU6W2bdBo9IkBwpCS9dGdidRpsLFJxAEBIUN2OSQ0ZCRVpyrlvMDRo+8ogp+f86JDcFyy9jYrnVYWp/60YctAk+9Y7WhnDExU0m8odKrO4McrR26H0tSu9rdPer1auV1t6qwxUlIl9dk+UugFcQqg2AYIxp6oKCINjgp+EAIDQZPVU6plNOo0ecQkhZVbRnUk16bBxCSG0ypCqAsKyhPJk5dwd0qW5gwOCRDMf9WXXr83Xm2RXl+w5zwyMzMZIoUfq0h+RYWFA80ITwkInO7+708YSq2usvfUWj9PAbsOpbuPNQdyGcBFVhtawHbJaQOhUXI2yu8KkA4JOo0ZcQki9HYQEAIRqFRBKgy2KfoDAjIphSAhEwr53/Wemt0psrH5al83TqVZPapQDzAJCNh4JLLD9DmZvdHUprma512l1tVtcXQZetO0lnls0XuTrCN9iFdrTkGYEX2BGC6vE2Ntk85wzUWlKncaZqNQYC9Vbz3zxAyf9TWhSbQjxjuSkmqUaQBhCQgARIhyjoiCOCZek668YP74w9Q/OmVebbH0XERa6MBAxABacIp45Qljwj3JkP29j52clF9qUDxusvU6Lq8XADHH28ewTQdf1dJtcTiEeDN2rxQcIVImxAVQwEztV5zg8gk5+RAVCqMI4sNXl1dxONLmIAOF0SuLphUl1SxNVQEgYREIoTUmqB0BYooRhVBTE0OBtrJqIsNBTavrorPzWsaRrTVbXBZu7g8MCRtqosgFxSiSeCA0s+BZcBgtUtK2TfJTW9+uUvhqzu0yt5XiZx1CNJVFHeAYIjYomXmsIrcFnVGzF1s+N1n4dEMadEAri3r51Wt18W3Ga8vx98okVlvP3ISDMC6OS0EQiBATrqZT4k4sAEJKqlw6qMqhGxfplCRWpSu03wwcEIg4Lbbxpy7UGyzvOWIdD+lN9nKtbBljwnkdPRKMaY9CsMTyGLTAIngoRztTCFlbMo7Rec1r6zptdDTE84WJs4xa4hHDe5G2y8o7wYaQzaFUG3gu+xURlq3UaN3r7VmB528Uvfq5iMYvuq0iLL02+pfVuufFu0+t3qF15wqkxNgHoCKurHHfsrpufW5hUm5FYszSheEAJIUGoDAAIZelK7YJIAYHI19YN83/fPhH9X2eM16tir7bG9jKrAqtC7O1A86CTxyD1DwcWfNVXNJoIQg1WZ+rvsPW2Wa+2x7razO5XY8Y4nIn3e/13U18DcLcWEAYznqjWElQZLiieevhTlxDGm978nHRprq39H+JLUma3fCupKj3p3FJWWKwsVTqwwFb9j9affJajAQjVYRcoHh/at55VTPrhvbeevJM9xaCA4AtdBkAoSZdrIpYQtMRl9cu8d0PfcUPvWXNPeez7lcrVVpbbCBMe/XHcARECFsIIXRAA4v9zLDfUirDQqbja5WudFtebJvfvVFioxbu6kaZ7upAbAMFpI40mLEBo1gDCRcXdYPXogDDu1PCvwAVy1YKEUqwdhCWGYCcRtsrUpMr0mYfm2dq/YLn0N7xAMWDCsZUTFBbQBiLvfyD++KKk+owkFRBCqwxq6HLiuYxZ5YuVyoUMDQD0RkY8I1Ldek6brnbEveVM+rhN7muVPZdYSB4lL3DfgQYWtGVVwuAjDbtRgaYmrF183ubpYLnV19rMrl+Y3a8YeBtKCme6MXELQkLodU5jEgI8aVM4NgQNIFxS3OdkT53uZRhvKsOEoNJkpm5Xp6uaNcJC3VJWK6A6HZSI6S8usB3PiH0hnXEN8F35IunU8gkHCwQI+x5IOnanFhBCSwgCEOAxy1MUHAc2GqNEHBMO8Z6w19+I7btgudZmdYESccnGEp9bQ8FCGN66AFVCiyQsnKkJw5mYJ1R2nZd7GhV3ucXjMPBYpjbJ2zH6sMDjELqYDaG/RXSEH8Lf6g8INned4inT4xDGmyghqIhVIGSAQNXGqOwYC/MDaYFhAqjks84uAWlBKULDwqW5wHqxFzJmXp4zgWwLBAhPPnBb4eKkcxlDuR199RASy9KUs+mDF1kdHvFApivcwuDujulrt3zslHtZMVJYylU9wuempPjnCGAhhB4BbIjVmVgUcZfMSi7UWV0NFlcjOiidmBMxqlGO5GXwAiBUogek3WcwjEBCqLN6akx66PI4kwoIiSgh+AqLkc0NYKEYa5jXsFwhUMwTziy8zb44tmIxzIA52AlFevf/MHyYCISAEPfEqoTDCxPrMhKrBgtMStBUTEpggDBE1eWREC+zjD5KYEP3azF9nZZrTda+TtnTbaPQAp7CwEFAE7oQZlpEMCyo4Uwsoum84uq0fVQb19dqdnWqUY5vjlqzOZIQvB1mVke9VdMRPiJAqJc9jXpy03iTkBBKUgkQqFA5igopgncYLFSkAosB+8CSGu9IltYskLsWSd4MhgZvpkpvrRy2RW40nwUA4XjqjKNLQEJIDFNCqE6fVbZErl/OOz/eGOI+SgELlyT3pRhXi6WvydbbZvN0y9RIvb+Zkv5Uy0CkudU+cNDAQjPK8B2sB6Wr3fpRQ5yr3eLuNIhmc6PzdF7J8xuLt3q6hxIew8iADgAEV4PiadPTn8ebCBDOgoSQKiQEXoOUqpdrYQGUiMpU1u6kbmlCzdLEM0tmtC6K+9lSJi8CGrzyBdYtcRyjHFF/sVZ/hakM9aAy8GzHhKBcBqqzymGhKj0eAKEbQy+uDFhkdVSIw8LbvDYjq67QZnDVxYKm34OWQIpCBHZ2+7KltD6FMGFB5ElpYKGFRT5jOJPN1a70NFg9l7EGy3ujAAscEH5tvnpuhqfN5gOEsCUE76s2b51aIEUHhHEkFRASKqjcKO9iEGB588FCCbJSBcICrMJVabMAFpozYkv+gbdTJKvCuBgW8LrmVxM+e2DBbMCrQdKfS3n5ZXjYhIq0xOK75NYHGSBcHgszKWOft3DzqumTPzP0XbZ8XDW1t1NmegQFNXFY8GUO8ijoiCwMTk00FMFCKwZSorTAete+PoWElpE+EQDCr4ze2ljWADq8GikBRsW+aviJUa+YNM6k9TLUaGwIWkxQo3xFvRGSH5gdEmChfllCeWpiMbPPxxb+nencV8YtnElc0T4vAcBtyJqKHPpSZpWkKGcxxMIxdn4THrrQiRsylOuioafJ8ufq6VfbWQ84TxfAgs3XONK32gpYCMOv5wwqwwI4Q+FMryjuBplJ6YVMbR+JbZ8HPPzK4C2bynSfrrDqrPoCk1oU70XFWx/bT0VW20ZvlD9lNIVRNGxB30cDwf+GPgXGIShVCxJL0N6uBYQAUYF3N1DdEFytYH8mVaUngrRQnppQjOFMFf8U9+pfcd68nMFqFY6Ng1IDQayEWsXAZdiFZoTqw2fKM2xnM3gex5iTX1ShXeppMv+70wL3cq1tGsj2LL36vMJtjKLgGPGRMDUMvRAHwUITxQfKPedkz1nDCJ19/OZ/afRWx3nbrQwQwqizKlyTHBDq4rydWO1Bb9s0LIqKYvxuNptjjOZog9lgNBvMZoMB9i0GE6NoQ8zQZwEWvjRXKU5NAr5uWObrgsp9Df4MJdqi8eZHXGygXyXVYDgTRjnesvcOm/NvLb/8EpviYxbOpKZoWU/PZ2HYZQIQBsxswt4NqbPLlistmLgB+s44kbAqUGrSv70e89tXjH1vxr7fpoAS0d+FxUub1TJrzYGhC2EYFvinXwYBsGGDMvJwIA4Ir5u8Fdb+TllTZzU8lYHlMti8VVbv61gs7p3RG9ZPE0VNmQLsH2MyASAYTLDDMIF9mhhEwE6MJSZqSCEB44vidn09sSidAQLjo5QgWFBje/yaH6X6jqE/CRZqmXk/qZLlREw/sOCmohVxFSsonGla4apbb2g4E5cQ7ErVfUkVqQkaSSDY58g7PCIgxDsybL/43xMkXyMgDcHVbehxWv7cGtfXinmU57FHTKsfLPT7DAVDw4K24TKVJeEBwyMofs7vtjXW62BZXSwNMxwbglZlOG/z1imeV4zkotVpeBTN2d8EsGAQn0aTgcAhxgSqwxCnAPacx9yF049+IaF4yeymZUz+r2B90gNgwQ8TtOZ6rXkBYAGDA5MwDGB21dJZRSmfKfz6zBNLb345ZS6GM8m1829UOBNxtCNDqV8+C/u6DigkiCCEClaI9bPV357++t9OEEAg8oom0Rhh2Ndk6Gm2vN+i9LbYPN1Us90HC8JsiOAwpJlR8ZV/bFXcjTYGCKPhgvSeUbwlNp6XHRYg+FQG0DKuNVpcTqOe7TgSMhrNKB6YDCgkcBxAQGDfG8zB5oXQlM14YVbnVz5zOjWhMpUFLVdhI/UKxuB+0kKgG0IVGPiyq+rmxUwax4QIjHJ0LPpMYUa8Y+msM0t5ONM7fyeNOizA2Sr/gZVEOJnM+r0KvWDgVEfWu6E0+baKxX/R9Y8TChCIGHdUYhqCgzkC+s4Ze+stV9tjsfCCIso09QtnRBj1FvxqLLTILvjt+dHpqOgtnuZ1TPOGLyEIAymTVeSPquNcTTogjIhizKQsmDga8I0rEQazYWgJgcgr3fr2rbCCJ1Z/M74sefbZNJClGSyABlGdzrqmamAhMdi2wGEhlfQIn3mhmC3BTIOArS4joTgVWG96953MsMBhYfgJhqHpBDZvenlhQgkZDNVezwGwIGynAAhlICEsmHFiwCZu406MR+qxdzya/lznYq43mf+7wvpxi811UQZY8Hbxoo5D2vH6hetBbbEKmCDEg5ECwsszvGUzvB1yJIDADZ5w/yBdeH5q0lWGYVPUlOgYISGoaICWBBPZE4xG45ToMBwNSIAGt3Z8GdgTVvCZpffMrFg+u2wJW/FB+AdYgLWewUJyor+0kBhCWkj1My+UYDgT67SYhkmUGbB2w1XMv7yDhzMBJrBKLKNkWHAwhFGKvjmrlEVcD6QyqCJNMrV+BkVm+o+/Nzo3cMNI1HwmaeH6ecPVdgtLr262ui8png5rvy8kKTwDI+oO3lbFW/3ZUQIE2fvCLcz+eSG8Xi0+z6nsbVa8dbdM8H40E5wAD2KMlhiuL2hMB6rKEM0AITwJgcg5b2blnTLl/Tntcafvn3l6OeuCVJ7CygtUpTHLALMtJPvceX65DymDwgKch+kgLO2oKn12aTILIjr/VTIDotVRkkaOCljTQK6Zn1icOgggJGoBoTT55hfvmXCZmwMQs/79BA2AWH7heuvUq07r78struY47IYWaVcIBITLoyQhnGDeCta8KUxA0IRTepqt3mr1NnS347AImJ1kAxUKTCQhxKg70SZTVESAQARoULlaOrGe/PJxr35hxsW/jXd8Ob6CBSokVS+lCiqUC5mgcn0CKeYqFISwN6p+TBbFBLBQv4xFOZanSBkZsfWLzTV3MjRwjjicqe0rDBDqvhFPHeFFElNooyJJCKBTJA/3emNEAa6HP3fEvNdg7OuO/dipsHzqizx9IFxfg9MfEEZLQnhW8h6TvOFLCCIwu0VxOxVfHQkdEIZFHBCMZEPQmBFIZQCZwWweDiAQUfBA7XxiT/nnn5lW87+BZ3FVTUtAmwBz4pekiAilxDLh6RNRTEJUUBMKSlVYKEtNqmJRjqBNJFWz1GPbi/OsVZ9Tw5nm8MILkVL130uX5soV/zqrbAlLuygfIDapVJUQakAVSoZ7G+Yo3XgKgILrb5n/6LQ47VJP+7TeVsVzAaGgRVMSIfxuCLg0u52yttn0iO7zJSy16udlGLLfK7dtssaOp1Tbpg4IwyIGCGbB/kFGRbMZAAHUihFdwz8I2bT3priDSTcd/7vbSpNnYWukJAxQ9IkKIlBBa3gMggUezoSHsQYKzEyRDiBzywt33HT+7+Lems1LQDvnRSoqTHPMlQrn3lI0LxE9CAMHK6qAgLaRWwq2T7SqkTywuVat1uiV+n4bc/0Ny7v10z5qU3pbZE83VkGhgmxqTbawko61bscWlkrZ32UUze5HdMNeqooQUQNo0QteLYYwGW0IMTESCwyOnhI13PV3NGhKTEx0MCAYNTKDwTJSQBBE7JLN96f+9MvTm++8+dSSpCqWzpCI6QwJ5WmJpQGhCxr9PXQ4kxrlWMuqM4HMMKsy7TOOjBmVC6e2f4mLCpGEE3+mcO7cwrlJJ+YlnMWasUNJCKxqXHnq9KP3s6fKzh6dsRoZ8dSnX6qpT3ap76Kx5w3L1bLYD1usfSzHQfZ2YXEkWuU1AcmqpS4cfYFxoheb2nvaMQ7hF6MBCBkgIUzznidPaFh2DB4OATv1ppHj0rgQk8PNzGRnkSwxUYbxggWQEIwWCzcgqM5HNV4RvzTERk0ZJUAQBEzjoMYu9tiXvnfTyeXAv2zBrUqbfS5DLMpqDzU1OEHVIPzCAEp9Fj8BC0lYnSm+PCXh5MJpjfN40ZKwMeEzJ/+eAcLL/5pUvARONaSEwEq+lKcozz6IcDfOgMCToz9Uw5gdkqsmpqfU8kGtfK3V5upkhde8uPiKmgl+XafDKJugDWCmcCDWUREkBLRVjvTmvWwUPR2a5KYwJYQ2BDcnAsJrkw8QgGIMJlqLDcB4kjlmyjjAAgMEozFGmBONYgc/DSbJaJSGbUMYhOB9vTVPcvJsoJkn7rnl+ZXxjozZuOAmkZuyMo07HLVpEVpYID2izN+8QDVRa1j8Q1LDsttPL7a8eDfvJxWePWFaOTMq3lLyjYSzzGCIcUehEh5FU4ZzGbPL05Ti9PHKbCIKLJ/ildw/iemtN79XO9XVZuvrUtytQeVTtFWVwvQpaEsqYbSz9xWbu0HubzeSejLSR/BK/S1GT2e46c++XK12AASrp2USpzrGaE36bN/EpIWxhQWW0chjkFQhAWWDGNXSKMEWZqTiMIisjsiqc7BU0fTi74L+PuvskqRaVlWJuSmx/7IPFijKUeOJCG1eoOBnVkc9nZd6hu3U8rDuqnwR8zKULI7ngBBaQvA1ZTjHHKnK8/exS+y7sdVRQhJTxb4jeUt8BdZcP4/pvWS5Vg/agdyHEUdYkUxt5SagIOyqCKF7OjThOt4pe502rvuPrLQpr7N6yeShom0RuB3Z8UxCaJvExRCMRnNMgKcPdHmLBWDBMMUYPSawAICAqY4m/2BFDhHwKUlmKWq0VYYAsttnHlodu2cj7My9tAomltz0beDiBAYLzEfJYAG7MGuVCE2S9cBxC4AJjctmnVkkn7ibOQ5e+lZY93NyuVS4Sn45PaF4cRLVSBlcQqjPSAAJ4SWUQ47cqPppAxEzEdyPmIC6M4OCFssHNXJvu+y5YGPVk0QJVuKgCAuk+IqraOqvsnbS7QwN3N3KtTrZc9nEUyxHFohxGc0d71SbWMWkNrWVWzhehmYm/DDJp3USAwKPDTaafII6Z0lQ6o1WyWqIMt7oe5gSExMjAMGoCVb0AUKsJI2JxGK333rqsbjyLQgLc2FmmC98dUb1v9xeDPL/sgRYqavSqSi6WtMsVIpBqS/SOAGLMrHijWdBYFgCEkhiSXihAiogxJ9ZTFJKyKJJCVpAqESV4YZVWA1JIgKHzIZ/+HF030XLh+dsve1WgAIP9ZdvpnVWVGOOoNMT/cTHcc28/hKTN84r7k6l77xytT62z2niNswRV2AGQLAjIHhbmZHQJ5aECQiNVgYIzskNCDFGnlXEJQRt9rHROE2aFg0LdFTUDboHAASDxaIigJ+jgaQXPOpGXT0EMVhYHvfGIqpZBJ+W179wU/WihKK0xOqlSRj/zFZtNdDR55oMZlVWqQCrQJcs+dyp5LmFqz7/XHiAgCqDUrw4oShZCwiBZRU1jR2TQGUoGSNACAwq6Lb89pQZ+Oijljh3i9VzkZnXAvo1eLRMHYnNsF9TMcndpHZYBijoUj5umQrP+scz2MNllDrDXkaV4Z0LJm8LiyvQ3nCwMKOt2cL2QVxxWie1ysCrEGiseWrWoYkXJcAQYoAFCQsX3AhYiAJAiI0lYYC7GIzBgDDmxBwQSdJbNh5L4JXiXv3bmxq/fnPZktm1rHfk7PoMzqeh8qkTtYBQlT6rNPWvixbMLZw71/H1sK5OoctVdySdWZRUl87CG0JVYvdJCA3LEqpuuIQQUPyEBRW8Zbx6Me73dbdcb7X1AWtcULztLL4odP/HoUqlCaYL0eXNyasNeLqUvnaltysO7ue5LYauk1P4XY1SyDa3IVwwuZttvvYxquWTB0s089JPQvHp9wGC7OlEQHh1dO5njEm145mEbKAJHjZplYhoLFMgMf6dEhU9qrAQHS0ZjVqVwQ8QTOMECERezptS/depRsGMkoWzyhcmViyZXZEOzM4CmHnxpcDcZBUQUrFYQfJfV/0LqCFz68Nq/TATL3pr5R23nV2SyPIvREf4AVSGhmWJ5WmzEBDibwAgcL3gCg/acTuj+/7D2Psz84cN1p52ua/Z6qFWsGr64TBawYaukNas9oHFCmzurmk9F62uN6Y2vGB87uloFvM12h5/0n08lSAhyNoC8oPfOQ+H6MadTqzTMgkBgVvzzGROxE9EBmJDkhBImxDqfDRGCklxUpQhevjhxAEUAhBMPs8jsyGMH7GEiDulE/eQO2/quXm3nFkY7/jy7MpkWLVZeECZWLhDRgiogFCS8j9q5gMgfL75q+Fcdo5z3jznvL+suCP+pWRmH/ABQggJYRYCwqzKpf+rJD3DkfGlUQUEJo0f4151FlFwdkpfueF6Rew1p+XjNsWNZj1KQHBzXtZ0bgofCoJ7v6qd4gFqPN3K9Talt326+2Ks+1WT+8+s/StAgf0GFLmkNIT+QkNfg0yeEXezWh62KdQmule3KZ5Xld6GaZ5zMUxC2DPKNzY2BPo7MGM0ZRrSxm2MPtlArNrcD0gVjQAWLJYog2EUYAHOYDJF+xsV1fRn9udoPGjkRIJB7XxMmZw39WTGTSeWxR+Zn1CRPhsdkcCnvnghTbRSSJUhoTj1czXJcxwZn28Oy4bAAaH8joSTC9mFBgUEsiEkVqf9r5KlowgIvpaOqob+UUM07L9bF3e9U+ZQ0EF9mlSNQM0CjqSro4oAamASQUE/9Um5IF9vUa7AFWtj3eVGbbbCDQr7IUDwHIxhO53Y3r2TVXtj1lF0l1APKd5Jqg3vswX3X1WuN1tFXJN31Q25vbGgKVMAFkwgAMQYYzQWRYPP0iiKm6k+CC0smM1TYgzhVjQKSRgwiVCgjUZQ91kiw9jGSlH2NEABYsLUigU3lS6+/UXqK53BEh9q07Eys9aoGKwv+ACB9VQqTv6rsoy5l1b9n6q7w7kFrjKUfyP+1BItIAxUQg3jEAB2RkdloAKhvNEh8uDv66OddunDWqvrguzuwH6LHWji0zZu1kYbRgAFfl3b2A6ty91KX6vt7erYj1vN1zuwPZMdEyIuj0UEILtWkcFTZb7WYL3WYu1pll1diuei0ndB6T2PWzfb91y0uWG/RelplT9okvt+bfa8F81GLHsi5ZMMi6YgLDDuNpi47qBGJqixxCYhMPAUAw4LFtgAT9jPw6mXHkRReF1NRRQ/3SEm0mIIIyFgw450qW0R1VeZUXnnjKoFs04vSChPo5ZqrDVkGUKBqiCEcjv6lH3udqxbmng2OTK3I8KRXLwg4cUF2EdmAAlBvQcWqViWOnKjIg8rels1GnilPzSaCwulq43WXoz/ISggadkn3kfaqS3YckgnbGc2SWCxvg7lvXNTr7Wae0QDx2LJe2rULIdhDQXaTl0dhr7XjB9dtLzqiPtjjfU/HbG/Pi3D9quX5CtFyp8qrD89NRUO/vmPjH0XDJMxf2EImhINWoAUGxttMBiNfrVKYoy+GEKNR4BbGGB/itGIeBJ2uTOVoqawSKgYEYlk8gcEgynqxoUpCiJbAUkFl1bNrEmNL1+cVLQYGJ8WaBAMMEs62Rd0VBpsPQhqG1ecnFiWltR0V7xj0Ywz86XCuTOPh+dlwOousmNR/MlFQ0sICAgJFcm2EQQm+YUaFrLP939uvuyYc80p93QwmyEJzIFQEJ4bcXAoYCFGWFMRVuHeTuX9RqWn2dLXpkLBaLd4DndAnNhx/hIHxo9+FfNui+H9WuO7tcY/1ZnerTW9X2t6r9z4k93RXI66NGo9ZyccRXODYTRK7CTMq0yqyvMmkxYfaHGHDWAhNjYWYCGCq7EoRIvBoLUhmAQgRN9oQKD0ZNVWEFefPLM2jVVdYwpCOoYNo1QQCAWBreISAqCghDWWBWVhNiuslDaz6avsKl2fA0wI665woZdPpSS+sDjRDxAGSG6qWzq7Kl05PpzQZa72Onyhhh++brhUIb3vtPa1sUoFnjZZQEFAo8bwbQX9wVDQrEJBO0CBrbfL9pEzrqfC4mowcM/mOEGBb2SAx1+XvL9Th4ju6pK6OXnqFgOEo1gu8pPdzDEqirohxMSYozUFzVQ7g1/YQIxPVOCwIFmtYWYkRUUZYqVYg3oSjUCCfg3jCKqjDE5e1auIYQZTW746ozxldgmroQr8xQKVy9MSygKruAfXZfVrDKc2k02qwjyIyvTZdWmfKfy69TWszPxWUrj3Rq0qTybfDipDgFGRF2FIETnaBAhwOeVHD0Vqec/Olhyqpe7qCcNHZ8y/rp7+ETA+izoWrRN8VYxUT1xE7gNuXtAUP5E9FHjcqXg6ZNd5W0+bta/b4u6KEVYLEg8mAnE0IKNKG4s4orgjtl8reU+g6XVyFK4bFYqizkpGo9FXBplzrp9z0F+DMJO7MCqMQKaoaIM0darG7SiiILjhYpRtCP7FUsxFX48rnj+jZlG8g/kHkzAKUe2FNEjldl4SQS28lsIPACjAKqwMEKrSbzu1eEbXXOtvEiKuqIaAYD2ZnHTsTupFK3IoQkoIIMnEl6fIkQOChBO+97+NH/3U8ssK6/VWhVcKalEDb3yWw+F5ElUQUFUMpnFQtGGb7OqwXW9R+qqnultNXEqBzwtjZDmMlALiMz+BFoOwCYUFbnWkVdvXVsmHBmY/wwJ8WgxDd1yS0MsQF6f1YmhTHUfTywBcpimnpvzir6dX/5Nkn5dUifXVa7GDG2f8wJZPfjwY1F+eL9YVqbPr0PZYlfrZ6gW3tvyz/PO/Hma72M4vwd1aWu/8i+fQqFjJBZWBNhYMWbFUPhtu+jNN5uefMFT/yHTxdOyfW6Zfbba62hQvJiDwHs2+pJ4IcpF8UNAcAAVqiNEFJnW4OuRrLZa+V2Ndv4h1Nxu4B+HKxIICr+gyc0UNygrYqBjUEdRuJtKdjxlFRcdEYfGiGBZKZOF1kk08hkFT28QUweJOigmTQMxaM6YqexhH5NOUJC6rg2pw4h5KQ57a/qUZXV9MOLkw8RzLCUJ5Oz1Ry18qp/unOYeAAl7FvSyV2QqwksmMsiU31S6Ydu6OETWP5s3o7TfVf4tlYVejo7OcGTa1Gyk1eN2UhKI0uWYBV4KGHBKczz2vG/7zpNLTgVLBBRuvO8T1gsCui2GZC/wNBb6OzyRvdCu9bXJvh3Lt/PS+16b2/cLU9x8xIvB4okndTFwRIKBqVR2l0f/dZfzP9tj/bjf+ui1aJFzzA66MNPl68hLry2wwGgxmjUfALOIWRFhj+Op/tCiRZDT7ZAw4c3TYbZuCiaAANtVWMPVH37vp5LeYYl6eloSpi0wwKPOVQPHFD/jZCkJBQUkKcSUZHOC/SSUp8uuf96ujOOwlA354hdk54167M75oUWIVq/o+u2EZsyfUZWD9lqWsDnxDxmfqM5i5ozTt5rZ/iv3t7eFclDHgO3ye97XJ/a9SdVBVHtDkFYbvRBgk2pDtoyfxY6f1zXOxVy9Z3L82TnDBmw1RJWfzvl/F9Lxm+lk5s5O/dUxq3z2Dtt/uY+/5d+XGay+bei/EcNyonCimj7GjKIkUAYPBQB2XuItBWwMN2TkiQMB+rzwIQZPZxMBheIBw66q5t56cK5IRLHXfuIkF7Sxh62zNUibY1y3FWojJiZqKqaEs+X6KA9kMORQAY9ayiKB4R8a02m/ya1EFxZHH1IIwg3LC1LpvzDizNKFoYcLpxYmnk28/vfi2U4tuP7VwVtGShJcXzzq7GLSe2M47wscf5k1zSp4ug7vJ5n3V5g7owhyelWAwcwHaDHm0YQfLUP64SfmPOuX9S+arr8WIxKgJiANEPFrbKfV1GXs6LR+ckz9okD9qlK+1WXs6bB+3Kh+3sQ1EnZ525cNztndr5T9VWK93xPZRlVfnpwgTyMAYw6IDfNnKfrVNfBELppiYCBZ3o9kXlSREBQqRitiGULhq2irm7p97aRVs07v+5abqhfFFrH8TkwcqEBCCoaA0NRAN/AMRmeWwGKGgMpVKJgIUzC5bcvvpu2c4lknYAVZ67fNcIBkVEk4Q57wZ5QtmVS6OL02dWZJqPZ1ifTllJmzFi2Wn2joq7Osy8/glydNscjVit7IIfQeBQQVOf8thI2JLO8YVXFCuN8nvVCsfNcZeazHyRirvYOGCCYsGqgrjKre8X830qb5u2dXGDClerLLo7WYFIb3YqZZKx7tbWS52T5fybs20PqdFSBefbCJ/QbTRIhKgeByjNgfBKAwIuGMwhONlIIoxmUJKCMZIIxVhYT2+AjBhbuGqvyxc8lf1y28/m0aGAtbxpDotoZQav6rFTEIEFfgaKfqHGKUmUo0U1hI6NfFsWnxJ+qyz6VR4Da4o3Yge8XDCYyspRgIuNOeyWqwVtzn4fUTNJdmEfwUlhEaT28kiD0W1gUgTEPqdmoAEKlbQjCdsAyayXW+RP3DK1xstvU4Dr6BymasqE5aE6OJusrhaprOwTGpE24qhmE0o+QQlN1HaBYuxvAiDYHXXmz/ZugMxNRPpKarZPwFKKyHw4AT60xxZUpKmQoumbRNWaIkYEBwZN5+8e5ZjOVvNqRULlkvlfO2reKZpxeIvFfhBAYYkMSsBOSWr0gAKZhQtm/HyXXMofOjI/TMPrb6xPdSOrbzNkfEXlal/UbvE1PotqfYe2ExnU01lS0wvfEH60V9JYRdaFlYyj8PohkneruoI4fVN02KCHxQ0YUlSFm0o97Rb/3BOvt5lYdG8lxAKankMzwQnGhxPufF6jc3dImOQNlV+UyWokOPQjI8Po3Ge5Ttcq7N6qjG26pUJjX4RU1RMFFYy5IHK3GZo0oQR+ho081Ai1VFIlRMiqqZiMMb6AYIIaTCG7XZkSnS26ecPTW/ZmFCMQQXYZ423QisRMQNcC/CVSBVSgX+NRCqUivFFaUk1GUllqX9ZfMfM09+KO/4dJg+w0OKMmTe6QpEmemoeJWCyftN2LiTQnxJWlQ9TXxCmPEnC1S3c0iXq5Bd+B+yXhIsmxhcpbtCpO+X368zXy8y99QZuZKvCEL4JDwVE7J4PRWMNdhsoU+5GbTVFmUtEvvQNRYyDQEgvpX01WLjf4e3xfqRRoSnRUdFGSbKqTZnNIlhIxCIaNPqC3wExGI8UeW2laJPFIFId1URL2EwmE8t+GpKEEu31Midg010sAaE8JTHIkxhY4CgYBAgcSpKZD4L599MTqtISTi6c2T3X+urf8PrJhx9iiskYVC+kVlNnvnjrc3fcVpR+W8VCpXGZUrdcqfuWXLoENtvBf7b++PMRWBQvq52JnPNczVZWJYwnLIeWEPwMBWpYQr8IMaLahu2gQct/rrNcP2jqy1ajDTt5rcXJQrwSQo7B2xqLNdV9TRk8KhRobSzaTjGi1RSr8HbB5qqXPS0xFOE8eSkg+kjrTFQFAJPIjNboC4yLmW0B2TYKwCQq4pxHtTUDnMdo4D0a8E+TWZLlqHAyI7AFfOxPvpVQkTzbeRfvphQUaqhRCgK1A020YQpPbKxg3888lHHz83cr1TycicU13WgFgQhlgNjuv1fq/qdkl0BPSUSTJgg/CY7kBJZ4lTyrNDmhePH/KFsyo3ZBbP03wrkrHnvvZQzr67ruDCEhaHqlcSgQ0YZuJ3oQLjAo6OuyfVQd13PZ3FceQx2wvft4IYXJRRSB3F9qcrdZmfrj11RuCD+Lz1HbyrozuBrjPO1GliP25qTUGoCFKYGIxycLU4DRV6UkoK6aQZUTosmbEB0DaBK+CTGAGBBFR/vCGqdO5TcWExNFfRmGpOKvSJfmKiXfSCzGEsQlQVDg1+c9yFDAO7CkMEUDzY+zK5KTjs27qW2Btewhv7iCsXm/cLnKOwEQbilJSypPY4pPVXrSuWWsmOq5DN/WsIz5O2CrTIGbjN2/bsjQZd6YGNOcfZ1MB1WQ1TmvxhW0ygAFvW2Kq8P2ntPW67C4ynnmL6sDMG/ChRiFSczQ4WC2Vlb25JIQD8L0vcpqaDf2gG6R+1osIiVqEhHjYh5UYIn2K2koSh9oDAW++GSNYzHaMBbpyUPSS/8q2ecpZxYkYt8lTYdWf6mA91gJijakuAKWRIxexWMrp7+yiGIFJaf9htTqGoTIs+CcZzu7MOHlJUl1GcwuSu0jsY1UQinbEtl+Kk+kqlsKzzX9ue/xvjMD6zIDAIJPQvBVLtKkJfpBQYf8YZO13jHtvaOxrlNGXiBokrY31RAvr9pt9uBjqibTcKs/cQ0CBgrVqHcd00RI9mQhnsxoRvncz2UgnAU+m6GvMjMmRMdgoaTRhYIpqLNEYfJ1VPSUKEOUFH4W9RFWQEA5lZyAZc8TS9S2CP7lCwIjk0tSyOTIpAJ0Q8xxZNxU/T3kmQzJO398FjxUSSzHl848NI+JAVXprKFbYM1GTdBUCWo6LLw5Le70t3hthwHIDxCcGkDwgwKhI6hQ0KawcsfnGRR0OuSflFp+VopBOBKO0+QvECQJF8NPYnnuVRgdqENoDTBcHYq3e5q3fJrrJLaBPjLeDxY2AftFo6pu0HoKhIQQkH4oqrKz4kijDAXR0TGG6BgLohPcUjSWeYyxxEjTpChDeBYJyhc+y8IMGCCotkEhGPg1VBLRhhXYgbEGjk+Od2TMbMAV9tIq6d2HJO9qNtfHhY7cz1Idn7+LJWBWpmtLLnPxpkykP4uyiqwWa2JZmuL4F8za+MJA59baENxNWpHA31zgRBs7VQvsUFzdyseN1t8Ux/2pwvxBJXMmwjhdXo01VcZyZG48eVrgqdFvItAyXD+sLACh/6LSW2H1lCMglI33I0VCIoNAE22odmTQdF8VKY0IBcOpkzYIgVQAFzLyVClfpQV0Z1hYgHQ4VkoChCKQDRggiE7NmpaLwpMIUMC18oTajFklKXOcGX9R9SgwCYsrgPndtohhwjhSJwME5dzdidT0Qe0Fn1CWGignECzQTmXarPLU6UX/LIFE45w5EKPyyGGsm8rYvwXbIzbLflCAjZY8naxKUt8F5VqD/Mfa2I/qzVcbDVRo8XKh5Dw1plrUmJGn2QobxmtFVgFGKyEwxapqmrsKu8tNHpVBwvhAtewAlwqEeCCqoIgoo2G4D8IhkFOA69U0SV/0sugdYwvnLCogJBAgCDahNu4ECxRtSHEFdRkJmI5080srbj6xkqINYy9snElphuNIlNbktSvFd4uu9KGysAUmpBL6kYo0vXpBbPX/5fkUIU8PaHAOrWcVRrczjrcvdPqggDFCF8YVdMtXW5U/VViu1Zl762N4iJGDyxifVPIBgmgaNTxAKJvmrjNPurwG0exVLW5g1kgIaEMwq3EFN4wAENTAJxMXDGiHCy2WaNAjhsQioTKU+SQEFRYoKkk1FFQtjS9OTTi5cIYjI+7Fu1mIkd0u71t/w6MNwyQKPOj6spSRAeoMNZ4eKOVKZGJyiagi7TPli+TGr7HR6PzSgFdoUEOXm1EjCIg27FRcnfL1NvnjqqnX2829HTG8UNikCjEaNjE0gK1DuFfCVhm0gAAqQ+U0V5V58kkIwmbIw4E0NZFimUXvBrVvCyD/pgwmgVFcYUG35hCnwKqkSilWPlRtCAkcClIw+yA9sWJpQnHqjKNLpv/4O1OPfofnDe3aLD3x2ISAAiIEhKnNf/XZAwtm16Vjy+nkxGBlQSMhCNcqHJxUkW4rShu89jKrYHxJ6q82MmXhFZsbK6SxNRFDjHo7p/2uOu76T82unxq47+CVT0W2Djcqvh7rblF8cQjDkxAu2noqZHeZcdLZEIxCGDCqeQqwxcZKLFhoStSUMWqxKsyV2mAnjUnTMjQgNPwrq1tevYCxRu1S5pUjgxvZDCtYPM8tL9wxrWRB7Ol07kYEkeDYygkEBUQECBcTk47NS6TSjoNLCL5CDfCw6QmOZOXMUICA9cz7Wwxe51TWb+VVNKq3W3s75fdKbNfeiO19W40rmGAljG4o0ZN6us2s8Up3pFVkZZ/bsV3xdsves7G9pdGTy8sAZNA0RIg2mqU4I9VRv6E6Qojb0ICAgWdTmgRKRFvCyIAuTmWBSaWps8pTk+qXJZSRdz6D9V6sTJtbOPf2Y2nWgw9QPVUefjzRoIAIAcH4i1ukwrmsVlJNmk9CCGlDEA6UUszlLFoo16DnsXPAYuyiE1PvK7F/qJA/7LB90CT/1hH/cafc+xvTBC9dcuOIxVo7pP52k6fNCjJ/mI3ghYvBI9rWs+RH21vHVOPt5FEZoqJZc0emKRiM0tSpUUaDFD2mOCDIyMOVTaLskrbQitEURsIjph4rRRkgM7N27dgNOak8Ff4z3bFkRsV9HAEoP2giz3MGCMywqXTPT6rGRvAigiJkEILWqFiWfNupxRbnP7KJeGX+YBehEmpvx1x7xXT9p6aPz0/9+Jexn1ooIGKlEQEQSkyeVhsLTGoSneiHDknyuW7xG3cTiFsG7tCZREZFVQzASonjGW1owMwm4ezwdWwxoucRhIQhbw/5Pbb+jptb/vH2H92Z6Fg0oyI59oUFPBBn7KMNR0LoZZAvLEwo5y4SFRMCFYcErduxgkUj3PQcuh0ZqoRbWVFbwuhTiAOCeC7DWaO3DZO21LrxohXF4JjgEw+6AEzi+puxKfwbn+ohHR6BeqJCgdnXaVottwKbOfzCy6SAN3zFUjePZwe/s3oiVvAcnGrXs9zqsu/cfnYJq+9UqRESNBaDBIqvUGOzMbw5WXmB1ZGWSsKqnsQQ4K2JXsJozIhrUo3R3goWnsSyOBvVIpNqsrMv81GTAulXOw7tD95zACkG6v2kU6REKVHMm2DwpUuIhMpoozk6nPRnIvK/izpCr3whzDpCE4vQATr1xWXxxxfxxo5nk0NbFDUx2JjBkRbb/g32yMcm4VNPACIZ6eKLBnc7qxHd30olEfyMh0GfaugyhnB4LyiuJlt/UywTD96cVPrChCKWxcDCk6J55jVPhcb6aWGjARFF8gMUjGJtwzEmNblp+lP/NLuSmUaTWKhVMqv8VozJTXxLmVWMgZcVrNzrrLJUXOQk6Z2ZkxIGJwCRAtW0n4mjPc44z0WM02iU3Wq1NFZASS2AAF/S9yywEzvV9rN0D5vXEc+Dwz8Z1VHGj6IoCTo62vc5ts6OCURY78V85LOwm1SzjEVXVrHoSpbrVLdU3eDPZSwtujotqWrpbRWLLW//zdglaH9CifTLxsemOO3StQ6bG4M2Yd0HZmcs38qym/tb8LNVIRDov6B4OxR3p+zpsr1bOM1VFCsa0umk06gR+R8b5yhnv8p6ylSmsrTHihRWvqmClY1lhV4r0pOqMpJKkyX7vKldmtYwOg2XeDaoXXq/Ofq9C6Y/V8f1dLD+171tcl8z2zwtstupuJtlV7PcC9+0Ktc65Q9bFXbkaxbXW0ZRt1knnUaTfF2f7HHtaUrZN6TCuTOOfiXpWArgA2zTX1wQv+/LM0q/GVv/FWPD/8N5PLE9qpOERJwG69LyX4arrxr/u1D6sMH685PmK5Vx1xrld6qtH9TL79RM/cVLtg8rrL9/4ZaPz8X2XDDozhqdbiyR9xDDEiS73XggedqRf779+fkJJxbd9uKdtx79ulK/jE1f4WfUl6VRooCQjA87Y/6YZXj3qPE3zxreKjNeKTde7TR++Evjh2eN7z0be+2QhccgXdbRQKcbT2rzqRBxFNSi5fIcyalrCqNPfrBAoOvFWFe7//fZvFuTjsc66fQpIWL8y7A52MaFAccnpFqUTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTjrppJNOOumkk0466aSTTmNAdqTNmzdnZ2fvQoKd48ePO51Or17m8sZQRkbG/fffv2/fPhp82Dl06JDD4Rjv+9Lp004wCcW0XLVqVSES7MCfgAZXrlwZHBPoh+uR5s+fP2+e3udoCILhAjSQcOTtGoI/Dx8+/MQTT9gnSyNdnT6J5ECCSbhhw4bMzMwcpG3bth08eBC+9yKF/CH85LHHHtuyZQvNZ5jkNJNXrFgBkDK2DzGZCDCTRmzNI4/Yc3O3ZWZuz8zMzc3dvXs3jBv9a7zvUadPIxGz19bV2XNygJ0BDUBryMrK2r59O+xs3Ljxx0eOACZcvnw5GBbgT1AoSKiAz3Xr1uXl5cGXsA+z+tKlS7quEUwwyHPmzIEdGDQY57Vr127avn3j1q2PP/44jDmNIehrAMg6LOg09kQmguePH7fn5RUUFOzYuXMnTMfdu+FjJ37m5OYyUfbMGTgMYEH7W4KIg4cObd6yZeXKlSAqrFmzBub5gQMHjh07Nohc8Wmm1atXAybce++9MKowtgU7duxEi81O3PLz8+E1wOeWrVtJ+hrv+9Xp00WkKRw6fBgm505k/9y8PLGBNAuwkJOff+LFF+HIzs5O+hUtXmfOnHnhxAkm6RYU5Iif5OTAJyxwJ06cgAP0ZU5LgJAAlTQmuTjU2gGHQcvLzweIyLTbNz/+OEDrI488Mt63rNOni7gxC40GAAhZdnu2xsYFMu0OQIn8/EM//CHZGfx+hbN67xNPwDSGM4gNvnwCvszL215QoAOClrKzswETGAjv20djle0/4DB2MJgACOsffXQV0njfsk6fLvIBQl4e6AvZuO/b7Hb4MgAQtFZxAJB84noxt/FXDCLs9jUHDmzDHR0TiGgAMxyOHYcO2cVQqzvZCAgwnoDDOiDoNC4kAMGuBQTxZShAAC3YjhELAgd8x6u/pbnNtGD0oK0+dGi8H3RCELlsHAgI2aEAARQHAARdQtBpvCg0IIhZGgoQQO7NyMiAT/hv/o4d2oN9sJCTwySH/Pwtu3bBgrh+377xftAJQWRohWE8OCQgPPaYDgg6jT0NAxBo5+DBg1w10OgXAXM7Nzf3u+vXw3fz779/vB90QpAOCDpNcBoGIFBMwvPPP79161YfDvirDDS9t2zZAuqwhHE44/2gE4J0QNBpgtMwAAGmNOkLD61ebc/N3b1nT47WYJ7D3Ax79u6Fz5dVlXm8n3KikA4IOk1wGgYgSOqKv3LlSjiAohco/EDEIezYsSMvL6+5uVkPT9JSBEZFHRB0Gg8aHiAQUajz2rVrQXeAOZyDgUzwzbZt2+6//374PIPxjTogCBJux8d1QJjk5A1FIY+E1RMkaqZow3LgcFzOziZWykYa9XtgAjxtly9T2CHMt3lOpxQeG44EEOinwPgbNm5cl529eufO9fv2bd6xYwsS/TfMR8vGUQp4rsv4RCMZOtJuiMQb8WJ0kNPpFJmGsDMqWd502stI/EJ4LfgTbgAmBkAo/HnjAIEuQU/E00/oHvBPuo0RTsKA59W+JroiXUK8TQdOzjBn40BX8XuJKtEbHBXOGgZRjJkfG/rfA7wFuEP60o5ZwwQZXkz2oW/gX3PmzInUwqZ99pCAsAL4FHeEeU9CfAAacp6PBBC0PAXzEOZ5hiaZl7hs8EfTDlrIR9MOHRxGuVThjBhc/QgS/TbgjRSeOqUdLvrvlSefhI2m3JCXCDmM9IK018pbsUI8CBy2Z8+egydOwGGP79gxWoAgHoHmlfaJgE499phXHVsxkhlII7H0ap9X+5qA7sNwa+1LhD/ur609hLFYw76KpH2J8Fzl5QHTIx6JEsfGgGDJKyoqampqamxshE/Yh28kzSwV90YGt2eeeQb+Ow855cknnzx0+LBdDVKV1OzXIS/qVVcc8exlZWXsHpqa4PsXT56kS9BVth07BleBSzx08ODqgwfFBCAAH+gSIwEEejWZ+/atfOSRH2zf/kB+/g/27t2Ql7dp06Z9+/apPBGaED8Yz9JNFhYWwoRpbm6uqakR85tuD77Pysr64Q9/6Id4Az/O6tWrCQdordyGlAVr1pkzJ+Gl4HDR+MM7opdy8NChcrgNVccffMQGHEOkZwsLX3Y4zhQVlT35JGM6vNUDeKFV3/8+TW/4fvv27SMHBLs/LV+58r5Vqx5++OGcnJxn4IRwgJplXfPMM3Ddl19+mRZTMYag2UVq8vX6C1elpaWnT5+GdwRz79jzz9Mj05xvRnqmsbGgtlZb9iHMCwFrz58/n34ibvoAspUdXyK9QcKZ+9GvTSUmaO2O6KHCJLuKunfccQdl+MLUglcJn3bM+d26bRstRnTbP/rRjw7/8Ie0nwdvFqiggMxrsJuJkXsPPvQQ3Hw4gyNkMDbNnn2W/P50D6CnZ+XksMw4ODOcv6BgZ0FBLv4J38N/6cy79+yhCjyDgPNIAAFGPufwYXgNGzZsYIrD9u0bcHDgT3g1hw8fHkhCILYl+bmysvKHOGi5ubnMQZGfrx23HEz5saMHE54CRniQcRNPCmfeuWvXvqee2rR585o1a7bhC8uhESsooC0fz89GLCuLXs3RDRvq9+yprKoafMT8CGDn8mVY/uwYxZ2F92kvKICHwYcoyMdrwaPZMYR7586dcC9wS4899pjdPiJA0EIBXHdbTs4q3M8loqxJ9Ulhg8eHqUvHw0A/jOGm8KTHjx8Pv3KFdk5m5+ZmYQENO+a85O3YAZeFt0bvLh//JEZYv349jAY8u12tCjX42Aq+g/e46fHH7XgedhUWX5+XQ3NDfYMiTxwOnp+Ssnrz5sFXopEQXei73/0uPNGuPXvgPkR6GrBJAb7co0ePCiaFHXhw+JedHcKO4Rv+kP+qoAB4YRu+mkHqZYl1Cl7WU089RZfgA56fD/dAQe++S+BVWHAgev0KMHmZBpbga6BrDQ8Q6H3dd999NMnzEI7orogT+Nt/6qmBLA9A+/fvpzJNcOfwAxrbAv+HYoFPNOD44HRX69avJ6E35IhRSQc4ENgc5gycMA/PDD9kM0k7YnR+dIsU4D3AT3ZgMnI4iA00B5QdXBFgtDcCo8Gjw4PgUOzQXAuukosTAx4B7m3Ltm1bRyYhwPcEKQzN7PZNOPGA5RjjIx4EPCkNrx2zKdn7gtmYmblr505Yx2GCAbCHqZvQLAJZq/Do0a3w0lXepJysgLGFK2bDmOMLhVsCqISBpdWTloOBLkRyzbJly+5dufJxu72AxhDvP2DOF4gJT1MoNxd+uf/554fUVYdHm3FiPJ6Ts2X79r379tELZagLo7prFwzFdqTHMVOV3jhNLbhDGGS2ZKifQLnIwvmIbFuBj/bsGWQlIrEH5E8YtxycyXBlOnk+rjjk9KdViV8Fv6FJRcfn4fyE26MXQSHHARcaHiCQkPPoo4/CVfbs3cvWQno0dQe+zEX+pesGv26YyTBDGBTAjMKcX1pQstVn4U8EoiIlBePYws1sz8rasHEjxU6L+xGq5XPPPw/ozTI34WXBQKkjlqMdMbiEeC/4mHRYHi3qO3fCGQjeB9GyvaoRiY0DLgo7xIMAI/i/GiZK4aSlmbNVZDgOCxDsWCcB/vX4li2wQOch7+fipQvEk4rphzt2NUSE7pBzUEGBkHUJXgbhBYGQBbt3F2BG/I7du+luaRkKeHE0G0VCN3wSzMJSuB7xnDJigi9EY84YCtcWwOc8VVykIQqcHmpZCfb64NHgMA0N8kTDoEf2719VWLgRFzu4MbifLHVSCX7Znp29ceNGMZ3smsEP2LLxBdEQ5WGc/y5cxAey+wHLbN26NQ9ZhkY+W53DIc+vDSGm4eLxADjNs7EgUvBbGB4gHMEySiC6+JKm1RvLxkm4E2v+wCepkwGP9v3vfx+AlOs7iBtZATzi/zh2fHA7RkrDw8Bvt+Tnww3sOn6cTkhoUFJWtgVmLCJAHuIh3Uz4I0bnJygjZhxIIRX2w4fXrMnFaxFfCJAJ+fZJSoHNjiIiyS0RAYJ4iczVm5lZgFIBv7S6IgzymGIkc1GNBSZl0wxhYXAOIhjfkpm5avNmWqZJXB/sxeEViV9yNKvh3Llz77nnnpAz365W4cPzsbHKU6e9feCrZGsm/E6Uz28QIGwuLARA2Lp3LwwaqAyUvkePmYOB+rCzAdd68Ub8Bl99BX7PgpSHyz0NV0iV52tf+xqBJCkg2QNMs2Dwyfa/B/gdrY8gVd6DVXpGBRBoH8QbuEOCtYB7ACjIw7cDhwlAsOOaC4oGfJ+DhhW7yiaBUwgZOeTQ7d6zB3ipYN8+eDVPPvecpKLBuYYGtlziEgwjxn8+AFMM9CfZ3OAkOeqdkA0t4O0QysEn6EQ5GglEy9qBN6+ZukzpAylCPHjYgAB/rlixgmnWGzdu27wZRp5UngEfNuDBg8aZjAygm2dr5mowI5AsBJ8gbQoZOPCiSFkDIHC2mgC7AxEMpgEMYMDYCkHFjtU5aFQzgx5NiHl2YWzUvD42G3NySCUH7WwYXD8I7cLiw7l798KSRIBAV6dHg5FhEL1rFwP8gBEI+jN4bhOGwFU2btpElme6KE3voqKiHeiZgqsEsIyYbH7Mqz2//z78vACVHQlxPkAMHiEgkBgQ/PbhSxihw4cPi18J/YiAFAYtW/uuxZ37a/r5lDylEQ5pUYOXAq9mF1rDtOIBvZdgkPEbE3UF8WNJ/xHbgahChtxgR+ScOXPgfa1Zs8ZOOVyoI4RYCAQvBDwpjkK2fwWJIQGBSUS7dsE3a9euZWrg7t0kNAZOg4DnDZp42suRUJSHFm9isWAtCZ4UhHwSh/IQDTgEBZ1QKPhcbxJPrV6dRAVmUgOlcs8eO2qUdoQgO/IvPB2Z4wjVQ9w2Mg4V+qML0TItBkHYf+BUox6cQNXIQR0GuNECgrhPGsyAFyHi+UO8I827gE+m+hUUbH78ca2JjGRU7anE8XwqwcqLoyF0VWIisuNpz0/7WSIn0W5fvHix5J9q5LuxGwwIZMfIQ7mInTOId7IRJWg/F+UHkkhz8HlpnHMwdYLZtPPyGCDs2iUAoam5mdCDL+4aZOY2HzQ6kWyZg4O/A0fM752qn2ThBLH82WefDVjIaPRA6N20aVM+6q2ZAW9fO4Zq1qe4mewcf+YNW0I4fvw4DCBofyAIseqLgAZajlMfJFtVTHaqdRpp9LJDPimOMJle4bSUiablI2F6evKppzLxtfoeULPD7WYUvo7eIv5l0FvOQgEMDtuyZcsmKqmhAoKgPC3GaqYHWc9oepDlNlud/GJRBpyki5Kx9IYAwhNPBAKCRgbI1twwDQJs8NTbYdKSxyHoFYg/yeYA8w04S2j39BQAksxJpBUJxIPDqKoDtXHjxkc3b358O1wti2Y+2TMDLsqWPLQ7Pfzww3b/VcD3Wm8wICxfvpzwPxtvhvOshhdIeSevJbV4gB2QITOxPjmVfISZAG8cHpZWEzhGUtX5g4cOZeJsEX4oO/kvCGTs9ky0ZMIP123cCGPL0AYkjQFGzK4+AgkJWkcJadMPrF6dQ3qfBn79ABzN4wAYZNbj6q3mbfrmg2bcBgIEOBYGk2Hg7t3ZGoQPYEz+okkbhetqFgWyq/hNQu28RYKhpvgN8bDkVoDTbkMzmlZr1s75XPTeAkLSPcOLIxenT5nSXDETbxJUFbjQ0qVLtfFUcM8kHgQsvtmqeYfeIE0PJgTAHzg9+PIKgwwqQy7Ij3tpxMYIEIK4xq7qRyTnwDRet27dNpz9QqoJ/mEWspsd0YxOQteFYYHxhOG1o+vcruIMHQNj8MQTTzy5bx98nnrppRdPnXr6wIEtW7eC3pRFBU6D7jMbL5QbytgyNoAg3MqkJNKM1V6lAJVZZgTAJeZRlQBG7r///oceeghGIwflrny0jYO4DmcDhVpA6GYsY04eN3peWhZhTOC0e2HEnnwSPk+eOgUbSH0gdcMiRTZev/VIffwdQuKy21euXEkPTtJC4fPPryKb7c6dWQFDTXyNt2FHZzFI+HAhclvQ/WtZOBwJAS5ai6EO8MbJNZMj1CJ/vub1WnNz4dVQQiVsMD3sGs71SSma9YIWXxhkbbo6TMvDGFBHwmeeWitPe8Pk1bXj2vQwEixt8OIo8ioLV7FgGCHxD44Rb5Bqz5JNgLRp7bsgiRHGcN++fQDRcJ+b1qx5dO3au5Ytu+vb3/72PfcwUyQ8GnqN4Xh4vzQ/xwgQgiQ9/qIRqR588MG77rrrm9/85ne+853HccqFxgTNrCNtVCzcpDXQygiiGk0hWv6ANWDkRRieNnCU+aEef5yE4eyg2SJUm+eee05rxhwbQKCQs7vvvpvxID5IwEiSp57e43HVd6BFLRgNeDpgzB9s2rRl+3YY5AA9C+YIzC4YaoBK8bDMMbdxI3BE8IjRn8yESHqxeDvqDucd/ELwCBnGD/3oRyS0+InEGqVgB54Qrg6aMqxWMIfh5rciAS8XhJQbBwYEh8iRRCgIEEvEJ81AAL6KigpvUGA2q5afmUmYYNeKZ/5M+sADD4ipCPgD8z8fjt+2jXTzAFU0H9FgE1JAZAjJUfesWMHq9AZNSJpX8NvVKLICp9ANwygRN/mOVPEKZhSwCR0WwOnM8/Lww/uffhqekWFadjaPlxjtvPvBJAT/mUPwtT0zkyJyxX3Cfw8cOJCjzhB70FsQpgbxE0ld8shTn4nXgdGA5xW2R5CXKFCfMlbgexAbYPEFZF6PFYpyApY8vFA2NmMKUIrHBhBglYc7f2TtWlgUKJgk+PZy0T4AA75//36HJtuIDKF0m089/fQBeOnbtgW8KWLzw88+uxllTuA7GHCaw/RGHGpqA40Y7ACuEs6QJJYrzP7qqxHjACBsVzuvUZg6PFc+uTMEDmg+SbvPxllN0ZiinQ2ubGvIeZQdMAgDAwJ5NIA94bkoviLL/zVlq+Y+upwddRyHJnWL9rdu374N2VM7e8V5SCODK9LIAJ06dYo9LEgayJLBiyBx7tNPPw2fMKQBgEA71HQmWAWgy8FfMCVAPKDJT4DgezT1YDIkrtiwgZ4FDlu0aBGdfw4Sadzw7C+99NK2oOkxWjS4DUGgAcyNbXjPZOaC5/rSl74UHx8PyyKwDNwkC3tGOSGAcegkJAB8b8UKeNKH775bXB3m6tHnnnvm4MFs9bXSxBDpG5TaBghA7AbSGoXpciEh4FZxg6n48ssv29FyTicZG0CgO38WeBlGTAsI6vH5GEkFepbQarzYwkB0ObSrbmv6b8CbIkB4yeEA/TQzi5FdjYnSBsWRVxGmLozYaiRAUZhdeRiw4Sf8I8HLhfGkuEeyVxCGEIIVBA1XttptYQPqsHY1tA/uHC5E54HLwWuiCDduhhpKQqBHKHjsMXguwYYBkxBm1+atWx+Fk6uVrrXjY8eYarjudrQ10ZTL9mc6EjzgMFC+4DuCU+FcCFjLslAXBmyHYSEH4qFjxzYXFtKWvX9/+g9+8CD6Jojrd2Lv4ACBxI7a3NGjRx1qUMdjmZlsTPwlBG5AQLfUnn37iCvhoQC4RPwtTQ+BgaMMBCoNbkPwaQrw3vfvh8/y8vKAsHCyAzPdFuO1tJNNnIesZHtycwtXrTpcUBAwyelYigAHAgah1syk1dJ/M1QqxMjGXLS/+c0ZdRWmKerVVDEaG0BwqGlx+9av95ng/FccHpsBWo/dDuIQ9ZyF/68rLSUDOy21gyQ82jVEIwbjD2wICyvsLF++XFKTT2m4aIehEJp//ZyVSHBL8O4yUfATOUHwsthChoXrA8xlgo+yBjHXoJ4COBNsuxjEqMhuW5IKtIZBMYw0D9EeS8f/vwULggfnxRdftGNykJ2mh4g90Ly4HMy5oMlDUc2g7DCf++7duQGGKUIh1R4CUFOIQTvUJ3gVvjj4pNB08hMF3DZ9AtRQ2tqp4mIW85OXR36lAB6xI9LCs7OIoN27t2zdSrzv8O+NGzzmo0uDA0IWBcXBC1q5cjumVAS7OWg1X7p0Kf0khBUCeScL2Arn237NGURaGc1e0TdcWCDFs999zz333X//GiTQW3O10qzmWiSWUzDAGAMCPEtdRwcc9dQzz2wTBg3/FSoH5fadu3eTsA1/wmInYSIknKG2tjbcVG6V30FCI9mJRozS02jE7rn33tU/+AGsvKDFwCIVGhBAeMPodOBQEnJo6MjC5rOL+gMCPRpJsMGB4iDUrV27FrTI3egsCBDhQgICnSTz4YfhPik/IuA+KYaHW6Ht9vT09JCDQ6MH0EoqWyBvqpZnYEbKQIHntSP6kRFAO3X5Iq7CEUsmBX0kP3/LE0/Qlrlr18Zdu7bB6KGXgRJ8QkhTOFaU8frD55+Hz63orQhph6QdcpYBRO/C5LV9KDDYVfwfdaNBAA1uVMxSjf/AhvvQIT7Q/cDMBEmMchmCPVzChRrSc6qNmWevCW2M+cKtbLcztXTLlrVIsNjBzWTjpPW7kEZCCEDRsQEE+MmiFStgCfjWffdlorM4O8iuRTdCo0SARm6RzUePPolIKJTxwd+adsQyMBOTWbxxUd6Iti/4/sENGzYiwXD9AAAhpMqAjLYFVlVsgQ0Esw4+d2IrK66J+z+1MGaShhsc5EM3BiehiIhwAIEszJQ1QwmMgWIqNoeFp4M5Ro64gYYFRq+oqChHBa5gxwoFnBMgkA+Rkh1ytV5OjQGBeTazsrZha2DgkeBtm6q+BS8Bdk2gAil3dtRrgNn94kOCpgdFleRipqodPctkoBOy6ODTYyQUjoQAGywHK/fs8WIlnJDnoag2wsngWUfeHMo4CwAEkbMDhz362GNwD3Y17oLQgF5NXj4n2glh/dZICOMFCHNVKxCwVY5qOQmWISkPiAIOYZ6zvg/43skLbx849UPyL8oBJ2D56dgylUIOBIxrR4wGzS+nIAgQnlZN1iSh0aP5PbVmhsO/Hn74YUkVDoNnAvk7fEbmoQCBZGMQ3YFTaFi0QESSFUuY2rr10UcftaMJdKD57Ouwoy5DAVen+MAjmKtFc4NnTPvfp/Z5qS8w+xx4o+CZAOnLrrrCyey2Z88eii3ZAjoantmXMZSjKpgCFtCmsRNDy3LQiAcSF8hdEwcQ9lRWSlgtKuR5KIQmfwC5lHr5ERQLeKdZfRkn/5njxxnGosMoD1OidiKn0LCE3gLQQAMIAX3GxwwQJKxpA6/+gQce2JqV5bNah5xvxMaoZO1Qg8PtWHFCsHzAIGvRwHHmzN69eyllAC7ERkxNfgx3xEIBgl1Du4Jlflph8/Ie/sEP7KH0BUmVIR2RVEyiy4nI7cAgEzs3b8LOI+vWkWo50HymmENaSnZqu3Rp3hrcP3X6FnPDz00s3pe6GIWz+X4eMLVQ46YQVtBQKEZlbV7eRjWgOlsMd5CokE0RaGqgO6VGAqqAeHPjav6HAwhwMwQI3oEB4a677oIXMZCEwP3dmjo2kjq9K06dss+bl4eTZAdO6UDeUV+NdrMHXWKCAILI1CvEOifEoVkhX7rmuejPnZgGC5erxKEO1h1ItG5sbATsZdFNNGLa2LyIRmxwQID70QKCelpiKLv/CGuJzCkZkVRd5vMfg3xCYBcSPOx6u/0ujM8ZpM4AjAzDoi1b7KEkHIoghUlCtbLp/tfv25eP8QkBKCRuOMwtkNTLwcmfQg2FLAmrDx2Cwdm4axeIQ6QfZfn/yg9VNHIyT/1G8elujatudCl8QKBZOpAou2zZMkCtfLII+fOyHSUE0nDtGgkBdugbQgM6xm9k1KHwg+5gztL8Oe6AIGm8h8dfeIHFJKjB2DmUaRg0ONrRJk0fTkt1k6gmgHacOXeo2fEDjZh2RoUYsfAkhJAqA3tqoW6HAgQuwAwLEHKCR0a9EDzsZrRiDQ4I5PzdiYkkPglHHYFsNeyZamXbKaNwzZoCbfhxUOyE767C3nLUCHOmF6jKC8UaARqsRsstBevmY0aGTy4K+bJUolkHCAODtnz58jCrFEZEoyUhrFixYs2aNXkDqwzZaqrXI4884lCjj+DPPDSviRT7bP9pIBJJgiU0u90fDSYMIEia0kagC2zC97hDzSD2Za4FzFXN48CZWT0ENKCRtEmnnTt3rh0zi0Mkn6pnyMWabGJaDjZigwNCqKe2hyEhjAQQAnlB8wk/2bp164r77htSZeBPESAhaN4aDAyFK9tFEAIlLvmPT7aYgRhiGtFGBax279kDMwSElm/fc4+WecUYr0ZTTBZqlyS6aHPEtNNDO1soGV8UixtdTAjfhnDfE09IAxsVAQ3gmAF9W2heI0A4cOAAMcsja9fateH0/tyRq1YVo7TBTIqVoZmDu34JuRMMECRtKjQmNLESkRibtAPnHlmkA8sFqJ8U+0Fl3h3ojiT8XIrR7D4/TtCIsX73ZKXUFNuhUCUy1gXLJ6GNipix4ntqcRVMmhaTNuRUFCpD+GXY+WtFo71dpG75Dwi8o43bt69DC+pDW7cONJ+pztV6tD3u0NoQ1FES9lK7alSkUmD2IJWBDNcsOjcrC7ZtIOKGt21HlwQbc5zACxcuDL5Pu4buvfdeitfNx9DTHCxO6HNKhoLHgdJ2Rk5huh03bdqUhhWYgwGBVoSDBw+SYusLVtTeP4ZgkYIAl4PjT58+TZWOcjHeOHBuUzA81szZsGHDgjVrnlq3jo7fu2cPAK9dG5g0IQFB+9LhkXdjMS5yo9txegunkt/rph2su8h83xiOsgMVEGBSlsBIcf5B85yqeDExbOtWnnS/fTt8Se/3mWeeobp/IbE6ABAc6HaksjAhvAxo5YBFLS0tLaRpi7xIME/WUMJR2ICQpRoVg2OZKA90S2bm47m5qwoLSXcISeT4IDNOsJfBrqo8O1HuIkmGXZcgTnOr3JyIQ82KOmZnb9m2bcOmTesefXToDRMVN23evD0z8/sPPjjQrdpVqyy8IJgJe/fuZXXFMcyS0tZ8Jscg6U7r3KdXNiiXR0BDSghUHfS7mzZJGve3lkhfBr6gFS0wq87OhU/hZQAWgyuSjuznrBdzG50sWbjPEkUffJCKhwvmYvv4TieyhCAuTYGXdOc70CsNrApzOweDgnwOVu0ObnDyzTt2ZGBDCgfmQ9HS5rPZatAgHxN7gQW2q/VXxXCRVDyI8DaI2zFgHhKbUFmhB3GqhwxM+ue77wa2/d4jj4QpIdDLBdSieNeASEUBCIwxyX2/f3/I0aabESldOWKt0ZyKCat5eT9Gt2NRURHV7KJyoIGwnMPrzHzve9+jwVwVIdGvBvcILFq0CNRtOpKFuT71FIzARszqzUcxQGvtzFbHMFuNzX7yyScdoXLlhk1DSAiU2pyfD5D7OLJzSECg7wu0ISX+L4K0nq3bt1O6Iuxvx4hu7r31f/tUhVibmUus7cWIPtjfvXevX6ToBAMEr9qFR3xDsEBH0jx5aPVqeLmZKAkEuN0F14D4CKydceQIYMJ9J0/uxtjaEOFwmJWThawN6hiFdosR82JlqvABgdrQrMbCC4EZ3Oqv8lFIWLtunQNTurSTwatmMUvYMYEMGtqhCwkI5D0BaRCWDFYcM1QAFSvehWxLCcshEZhaMMDq7BsoQer0gJ3teXmHMf2NCkHAVTMx8TlYVWFSHEbaE9Tcc8894aPBI488sllTHUU7RAHR6XQMJUfTDF+bm7s+Jwd+LuoQBsMyTwfAXLlRrIowRPqzyi9U298epK3Qs3znO99h+/4JR9p6PiyDJj9/C0IKTGyR8xWcpkohNDAxdqBQR0WoKMxeZJTbMcZ+oFyGiQAIXrV5UEBlHhAVYLkRt5SHWbfcX+DPNSxWbc+ezZmZD2P0/hoc/OyAcDhNBg1AAV0ahgg4a/78+dlqr7FDmDDrZ+/VnCEAEKhB3gM4LWmR9R2s7jCbp2r8IYlCPCOxNpxnw8MPU0BmgDtvIECA38Jtw7qzQy2/EyAFMSEBi0XvVkt5B89kMhXCGag+iT3ILEAhzaCEHsbkAppOmSB4YMWSABsLT27asQMump6ebh8g7iKYKHeVJOeBgknsqivKq0nSJ2Fy6+HDGbhwABesW7eOxL+AtCkhMpHWuWtgHSpSCifbkUw6sMQ8+NBDJP+I905mK7h5Cs8O5FAVZllRKZRwampqvCIrXJ1vAhBIzIALbcVS4ZTrJG5VuPiJtSeshNDY2AjP+0Ncg0KOOYwhyckOLKuyQ63pEQgIu3Zt2Lp1zbp1qzCnmB4iWL8WUlYeZjeLi4qVaOOmTStXrgxTQoCfwVReqQJvYLClupNH6c94S4Q8QiiCb44ePUrxtyGLyIVMbiJjYD7alLRPp2UB+hVPtTt0CEQRsk7DdeHnFOcJZ4CplR+QbYo7rFA2Rv1JmiQaO6oqoO2S9yEAbMmgIfq/wPFXrlxxaKrukzRIQ0ddBUVxJIea7Bbw9gF8nnvuORBOfvzjH4t3JPo5Smh+eaakZD82l8kNzndQ5/kujGSgZdo+enbFcAqk0DcFWDceMIFqgIj7/z5278pDV0vIOl1UJDYfDVxaQKDn8gMEtaYcSQjwHUgIlHwqKomRPj5QCaBxBAS6CIVVs/xBHEmQ+r74xS/SCWFwao8cOZKdDRvFstqxTtEOrYSgjgOFa4IuuX7DBmAZGHCQAexqiJf2BeVpogLsmMJMvAn8QkPNKpCr0cvB5p1AQNAMVwgzghoWQoVK4OX9WMU9+ITJ+cCqVauxz5rf5fzF3ZCAQJaxPMxgylOrVQdIJnmoRlFmIg24mITEgE8//TRBSp5/YoLYpywkQjLiRBoi9nco5yyVj4azbdy4kcpriytmaxqhSpp1f8WKFd974IEfYCTnCy+88POf/1wcQwbPe++9F2QhuMk1a9YceOYZgAXxIAAL8NZgKC5cuMAtPxpHpHbuCZVhrAHBHxOoTJwdRYLTp0/v379/ldpsIk9UtAiaA8Tjz+GiU15eDiMAvLAdXYd5GnePmKsUewBswrVaJBgfGIFHH32U4rt4TI52wow3IFCCKtbK3UUGakrQg5tfsmTJd7/7XXrFDixmQnf02GOPbQtQGdShyMERAMFgGw4UCYfZWL0zgFOyNQlHRBnqJZ599lm6fz5iQSw2CCCwF4QvIhhD6ACW1ABSB1qSnzl4kLCdlapAq4jWaxaQAx4SEMhFm7djx8NbtpCNPfgxs1WlHj5grh45evT0yy/D+g7D8tijj/KVgvxcQfOQ/GVwWiqhRmcVWgPMZF4GOXgCa4r7AZyXlJTA8rR27doHH3yQfg5z8v+3d229UR1JeH4PPKJ9QULaN14s5WUDQiaYW1g2sTdcYhvDnDk947ksAzYwHoyNbTARl4C0SZTwAgYpL0j7wAP/aLa6vlPlPufMzcbGklOfrIlj5pzurq6uvlV9RY8jygB3bfAnrHGAHlJwEmANHCc6dCIEH+5KZqHdpob8T4yA2rr5u3dpy1DN7KHUIHC0F1R9yL3MDhoE7Rqsb8HEC5JAXISFiV3CmuMujEZNRxJEksSoRJwh1MMukE9QjXlyuTt3/I3n9DQVhL1qhRU7KU4ULKzqHhoE0gqwxyBwNdlnsbWscJIv6rVWozHn3BzrLWzpbHiNGLSCpFoslUja9M4W39+Ba7fRgztORxAJlirgHQbqdb945n+qBPyEmeb3Mgg3ikXPescukfmTB8frOiQ4q/ERE1QCHIybAs+t23sZBCe4zg4nXbcb2ljk+nTM5ICrqypf4iMQxuUeDBf/d5jWI8OmRcqJjUZoAMOjLWTGwX6EupLWdUrQAZpcUNrWJAETVWNufh45DdfX1zsc7UWg5fQ0Z4FxWHLz+YDneahW6T3t+/dX1tbqnLKQ6ka7xLKeOedEQV3TYt68QreY0503COlxnfzC23xwpCNJDdyrNr8WTAfoBWjLOkeCoxdILKQDJCgy1A0NsA0+I54HYRZKzG+jKZBqgUdZ9op2rw0CGQFcKIPgwoHxGCk/eMdUY7ZAJF2tcE7GarjxCaWNI6MbN2h+oXe+efOG3r+yulrOUCPq9McXDTXOGYQx4n1iJRNrSmLphnc1CAVlXb5wIfGKzK8Q+LMkiT6h2Anpqz8b2pxBNj1y+64QtJs8ZfSlS8huFlZYX1KSZQA8MGfZk9DxJmXzxCO3tKhzggMq69atW500vV5yktBuF5V7LSclf5UDnUR6Jr6Lh0ImWbR4UaQjAs6HVDlqDi0hCuxT/Yop+kmfoQmaXAB2rMbL74ipsyt8/aSU7BlrgJmUhskSD15Yj103CEE1ssFcksnRBakDU0ZMvoblQTk9Qg8dOkSfZGMda76niOmmbDCziWHnz5rk10tqleO8+lyDkD4e3KpB0OBuh9lchFaSpIcJJ4a8AWcyqeleutuP4mp1mlfjpL2fPn2CY5KfDXleTrkiyFOzclCAgEdITK/eYicslOnm9zII9Dk6OuqTSMItpGsvMzS9WirPEf+CxUk5fU7YxyAgdoMGUTJfZ3I/BaXHkjEQM+ymQ3i5HKfr6a0lz8W0cx9nImscBegQcGITSAEQJBJn0q8EjcXI1QWDJtzZPMZ0iQGBefRkVlevFiQzYLPZTAyCTKNYWieWhFPHqg5UtftCa8AzMmiLsO+mveqXMAgqeTEILsxlk/nJ6AlPjlVZ7ahOaqGYgMi+RXJInrIqwe9KIe6C5HclIcNPjevAIOD6SdVb31nuEeMPd9yMQYBJz4bJBAYBpFtKfotLZyTlyXw/lsR8WtU4nT1Tv1xC+oBG43IUzfD5AzahqBXmqSyhsaoK/ss2FqfTpeDvtbzE3KZBaOUofJG56cfJSayEQe2Vf7yXPoB+n2rrD/DZS1NXKb0MQiHIdDbLi0N0fRQWFMgQv8bpiNH8F6p81kGd8iMfzyJPXGYU4HQRPgBVJFzu/U7NO+yCdLcpSyuzoe+FiYkr4+MFuUahhTG8CzICjNPqoUYvo+ElXjz4FMyl0g8cCd4rn+wuGQRYV79057AsOId0NwXpv8xqsuNAabVQJ5ewiw8eOOGoj3I3YqrM4fWTk+TsRdbPcM7FDwxCJ8+pyCYhtUII+i6/Qgjj5vJXnLHkZrqfDgtS8jFs27NldZUYXsjAXOMd9pjP1jsmCUk1tQjundjXd7fPQbRsIjH+DrZ4/gwq5C/lHxw+LOQMAvjZHKcsr0gGmeQ4LhBCvjlJLjMOypuamqK2NCQD10CDUJDreGp7cX4e3la10EWtlwnKCFOyr0Ke3hpMTqrLUH4UdCTYvME3O7Oym4vYkXjIQrXomH0bbjabVHdaCU9yzI5mfCYLGecYnrPq0e3vkcRAeRZNSYW2g8eJAwyCdC5VgDAxMVESygKXCbEXPYwlj97mZrl3/AWOXjc2NpDZ3C+BeH+RxON0fTmL0TvY1Ouga0sUXlASt2cMzyzrshN3jnT0Osm5QQ2vVuckaB1PIbIe/tg+t1rwFFpKf/RkXHxfhlB3GAd/3S99FxJBZCaROF0N7CCQWgj2B+MiFJc3ocvLl9mygQ4llr1bSouCN+MuoMYphJIpXiYjfMe78VQqc9KKsI/0qOrixYu4y6sFKZiT4vQzbAh/k3Rm5vr1iIsoM52abqBAmHP5ypWuI5Ra/dWff9LK/tzqqvfHRqeLWchYvHyTsajGjRjhknPT7NzVyxqokqD7bszPI5lOMqnJNWWvpUjyd4kjgxXC9o0aDj4r6P/IyIhjCjVPP8tBviFBSq8WoYOxOcIk+0++qTya5oTfKQwV/sycirSljbgrlfBtU6t5x4rNFLauJNJILndcjyUNNJx6nH4H+6hnGmQ9RzXw5rJQzGEBSQKhVR/uXOh/aVTWJbsfdu7gwO/k8zLQC7mz6nw1qD81TCKVyr8vXXJBggxw7zhZV2SeShpbqdxkr2w89UrCutEuBNWCPlG5jBL9Qbt4M6jseQRaMOO+FddwGXFhFpvh8/9ZGemQTNgX5ZCUjxcANE9VeRNND3iJaVt410NfQORp3v2PNg4Q3eP1dS9wHptojvYRCkUzcQNVZDN78uTJ02fOXCsW68x/1Qi6iX7obfSvvQbpATLmfAfn7YZjJwHufUheGeHCJif6I3Wr8Ykcveo8+8nkmSXy8AdB7Lx9lQnVE09L9CCTpKV6MND8quRmxakmZiU/lXiP2lkdAqqHS0tLOPXFStIrYUY9pFFhi3Dj//r16+WPH+E/NmBsbwtDhj/T2g8rFu+Kr5k9JeI+sW1M34f2Pnr0qL81KAQaDjNIApyFJx5mPd0O8PDxZ1Ms5KdPn3748KHDmSDK7MPsJCqtKjHF79+/zziFqr31MVNgPJZL9gonPKWv/ePYsbDCWI+BxM+Pbp7r4SgCbbkRTs1p911q/n9/+YVqiMxrFUlE6Jzk/pAppgyd0ag6PibqRa6rZf36228vXrzwOZJYMtpflaA79HB1YWEB/mB+McZxIk741cu12rUoOsWe513ZNvAq7O/i339Xbdf6qxpD+ZFpkazHS0l5TKMet6soFJ9l1u0CnzB3H6es8Ais+PWPP56/fEkaqHe4uirKCDN5ebmMLEtwfhuohyGUvYQ2SseYBAx9XU6v57VcjTGPYhCeeZ9GknOr1aJZ6d27d3l5QpgfP37EOhbGLNWEoBOTPuX0Qxc49QkUmzaQhb7s3NvG8AQp58+fpyb867vvzpw9i4SqNDyv8bW3d+ycmChHES1mzqyvx+yHOQx7cKhvNIjAIaOeujigc+y6sNBuLy0vP+KpH/KkauPUHbX1s0+jgQyYvcqKeZOITZB2brvdjjkXUs+n2LEwfKohCwacc+afQiWRly1mh38sNkhiNIqRdwmk4npShyQUA+XmAh/4UGL09+koIoFc5/Bn7+7Saj18+BCX4B2OOVpdW4uCg0Gq/8PV1Z9fvYo412EfqAaeXl8vC1/BFNtJKvEauz1c4IBrWAO0Ai79Bw8epLFFyjODxbMsiclQDLw9DxtLRp4mmpWVFWogyQoaEgk8VQIv8BxTsNLihCx5OOP0LyhTqJMtPy107927R/YZpLLeT4zdHpB07xorJ30HnC1Ii3P27NnR0VEXeFHmhamgryFZZIFDtpELz6sHqwqpPfQ/ZubnI0eOFIRuouubdwTDGwRkj0K0UYFpXhYXF++1WvTzoN1++dNPjvuowMqzpTrjy9p3x48fHxsbo30rFUGzG3XHxsZGJx0P0knn9QNeDUpqk/maon9tuz7Vv6zEjItvP/1OK72fGTROqZfRrmfPnkHBoIFYGg2Um0pAJXbixImx06fJUE9OTVEHURHqtxxG0HSV2LA9xQbh71Li2uPHi0tLt27fnpqeps/WwsKypNLoGtQDe5jB4ELTwwctooF/9OjRU6dOkYZgpFDpz54/pyUZ3ICdZBj//IGDl+CdNB5p1KPvqNBiFFEFinx5AU8DLRqRTQOps0M3sMOHD5OVIyNAMxpESi16+/YtaciWxPX52AKFGtOw00IIlj+UlXYWCQLhctuoiTY8zJ2tL8/nM8po1zwjv/XOFEFfwDfDQdE/PUr+qWHKAiCornYsb9Y+X2JfB8leh5EY4oOGHzX6rEYBaIlqnfINOXDgAA1hZOTcdnvzLQpfFVpIqtJOee51ghgovDPfZbD8yDm41aLxVOblmU7ctnpsD9smWVVZDRxQ24AKajdevieA6BCdtxvtIolh0K2urn4BiaH3qawvVmIGqiEQZhgtuHvAEk4XhxqVvyPl4lUq0i/Tojx2inXZYDDsA+wU67LBYNgH2JJB6JO5yWAw7AP8hw1CZQiDMMYrhK/NIBgM+xfx3bsXlpejRqM/DTsMQsEMgsGwrzHNbvMzzFrmo/gljDTx62OeyVk2CH9rNv194i6npzcYDHsIXKD4xDRkEJrNCjKtS7Jyx4FyNd4yHPjmG/9AjobdYDDsG8AgnPv22ymm7k8FoXAcis8LVqmMnjy51zU1GAy7DvWSqt+8ubK2FjEvB4II6Bf6vOxc4/btc5xNwGAw/BWgeRa+Hx+fYUSlkqfFaDbzXDoGg2F/g6zB3NxcQZLhqvs0EimaQTAY/oKgvcPIyMg0w3F+qMXFxSdPnpg1MBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMW8L/AUtJlu4NCmVuZHN0cmVhbQplbmRvYmoKNDcgMCBvYmoKPDwKL0Y3IDU4IDAgUgo+PgplbmRvYmoKNDggMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgMAo+PgplbmRvYmoKNDkgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgMQo+PgplbmRvYmoKNTAgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgMgo+PgplbmRvYmoKNTEgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgMwo+PgplbmRvYmoKNTIgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgNAo+PgplbmRvYmoKNTMgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgNQo+PgplbmRvYmoKNTQgMCBvYmoKPDwKL1R5cGUgL01DUgovUGcgNyAwIFIKL01DSUQgNgo+PgplbmRvYmoKNTUgMCBvYmoKPDwKL2NhIDEKL0JNIC9Ob3JtYWwKPj4KZW5kb2JqCjU2IDAgb2JqCjw8Ci9DQSAxCi9jYSAxCi9MQyAwCi9MSiAwCi9MVyAxCi9NTCA0Ci9TQSB0cnVlCi9CTSAvTm9ybWFsCj4+CmVuZG9iago1NyAwIG9iago8PAovTGVuZ3RoIDEyMjAwCi9UeXBlIC9YT2JqZWN0Ci9TdWJ0eXBlIC9JbWFnZQovV2lkdGggMzQ3Ci9IZWlnaHQgMjMzCi9Db2xvclNwYWNlIC9EZXZpY2VHcmF5Ci9CaXRzUGVyQ29tcG9uZW50IDgKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtDQp4nO1dB3wUxfef3b27NBJChxBKQBBpCkFAEQvyExUQgZ+CiqiIPyuKgAUbSLUgCIpgQ7GAIKIiKCgiWCgqTRAQCL1JCyWX3G2b/7yZ2bvdvZJLLgH+YR98ILnb3Zn57ps377158x5CDjnkkEMOOeSQQw455JBDDjnkkEMOOXTOkyC63G63x+Mh/7rEs92bMkSSx2X+VXB7pLPVlTJGgGN6wzYdu/To3r1Lh0vrJ/HPHIqfmo5euCFn3+FjJ06cOPbvvu3r5j5dHzlSoSRo4EkNW0k7NBhJwtnu1/93ElBLjFVV1YJEfsO+25D7bHft/ztJaCD2Y93Ms7qu+/AU5Cr8ZoeikYSGYBnQNBHWZfw2cjkSIT6S0IMArYVrAdpJDrTxkoTuDQOtgsc5AiFeElEfrITh2jHOMhYviejWsNAOd6CNl0TULQy0Cn4Wec521/6/k4iux2oYaIc4XBsviagD1ojyZYFWU/FAB9p4SUDtAFpsh/ZhB9p4SUCtsaZjG9dq2v8cWRsvCeiScNAq/Rxo4yURNQFJa4NW993pQBsvCaiBEgKtquf1dmRtvCSgugW6ZocWn+zhQBsvCahmXhhoj3d1oI2XBFT1OA6F9sj1DrTxkoAqHbaZYwDtwQ4OtPGSgNL2hYF2X/vzBVpRKr0d1nI7wkC7q+15Ai3gKpVWaEDKP2Ggzck+X1zhDa7MRMhdOuAmb7B5FQHarc3OC2hFlPm7b/nTdREqlYChpNUh0Cp4c6PzAloPeo7wEV77fC3yc8nL3KTlYaDdUL8MQSuIoiRJohi6j+pCc7Hf78d4w7AMIhZKeqM1cVkYaNfVKivQCuYwILs2IKFfQKmXFYy3j6yBSjocK2GRbUsXoF1dowxF1CVmNLioaeOGtVIQKJsmIuy8Dvx+WFc0jPc+X9X2fbzkmRcG2lWVyga0Aqrx7NKdJ7zefK/31J5fX2lm+VYUE7eCegTgAsL7h1ZDJYmu+/Mw0C4vXyagFVHd37GZ8ruZxyUKydup5gnDp+Due6ImMHMJNe/6xBr0pQO0vySX2PPPJkmoD85XCGiUNNWHfw0HrY5pOBbWVB3vHlynpIwIwTXNFk8H0C71lAloRXQ3lnXMYtrI0DT8i3mpCnAtHTVcocGC9mT9kjEiBOktG9fCXvmPRMLH/+yzThLqjWWNsSX8VfH3NmhzgqYoA1clqtimZxuQNShuFUkQJ4RAK+NFZSN0WUI9KbTGyDT8rVUgsGXMNHTys+LDeOOLF8RvoQnC2DBc+03ZCFR0oa4WaFX8lVlfF0XXRqsDhbG3TDh381gQC3FNXRENDwPtnLICbSesWKD93AwtWU9Wh0ZhUHBljHPGEPM3nskromfCQPtZ2TDGJHStplqg/cQyMAkts4dlcamgKyoxIkaDhVZscEU0OAy0H5UNd62ErvapqhnaaRZoXehzrOm2HW0DXPJO8MFh1UFmFq91AQ0IgVbF75UVaNvnaRZorWc03GgEqFtwQThwNQLugWczimtEiOg+qzVGufatsgLt5bm6BdoJNoHQwgvrHEMyVORSI2L/0NrFMyJEdFcYrp1QNoJnJNTmSNCvB9C+bF1EJDTKjzW/GgZcysoEXIVYaEOJtuAqMrgiug2g1a3QvlJWoG110Arti7bpKKC+v8hY9TFwrdiajIhtz11YdCNCAK3axrUaHlVWoL1kjxXaZ+2qj4jS757nw1o0cMGI2PRCo6IaEQK6Ccxs3Qrti2UF2mY5QcaBYLbBdmgFwsXluk33GuDa0WXgygTcLaMaFs2IgIj7UGifOReghY2X+J4goYs2W6F9JFRhFwm4ie0mnsDhFzSThbZ9bFZRjAiIuFfs0GpDzgVoKcVlakqowV9maDXtvnC2ELxB6aJRhzC5gLpu7aGbzIggatrulzJjNyIg4t4Ora48evahFVDl+fNvRHGBK6F6qy3Qqn3Cm5kCTI8aT+0gV6m6HglcMCIOjM2I1YgQ0KV2aDXd/+DZh9aNniTjXHp9Qhy2pojqrLRCe2tECx7eYIUHNxL0VC0iuGBEHBwR404ERNyrmg3agnvPPrQu9AkVfos7lQvdio2RRFTz56DWDsZQt2jOEdJI4p2/5RGVQNVDpK4BLlnsDr1Qm0mR6CSgpqodWpzX5+xDK6EF5CWrRMB9e2MaQbo44Iqo+hIrtDdE3/ODNar7vGNkRVPCcq5hROx/nhgRUiF6roAuLFBVG7Snep0L0P4IkS26ImP85U3liJZUdHBFVHmhBVr5msK2U8Houv6jAxj75VBwjW0eoi3sfJboue6o4Iqo3mnNDm3uzecKtDA2PwH3i1uSkegpqswVUYVvTNDqWG5X6E61AHi1m5CDsU/WQxXdoBGR82KT6BaagGqbPBgc2mOdz757xoCWcomC5a9uT0BSEXslotQvzFyL/a1iCAIQ3KSZi4dvjMq5AO720Y2jhTMJKPPfEGiPdDx3oOVTUMP5i+4gYBXJjhdQ8mcBaOEt+ZvHpMsJLtJM/QF/EASVSJyrU859pWHkkBABZey1BH0BtIeuPBeg/YFtrnBwCQN5l/bmKmiMJKCET7DPJBD8F8WoJgvgRax++8+Q80jnm+1W3V9ny8CeMYmRwBVQjR0h0B44o0HhgigA2bRXCX3DD2bzSQlO64JfewhFMCIEwfVBkGvJH18RMphBuEDajYvUoBVhA5cbEW9GwkpA1baEQLu3xZndG4MgTfurd6GPuUAIcAko7MqvXWI3IsgLe9cELdEqaxfFuAO7wHXFnDxuAIdyLigweH+DCPJbQFXWh0C7u+kZhNZ9zbB3Pvn04zceudAys9zoRewPDIiDSwOzfro+hfFUoUSwmRzwRsPATlYrYrQctJL94XGIq9HsKxoTuVrB/RHUKWKq/2lx2EIPdjY8Y9F0AupvtHz4MkuQAGpxiqwiAWcU31Kh5tD3MRoRBNqJFq49VrHIgYgwQRpP2kPEghwKLt03eD2iplpxeQi0OVlnDlppKs6TCfl9stXfJqD7thEBYBoQl3jggpp/c/kYnKdkyRsHrB/g2kNpxYjxBKsra+Q62G/QQrfQILYhkgKW/pNlcwx6sLXmmYNWeIk0T9vNt2UKEFDDF9YS5dJvB5caEV/3IuAWZkRIaAz2BaDV8d7kYnUS9OmMQUsJuD4N2wSughdH9OGWX2iDVsVbqpxBaEeCegSUf5dtZpEB1XmU6D9yKLg+BWsL7irUQnNBbJCJa3ckFLObEulZ5b5faxjbxa2Cf3VHEvypX4dw7ab0M5ZlVUTPsYVGx3m97GqMiwyoWp8FVjlnGBEqln+42xXdiHBBbFAAWsIzxR8WcG6FrpP3WiOZ4KHLEyK5GFPm2HbLVbwh5YyF14roSQPa02EyBYCcS79hFvM3YT2oLoARoeGCn/sK0XynLvREYHsKeOaveLrqIS+67Z/YaroStJZ7InFtkslg4dCuTzhj4bUCetSA9uSN4ZRv8IsmtX7fS13UXBzw9QyMCN/K26TIRoQLDbBAuzqejiLUcd5hv10iROPaxGkh0P4hnkFoHzCgzY3guYCOi43fOAZdM4LjDXCJJiavuNkTaU/FTZ6umATCiuL2kjzddcMiL4fTKmt/kSKh5ZkaAu2KOCOXhdh3BUTUz4D2WOTj7PC4WqP3Y7a7YgKXGRG/dY1gRLjRvYEoUIB2abGGA6pt+Vt+05hVFqIhfBspYlZwTQyBdln8QeGxBklBvkwO7ZE2UWxA4JtqT29WIDyTAYuN/1TCustuKh9OLLjRnRZoFxVdraVsktFvDaYBYOH02ukRnQjSqyHQLo4zcjnr1pqxnrSAfJky3RfBh6N7LgTyZflHfs0nmm4wGEMPGBHf96wQqjG6Ue/Azh9A+01RoRVg1+GCx/+G/bLwrlsNj45kjQnCqBBov43LhSAI4/AfD9WOLY5HQDczY5CYSo0L0aZFMoSkvl+dIosXDd3kk5OGtxAj4ru2Ife70H9VNXDGBqLdi7SG0B2Hi4cTo9Cv2N0zhg9BPdk7IrTohRBov4rPO1PuS/KY1QNjOq4tohvZGq7hgxcUaqiI5IHumz44BuDqAecCs9D8+LcQLciFuvs1E7Qzi+T4AmCvmLDb3JgNWKzm4+3pkRQEAT0VAu3s+Ny1qfOxl3DRmqcyC7fzRXStAe3+2jHYgCJ5oNRuwiFDLJikgvrvdfZ+u1EXr26C9oPYrUwqCjp9cIABGy4uQaebH8d6RwltGBQC7SdxQruAoAXHtdcPrUz00qjiTURXMvVIw/tiO3MN4KKLR+4hUkA1GImOVJMH2KemG91wEpugnRoztLB43TTnCIiC0C0cY5dBxXmTW0SeCCLRqn2WAFuNvNy4BAKB1s89nH8PLR/dcS2gtnQNJ63uqRzr7gossvUGb8U0HWQAXBUPC7GUUcdjZmhfjxFa8IHfuuQUvL0Qjg0oJhr2TmkmRemziO7HQRMD7lPJy42La1P4NirVVrY+mRotSEpA2WwN1/DO8jFLQrrbU63/xiA/UQ3z1VBor/7XcPQDtC/HAi10NuXu1X62LxYWWOo1Pj31oui7SSK6h3owglUZVPxGfNC63sT5GhsO/JczKD3yroCAmrPzRyreXqQz13T7seturJmgnRgKbbv9ZmhHFB6GAF2o9MgWHDb2ywRs7jsXokK26UR0OwHCdDfpYnxHGUTUgSzhKtsgoBtaOQOqRjIiRNRIpuqRhje5inCMiLoW2r+TaxIIMN9DoG2z2wztM4VAS99X5lO7KFOEBRZYj6wih99uggpNnSCiS1UiqwkRDZD8S5Yf7Y74uFZAjaZuD24QgBdl68O1whsRIqrv1Ri0f9k3dSMTOMQqdp2Pmf82AO1LdpZwoewcM7SDo0IrEhkuNh1xMLhDHkaPVYig2D+1aWwxzOITp7CZTr4d+xgjPBGhhqOIaajwHQ84abFxQFY4I0JAdVj0job/iHWjFgIwMvsuAUPBWMaYIHs2lGubbzFD+0gUaMGr7mo/4UhArQu1vFjQzK43WsQeeX/Z2HkrNuTs2rUrZ+PKb1+7MbabohE45WsP/DkILnRp/ZAwx7UFVPsIhzZGpxD4x+sPXAGbD4Hhsw1Jb/9Qrm28MQitjO+LCC0A6+46jQiYsGcaTMCOu6QIh3HguioXZrdt26ZVo2piydTIcxH+qdF3obH7gnU4abFuaB17kJSAMtlCo+GlsTQMz200fI0BrMlk8OH9re1cK6EGa41NVeDavpFCBkinPL3nnDSAteJqWF5k7u19pWXRTjm5TQap6CmhwBm643HTbI3vvrDj2n89n2HVxARUcxeFVsU/FK5Ng4xtPukfy0avseeA8fSQB0go6w8ztLeGXdJBT06+d4mXWMuRgYVNjf0jmzJxVBQSJBcUJXW5SjIxJoCbfNX7hF2ZY08H82nTsErm8QmoxlYO7YLCuBZEcevpBwDYIMfSgYM2tGNA+ZAlW0KZKwJFv0gz3cJAC+pW2qNr/Ny6s8pY3Yj3Iuva4RENUWHG5ZkjUJHczSeepE5k6DRoZB+lmgYooGobuUD4MjrXwj1XzTnFogcNtxfzhpOH7ny8chhlSELVlxFoOan4hhBoYQpVfmYLhDwZTnYzsPQ32sCR4Vm2hGxnm6gKXnvU3kBX/fiw2fknoKrskIyGZ0fzhIPp2WmhbLaQuP4Ov297PC2sySehKsuwD14rgYfIjOvCcG2tMXup2aoHNbkgsDoH9ugoojwWe5ubhgsW895CHkyU0KGbfXRji8xduZMF2sosekeNkoQBpFSF7suwOWiQS0BwnvzzRGoE9Z28kQ+oasYyJvnq2S4TUIVxxwNTKoway3JOHBidiYqZW00QJa5Skp9Kg+fB89l3F6R1AAOiq4k9BVRxGfU4QBKG8Ge6ILgr8+7fuSJv7IyxpYWobX8NTo+8X05UhBm7juYTc8h3Yt/v/w15trSYLQQBN48NWPDf7YSjYsVkWLp0CYmpqalJ8FO8xzdDCI5qJrR+eg/lObLidLFAm/49hzZsEgYaoH3BgDXsUIwlyoOqHOueqMg8YREIfGRd+g98YvCDt17qsjOeC3VTQiMQTQ2Qte2fV4iMLe5+Ful7g+sHvPTujBkz3n91cOdGqGQjQKkyfv27p3iHNdnb0aIKpM3n0IZ4APixgubD/mbHCozVm0EM4/59ULXCDCNTdE1IoI0bva6r4YBlDRDVZsOI+nGktiVW/MsbTQ/eNrl5CWILjuvEnl+c4pFoWPfi/dkWaMt9waF9OQRaALatcRjGyrIw7hUP14hl3C4Po1DWc6GvYCaFAxaDr+DvYXGl+xLQ5VvhQT5OZJLt6FZSZ5sA2OR7fvByK4fN4ZEey3KZPINCq4WkgYAX0OH9fdZTRnpg3CvvrxFnmjOB5q8NK2RV0s9NQxrElQFQQHX3YK+sGc/XNbkAH7q8RPgWtrfTHlthKOMsYmB2pySryEuajgt0jYD+fAjXdpl71HrwkK308Jjf76oa8348GEKS5ApZnwVRWIOtJz1ZA+Cr2/ZYVrx5K4WJmMbbGJ5w0FHAMIpbT6DK+HMbVboEY7p7gfGX1yTYtRj3ZMzcxY9ZoRUECPRS7Gos9a6vu7NCrAd3hQg/w6+i629TdFxAjyUd3T6gVvzreY1T4OQ3TTcQe0c7xXsEB0ZR26SMQ3+1r6+UQnbKBNTkq51HTxzd+pY1GN6FuuoBR38wiA7ez5o7yhUh+5bY8uHxH8/5bMozXVLt3whJ2wLQBoAl/+x8tEpxM1CZno66B5Lj8ncHEjF0A69oBLkbmkzIDSjj4JjPn98eRdC7PVWrV7JziBu9hm2BHPR8k7q2b2LsaqaE2i4NjG3Pg9bbzElWMQ/PI2y2nSjKxc6bZm56VJjqz378SZwSIaHNlHxqO1HXBlkTTizoGGlrl9gEcGzMJt1daKZxuIkzFDi3/H/2SywCP7lQu4NENMMGigoi+wXLOwlCa5h25JctTxayBx0rCegzcI7YsJXx97HvroYhqcPHCvUlGm7OY192jHxkRrL9b/y2gC8xfNx+FfuX9/MURQKKqNw8nBcYGGHJlpGyLsObI3Ni7dCKxTcQ7PRTSB1dgHZlLJEsEUhENwc84ESd1fGRWddHPeCe2aFbj67tbOnJJbSYiyoGrIyVJf2TqGUXM0noqtOyGtitJrPoLcv5KUPW6lwtXDGkaryZVc20OkwuTBn/1SiOvQbPO/iUAayKc6f9h20ORKJef5wgFx+Z19oyUfjxZ7a2QFmKhXeXQ0LRzu1DsXu/aftMw6ssvmLRtYEdVqWm3fLHYrJAYqf19gyu1B2/OZ4iOCzEQ6dz2Pt2BwFJ0U62t1KoNCQLvyUuLXhonzLUd33Siz5uEY0wBV0BtHssB8ck9CtWuQXyy30xB67GSuvDcC2BNh5jlwUm6XCMY/IVLrrxFJEENBKfVsGZKvssaVkMrqXBs9/2qFCMmUre6SRz0BX54YhF0EGhFhWD+rLqzurFSZsYncJz7aZ4TulSaEl//W9nuwpdFBK+YNsARKEcbYV2EfQMmOpH0EiLkXxGQAnvmbmWQHvUIujc6BmaOuiP26sUO3VQFFoVdhlbG+m4dCyUOh8XED32gxahBkIoJS3mm1cqnmRWpl1oNjPFlnaJ9bSznQSU+L4N2mOW8GgRVd9NTLvbowROFZ8EtDDsMvZLPEVwkmZg/eTHTVFMjvmkH1nAvT3UzE00bqJo/NLZXWw1U0Dut2wC4Wg9y7gE1GDQjUklo8faSULv2fVayrXfJBbfzhPRnSc+bB5rfxPnM5vFntpVRI12n1jSrSiZJUJIQC9bljEdHwwXaFo6B7kkNNBeE4taY1Pj0pulmJ0nRAGaiL1UHsi+R2y+zIaXxTlRRTI6fyBMFDSEDSEujGIlEIut8TYUWnPoMmxpPxSXD0GI3RsnoE5gX/iJSbDPlipAKDxLWSEkoc7Yr5lMBmvxgNKm5LWBGAijBzLe1iC+LhSFE1zP/0tf6abb7eJditfiFFHN35nLlBIxubufQWhF1Avny9hMxDIZcebS+hD02vZ7+rknbos1mVFRyIX6YFzg88uKTGaGgueWxnoVmYQJWPP5FZXFQSh+H8YfJ53BLgSyS5UKPwl37Q1y7dTKJVsGrxASkWvQQaqyqyr1POPcYXG5vYpMgsslSVLp1LKFvfJ+UxauWv3H0k+fbhOyW17KRFBs/NDnm0/ShSxv+7wnGpeWOnI2CHRId2p6hQppRH094+MCh3pypTpNW7dr27xu5ZSS8LCfSxRQ38KVey11ska8lh2WPUdIEESxtALqHHLIIYcccsghhxxyyCGHHHLIIYcccsghhxxyyCGHHHLIobAkQhocd0mdgykSQQ4eV0lHh587ZMQ4Wk6uiYFsGKVJxuG1kjuFc46Rp9mV13a4vL7pExeMVYh2jKdECNKLXtSyxYXppdzOWSIBdfx12/5DB/dsmp1hxBGR/xIrV0kuZvqXmElCLees37wjZ9OaSTXLUABRgEQ02wh/6xWICu391fqtWzcsuKd0m3ahV42m+5/94pYlTwRa3QehfX58B4NWRP15lKr2eGkykyCh17FXUVXZhx8om9B+Rk+UYxXfxrlW3IplVdNUGe8tblGrWIhAO57WPNRUfF/ZhHYWO7hiQCuiel4jJ4K/USmyLYF2ggHt/84LaCXUKt/IiugPrWpRcnQ+QtvSyw7QYOyPkqo/bjr/oBVQtVP0PKmuqqerlKL+df5BSz5ZRI8/+WU8tzQ12/MQWgE1XsaUr4VZDrRxUCi0hKpd0//xgf2uqFiqLZ+X0IrBL0uRzlloLelvBRq7DxH8ka8l38Eldn9WWK4V4fiTK+Q4LX9OhIYE1gsEjUT0mtFr6KWgi0SDlvYVLhUiDYs3yq+KdHhB4E1GRMd+vfl/Og6BeT1DxyTwIzGC4Re1VoEIB23wlZkfxG4TXcyxazvdYpSXFI0XEjYXNesKO7AqSlGgFW1Pi/Cu+GUSH1mYI+DsCoE/J4YE2QJyV6+TVSfDTX6CmzOufHj05InD7squaD/ECHkWUblal942ZMwbb417cUD3i6sLyJr+IQzXSlVrZWXVqmo2F6BTVVv0evLlyW+MGtC5USWLtKAJq9LqtL3jyZfefOvVYQ93bVIVhSmKQPpS6ZI+z70+ecwDrUlPE4KGrhVacOMmZ17W5+mXJr/16nP3dbwgPWxWQBhbSt1r739xwpTXXvhfh6ykkBOccLIzvVHnx0a98dbLT9xySXUphmIvFV9eu/vg7vWT68C5+6YvGklblaX9ylkeT56V3OqhWTmmE8GrX+ngMl0TTkPIGL98+4EDOStezzSj0uq5FYHDxad+eqappZW0NgPn7g62kr9yRLuQtyyh6o8sVfgVS+8qj9DEsNCS2zJu+dDU5yPfPt4EhYgM0mq122ceCFy1/5NbK1kZl/xct/+C48YFytrxVxV2etaDeigsieZ9BLqBe2jSVvIHKiJ+19ySGVzqOfMoPeTt9/ngL1RC8L1rKj0YCi1Ux+bW2HPBha3SyMOQC5Umh/VDAplDA4LyI+nOuVB8RvFDJ8hfyER68nWrE5ZIkBtXsfSy5BLS03ktiUDwhULrRin3rgw+zQdZivG+l2rZsCUg3bmcpmr2sT6RqxZ3QZbRo7vWQa423mlIM/lOuejQulGPo7KsyF78EKr/OSSc5UlbNX8B3nN5YEQCqjFDIR/6lWBSV1X2KXhLMGtcKLQSGqHkQ/0jrzqKXyag+j9jPdAMeYo/H5/IMri84df05VlbUfGfDU3TjyD7gIwL/IGe+vH+DiPDcK0HNZlHTGyf+WlwRnzttZZZIKLyb+vYR6sVsqsUnx/Lz7lN+o3nTfIgv2p0WoOhR65fyKHtfpIm/sU3V/0dKywDEkvZqufj3aa88p8Su4o3rvN0/QSFPLwl0+hBOGhfwCqdQHiY8QYqrMIFChsA8CQ1g48ahblTFmCfMQA9UMNC8eJVpixfEroL4wLduIQA6sNHNvGklSZoPejyjYRhTYlO6I9yAT5hTSdfYRG/TKdvlT6yAOORgTPXLjTJuEKR/TK9xC+PKQzaHiexBgmMJy+j2Z0objxXYT6eHxhPFa9CAdEIj6uqKvN0ytiL3zCuCQftcPaJgl80TN93ABW4iKW+VYF/T3aneBDjTWOwk+dDnkrZqM5YgEcGWEhCrU9DXieWUFOjHClDyVla3yAIrRtlb8N+nqqdPFCWVd7nfJx7WRAWQfwG++jdqspeOc3LIiu4P2/TjfpAbmTduIImcQybIT0MtDrkgYaWYcC6aiSk9+NbjZl6AU3GotpYAHKzK434y40BWgm1zGfvjbSz4bsFP+8DHLVTBrStaSFONVifTtd4csbj1XgrAkr9DufzRKOM6TWazlnXLdBKqOpvRu5A43maZmC7sqrBkRIayy8jsG1bNH+Vj3OWH+c2ptiKYuJaNgzynD0/zF/8j0YLgMQGLeCk0VsZ4/PqPypeb4wny0fL1eD9nw28qf3lHe+Z5cds1H48hL/cGKB1ozdYAk8FL2ydkpSUUrn9WLJ+53Xm0GZjheK6e/ojna9o1/H+eZi3ohIWcvFWemIf6yHMr9xjGqvBybL/BKF1M1WXAeJbOmHo8A+3YyPVfj4ezKGVUNs8NlwV//yf1OTklKzhXpausQC/SUfhQbeept1QcE6v8klJyeWbDVxJsBgbk0Awcjb7v+nRqN4Vk70Gtrp2Hb8/FdLMnJ7/3/LGndccYR1Q8HdRZK0VWqI0/Umzp8t4dVXjQan35GypyZ2QWadBIfi8c4rxZc98ymnkkTOMgSTT9GI0z6L80RXVqlz66inMK2qYoHWhqw5zVtPxF03onYl991Dugf7s5ZvOLrSIvWw//tDI+tT5NHu+WlAfLvKg12j+RlU7fZXRL6nDohNdojv3TdCS9g7dygeUxxP7BgqFCOhtfGTmpYgaS2ASutG9DFoN70yMFVoRVdlLofXhQYbGQN5LYobRnZSZ+OC0poFWiGo5mKVpU/FqbiqiFhxIDRf0Z3c1X8FqJZugJS/xTc7cMn4VGSZzs+08/asf30nnmhtdeZyOX8Y/p/IuedAwNjQZP0guIubE50ROwT1z0wRuipF/gpp6YdCSB23PRpIkCJIHDeCpPDW8mdnLAqo08HLIGx/UgZIO8BvzanIruVBoJZS1n97kw48EBZV5sa496BJz8kkRZZ7WWUHZfelM8KHnONPqeDRKJP0hbzntW5pl0wSthJpuZdf58RcegTeRiLp6GRvJ+Es3g3EKq5Qi4+uNjghC+UNcAHwtUhwXcGhnpwRSdRUeEBSUtSo+2MoYruD+y8ghdrxpkO1tEVWL2TVYb8KEbSzQZjJo/XhJOgpUsrA6EaytVFjJMm7hI7V4K0uMJWV7CncLeFDaYipXzdDewThZxbmdUGA3OYGgpLK7/61EASrHsgD78a81Te6UWbza154UagR/TaFV8MErkNsdVIhigpY8RjsdLHAjokfZK9d1by+OtxAo3kcmF03mM4unVMQtY+VaAZIlU1ZX8KcXQM0Re//EYCsCrciQ/i3NqKvjozwNZ4WjmEvfUYH+elCdtQwMDi25mSVpJQ3NNxVOdKHevB6PhtuSDrtRh4N8Gr2ZmJgAsYBujzvJ9TyfGWoDcLKgaayCuIzX/AeqX8a2fxqA1ofnJJpeW3PMChNo2rOWnH6i2+Ny8cnwrgFtdqxcS4a2kNdXUfCGZxrD/AjXT4mMj7eS8hWH9thFTFq3MhK8qy2DjJMAKRhNJoOAqv7EKmNDze7gAIhRedxYWO4hz/Ogh1UoSkaMjrvNHehuzIyrSRseNFRTuRg5PBlWsigpvsNC+2maCdoqO+jDSc/eDApF7m1EYmqVatVqVPmUtQ9cGyu0bvSAyjVVouVvnXF7WmiWXKMVKa1q9epVshZxgcCSrEroFmNG7zGxAkTP+C3QNtjL53Rud6v6uc5YoqCetBuNo4u/puCx13S/iVO3G8awRlTck1zkQhcfwBrPBooPLXqsbky5iE3QzipnstPTlrARgdJj9Ay+zew+fPr3qzdv3bZt+7ZTPA16EbiWSIStRop2sO7y1r1Uzya1oJV6vUZ9sngNtLJtu5drhgxaF0vSyWp1Bp3BdmhF1MKoTL37Ekv+djTPUKzfJpLWhWZgVv9I9x7PNejEcb7Yqbg3NOpG07HCFGLI86zkfHRlDMVfTdDONEOb9Bmbh2qwbqOA0u774ZDXYo+xP7FDS/65AVM+Mcpp4MMTq5raJepI1UeXHcnHtlaC0I5kT5Txh8FXErKBI6KrwWyHrvyTaUZBQu8bPfqM7it8y8SoNQ+w0bDK4gJFVH0HXcioyQwehLx5rQpD1gztZ2ZoPe+wVQCqjXLF1nVPDjMWIRcekF50roWLHjCqPIIZC4JuT0+TJEp67AD1k9haCUI73oD29ajQdjUUhL8rWqGdYPRoLmySJC02VCHNQizmRwnEqmTv51Y+XAtmqX9EYQnzIkHresOA9hvOtWkfs+It5DMoj64oSrGgJW30yIEM7Zoe7OcjvA0BVf+GelxYK5Q0G7RTDGhfiwptN6aNqXhDecEC7StGj74ApS9tqQGt0Z6ZVHyNMbQLf9KgZglnCSjo82Eh25uRoE2YZgiEL5h1455FyzjpVNoUXyDQDzOm7APHM3drkcvlHtyHkL4Is7pxiq2VILSTDGjfiQrtTQa0m6tZuXay0aMZsE1GrGbFcCyEoZ+S+H3EKBu8QYVa6xo2luHxMfoQbLK23DwDWlptVEJDqNkIXcLH/lq24PMZn32aw4tOFBFa6FCzcWugLA7XMGS8oRJVe9EYyFnLWjm8bun8z2fMnLUPaxauHRvkumjQXmcIhJwG1mXsU+P+N8gyJroXMq7V8KLXp06x0FtvTXk+GE0liqjSoB9zeSF7eot8VVRsTdB+nmqCtuIGQ/maQPQ6ooz9q3OPwZFJnbOYdfN20fVa3k0y+Iy7Z+3FXK/SFe1J2kqDApn7qva/3Kk2m3BfGnotV76eNNr4PTgKAu0kG7StWd1qDR9sb1a+BLSMaQgyfhpJ5L4vySugdu5NYfExLbDkKUldJ/+F2XQwLUOFQzs9VQiuJg1l5tvUlEFk0EQdZXVVNf3gDYgdJHJLHxh6bVGh5dVf2ow6yGqXkMn1NR3nk+xlaTinDaL7224hfb4FWsgYzPXaoxWCA3cTk8EK7UXHdaZ85d9jMRmS9xsOu/9Sa+xtplb58SCU6HHbyFoDBaqIogYP/wnaLXUC76kbTQULQOvHy8oFZpiI+rIB6ZiYv24yoE+pE4P0oD9yeaAUFvlsWnG5ln4FNm475iAgX/9Ba4V8ZxhB3VGCW6LxFOWs0IqoPVvWyFu/NrgrJ7g+ZWUmAyZDrfXMVlXxRBPXSqi9j00UXb+QXOWBHSadctbrMUQWiGCG1X6PyRoNH+8azRke9HzJxCYPuHOEZcbCeaQOGQ4tUgmSS8utFdyZLRa0pjgdlwtdvoe2Qyv3uJG4GTNO25cQuCz1Gwu0AsrIM9T5dwIaNywFVI0NQltuFpsRCv7NpH15yCrG7ayNKfT325lnzY+XRzm6Y/IfSW6U/h17jbplQkSClknyIchwD3cOOBWhYBJ58lruZfu7hnGnC71XPK4NgitJLlbTVMFbmpOuJO7irfwRlGGJX1mgJe2uYX3T8eHaXOUW0d2YiwnD8yWip8GpAJ/o/VBiYLT1DjB54MfjoB8u1DiHvU6/pbI1HY5oVlyDP7rRQ9RfoesF/WOAljE4HkpHLqDMHYF9kzHkEtJ3Vv9T0f/l75YwSvN9xdEQLmoSRFcSE2dxaP9qQNpJ2MnmBt7Je0wavjJXN2sIpJGJgU2GOSAK4FH3aWzRNTsVO/oVvl5B6RpazU+CRVFlJaXwFYieFUQ/BLxa1Ym8Z85puBgFDHAB1bg0wWgKbnmUVuPQ8KlesUDLsZ1el/TLfekGXi+MvN2LoQEJLWGfyLAPSYh8dtleDn8RoBVR+Z148oUwsUXyEBG13csFwk+p8Iz1xhLVAblE2kqnXGMPJABtRw4j+ftBFSiCVPNNTLfkrVxbaRGv9OLHv13ER1v1I8wsFR9exCpxetBAVuCTYLsww8yfSU2fX9YLfhFgpVkI9Vh5hXhDIOBDraOpCAGuZUY9Pv7+oKdmKUbZRIX7QNzoXfa2Vbwrm95Xrt/pQJW62KF1o3bHiAE+5aoq9G27WyxlHAj6PwRUzDGWsQ0X0FYqDJKx1RojlLbRcMFreNe4Bx995zDthWZxhZMxP4T5hr6CD4+5sn6deq2HbMfMOUimf2feZ6HC9sAM3fHQBZUSJcmTUjnr2qE/kbe1pBwdRPkV5MXN71mT7tiJmZPYg4m4TogWsWgWCGAEsZAJlZdl9FN/JVzVS+WbT/jI6M6XdXz4R/C4aiwUoCjQ/ieXRjv8Oemxvn0GfngcbAM6tbqR7yR0f2Cu733mxsuvG7gcsxgJM7Qi+l9wr5yZawrlCmwJ8RBR+mJcwK4D8/T4btg3ZcgSu2Sqm3OnCz1mOGjAGlo8bfxrU2f+cghQyNdXlqeDqPI7hhLYO6Y/1a/PA+O3ci+YQkztaLauWSDAmCGiy89+18kIpiM+R5J3Gu9WpwEVcLFhlkSDdpgN2o7HCX/5DStWY1Pbj+eD11ZANY8pqjFK2opfZ+CboBVQxVUGtlj1+WnsGeH73HwL15J/rz7J1WYyKkBP8Sks3sOLVwfLW7nQAh4wglU5EJok+2XyxlZxaFeRYcuBAECF+9L314q6iWPi2lO0KjV7Mg+E2JQRCDG4mxr30L5PVhW/XyejyWd1YoLQzjagvT0817pQu38pc8kQjuf3saJePryvGfPmokHYyycxtOKTwUzyylaBIKFrvLoPBypeAzMW4G+GwMaOOXrGA/FLPsxjD1RFYd5srObjbc1M/n2U+SeLnoHveEAdnb2y/msaHVb530CWk1dDY/KYz4EgfU90n63JZJg9gnIJ5j0mPdiVbQqnewf7OPJQDRjCw5YOwCxOrYWxNzYD84OkvQxon7fEfJFZ+i8u0Pjb47PZj49cx0YqoMS52GtpRcVzn2YRHMEqeeQ9y1T94UGQUC9xUeqNeQCtZjr+7EZ35VGT30waufGXJuaZLKIGS1kZVuuFSh6eBVobsXC/xl7V4Dn2Lv0YP1OIN9xk6H6UcG8eVpmzT1NId1dZTtElvQn+KJVGhGngmJpd6TKcB6FUuLXBtfNwvkI+KMB3GdCOpZ8o+fgl7vh8jO4vQG0Z8B6qEE75W3bQ215xBm2FCiQNvnxX6olPy7JfP9Ys4JYU0O0nwQdFr6KPGJGG+nv9PsWfj02VqyR02TIaoMWa0zUaRFYwKt0qIyVU9aUCAqWsUO8wLXpDK9kfuYoOzIWu2A28pLJ62BqdAPv7FLala4J2dhq69IfgW8sdXtV8s4DE2zaZXuqWvinoSj4dGxhcO9oQKO3ZrRK607j+bq4PC+1nerGZNg2oHHz9Ikq4b6fpy9X/TUS92Y+Hq5vscNR6vumqJR3Jh1cfoz/Lt5jmuogq9ltlZcbcqdmCXWUizbd5N9fGtf5fhjQMtFf/5X2WL4+Ob1RoVLjZqUg0veQus/fKuq4c/H6gfc+K/FKpx/RNp8lb9e2Y1asSzJT/DH91/KtPtw1ck9jpyZfHvzb6oUuCu9PtHhv72mtjB14RHI07q9/0dcdAgfUd+HncdRWt8fYIVbtt5lY4NZ2/9eNuRNYJSV1Hjhv/6uAWVtvI1WHKRtIX7N38XqdEGnFx6VPjxo97/hrb8QqUetkL3+2AXuvy4dXT7syUwkQQgOpeu/fUlQd8VM7nbvnulV4NkszNoYpdJ/x6wA87D8fWvn9HLVR44RarKxy4J7lRdnaDNP5AC1ENN6N5qxZ1odXiltigt0kVG7XMviSLqu3WB9Hf3JkXt7qkdkKYPpifImY0a9WitjvKVcbDEzMaZ2e3qJ8uokgXM0MrtU5zct0F/OSF6SUxI61cvUuyWzasGLwhKtl3GQKPCxt4EyhKIxinWURI2mO6QBJt55HYJ5ZzAeZzO+GqFIZvxd4fU4EcfjVrO+Rx5ticKMV0BPOtQsiF5q9jK60YZgMn9LG2JkqkmH305wgxZVmK7Sp2pRjLYS8h+nU0wC/m2Rppb8yhuMmBttTIgbbUyIG21MiBttTI2MCxxyE4FDd5UM9cTSW2c4EDbQkTgZZ5mhQ814G2RElEGXMPHD15Knd/Tl8H2ZIlAmeTG2/pffNVpZsd5rykQIhkbIcfHCoCCS63x+MpnerYDjnkkEMOOeSQQw455JBDDjnkkEMOOXTm6P8Af29k4A0KZW5kc3RyZWFtCmVuZG9iago1OCAwIG9iago8PAovVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTAKL0Jhc2VGb250IC9BQUFBQUErQ3JpbXNvblByby1SZWd1bGFyCi9FbmNvZGluZyAvSWRlbnRpdHktSAovRGVzY2VuZGFudEZvbnRzIFs1OSAwIFJdCi9Ub1VuaWNvZGUgNjAgMCBSCj4+CmVuZG9iago1OSAwIG9iago8PAovVHlwZSAvRm9udAovRm9udERlc2NyaXB0b3IgNjEgMCBSCi9CYXNlRm9udCAvQUFBQUFBK0NyaW1zb25Qcm8tUmVndWxhcgovU3VidHlwZSAvQ0lERm9udFR5cGUyCi9DSURUb0dJRE1hcCAvSWRlbnRpdHkKL0NJRFN5c3RlbUluZm8gNjIgMCBSCi9XIFswIFs1MDAgNTY4LjM1OTM4XQogMzAgWzU4OS44NDM3NV0KIDY5IFs2MzIuODEyNV0KIDc2IFs2NTYuMjVdCiA4MSBbMzAyLjczNDM4XQoyMTUgWzkxOC45NDUzMV0KIDIzNyBbNDYyLjg5MDYzXQogMjY1IFs1MTMuNjcxODggNDE1LjAzOTA2XQogMjczIFs1MjYuMzY3MTldCiAyODAgWzQzOS40NTMxM10KMzA1IFs0ODMuMzk4NDRdCiAzMTcgWzI2MS43MTg3NV0KIDM0MCBbMjYyLjY5NTMxXQogMzQ5IFs4MDMuNzEwOTQgMCA1MzcuMTA5MzhdCiAzNjIgWzQ5Ni4wOTM3NV0KMzk3IFs1MjQuNDE0MDYgMCAwIDM1NS40Njg3NV0KIDQyMCBbMzMxLjA1NDY5XQogNDI4IFs1MzAuMjczNDRdCiA0NTIgWzczNS4zNTE1Nl0KIDQ1OCBbNDYwLjkzNzVdCjU4MiBbMjU5Ljc2NTYzXQogNjI1IFsxODcuNV0KXQovRFcgMAo+PgplbmRvYmoKNjAgMCBvYmoKPDwKL0xlbmd0aCAzNTMKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtDQp4nF2S3WqEMBCF732KXLYXi0lWzS6IILoLXvSH2j6Aq+NWqDFE98K3b5zJbqEBhY85ZziZSVhUZaWHhYXvdmprWFg/6M7CPN1sC+wC10EHQrJuaBdP+G/HxgShM9frvMBY6X4K0pSx8MNV58Wu7Cnvpgs8B+Gb7cAO+sqevoracX0z5gdG0AvjQZaxDnrX6aUxr80ILETbrupcfVjWnfP8KT5XA0wiC0rTTh3MpmnBNvoKQcrdyVh6dicLQHf/6nJPtkvffjcW5cLJOY9EtpE4Ee2RophIERVEB6TY+45IpUKKSXkqkRLsKQQpk4joQBQj7X1NEXkf9hRxRFQQ+VpJdCY6ISU50RnpQErFkY6cSCLl1FNRlpxupChL4WuUpaCeCrPIKEGSeCOpKLXkOGI/S3Gf7H0Tgh83meA+nfRqqm+72d7QY/HtzVq3c3xouOxtzYOGx1s0k9lc2/cL9Ma5Qw0KZW5kc3RyZWFtCmVuZG9iago2MSAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnROYW1lIC9BQUFBQUErQ3JpbXNvblByby1SZWd1bGFyCi9GbGFncyA0Ci9Bc2NlbnQgODk2LjQ4NDM4Ci9EZXNjZW50IC0yMTQuODQzNzUKL1N0ZW1WIDEzNy42OTUzMTMKL0NhcEhlaWdodCA1NzMuMjQyMTkKL0l0YWxpY0FuZ2xlIDAKL0ZvbnRCQm94IFstMTA0LjQ5MjE4OCAtMjc2LjM2NzE5IDExMzEuODM1OTQgOTYwLjkzNzVdCi9Gb250RmlsZTIgNjMgMCBSCj4+CmVuZG9iago2MiAwIG9iago8PAovUmVnaXN0cnkgKEFkb2JlKQovT3JkZXJpbmcgKElkZW50aXR5KQovU3VwcGxlbWVudCAwCj4+CmVuZG9iago2MyAwIG9iago8PAovTGVuZ3RoIDMyMTEKL0xlbmd0aDEgODkxMgovRmlsdGVyIC9GbGF0ZURlY29kZQo+PgpzdHJlYW0NCnic7VkJcFvVFX2LLMmLFmuXJWvX/5atxdbiL9uybFleEid27NhxSBonEcGJHbwRO4RAoeyknQbaAdLSdKBQ2oEWGIaBNNAMk7KUgbKUbmGAaadTSqdrhjIBOk2s3vclr8RAKKFlyLv5+W/799xz3333vy8jjBBSoCsRRT1r+kLhjT1Tv0IIPw+9W/v6U/1v3P+8Edr7oN26bSwzqXlK81eEiB/ah3ZkpibhrobrDbjkO0b3bleMlP4CIcPXECpJDg9lLrC8XHEAnu+B8dph6JA9IimDNnveMzw2fUmMUClCxZvgCo9ObMtUBrkXECooQUjaPZa5ZJIKksdh7t/gcoxnxoaCuv5ewAc8wk1OTE1n70Bh0FfCxid3DU0OS796HdRfh0t2GGeve1CyH4YRzmaRCu6IcpLVSIbGUQFrLSgS8ADNVbPfZjpPU9jzB7IemPzd7K2nfie5YYkORKbEHoyuufaZf3VvUSVOyKlEHHml8Mil4v0W/vfZW2f+KLmBvgJNKSJzujGSiy2d+L8HkT07hqeZupHpzCgugnEJ+FpExBSDh8FeaOHCWduQhLD+ArDjW2Q7tLtyd7wd+GTfz2W+tE+syKCjyIH25vjRf2DegfDN4sR7ySrmXdE7RERlNorYcJegu+BuBcsoKkFOlEJptBKtR0NoBxpBk2gX2o32oL3ZrKiDjXbAaEYcHYXR6dnR7NHsazDnSrieQywGbXB14V7AsYpGlok4SNSDEJf3+EWkHbWh1agX/R0X4iJcik24Hw/iLXgn3o/vxveJ04rRV5jFEuarh9Bf8nWMDICUqxOkRD/N1ylqRtfn6xJA2pqvF4AN7fm6FFbIlauDI0pAU65O5/XAyhTBSK7Oagg8Mw3cR4H7NrQWTaAxaI2L3hoBjwzDaBp8MgL9UzA6jnqgNYECMJf5czc8mYGeAWjtghkj4hwHqkFBVA0SXvK0I/+8Y8nz8/MbgM8EWgG9Dcsgt6FLwKpdMGP1nI2LtZ0eMw3XJNorjuWecgBeNWDHodYPPUNwX87e3H0nzNkmPpkCtGl4ZkLk7UAVosZpQJhC9SgEwuKJzdiNzgd220TfhkR242J/BkamQN849J6OqS+/Feo+tkx8ovLIQsHWOVlxTubk8jOQlz6eEC3pWCRfXyRPn6nQ6mUkQbedgdy4VCQEZPVp5bJPRJ5jUiD7iBJcIlNnKPcskNc/nyKVn5PPsNjFw1IETidLzohnv5BNKPppY37UQvahyLJj34E395KCa+F9fa6cK0sKvR++4s5Cwa/CSfhTLESJCL537vT5/nELnHWXKfgJVHl2rFqAcRy+6c5kPnwJseus2KIQf2v4wEIFZGZ3wiHTR9Z7cnn/f9qFmJFxto5Hc1w+qYL7kOYDse0f7t//ppC3kOR0/fiV7MmziXuu/H8VXATf5Z+VcvR/bcDnqBxH7NAM52XSTgaRlr15pnhBK2iNMjcv8DwVqExmlPExgyESrhX0HOd2SSmpebH+1TB9jjQ2FI8Zh4tjjcSqbakOpTWutN2W4pRE6j51TMlxylvsWK2Y+YPtViX3aAwTgmMYs99do9l3STPpRy6E2lwcF4smSSRsMAKSiCDV63KAglEmleLJ9TcN+Ndd2u3b5JEFbNpk+aotgYahRnvM5A9a3YOF6evOb5pYV61WfVmyRaFIjabWXZxQKjcXXFFqAawIYF0AWKEPwZLFohzPCxGjTKeDPiMAX7zx5oHAusvWVA66pP5ybbOlKxNsHE5Z2ywmQlrJk8RPMD9WmL4W8AcA/xrJBmUJw9+TUOviI86woriCKyngLO7qsrJyRNlpm0yRVciCBNSGUIYXkiQGuHlgJdEza8A257IjdNFa4NtII6EYU7gdo0kiliQ99ROog6sx1H89P6NL2y0IqzSG9nj9Ck4pcSarj4STTspWijqT4SPVSaeENQo8Sf+RQJN36QhWN7BFbAB17Pds8CiWi2xY1Cxjbw3gipY0kgWWLI8B8SjLnqL3gN5ipjlF3VRUqXVrtXM18U7xnx6qejlMnqSRu/V33PtSmPyMNt6lu72DHqaRY/jwTBspA+2KmVX4e6dOzNUfZoin3mSR35B9Dz+NDyE/rAdaL0YHBB2jAb6VyaRitAgQLTIICKMegtHt4vmIVMrDCsRkYvA8VZF0V0W+aQvbzRpCPGFbtWBZuRETSptrlCa5vbBY5WwsMPo0kyZ9QO4IW5z+0kjE7DPp7ZVyvanUZTdIuprv0XgLS+waAOC93ga9YcQmLdOZfWAjARvfJIeQAwUQ2lMbjYKbgyQGfjYqicxYWwvRbNDrmGmcAGbVirbrDYY1zO+dZL+WrRk1dfrDfdH0ZGvVugMNXZisidvq9PqwxRUNW+9zd6+MFRU6fMrK/vFe7Kqs+UKqY6xRZ9/Q19Nlsb6tLIXV9oEdd4KvnHOemkUGYJ43cqI3WGjC1nk3vjnesD3lT9h1mlKDJuKp6KzubeQEs8U6II9k0qnhpMGjU+tLNJGiQmdvc8+A1W6MRtiaVGRPQAwfQh5UAzsWspDIbG7fAvcQYRD63JqIKyGuF6DjL6kwLW92122MeJp6qiwNOhZ63eRmWBlcgJv/bY2UaevLmzrWFCtSuNJVO9gYO6/JpVRp6ptqCpVOnzI53fRmZKVK7eTMjmMu62Bnz3rgXgnc78A/YvlqlrsRqOudsQhLG2IamSf/Xv3mWvjXfd7MC28VR4LWqKO32SOUmYF6NNPSMVyH8dBmjV+hr2typiMlazdZHCJ3CXJmrfi3+BE4HddBflgporGQNM76FsBCZGGf0ZhbfLp4SViA5gyDOV6IlT9vGq1NcE5Pc11zb3XdhSpPNBpUaDVm41VtKxLb47G+YPXaSKQnFOr1+6pjVb6ogPV7+NvfSIRd9Ta5Llxe4Y/7XU5JoYS3WqI6nUJfpNRShSweDXZW4a1ca2V1Z1VFR8DfUVmRCNelm6LR9LPyCjXKZsVvkxPkoJTDSajJqDe7ESHxb0HQjx9m5++5/MHzMZGZSE1mIWnIHBKavlFFKSVp4lVKdH6+pbJVo3R6lfZExRXBhFzpZdrYSfptiJuyeW1z2yQXKzIrSREMu4J0kpu0rEKhw6ssDqwWLvf2rIjIi2ALVPVtX3FnwxYl00rY9wAth6iPoJ5ZvYKY5cDMuc1nFLW7xVE+nzxyBObzOOvj8/NzYw6SBFOAHmm+vrSA2ls87pYqTGmCPkkFZp3Fb7YJekog3+6kEDTEFjC664xa9xp/ZWsFJUniUckMlZ5uvZ8vAX+oazHv4robd3rqilUej0pu4izjVVGTzXEgON8z5o+aXG5vdaBide1F4kzmPfh2IyrgGV7AcjFJ/QKSsUUk7TkiBVTk4W6t8KZ8lCbps6Qeuu1BkzMOLJIUrDVVurqcLWzx1DHMubxd9ROe+qJZ00YDMaPDdiAUZ0YRMeP8EmxSofLZd/eiAJ+PcAPGqUxtw1AiuSka31LjCls76mtT7qQ8PpRqvSAubGtp2pEI+VaGBi7csCo+UM1W1pj14OOg3ct+u1vPC9GFpCOL0iqf46kFSEivs/m1XKRN8rStaV5YV5PYkTJF9BSvJSpfwo2xq55zxAza2OseVYHJF/e+a2sSyQser7C1MT3SWChfxcUUCg5L+c4obKFy6z9V7G/OEHd4A+wNfpn1yKdd2Coyo42wSKNJFjPMGqWE2hqtRrvTrHGqNBqXipmJ10O0FJgDjm5nStw8/bjeZzIYy9RKaYnfPmiOBYoV7O2rAa9vIAVwGoR9OSTyjwnumBCRyXIZjsYierxB2+JJrKHtV2kJDviMvPqZHyed3s7O4/rdU+9Ew1ol9oTDbFdCNpcCDzj5DMELdDadL9zqebWx2W0llepYsOFnmq5m2bzRnd5ISes+lsLrA5a4Hsis3A+cCtrVToeiCfucfX0DlloL86u9nRtPxqyuaUMVr2QsrVVV1g4hDbwk2ZPkLfwARBOcKtryJwlZ/iThnGsbKX7vtgg5TKLT5j1RcpCEfjjz+F0h8iiJfNF2reUbEnjHQLjO/BzjGrFy1XxHfXLmELyvT4pZ/AEkY389X5/XDEdqKstxZ2cMmSAA9GsPhugTJLnvRRr6QY+hxmFymMyDwYniq5NkHmcb/H/w+0HOpFWVya3h32gYMIvg9uw76CnyGCpi0bKHcrmYBZjFW2WXkyhNarVJSZxajbW01KrRlKs9h/BjZkdpqcM8035UbS3VWNRqi6bMzXLBceLFI+QgnL9QSjQ3F284UObxlJW5XMTrMpvcbpMZXofoP5F+JXQNCmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDY0CjAwMDAwMDAwMDAgNjU1MzUgZg0KMDAwMDAwMDAxNSAwMDAwMCBuDQowMDAwMDAwNDE5IDAwMDAwIG4NCjAwMDAwMDA0NzYgMDAwMDAgbg0KMDAwMDAwMDU4NCAwMDAwMCBuDQowMDAwMDAwNjM0IDAwMDAwIG4NCjAwMDAwMDAxNTIgMDAwMDAgbg0KMDAwMDAwMDY3NyAwMDAwMCBuDQowMDAwMDAwOTYwIDAwMDAwIG4NCjAwMDAwMDEwNTcgMDAwMDAgbg0KMDAwMDAwMTE2MCAwMDAwMCBuDQowMDAwMDAxOTE2IDAwMDAwIG4NCjAwMDAwMDIwNDEgMDAwMDAgbg0KMDAwMDAwMzA5NCAwMDAwMCBuDQowMDAwMDAzMTg2IDAwMDAwIG4NCjAwMDAwMDMyODEgMDAwMDAgbg0KMDAwMDAwMzM3NiAwMDAwMCBuDQowMDAwMDAzNDcxIDAwMDAwIG4NCjAwMDAwMDM1NjYgMDAwMDAgbg0KMDAwMDAwMzY2MSAwMDAwMCBuDQowMDAwMDAzNzU2IDAwMDAwIG4NCjAwMDAwMDM4NDQgMDAwMDAgbg0KMDAwMDAwMzkzMyAwMDAwMCBuDQowMDAwMDA0MDIyIDAwMDAwIG4NCjAwMDAwMDQxMTEgMDAwMDAgbg0KMDAwMDAwNDIwMCAwMDAwMCBuDQowMDAwMDA0Mjg5IDAwMDAwIG4NCjAwMDAwMDQzNzggMDAwMDAgbg0KMDAwMDAwNDQ5NSAwMDAwMCBuDQowMDAwMDA0NTg0IDAwMDAwIG4NCjAwMDAwMDQ2NzMgMDAwMDAgbg0KMDAwMDAwNDc2MiAwMDAwMCBuDQowMDAwMDA0ODUxIDAwMDAwIG4NCjAwMDAwMDQ5NDAgMDAwMDAgbg0KMDAwMDAwNTAyNyAwMDAwMCBuDQowMDAwMDA1MTE2IDAwMDAwIG4NCjAwMDAwMDUyMDUgMDAwMDAgbg0KMDAwMDAwNTI5MiAwMDAwMCBuDQowMDAwMDA1MzgxIDAwMDAwIG4NCjAwMDAwMDU0NzcgMDAwMDAgbg0KMDAwMDAwNTU3MSAwMDAwMCBuDQowMDAwMDA1NjU4IDAwMDAwIG4NCjAwMDAwMDU3NDcgMDAwMDAgbg0KMDAwMDAwNTgzNiAwMDAwMCBuDQowMDAwMDA1OTI1IDAwMDAwIG4NCjAwMDAwMDYwMTIgMDAwMDAgbg0KMDAwMDAwNjA1NiAwMDAwMCBuDQowMDAwMDM2NDMyIDAwMDAwIG4NCjAwMDAwMzY0NjUgMDAwMDAgbg0KMDAwMDAzNjUxNiAwMDAwMCBuDQowMDAwMDM2NTY3IDAwMDAwIG4NCjAwMDAwMzY2MTggMDAwMDAgbg0KMDAwMDAzNjY2OSAwMDAwMCBuDQowMDAwMDM2NzIwIDAwMDAwIG4NCjAwMDAwMzY3NzEgMDAwMDAgbg0KMDAwMDAzNjgyMiAwMDAwMCBuDQowMDAwMDM2ODYyIDAwMDAwIG4NCjAwMDAwMzY5NDEgMDAwMDAgbg0KMDAwMDA0OTMxNiAwMDAwMCBuDQowMDAwMDQ5NDY5IDAwMDAwIG4NCjAwMDAwNTAwMzcgMDAwMDAgbg0KMDAwMDA1MDQ2NSAwMDAwMCBuDQowMDAwMDUwNzIwIDAwMDAwIG4NCjAwMDAwNTA3OTUgMDAwMDAgbg0KdHJhaWxlcgo8PAovUm9vdCAxIDAgUgovSW5mbyA2IDAgUgovSUQgWzwwQ0M3NzdBRDZENEFBMThFRkFGQ0ExODQ3QjI1QkMxQz4gPDBDQzc3N0FENkQ0QUExOEVGQUZDQTE4NDdCMjVCQzFDPl0KL1NpemUgNjQKPj4Kc3RhcnR4cmVmCjU0MDk2CiUlRU9GCg==',
+};
\ No newline at end of file
diff --git a/src/components/CheckIn/types.ts b/src/components/CheckIn/types.ts
new file mode 100644
index 0000000000..2fcb2d63cf
--- /dev/null
+++ b/src/components/CheckIn/types.ts
@@ -0,0 +1,44 @@
+export interface InterfaceUser {
+ _id: string;
+ firstName: string;
+ lastName: string;
+}
+
+export interface InterfaceAttendeeCheckIn {
+ _id: string;
+ user: InterfaceUser;
+ checkIn: null | {
+ _id: string;
+ time: string;
+ };
+}
+
+export interface InterfaceAttendeeQueryResponse {
+ event: {
+ _id: string;
+ attendeesCheckInStatus: InterfaceAttendeeCheckIn[];
+ };
+}
+
+export interface InterfaceModalProp {
+ show: boolean;
+ eventId: string;
+ handleClose: () => void;
+}
+
+export interface InterfaceTableCheckIn {
+ id: string;
+ name: string;
+ userId: string;
+ checkIn: null | {
+ _id: string;
+ time: string;
+ };
+ eventId: string;
+}
+
+export interface InterfaceTableData {
+ userName: string;
+ id: string;
+ checkInData: InterfaceTableCheckIn;
+}
diff --git a/src/components/CollapsibleDropdown/CollapsibleDropdown.module.css b/src/components/CollapsibleDropdown/CollapsibleDropdown.module.css
new file mode 100644
index 0000000000..4337742ecc
--- /dev/null
+++ b/src/components/CollapsibleDropdown/CollapsibleDropdown.module.css
@@ -0,0 +1,14 @@
+.iconWrapper {
+ width: 36px;
+}
+
+.collapseBtn {
+ height: 48px;
+}
+
+.iconWrapperSm {
+ width: 36px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/CollapsibleDropdown/CollapsibleDropdown.test.tsx b/src/components/CollapsibleDropdown/CollapsibleDropdown.test.tsx
new file mode 100644
index 0000000000..efee248ffb
--- /dev/null
+++ b/src/components/CollapsibleDropdown/CollapsibleDropdown.test.tsx
@@ -0,0 +1,110 @@
+import React, { act } from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+
+import CollapsibleDropdown from './CollapsibleDropdown';
+import type { InterfaceCollapsibleDropdown } from './CollapsibleDropdown';
+import { store } from 'state/store';
+import { Provider } from 'react-redux';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useLocation: () => ({
+ pathname: '/orgstore',
+ state: {},
+ key: '',
+ search: '',
+ hash: '',
+ }),
+}));
+
+const props: InterfaceCollapsibleDropdown = {
+ showDropdown: true,
+ setShowDropdown: jest.fn(),
+ target: {
+ name: 'DropDown Category',
+ url: undefined,
+ subTargets: [
+ {
+ name: 'SubCategory 1',
+ url: '/sub-category-1',
+ icon: 'fa fa-home',
+ },
+ {
+ name: 'SubCategory 2',
+ url: '/sub-category-2',
+ icon: 'fa fa-home',
+ },
+ ],
+ },
+};
+
+describe('Testing CollapsibleDropdown component', () => {
+ test('Component should be rendered properly', () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('DropDown Category')).toBeInTheDocument();
+ expect(screen.getByText('SubCategory 1')).toBeInTheDocument();
+ expect(screen.getByText('SubCategory 2')).toBeInTheDocument();
+ });
+
+ test('Dropdown should be rendered and functioning correctly', () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ const parentDropdownBtn = screen.getByTestId('collapsible-dropdown');
+ const activeDropdownBtn = screen.getByText('SubCategory 1');
+ const nonActiveDropdownBtn = screen.getByText('SubCategory 2');
+
+ // Check if dropdown is rendered with correct classes
+ act(() => {
+ fireEvent.click(activeDropdownBtn);
+ });
+ expect(parentDropdownBtn).toBeInTheDocument();
+ expect(parentDropdownBtn).toHaveClass('text-white');
+ expect(parentDropdownBtn).toHaveClass('btn-success');
+
+ // Check if active dropdown is rendered with correct classes
+ expect(activeDropdownBtn).toBeInTheDocument();
+ expect(activeDropdownBtn).toHaveClass('text-white');
+ expect(activeDropdownBtn).toHaveClass('btn-success');
+
+ // Check if inactive dropdown is rendered with correct classes
+ expect(nonActiveDropdownBtn).toBeInTheDocument();
+ expect(nonActiveDropdownBtn).toHaveClass('text-secondary');
+ expect(nonActiveDropdownBtn).toHaveClass('btn-light');
+
+ // Check if dropdown is collapsed after clicking on it
+ act(() => {
+ fireEvent.click(parentDropdownBtn);
+ });
+ expect(props.setShowDropdown).toHaveBeenCalledWith(false);
+
+ // Check if dropdown is expanded after clicking on it again
+ act(() => {
+ fireEvent.click(parentDropdownBtn);
+ });
+ expect(props.setShowDropdown).toHaveBeenCalledWith(true);
+
+ // Click on non-active dropdown button and check if it navigates to the correct URL
+ act(() => {
+ fireEvent.click(nonActiveDropdownBtn);
+ });
+ expect(window.location.pathname).toBe('/sub-category-2');
+ });
+});
diff --git a/src/components/CollapsibleDropdown/CollapsibleDropdown.tsx b/src/components/CollapsibleDropdown/CollapsibleDropdown.tsx
new file mode 100644
index 0000000000..2991c2ade5
--- /dev/null
+++ b/src/components/CollapsibleDropdown/CollapsibleDropdown.tsx
@@ -0,0 +1,106 @@
+import React, { useEffect } from 'react';
+import { Button, Collapse } from 'react-bootstrap';
+import type { TargetsType } from 'state/reducers/routesReducer';
+import styles from './CollapsibleDropdown.module.css';
+import IconComponent from 'components/IconComponent/IconComponent';
+import { NavLink, useLocation, useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+
+export interface InterfaceCollapsibleDropdown {
+ showDropdown: boolean;
+ target: TargetsType;
+ setShowDropdown: React.Dispatch>;
+}
+
+/**
+ * A collapsible dropdown component that toggles visibility of sub-targets.
+ *
+ * @param showDropdown - Boolean indicating whether the dropdown is visible or not.
+ * @param target - Object containing the target information, including the name and sub-targets.
+ * @param setShowDropdown - Function to toggle the visibility of the dropdown.
+ *
+ * @returns JSX.Element - The rendered CollapsibleDropdown component.
+ */
+const collapsibleDropdown = ({
+ target,
+ showDropdown,
+ setShowDropdown,
+}: InterfaceCollapsibleDropdown): JSX.Element => {
+ const { t: tCommon } = useTranslation('common');
+ const { name, subTargets } = target;
+ const navigate = useNavigate();
+ const location = useLocation();
+ useEffect(() => {
+ // Show dropdown if the current path includes 'orgstore', otherwise hide it.
+ if (location.pathname.includes('orgstore')) {
+ setShowDropdown(true);
+ } else {
+ setShowDropdown(false);
+ }
+ }, [location.pathname]);
+
+ return (
+ <>
+ setShowDropdown(!showDropdown)}
+ aria-expanded={showDropdown}
+ data-testid="collapsible-dropdown"
+ >
+
+
+
+ {tCommon(name)}
+
+
+
+
+ {subTargets &&
+ subTargets.map(({ name, icon: stringIcon, url }, index) => {
+ return (
+
+ {({ isActive }) => (
+ {
+ navigate(url);
+ }}
+ data-testid={`collapsible-dropdown-btn-${index}`}
+ >
+
+
+
+ {tCommon(name || '')}
+
+
+
+
+ )}
+
+ );
+ })}
+
+
+ >
+ );
+};
+
+export default collapsibleDropdown;
diff --git a/src/components/ContriStats/ContriStats.module.css b/src/components/ContriStats/ContriStats.module.css
new file mode 100644
index 0000000000..7d6c83ea8e
--- /dev/null
+++ b/src/components/ContriStats/ContriStats.module.css
@@ -0,0 +1,7 @@
+.fonts {
+ color: #707070;
+}
+
+.fonts > span {
+ font-weight: 600;
+}
diff --git a/src/components/ContriStats/ContriStats.test.tsx b/src/components/ContriStats/ContriStats.test.tsx
new file mode 100644
index 0000000000..8edb853684
--- /dev/null
+++ b/src/components/ContriStats/ContriStats.test.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import ContriStats from './ContriStats';
+import { I18nextProvider } from 'react-i18next';
+import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
+import type { NormalizedCacheObject } from '@apollo/client';
+import i18nForTest from 'utils/i18nForTest';
+import { BACKEND_URL } from 'Constant/constant';
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ uri: BACKEND_URL,
+});
+
+describe('Testing Contribution Stats', () => {
+ const props = {
+ id: '234',
+ recentAmount: '200',
+ highestAmount: '500',
+ totalAmount: '1000',
+ };
+
+ test('should render props and text elements test for the page component', () => {
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('Recent Contribution: $')).toBeInTheDocument();
+ expect(screen.getByText('Highest Contribution: $')).toBeInTheDocument();
+ expect(screen.getByText('Total Contribution: $')).toBeInTheDocument();
+ expect(screen.getByText('200')).toBeInTheDocument();
+ expect(screen.getByText('500')).toBeInTheDocument();
+ expect(screen.getByText('1000')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/ContriStats/ContriStats.tsx b/src/components/ContriStats/ContriStats.tsx
new file mode 100644
index 0000000000..a2db307d91
--- /dev/null
+++ b/src/components/ContriStats/ContriStats.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+import styles from './ContriStats.module.css';
+
+interface InterfaceContriStatsProps {
+ id: string;
+ recentAmount: string;
+ highestAmount: string;
+ totalAmount: string;
+}
+
+/**
+ * A component that displays contribution statistics.
+ *
+ * @param props - The properties passed to the component, including `recentAmount`, `highestAmount`, and `totalAmount`.
+ *
+ * @returns JSX.Element - The rendered component displaying the contribution stats.
+ */
+function ContriStats(props: InterfaceContriStatsProps): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'contriStats',
+ });
+
+ return (
+ <>
+
+ {t('recentContribution')}: $ {props.recentAmount}
+
+
+ {t('highestContribution')}: $ {props.highestAmount}
+
+
+ {t('totalContribution')}: $ {props.totalAmount}
+
+ >
+ );
+}
+export default ContriStats;
diff --git a/src/components/CurrentHourIndicator/CurrentHourIndicator.module.css b/src/components/CurrentHourIndicator/CurrentHourIndicator.module.css
new file mode 100644
index 0000000000..d2c250035a
--- /dev/null
+++ b/src/components/CurrentHourIndicator/CurrentHourIndicator.module.css
@@ -0,0 +1,19 @@
+.round {
+ background-color: red;
+ border-radius: 100%;
+ width: 15px;
+ height: 15px;
+}
+.line {
+ width: 100%;
+ height: 1px;
+ background-color: red;
+ margin: auto;
+}
+.container {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ top: -8px;
+ left: -9px;
+}
diff --git a/src/components/CurrentHourIndicator/CurrentHourIndicator.test.tsx b/src/components/CurrentHourIndicator/CurrentHourIndicator.test.tsx
new file mode 100644
index 0000000000..084817dc0d
--- /dev/null
+++ b/src/components/CurrentHourIndicator/CurrentHourIndicator.test.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import CurrentHourIndicator from './CurrentHourIndicator';
+
+describe('Testing Current Hour Indicator', () => {
+ test('Component Should be rendered properly', async () => {
+ const { getByTestId } = render( );
+ expect(getByTestId('container')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/CurrentHourIndicator/CurrentHourIndicator.tsx b/src/components/CurrentHourIndicator/CurrentHourIndicator.tsx
new file mode 100644
index 0000000000..98e02bbf5f
--- /dev/null
+++ b/src/components/CurrentHourIndicator/CurrentHourIndicator.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import styles from './CurrentHourIndicator.module.css';
+
+/**
+ * A component that displays an indicator for the current hour.
+ *
+ * @returns JSX.Element - The rendered component showing the current hour indicator.
+ */
+const CurrentHourIndicator = (): JSX.Element => {
+ return (
+
+ );
+};
+
+export default CurrentHourIndicator;
diff --git a/src/components/DynamicDropDown/DynamicDropDown.module.css b/src/components/DynamicDropDown/DynamicDropDown.module.css
new file mode 100644
index 0000000000..3cd40fe35d
--- /dev/null
+++ b/src/components/DynamicDropDown/DynamicDropDown.module.css
@@ -0,0 +1,13 @@
+.dropwdownToggle {
+ background-color: #f1f3f6;
+ color: black;
+ width: 100%;
+ border: none;
+ padding: 0.5rem;
+ text-align: left;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ min-width: 8rem;
+ outline: 1px solid var(--bs-gray-400);
+}
diff --git a/src/components/DynamicDropDown/DynamicDropDown.test.tsx b/src/components/DynamicDropDown/DynamicDropDown.test.tsx
new file mode 100644
index 0000000000..dac98ca9e6
--- /dev/null
+++ b/src/components/DynamicDropDown/DynamicDropDown.test.tsx
@@ -0,0 +1,149 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ act,
+ waitFor,
+ fireEvent,
+} from '@testing-library/react';
+import DynamicDropDown from './DynamicDropDown';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import userEvent from '@testing-library/user-event';
+
+describe('DynamicDropDown component', () => {
+ test('renders and handles selection correctly', async () => {
+ const formData = { fieldName: 'value2' };
+ const setFormData = jest.fn();
+
+ render(
+
+
+
+
+ ,
+ );
+
+ // Verify that the dropdown container is rendered
+ const containerElement = screen.getByTestId('fieldname-dropdown-container');
+ expect(containerElement).toBeInTheDocument();
+
+ // Verify that the dropdown button displays the correct initial label
+ const dropdownButton = screen.getByTestId('fieldname-dropdown-btn');
+ expect(dropdownButton).toHaveTextContent('Label 2');
+
+ // Open the dropdown menu
+ await act(async () => {
+ userEvent.click(dropdownButton);
+ });
+
+ // Select the first option in the dropdown
+ const optionElement = screen.getByTestId('change-fieldname-btn-TEST');
+ await act(async () => {
+ userEvent.click(optionElement);
+ });
+
+ // Verify that the setFormData function was called with the correct arguments
+ expect(setFormData).toHaveBeenCalledWith({ fieldName: 'TEST' });
+
+ // Verify that the dropdown button displays the updated label
+ await waitFor(() => {
+ expect(dropdownButton).toHaveTextContent('Label 2');
+ });
+ });
+ test('calls custom handleChange function when provided', async () => {
+ const formData = { fieldName: 'value1' };
+ const setFormData = jest.fn();
+ const customHandleChange = jest.fn();
+
+ render(
+
+
+
+
+ ,
+ );
+
+ const dropdownButton = screen.getByTestId('fieldname-dropdown-btn');
+ await act(async () => {
+ userEvent.click(dropdownButton);
+ });
+
+ const optionElement = screen.getByTestId('change-fieldname-btn-value2');
+ await act(async () => {
+ userEvent.click(optionElement);
+ });
+
+ expect(customHandleChange).toHaveBeenCalledTimes(1);
+ expect(customHandleChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ target: expect.objectContaining({
+ name: 'fieldName',
+ value: 'value2',
+ }),
+ }),
+ );
+ expect(setFormData).not.toHaveBeenCalled();
+ });
+ test('handles keyboard navigation correctly', async () => {
+ const formData = { fieldName: 'value1' };
+ const setFormData = jest.fn();
+
+ render(
+
+
+
+
+ ,
+ );
+
+ // Open dropdown
+ const dropdownButton = screen.getByTestId('fieldname-dropdown-btn');
+ await act(async () => {
+ userEvent.click(dropdownButton);
+ });
+
+ // Get dropdown menu
+ const dropdownMenu = screen.getByTestId('fieldname-dropdown-menu');
+
+ // Simulate Enter key press
+ await act(async () => {
+ fireEvent.keyDown(dropdownMenu, { key: 'Enter' });
+ });
+
+ // Simulate Space key press
+ await act(async () => {
+ fireEvent.keyDown(dropdownMenu, { key: ' ' });
+ });
+
+ // Verify the dropdown menu behavior
+ const option = screen.getByTestId('change-fieldname-btn-value2');
+ expect(option).toBeInTheDocument();
+ });
+});
diff --git a/src/components/DynamicDropDown/DynamicDropDown.tsx b/src/components/DynamicDropDown/DynamicDropDown.tsx
new file mode 100644
index 0000000000..05cd064ac2
--- /dev/null
+++ b/src/components/DynamicDropDown/DynamicDropDown.tsx
@@ -0,0 +1,108 @@
+import React from 'react';
+import { Dropdown } from 'react-bootstrap';
+import styles from './DynamicDropDown.module.css';
+
+/**
+ * Props for the DynamicDropDown component.
+ */
+interface InterfaceChangeDropDownProps {
+ parentContainerStyle?: string;
+ btnStyle?: string;
+ btnTextStyle?: string;
+ setFormState: React.Dispatch>;
+ formState: T;
+ fieldOptions: { value: string; label: string }[];
+ fieldName: string;
+ handleChange?: (e: React.ChangeEvent) => void;
+}
+
+/**
+ * A dynamic dropdown component that allows users to select an option.
+ *
+ * This component renders a dropdown with a toggle button. Clicking the button
+ * opens a menu with options. When an option is selected, it updates the form state.
+ *
+ * @param parentContainerStyle - Optional CSS class for styling the container.
+ * @param btnStyle - Optional CSS class for styling the dropdown button.
+ * @param setFormState - Function to update the form state with the selected option.
+ * @param formState - Current state of the form, used to determine the selected value.
+ * @param fieldOptions - Options to display in the dropdown. Each option has a value and a label.
+ * @param fieldName - The name of the field, used for labeling and key identification.
+ * @param handleChange - Optional callback function when selection changes
+ * @returns JSX.Element - The rendered dropdown component.
+ */
+const DynamicDropDown = >({
+ parentContainerStyle = '',
+ btnStyle = '',
+ setFormState,
+ formState,
+ fieldOptions,
+ fieldName,
+ handleChange,
+}: InterfaceChangeDropDownProps): JSX.Element => {
+ const handleFieldChange = (value: string): void => {
+ if (handleChange) {
+ const event = {
+ target: {
+ name: fieldName,
+ value: value,
+ },
+ } as React.ChangeEvent;
+ handleChange(event);
+ } else {
+ setFormState({ ...formState, [fieldName]: value });
+ }
+ };
+
+ const getLabel = (value: string): string => {
+ const selectedOption = fieldOptions.find(
+ (option) => option.value === value,
+ );
+ return selectedOption ? selectedOption.label : 'None';
+ };
+
+ return (
+
+
+ {getLabel(formState[fieldName] as string)}
+
+ {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ const focused = document.activeElement;
+ if (focused instanceof HTMLElement) {
+ focused.click();
+ }
+ }
+ }}
+ >
+ {fieldOptions.map((option, index: number) => (
+ handleFieldChange(option.value)}
+ data-testid={`change-${fieldName.toLowerCase()}-btn-${option.value}`}
+ role="option"
+ aria-selected={option.value === formState[fieldName]}
+ >
+ {option.label}
+
+ ))}
+
+
+ );
+};
+
+export default DynamicDropDown;
diff --git a/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.test.tsx b/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.test.tsx
new file mode 100644
index 0000000000..19d2249a43
--- /dev/null
+++ b/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.test.tsx
@@ -0,0 +1,64 @@
+import type { Dispatch, SetStateAction } from 'react';
+import React, { act } from 'react';
+import { render } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+import EditOrgCustomFieldDropDown from './EditCustomFieldDropDown';
+import userEvent from '@testing-library/user-event';
+import availableFieldTypes from 'utils/fieldTypes';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import type { InterfaceCustomFieldData } from 'utils/interfaces';
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Custom Field Dropdown', () => {
+ test('Component Should be rendered properly', async () => {
+ const customFieldData = {
+ type: 'Number',
+ name: 'Age',
+ };
+
+ const setCustomFieldData: Dispatch<
+ SetStateAction
+ > = (val) => {
+ {
+ val;
+ }
+ };
+ const props = {
+ customFieldData: customFieldData as InterfaceCustomFieldData,
+ setCustomFieldData: setCustomFieldData,
+ parentContainerStyle: 'parentContainerStyle',
+ btnStyle: 'btnStyle',
+ btnTextStyle: 'btnTextStyle',
+ };
+
+ const { getByTestId, getByText } = render(
+
+
+
+
+ ,
+ );
+
+ expect(getByText('Number')).toBeInTheDocument();
+
+ act(() => {
+ userEvent.click(getByTestId('toggleBtn'));
+ });
+
+ await wait();
+
+ availableFieldTypes.forEach(async (_, index) => {
+ act(() => {
+ userEvent.click(getByTestId(`dropdown-btn-${index}`));
+ });
+ });
+ });
+});
diff --git a/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.tsx b/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.tsx
new file mode 100644
index 0000000000..2350d2c9c4
--- /dev/null
+++ b/src/components/EditCustomFieldDropDown/EditCustomFieldDropDown.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import type { SetStateAction, Dispatch } from 'react';
+import { Dropdown } from 'react-bootstrap';
+import availableFieldTypes from 'utils/fieldTypes';
+import { useTranslation } from 'react-i18next';
+import type { InterfaceCustomFieldData } from 'utils/interfaces';
+
+/**
+ * Props for the EditOrgCustomFieldDropDown component.
+ */
+interface InterfaceEditCustomFieldDropDownProps {
+ customFieldData: InterfaceCustomFieldData;
+ setCustomFieldData: Dispatch>;
+ parentContainerStyle?: string;
+ btnStyle?: string;
+ btnTextStyle?: string;
+}
+
+/**
+ * A dropdown component for editing custom field types.
+ *
+ * This component displays a dropdown menu that allows users to select a custom field type.
+ * It shows the current type of the field and provides a list of available types to choose from.
+ * When a new type is selected, it updates the custom field data.
+ *
+ * @param customFieldData - The current data of the custom field being edited.
+ * @param setCustomFieldData - Function to update the custom field data with the new type.
+ * @param parentContainerStyle - Optional CSS class to style the container of the dropdown.
+ * @param btnStyle - Optional CSS class to style the dropdown button.
+ * @param btnTextStyle - Optional CSS class to style the text inside the button.
+ * @returns JSX.Element - The rendered dropdown component.
+ */
+const EditOrgCustomFieldDropDown = ({
+ customFieldData,
+ setCustomFieldData,
+ parentContainerStyle,
+ btnStyle,
+}: InterfaceEditCustomFieldDropDownProps): JSX.Element => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgProfileField',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ return (
+
+
+ {customFieldData.type ? t(customFieldData.type) : tCommon('none')}
+
+
+ {availableFieldTypes.map((customFieldType, index: number) => (
+ {
+ setCustomFieldData({
+ ...customFieldData,
+ type: customFieldType,
+ });
+ }}
+ disabled={customFieldData.type === customFieldType}
+ >
+ {t(customFieldType)}
+
+ ))}
+
+
+ );
+};
+
+export default EditOrgCustomFieldDropDown;
diff --git a/src/components/EventCalendar/EventCalendar.module.css b/src/components/EventCalendar/EventCalendar.module.css
new file mode 100644
index 0000000000..67ecd2dec6
--- /dev/null
+++ b/src/components/EventCalendar/EventCalendar.module.css
@@ -0,0 +1,342 @@
+.calendar {
+ font-family: sans-serif;
+ font-size: 1.2rem;
+ margin-bottom: 20px;
+ background: rgb(255, 255, 255);
+ border-radius: 10px;
+ padding: 5px;
+}
+.calendar__header {
+ display: flex;
+ margin-bottom: 2rem;
+ align-items: center;
+ margin: 0px 10px 0px 10px;
+}
+.input {
+ flex: 1;
+ position: relative;
+}
+.calendar__header_month {
+ margin: 0.5rem;
+ color: #707070;
+ font-weight: 800;
+ font-size: 55px;
+ display: flex;
+ gap: 23px;
+ flex-direction: row;
+}
+.calendar__header_month div {
+ font-weight: 400;
+ color: black;
+ font-family: Outfit;
+}
+.space {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+}
+.button {
+ border-radius: 100px;
+ color: #707070;
+ background-color: rgba(194, 247, 182, 0);
+ font-weight: bold;
+ border: 0px;
+ font-size: 20px;
+}
+
+.calendar__weekdays {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ background-color: black;
+ font-family: Outfit;
+ height: 60px;
+}
+.calendar__scroll {
+ height: 80vh;
+ padding: 10px;
+}
+.weekday {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: white;
+ font-weight: 600;
+}
+.calendar__days {
+ display: grid;
+ grid-template-columns: repeat(7, minmax(0, 1fr));
+ grid-template-rows: repeat(6, 1fr);
+}
+.calendar_hour_text_container {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ border-right: 1px solid #8d8d8d55;
+ width: 40px;
+}
+.calendar_hour_text {
+ top: -10px;
+ left: -5px;
+ position: relative;
+ color: #707070;
+ font-size: 12px;
+}
+.calendar_timezone_text {
+ top: -10px;
+ left: -11px;
+ position: relative;
+ color: #707070;
+ font-size: 9px;
+}
+.calendar_hour_block {
+ display: flex;
+ flex-direction: row;
+ border-bottom: 1px solid #8d8d8d55;
+ position: relative;
+ height: 50px;
+ border-bottom-right-radius: 5px;
+}
+.event_list_parent {
+ position: relative;
+ width: 100%;
+}
+.event_list_parent_current {
+ background-color: #def6e1;
+ position: relative;
+ width: 100%;
+}
+.dummyWidth {
+ width: 1px;
+}
+.day {
+ background-color: #ffffff;
+ padding-left: 0.3rem;
+ padding-right: 0.3rem;
+ border-radius: 10px;
+ margin: 5px;
+ background-color: white;
+ border: 1px solid #8d8d8d55;
+ color: #4b4b4b;
+ font-weight: 600;
+ height: 9rem;
+ position: relative;
+}
+.day_weekends {
+ background-color: rgba(49, 187, 107, 0.2);
+}
+.day__outside {
+ background-color: white;
+ color: #89898996;
+}
+.day__selected {
+ background-color: #007bff;
+ color: #707070;
+}
+.day__today {
+ background-color: #def6e1;
+ font-weight: 700;
+ text-decoration: underline;
+ color: #31bb6b;
+}
+.day__events {
+ background-color: white;
+}
+.btn__today {
+ transition: ease-in all 200ms;
+ font-family: Arial;
+ color: #ffffff;
+ font-size: 18px;
+ padding: 10px 20px 10px 20px;
+ text-decoration: none;
+ margin-left: 20px;
+ border: none;
+}
+.btnsContainer {
+ display: flex;
+ margin: 2.5rem 0 2.5rem 0;
+}
+
+.btnsContainer .btnsBlock {
+ display: flex;
+}
+
+.btnsContainer .btnsBlock button {
+ margin-left: 1rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.dropdown {
+ border-color: #31bb6b;
+ background-color: white;
+ color: #31bb6b;
+ box-shadow: 0px 2px 1px rgba(49, 187, 107, 0.5); /* Added blur effect */
+}
+
+.btnsContainer .input {
+ flex: 1;
+ position: relative;
+}
+
+.btnsContainer input {
+ outline: 1px solid var(--bs-gray-400);
+}
+
+.btnsContainer .input button {
+ width: 52px;
+}
+.searchBtn {
+ margin-bottom: 10px;
+}
+
+.inputField {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ background-color: white;
+ box-shadow: 0 1px 1px #31bb6b;
+}
+.inputField > button {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+.btn__more {
+ border: 0px;
+ font-size: 14px;
+ background-color: initial;
+ color: #262727;
+ font-weight: 600;
+ transition: all 200ms;
+ position: relative;
+ display: block;
+ margin: 2px;
+}
+.btn__more:hover {
+ color: #454645;
+}
+
+.expand_event_list {
+ display: block;
+}
+.list_container {
+ padding: 5px;
+ width: fit-content;
+ display: flex;
+ flex-direction: row;
+}
+.event_list_hour {
+ display: flex;
+ flex-direction: row;
+}
+.expand_list_container {
+ width: 200px;
+ max-height: 250px;
+ z-index: 10;
+ position: absolute;
+ left: auto;
+ right: auto;
+ overflow: auto;
+ padding: 10px 4px 0px 4px;
+ background-color: rgb(241, 241, 241);
+ border: 1px solid rgb(201, 201, 201);
+ border-radius: 5px;
+ margin: 5px;
+}
+.flex_grow {
+ flex-grow: 1;
+}
+
+@media only screen and (max-width: 700px) {
+ .event_list {
+ display: none;
+ }
+ .expand_list_container {
+ width: 150px;
+ padding: 4px 4px 0px 4px;
+ }
+ .day {
+ height: 5rem;
+ }
+}
+
+@media only screen and (max-width: 500px) {
+ .btn__more {
+ font-size: 12px;
+ }
+}
+
+.calendar__infocards {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ gap: 10px;
+ margin-top: 35px;
+}
+.card__holidays {
+ background-color: rgba(246, 242, 229, 1);
+ display: flex;
+ flex-direction: column;
+ width: 50%;
+ font-family: Outfit;
+ border-radius: 20px;
+ padding: 30px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.month__holidays {
+ display: flex;
+ flex-direction: column;
+}
+.holiday__data {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+}
+.holiday__date {
+ font-size: 20px;
+ color: rgba(234, 183, 86, 1);
+ font-weight: 700;
+}
+.holiday__name {
+ font-size: 20px;
+ margin-left: 35px;
+}
+.card__events {
+ background-color: rgba(244, 244, 244, 1);
+ display: flex;
+ flex-direction: column;
+ width: 50%;
+ font-family: Outfit;
+ border-radius: 20px;
+ padding: 30px;
+}
+.innercard__events {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 1rem;
+}
+.innercard__events p {
+ margin-bottom: 0;
+}
+.orgEvent__color {
+ height: 15px;
+ width: 40px;
+ background-color: rgba(82, 172, 255, 0.5);
+ border-radius: 10px;
+}
+.holidays__color {
+ height: 15px;
+ width: 40px;
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 10px;
+}
+
+.userEvents__color {
+ height: 15px;
+ width: 40px;
+ background: rgba(146, 200, 141, 0.5);
+ border-radius: 10px;
+}
diff --git a/src/components/EventCalendar/EventCalendar.test.tsx b/src/components/EventCalendar/EventCalendar.test.tsx
new file mode 100644
index 0000000000..8e2395968a
--- /dev/null
+++ b/src/components/EventCalendar/EventCalendar.test.tsx
@@ -0,0 +1,440 @@
+import Calendar from './EventCalendar';
+import { render, screen, fireEvent, act } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import React from 'react';
+import { ViewType } from 'screens/OrganizationEvents/OrganizationEvents';
+
+import {
+ DELETE_EVENT_MUTATION,
+ UPDATE_EVENT_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { weekdays, months } from './constants';
+import { BrowserRouter as Router } from 'react-router-dom';
+
+const eventData = [
+ {
+ _id: '1',
+ title: 'Event 1',
+ description: 'This is event 1',
+ startDate: '2022-05-01',
+ endDate: '2022-05-01',
+ location: 'New York',
+ startTime: '10:00',
+ endTime: '12:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ viewType: ViewType.DAY,
+ },
+ {
+ _id: '2',
+ title: 'Event 2',
+ description: 'This is event 2',
+ startDate: '2022-05-03',
+ endDate: '2022-05-03',
+ location: 'Los Angeles',
+ startTime: '14:00',
+ endTime: '16:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+];
+
+const MOCKS = [
+ {
+ request: {
+ query: DELETE_EVENT_MUTATION,
+ variable: { id: '123' },
+ },
+ result: {
+ data: {
+ removeEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variable: {
+ id: '123',
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: true,
+ recurring: false,
+ isRegisterable: true,
+ allDay: false,
+ location: 'New Delhi',
+ startTime: '02:00',
+ endTime: '07:00',
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 200): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Calendar', () => {
+ it('renders weekdays', () => {
+ render( );
+
+ weekdays.forEach((weekday) => {
+ expect(screen.getByText(weekday)).toBeInTheDocument();
+ });
+ });
+ it('should initialize currentMonth and currentYear with the current date', () => {
+ const today = new Date();
+ const { getByTestId } = render( );
+
+ const currentMonth = getByTestId('current-date');
+ const currentYear = getByTestId('current-date');
+
+ expect(currentMonth).toHaveTextContent(
+ today.toLocaleString('default', { month: 'long' }),
+ );
+ expect(currentYear).toHaveTextContent(today.getFullYear().toString());
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render the current month and year', () => {
+ const { getByTestId } = render( );
+
+ // Find the element by its data-testid attribute
+ const currentDateElement = getByTestId('current-date');
+
+ // Assert that the text content of the element matches the current month and year
+ const currentMonth = new Date().toLocaleString('default', {
+ month: 'long',
+ });
+ const currentYear = new Date().getFullYear();
+ const expectedText = ` ${currentYear} ${currentMonth}`;
+ expect(currentDateElement.textContent).toContain(expectedText);
+ });
+
+ it('Should show prev and next month on clicking < & > buttons', () => {
+ //testing previous month button
+ render(
+
+
+
+
+ ,
+ );
+ const prevButton = screen.getByTestId('prevmonthordate');
+ fireEvent.click(prevButton);
+ //testing next month button
+ const nextButton = screen.getByTestId('nextmonthordate');
+ fireEvent.click(nextButton);
+ //Testing year change
+ for (let index = 0; index < 13; index++) {
+ fireEvent.click(nextButton);
+ }
+ for (let index = 0; index < 13; index++) {
+ fireEvent.click(prevButton);
+ }
+ });
+ it('Should show prev and next year on clicking < & > buttons when in year view', async () => {
+ //testing previous month button
+ render(
+
+
+
+
+ ,
+ );
+ await wait();
+ const prevButtons = screen.getAllByTestId('prevYear');
+ prevButtons.forEach((button) => {
+ fireEvent.click(button);
+ });
+ await wait();
+ //testing next year button
+ const nextButton = screen.getAllByTestId('prevYear');
+ nextButton.forEach((button) => {
+ fireEvent.click(button);
+ });
+ });
+ it('Should show prev and next date on clicking < & > buttons in the day view', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ //testing previous date button
+ const prevButton = screen.getByTestId('prevmonthordate');
+ fireEvent.click(prevButton);
+ //testing next date button
+ const nextButton = screen.getByTestId('nextmonthordate');
+ fireEvent.click(nextButton);
+ //Testing year change and month change
+ for (let index = 0; index < 366; index++) {
+ fireEvent.click(prevButton);
+ }
+ for (let index = 0; index < 732; index++) {
+ fireEvent.click(nextButton);
+ }
+ });
+ it('Should render eventlistcard of current day event', () => {
+ const currentDayEventMock = [
+ {
+ _id: '0',
+ title: 'demo',
+ description: 'agrsg',
+ startDate: new Date().toISOString().split('T')[0],
+ endDate: new Date().toISOString().split('T')[0],
+ location: 'delhi',
+ startTime: '10:00',
+ endTime: '12:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ ];
+ render(
+
+
+
+
+
+
+ ,
+ ,
+ );
+ });
+ it('Test for superadmin case', () => {
+ render(
+
+
+
+
+
+
+ ,
+ ,
+ );
+ });
+ it('Today Cell is having correct styles', () => {
+ render(
+
+
+
+
+
+
+ ,
+ ,
+ );
+ // const todayDate = new Date().getDate();
+ // const todayElement = screen.getByText(todayDate.toString());
+ // expect(todayElement).toHaveClass(styles.day__today);
+ });
+ it('Today button should show today cell', () => {
+ render(
+
+
+
+
+
+
+ ,
+ ,
+ );
+ //Changing the month
+ const prevButton = screen.getByTestId('prevmonthordate');
+ fireEvent.click(prevButton);
+
+ // Clicking today button
+ const todayButton = screen.getByTestId('today');
+ fireEvent.click(todayButton);
+ // const todayCell = screen.getByText(new Date().getDate().toString());
+ // expect(todayCell).toHaveClass(styles.day__today);
+ });
+ it('Should handle window resize in day view', async () => {
+ const date = new Date().toISOString().split('T')[0];
+ const multipleEventData = [
+ {
+ _id: '1',
+ title: 'Event 1',
+ description: 'This is event 1',
+ startDate: date,
+ endDate: date,
+ location: 'Los Angeles',
+ startTime: null,
+ endTime: null,
+ allDay: true,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ {
+ _id: '2',
+ title: 'Event 2',
+ description: 'This is event 2',
+ startDate: date,
+ endDate: date,
+ location: 'Los Angeles',
+ startTime: null,
+ endTime: null,
+ allDay: true,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ {
+ _id: '3',
+ title: 'Event 3',
+ description: 'This is event 3',
+ startDate: date,
+ endDate: date,
+ location: 'Los Angeles',
+ startTime: '14:00',
+ endTime: '16:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ {
+ _id: '4',
+ title: 'Event 4',
+ description: 'This is event 4',
+ startDate: date,
+ endDate: date,
+ location: 'Los Angeles',
+ startTime: '14:00',
+ endTime: '16:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ {
+ _id: '5',
+ title: 'Event 5',
+ description: 'This is event 5',
+ startDate: date,
+ endDate: date,
+ location: 'Los Angeles',
+ startTime: '17:00',
+ endTime: '19:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ ];
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ // Simulate window resize and check if components respond correctly
+ await act(async () => {
+ window.innerWidth = 500; // Set the window width to <= 700
+ window.dispatchEvent(new Event('resize'));
+ });
+
+ // Check for "View all" button if there are more than 2 events
+ const viewAllButton = await screen.findAllByTestId('more');
+ console.log('hi', viewAllButton); // This will show the buttons found in the test
+ expect(viewAllButton.length).toBeGreaterThan(0);
+
+ // Simulate clicking the "View all" button to expand the list
+ fireEvent.click(viewAllButton[0]);
+
+ const event5 = screen.queryByText('Event 5');
+ expect(event5).toBeNull();
+
+ const viewLessButtons = screen.getAllByText('View less');
+ expect(viewLessButtons.length).toBeGreaterThan(0);
+
+ // Simulate clicking "View less" to collapse the list
+ fireEvent.click(viewLessButtons[0]);
+ const viewAllButtons = screen.getAllByText('View all');
+ expect(viewAllButtons.length).toBeGreaterThan(0);
+
+ // Reset the window size to avoid side effects for other tests
+ await act(async () => {
+ window.innerWidth = 1024;
+ window.dispatchEvent(new Event('resize'));
+ });
+ });
+ test('Handles window resize', () => {
+ render(
+
+
+
+
+
+
+ ,
+ ,
+ );
+
+ act(() => {
+ window.dispatchEvent(new Event('resize'));
+ });
+ });
+ it('renders year view', async () => {
+ render( );
+
+ await wait();
+ months.forEach((month) => {
+ const elements = screen.getAllByText(month);
+ elements.forEach((element) => {
+ expect(element).toBeInTheDocument();
+ });
+ });
+ });
+});
diff --git a/src/components/EventCalendar/EventCalendar.tsx b/src/components/EventCalendar/EventCalendar.tsx
new file mode 100644
index 0000000000..592456f295
--- /dev/null
+++ b/src/components/EventCalendar/EventCalendar.tsx
@@ -0,0 +1,657 @@
+import EventListCard from 'components/EventListCard/EventListCard';
+import dayjs from 'dayjs';
+import Button from 'react-bootstrap/Button';
+import React, { useState, useEffect } from 'react';
+import styles from './EventCalendar.module.css';
+import { ChevronLeft, ChevronRight } from '@mui/icons-material';
+import CurrentHourIndicator from 'components/CurrentHourIndicator/CurrentHourIndicator';
+import { ViewType } from 'screens/OrganizationEvents/OrganizationEvents';
+import HolidayCard from '../HolidayCards/HolidayCard';
+import { holidays, hours, months, weekdays } from './constants';
+import type { InterfaceRecurrenceRule } from 'utils/recurrenceUtils';
+import YearlyEventCalender from './YearlyEventCalender';
+
+interface InterfaceEventListCardProps {
+ userRole?: string;
+ key?: string;
+ _id: string;
+ location: string;
+ title: string;
+ description: string;
+ startDate: string;
+ endDate: string;
+ startTime: string | null;
+ endTime: string | null;
+ allDay: boolean;
+ recurring: boolean;
+ recurrenceRule: InterfaceRecurrenceRule | null;
+ isRecurringEventException: boolean;
+ isPublic: boolean;
+ isRegisterable: boolean;
+ attendees?: {
+ _id: string;
+ }[];
+ creator?: {
+ firstName: string;
+ lastName: string;
+ _id: string;
+ };
+}
+
+interface InterfaceCalendarProps {
+ eventData: InterfaceEventListCardProps[];
+ refetchEvents?: () => void;
+ orgData?: InterfaceIOrgList;
+ userRole?: string;
+ userId?: string;
+ viewType?: ViewType;
+}
+
+enum Role {
+ USER = 'USER',
+ SUPERADMIN = 'SUPERADMIN',
+ ADMIN = 'ADMIN',
+}
+
+interface InterfaceIOrgList {
+ admins: { _id: string }[];
+}
+
+const Calendar: React.FC = ({
+ eventData,
+ refetchEvents,
+ orgData,
+ userRole,
+ userId,
+ viewType,
+}) => {
+ const [selectedDate] = useState(null);
+ const today = new Date();
+ const [currentDate, setCurrentDate] = useState(today.getDate());
+ const [currentMonth, setCurrentMonth] = useState(today.getMonth());
+ const [currentYear, setCurrentYear] = useState(today.getFullYear());
+ const [events, setEvents] = useState(
+ null,
+ );
+ const [expanded, setExpanded] = useState(-1);
+ const [windowWidth, setWindowWidth] = useState(window.screen.width);
+
+ useEffect(() => {
+ function handleResize(): void {
+ setWindowWidth(window.screen.width);
+ }
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ const filterData = (
+ eventData: InterfaceEventListCardProps[],
+ orgData?: InterfaceIOrgList,
+ userRole?: string,
+ userId?: string,
+ ): InterfaceEventListCardProps[] => {
+ const data: InterfaceEventListCardProps[] = [];
+ if (userRole === Role.SUPERADMIN) return eventData;
+ // Hard to test all the cases
+ /* istanbul ignore next */
+ if (userRole === Role.ADMIN) {
+ eventData?.forEach((event) => {
+ if (event.isPublic) data.push(event);
+ if (!event.isPublic) {
+ const filteredOrg: boolean | undefined = orgData?.admins?.some(
+ (data) => data._id === userId,
+ );
+
+ if (filteredOrg) {
+ data.push(event);
+ }
+ }
+ });
+ } else {
+ eventData?.forEach((event) => {
+ if (event.isPublic) data.push(event);
+ const userAttending = event.attendees?.some(
+ (data) => data._id === userId,
+ );
+ if (userAttending) {
+ data.push(event);
+ }
+ });
+ }
+ return data;
+ };
+
+ useEffect(() => {
+ const data = filterData(eventData, orgData, userRole, userId);
+ setEvents(data);
+ }, [eventData, orgData, userRole, userId]);
+
+ /**
+ * Moves the calendar view to the previous month.
+ */
+ const handlePrevMonth = (): void => {
+ /*istanbul ignore next*/
+ if (currentMonth === 0) {
+ setCurrentMonth(11);
+ setCurrentYear(currentYear - 1);
+ } else {
+ setCurrentMonth(currentMonth - 1);
+ }
+ };
+
+ /**
+ * Moves the calendar view to the next month.
+ */
+ const handleNextMonth = (): void => {
+ /*istanbul ignore next*/
+ if (currentMonth === 11) {
+ setCurrentMonth(0);
+ setCurrentYear(currentYear + 1);
+ } else {
+ setCurrentMonth(currentMonth + 1);
+ }
+ };
+
+ /**
+ * Moves the calendar view to the previous date.
+ */
+ const handlePrevDate = (): void => {
+ /*istanbul ignore next*/
+ if (currentDate > 1) {
+ setCurrentDate(currentDate - 1);
+ } else {
+ if (currentMonth > 0) {
+ const lastDayOfPrevMonth = new Date(
+ currentYear,
+ currentMonth,
+ 0,
+ ).getDate();
+ setCurrentDate(lastDayOfPrevMonth);
+ setCurrentMonth(currentMonth - 1);
+ } else {
+ setCurrentDate(31);
+ setCurrentMonth(11);
+ setCurrentYear(currentYear - 1);
+ }
+ }
+ };
+ /*istanbul ignore next*/
+ const handleNextDate = (): void => {
+ /*istanbul ignore next*/
+ const lastDayOfCurrentMonth = new Date(
+ currentYear,
+ currentMonth - 1,
+ 0,
+ ).getDate();
+ /*istanbul ignore next*/
+ if (currentDate < lastDayOfCurrentMonth) {
+ setCurrentDate(currentDate + 1);
+ } else {
+ if (currentMonth < 12) {
+ setCurrentDate(1);
+ setCurrentMonth(currentMonth + 1);
+ } else {
+ setCurrentDate(1);
+ setCurrentMonth(1);
+ setCurrentYear(currentYear + 1);
+ }
+ }
+ };
+
+ /**
+ * Moves the calendar view to today's date.
+ */
+ const handleTodayButton = (): void => {
+ /*istanbul ignore next*/
+ setCurrentYear(today.getFullYear());
+ setCurrentMonth(today.getMonth());
+ setCurrentDate(today.getDate());
+ };
+
+ const timezoneString = `UTC${
+ new Date().getTimezoneOffset() > 0 ? '-' : '+'
+ }${String(Math.floor(Math.abs(new Date().getTimezoneOffset()) / 60)).padStart(
+ 2,
+ '0',
+ )}:${String(Math.abs(new Date().getTimezoneOffset()) % 60).padStart(2, '0')}`;
+
+ /*istanbul ignore next*/
+ const renderHours = (): JSX.Element => {
+ const toggleExpand = (index: number): void => {
+ if (expanded === index) {
+ setExpanded(-1);
+ } else {
+ setExpanded(index);
+ }
+ };
+
+ /*istanbul ignore next*/
+ const allDayEventsList: JSX.Element[] =
+ events
+ ?.filter((datas) => {
+ /*istanbul ignore next*/
+ const currDate = new Date(currentYear, currentMonth, currentDate);
+ if (
+ datas.startTime == undefined &&
+ datas.startDate == dayjs(currDate).format('YYYY-MM-DD')
+ ) {
+ return datas;
+ }
+ })
+ .map((datas: InterfaceEventListCardProps) => {
+ const attendees: { _id: string }[] = [];
+ datas.attendees?.forEach((attendee: { _id: string }) => {
+ const r = {
+ _id: attendee._id,
+ };
+
+ attendees.push(r);
+ });
+
+ return (
+
+ );
+ }) || [];
+
+ return (
+ <>
+
+
+
+
0
+ ? styles.event_list_parent_current
+ : styles.event_list_parent
+ }
+ >
+
+
+ {expanded === -100
+ ? allDayEventsList
+ : allDayEventsList?.slice(0, 1)}
+
+ {(allDayEventsList?.length > 2 ||
+ (windowWidth <= 700 && allDayEventsList?.length > 0)) && (
+
{
+ toggleExpand(-100);
+ }}
+ >
+ {expanded === -100 ? 'View less' : 'View all'}
+
+ )}
+
+
+
+ {hours.map((hour, index) => {
+ const timeEventsList: JSX.Element[] =
+ events
+ ?.filter((datas) => {
+ const currDate = new Date(
+ currentYear,
+ currentMonth,
+ currentDate,
+ );
+
+ if (
+ parseInt(datas.startTime?.slice(0, 2) as string).toString() ==
+ (index % 24).toString() &&
+ datas.startDate == dayjs(currDate).format('YYYY-MM-DD')
+ ) {
+ return datas;
+ }
+ })
+ .map((datas: InterfaceEventListCardProps) => {
+ const attendees: { _id: string }[] = [];
+ datas.attendees?.forEach((attendee: { _id: string }) => {
+ const r = {
+ _id: attendee._id,
+ };
+
+ attendees.push(r);
+ });
+
+ return (
+
+ );
+ }) || [];
+ /*istanbul ignore next*/
+ return (
+
+
+
+
0
+ ? styles.event_list_parent_current
+ : styles.event_list_parent
+ }
+ >
+ {index % 24 == new Date().getHours() &&
+ new Date().getDate() == currentDate && (
+
+ )}
+
+
+ {/*istanbul ignore next*/}
+ {expanded === index
+ ? timeEventsList
+ : timeEventsList?.slice(0, 1)}
+
+ {(timeEventsList?.length > 1 ||
+ (windowWidth <= 700 && timeEventsList?.length > 0)) && (
+
{
+ toggleExpand(index);
+ }}
+ >
+ {expanded === index ? 'View less' : 'View all'}
+
+ )}
+
+
+
+ );
+ })}
+ >
+ );
+ };
+
+ const renderDays = (): JSX.Element[] => {
+ const monthStart = new Date(currentYear, currentMonth, 1);
+ const monthEnd = new Date(currentYear, currentMonth + 1, 0);
+ const startDate = new Date(
+ monthStart.getFullYear(),
+ monthStart.getMonth(),
+ monthStart.getDate() - monthStart.getDay(),
+ );
+ const endDate = new Date(
+ monthEnd.getFullYear(),
+ monthEnd.getMonth(),
+ monthEnd.getDate() + (6 - monthEnd.getDay()),
+ );
+ const days = [];
+ let currentDate = startDate;
+ while (currentDate <= endDate) {
+ days.push(currentDate);
+ currentDate = new Date(
+ currentDate.getFullYear(),
+ currentDate.getMonth(),
+ currentDate.getDate() + 1,
+ );
+ }
+
+ return days.map((date, index) => {
+ const className = [
+ date.getDay() === 0 || date.getDay() === 6 ? styles.day_weekends : '',
+ date.toLocaleDateString() === today.toLocaleDateString() //Styling for today day cell
+ ? styles.day__today
+ : '',
+ date.getMonth() !== currentMonth ? styles.day__outside : '', //Styling for days outside the current month
+ selectedDate?.getTime() === date.getTime() ? styles.day__selected : '',
+ styles.day,
+ ].join(' ');
+ const toggleExpand = (index: number): void => {
+ /*istanbul ignore next*/
+ if (expanded === index) {
+ setExpanded(-1);
+ } else {
+ setExpanded(index);
+ }
+ };
+ /*istanbul ignore next*/
+ const allEventsList: JSX.Element[] =
+ events
+ ?.filter((datas) => {
+ if (datas.startDate == dayjs(date).format('YYYY-MM-DD'))
+ return datas;
+ })
+ .map((datas: InterfaceEventListCardProps) => {
+ const attendees: { _id: string }[] = [];
+ datas.attendees?.forEach((attendee: { _id: string }) => {
+ const r = {
+ _id: attendee._id,
+ };
+
+ attendees.push(r);
+ });
+
+ return (
+
+ );
+ }) || [];
+
+ const holidayList: JSX.Element[] = holidays
+ .filter((holiday) => {
+ if (holiday.date == dayjs(date).format('MM-DD')) return holiday;
+ })
+ .map((holiday) => {
+ return ;
+ });
+ return (
+ 0 && styles.day__events)
+ }
+ data-testid="day"
+ >
+ {date.getDate()}
+ {date.getMonth() !== currentMonth ? null : (
+
+
+
{holidayList}
+ {
+ /*istanbul ignore next*/
+ expanded === index
+ ? allEventsList
+ : holidayList?.length > 0
+ ? /*istanbul ignore next*/
+ allEventsList?.slice(0, 1)
+ : allEventsList?.slice(0, 2)
+ }
+
+ {(allEventsList?.length > 2 ||
+ (windowWidth <= 700 && allEventsList?.length > 0)) && (
+ /*istanbul ignore next*/
+
{
+ toggleExpand(index);
+ }}
+ >
+ {
+ /*istanbul ignore next*/
+ expanded === index ? 'View less' : 'View all'
+ }
+
+ )}
+
+ )}
+
+ );
+ });
+ };
+
+ return (
+
+ {viewType != ViewType.YEAR && (
+
+
+
+
+
+
+ {viewType == ViewType.DAY ? `${currentDate}` : ``} {currentYear}{' '}
+
{months[currentMonth]}
+
+
+
+
+
+
+ Today
+
+
+
+ )}
+
+ {viewType == ViewType.MONTH ? (
+
+
+ {weekdays.map((weekday, index) => (
+
+ {weekday}
+
+ ))}
+
+
{renderDays()}
+
+ ) : (
+ //
+
+ {viewType == ViewType.YEAR ? (
+
+ ) : (
+
{renderHours()}
+ )}
+
+ )}
+
+
+ {viewType == ViewType.YEAR ? (
+
+ ) : (
+
{renderHours()}
+ )}
+
+
+ );
+};
+
+export default Calendar;
diff --git a/src/components/EventCalendar/EventHeader.test.tsx b/src/components/EventCalendar/EventHeader.test.tsx
new file mode 100644
index 0000000000..e18d066306
--- /dev/null
+++ b/src/components/EventCalendar/EventHeader.test.tsx
@@ -0,0 +1,99 @@
+import React, { act } from 'react'; // Import act for async testing
+import { render, fireEvent } from '@testing-library/react';
+import EventHeader from './EventHeader';
+import { ViewType } from '../../screens/OrganizationEvents/OrganizationEvents';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+
+describe('EventHeader Component', () => {
+ const viewType = ViewType.MONTH;
+ const handleChangeView = jest.fn();
+ const showInviteModal = jest.fn();
+
+ it('renders correctly', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ expect(getByTestId('searchEvent')).toBeInTheDocument();
+ expect(getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ it('calls handleChangeView with selected view type', async () => {
+ // Add async keyword
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ fireEvent.click(getByTestId('selectViewType'));
+
+ await act(async () => {
+ fireEvent.click(getByTestId('selectDay'));
+ });
+
+ // Expect handleChangeView to be called with the new view type
+ expect(handleChangeView).toHaveBeenCalledTimes(1);
+ });
+ it('calls handleChangeView with selected event type', async () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ fireEvent.click(getByTestId('eventType'));
+
+ await act(async () => {
+ fireEvent.click(getByTestId('events'));
+ });
+
+ expect(handleChangeView).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls showInviteModal when create event button is clicked', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ fireEvent.click(getByTestId('createEventModalBtn'));
+ expect(showInviteModal).toHaveBeenCalled();
+ });
+ it('updates the input value when changed', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const input = getByTestId('searchEvent') as HTMLInputElement;
+ fireEvent.change(input, { target: { value: 'test event' } });
+
+ expect(input.value).toBe('test event');
+ });
+});
diff --git a/src/components/EventCalendar/EventHeader.tsx b/src/components/EventCalendar/EventHeader.tsx
new file mode 100644
index 0000000000..1e4653ebf4
--- /dev/null
+++ b/src/components/EventCalendar/EventHeader.tsx
@@ -0,0 +1,127 @@
+import React, { useState } from 'react';
+import { Button, Dropdown, Form } from 'react-bootstrap';
+import { Search } from '@mui/icons-material';
+import styles from './EventCalendar.module.css';
+import { ViewType } from '../../screens/OrganizationEvents/OrganizationEvents';
+import { useTranslation } from 'react-i18next';
+
+/**
+ * Props for the EventHeader component.
+ */
+interface InterfaceEventHeaderProps {
+ viewType: ViewType;
+ handleChangeView: (item: string | null) => void;
+ showInviteModal: () => void;
+}
+
+/**
+ * EventHeader component displays the header for the event calendar.
+ * It includes a search field, view type dropdown, event type dropdown, and a button to create an event.
+ *
+ * @param viewType - The current view type of the calendar.
+ * @param handleChangeView - Function to handle changing the view type.
+ * @param showInviteModal - Function to show the invite modal for creating an event.
+ * @returns JSX.Element - The rendered EventHeader component.
+ */
+function eventHeader({
+ viewType,
+ handleChangeView,
+ showInviteModal,
+}: InterfaceEventHeaderProps): JSX.Element {
+ const [eventName, setEventName] = useState('');
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'organizationEvents',
+ });
+
+ return (
+
+
+
+
setEventName(e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+ {viewType}
+
+
+
+ {ViewType.MONTH}
+
+
+ {ViewType.DAY}
+
+
+ {ViewType.YEAR}
+
+
+
+
+
+
+
+ {t('eventType')}
+
+
+
+ Events
+
+
+ Workshops
+
+
+
+
+
+ Create Event
+
+
+
+
+ );
+}
+
+export default eventHeader;
diff --git a/src/components/EventCalendar/YearlyEventCalender.module.css b/src/components/EventCalendar/YearlyEventCalender.module.css
new file mode 100644
index 0000000000..723cb20e22
--- /dev/null
+++ b/src/components/EventCalendar/YearlyEventCalender.module.css
@@ -0,0 +1,354 @@
+.calendar {
+ font-family: sans-serif;
+ font-size: 1.2rem;
+ margin-bottom: 20px;
+}
+.calendar__header {
+ display: flex;
+ margin-bottom: 2rem;
+ align-items: center;
+ margin: 0px 10px 0px 10px;
+}
+.input {
+ flex: 1;
+ position: relative;
+}
+.calendar__header_month {
+ margin: 0.5rem;
+ color: #707070;
+ font-weight: bold;
+}
+.space {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+}
+.button {
+ color: #707070;
+ background-color: rgba(0, 0, 0, 0);
+ font-weight: bold;
+ border: 0px;
+}
+.calendar__weekdays {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ background-color: #707070;
+ height: 60px;
+}
+.calendar__scroll {
+ height: 80vh;
+ padding: 10px;
+}
+.weekday {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: #fff;
+ background-color: #31bb6b;
+ font-weight: 600;
+}
+.calendar__days {
+ display: grid;
+ grid-template-columns: repeat(7, minmax(0, 1fr));
+ grid-template-rows: repeat(6, 1fr);
+}
+.calendar_hour_text_container {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ border-right: 1px solid #8d8d8d55;
+ width: 40px;
+}
+.calendar_hour_text {
+ top: -10px;
+ left: -5px;
+ position: relative;
+ color: #707070;
+ font-size: 12px;
+}
+.calendar_timezone_text {
+ top: -10px;
+ left: -11px;
+ position: relative;
+ color: #707070;
+ font-size: 9px;
+}
+.calendar_hour_block {
+ display: flex;
+ flex-direction: row;
+ border-bottom: 1px solid #8d8d8d55;
+ position: relative;
+ height: 50px;
+ border-bottom-right-radius: 5px;
+}
+.event_list_parent {
+ position: relative;
+ width: 100%;
+}
+.event_list_parent_current {
+ background-color: #def6e1;
+ position: relative;
+ width: 100%;
+}
+.dummyWidth {
+ width: 1px;
+}
+.day {
+ background-color: #ffffff;
+ padding-left: 0.3rem;
+ padding-right: 0.3rem;
+ background-color: white;
+ border: 1px solid #8d8d8d55;
+ color: black;
+ font-weight: 600;
+ height: 8rem;
+ position: relative;
+}
+.day__outside {
+ /* background-color: #ededee !important; */
+ color: #89898996 !important;
+}
+.day__selected {
+ background-color: #007bff;
+ color: #707070;
+}
+.day__today {
+ background-color: #def6e1;
+ font-weight: 700;
+ text-decoration: underline;
+ color: #31bb6b;
+}
+.day__events {
+ background-color: #def6e1;
+}
+/* yearly calender styling */
+.yearlyCalender {
+ background-color: #ffffff;
+ box-sizing: border-box;
+}
+
+.circularButton {
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ background-color: rgba(255, 255, 255, 0);
+ border: none;
+ cursor: pointer;
+ margin-left: 0.75rem;
+}
+.circularButton:hover {
+ background-color: rgba(82, 172, 255, 0.5);
+}
+.closebtn {
+ padding: 10px;
+}
+
+.yearlyCalendarHeader {
+ display: flex;
+ flex-direction: row;
+}
+
+.yearlyCalendarHeader > div {
+ font-weight: 600;
+ font-size: 2rem;
+ padding: 0 10px;
+ color: #4b4b4b;
+}
+.noEventAvailable {
+ background-color: rgba(255, 255, 255, 0);
+}
+
+.card {
+ /* box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); */
+ padding: 16px;
+ text-align: center;
+ /* background-color: #f1f1f1; */
+ height: 21rem;
+}
+.cardHeader {
+ text-align: left;
+}
+
+.row {
+ margin: 1px -5px;
+}
+
+/* Clear floats after the columns */
+.row:after {
+ content: '';
+ display: table;
+ clear: both;
+}
+
+.weekday__yearly {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ /* color: #fff; */
+ background-color: #ffffff;
+ font-weight: 600;
+}
+.day__yearly {
+ background-color: #ffffff;
+ padding-left: 0.3rem;
+ padding-right: 0.3rem;
+ background-color: white;
+ /* border: 1px solid #8d8d8d55; */
+ color: #4b4b4b;
+ font-weight: 600;
+ height: 2rem;
+ position: relative;
+}
+* {
+ box-sizing: border-box;
+}
+
+/* Float four columns side by side */
+.column {
+ float: left;
+ width: 25%;
+ padding: 10px;
+}
+
+/* Remove extra left and right margins, due to padding */
+.row {
+ margin: 0 -5px;
+}
+
+/* Clear floats after the columns */
+.row:after {
+ content: '';
+ display: table;
+ clear: both;
+}
+/* yearly calender styling ends */
+.btn__today {
+ transition: ease-in all 200ms;
+ font-family: Arial;
+ color: #ffffff;
+ font-size: 18px;
+ padding: 10px 20px 10px 20px;
+ text-decoration: none;
+ margin-left: 20px;
+ border: none;
+}
+.btnsContainer {
+ display: flex;
+ margin: 2.5rem 0 2.5rem 0;
+}
+
+.btnsContainer .btnsBlock {
+ display: flex;
+}
+
+.btnsContainer .btnsBlock button {
+ margin-left: 1rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.dropdown {
+ border-color: #31bb6b;
+ background-color: white;
+ color: #31bb6b;
+ box-shadow: 0px 2px 1px rgba(49, 187, 107, 0.5); /* Added blur effect */
+}
+
+.btnsContainer .input {
+ flex: 1;
+ position: relative;
+}
+
+.btnsContainer input {
+ outline: 1px solid var(--bs-gray-400);
+}
+
+.btnsContainer .input button {
+ width: 52px;
+}
+.searchBtn {
+ margin-bottom: 10px;
+}
+
+.inputField {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ background-color: white;
+ box-shadow: 0 1px 1px #31bb6b;
+}
+.inputField > button {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+.btn__more {
+ border: 0px;
+ font-size: 14px;
+ background-color: initial;
+ font-weight: 600;
+ transition: all 200ms;
+ position: relative;
+ display: block;
+ margin: -9px;
+ margin-top: -28px;
+}
+.btn__more:hover {
+ color: #3ce080;
+}
+
+.expand_event_list {
+ display: block;
+}
+.list_container {
+ padding: 5px;
+ width: fit-content;
+ display: flex;
+ flex-direction: row;
+}
+.event_list_hour {
+ display: flex;
+ flex-direction: row;
+}
+.expand_list_container {
+ width: 200px;
+ max-height: 250px;
+ z-index: 10;
+ position: absolute;
+ left: auto;
+ right: auto;
+ overflow: auto;
+ padding: 10px 4px 0px 4px;
+ background-color: rgb(241, 241, 241);
+ border: 1px solid rgb(201, 201, 201);
+ border-radius: 5px;
+ margin: 5px;
+}
+.flex_grow {
+ flex-grow: 1;
+}
+
+@media only screen and (max-width: 700px) {
+ .event_list {
+ display: none;
+ }
+ .expand_list_container {
+ width: 150px;
+ padding: 4px 4px 0px 4px;
+ }
+ .day {
+ height: 5rem;
+ }
+}
+
+@media only screen and (max-width: 500px) {
+ .btn__more {
+ font-size: 12px;
+ }
+
+ .column {
+ float: left;
+ width: 100%;
+ padding: 10px;
+ }
+}
diff --git a/src/components/EventCalendar/YearlyEventCalender.tsx b/src/components/EventCalendar/YearlyEventCalender.tsx
new file mode 100644
index 0000000000..63870ded3c
--- /dev/null
+++ b/src/components/EventCalendar/YearlyEventCalender.tsx
@@ -0,0 +1,412 @@
+import EventListCard from 'components/EventListCard/EventListCard';
+import dayjs from 'dayjs';
+import Button from 'react-bootstrap/Button';
+import React, { useState, useEffect } from 'react';
+import styles from './YearlyEventCalender.module.css';
+import type { ViewType } from 'screens/OrganizationEvents/OrganizationEvents';
+import { ChevronLeft, ChevronRight } from '@mui/icons-material';
+import type { InterfaceRecurrenceRule } from 'utils/recurrenceUtils';
+
+/**
+ * Interface for event data used in the calendar.
+ */
+interface InterfaceEventListCardProps {
+ userRole?: string;
+ key?: string;
+ _id: string;
+ location: string;
+ title: string;
+ description: string;
+ startDate: string;
+ endDate: string;
+ startTime: string | null;
+ endTime: string | null;
+ allDay: boolean;
+ recurring: boolean;
+ recurrenceRule: InterfaceRecurrenceRule | null;
+ isRecurringEventException: boolean;
+ isPublic: boolean;
+ isRegisterable: boolean;
+ attendees?: {
+ _id: string;
+ }[];
+ creator?: {
+ firstName: string;
+ lastName: string;
+ _id: string;
+ };
+}
+
+interface InterfaceCalendarProps {
+ eventData: InterfaceEventListCardProps[];
+ refetchEvents?: () => void;
+ orgData?: InterfaceIOrgList;
+ userRole?: string;
+ userId?: string;
+ viewType?: ViewType;
+}
+
+enum Status {
+ ACTIVE = 'ACTIVE',
+ BLOCKED = 'BLOCKED',
+ DELETED = 'DELETED',
+}
+
+/**
+ * Enum for different user roles.
+ */
+enum Role {
+ USER = 'USER',
+ SUPERADMIN = 'SUPERADMIN',
+ ADMIN = 'ADMIN',
+}
+
+/**
+ * Interface for event attendees.
+ */
+interface InterfaceIEventAttendees {
+ userId: string;
+ user?: string;
+ status?: Status;
+ createdAt?: Date;
+}
+
+/**
+ * Interface for organization list.
+ */
+interface InterfaceIOrgList {
+ admins: { _id: string }[];
+}
+
+/**
+ * Calendar component to display events for a selected year.
+ *
+ * This component renders a yearly calendar with navigation to view previous and next years.
+ * It displays events for each day, with functionality to expand and view details of events.
+ *
+ * @param eventData - Array of event data to display on the calendar.
+ * @param refetchEvents - Function to refresh the event data.
+ * @param orgData - Organization data to filter events.
+ * @param userRole - Role of the user for access control.
+ * @param userId - ID of the user for filtering events they are attending.
+ * @param viewType - Type of view for the calendar.
+ * @returns JSX.Element - The rendered calendar component.
+ */
+const Calendar: React.FC = ({
+ eventData,
+ refetchEvents,
+ orgData,
+ userRole,
+ userId,
+}) => {
+ const [selectedDate] = useState(null);
+ const weekdaysShorthand = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
+ const months = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+ ];
+ const today = new Date();
+ const [currentYear, setCurrentYear] = useState(today.getFullYear());
+ const [events, setEvents] = useState(
+ null,
+ );
+ const [expandedY, setExpandedY] = useState(null);
+
+ /**
+ * Filters events based on user role, organization data, and user ID.
+ *
+ * @param eventData - Array of event data to filter.
+ * @param orgData - Organization data for filtering events.
+ * @param userRole - Role of the user for access control.
+ * @param userId - ID of the user for filtering events they are attending.
+ * @returns Filtered array of event data.
+ */
+ const filterData = (
+ eventData: InterfaceEventListCardProps[],
+ orgData?: InterfaceIOrgList,
+ userRole?: string,
+ userId?: string,
+ ): InterfaceEventListCardProps[] => {
+ const data: InterfaceEventListCardProps[] = [];
+ if (userRole === Role.SUPERADMIN) return eventData;
+ // Hard to test all the cases
+ /* istanbul ignore next */
+ if (userRole === Role.ADMIN) {
+ eventData?.forEach((event) => {
+ if (event.isPublic) data.push(event);
+ if (!event.isPublic) {
+ const filteredOrg: boolean | undefined = orgData?.admins?.some(
+ (data) => data._id === userId,
+ );
+
+ if (filteredOrg) {
+ data.push(event);
+ }
+ }
+ });
+ } else {
+ eventData?.forEach((event) => {
+ if (event.isPublic) data.push(event);
+ const userAttending = event.attendees?.some(
+ (data) => data._id === userId,
+ );
+ if (userAttending) {
+ data.push(event);
+ }
+ });
+ }
+ return data;
+ };
+
+ useEffect(() => {
+ const data = filterData(eventData, orgData, userRole, userId);
+ setEvents(data);
+ }, [eventData, orgData, userRole, userId]);
+
+ /**
+ * Navigates to the previous year.
+ */
+ const handlePrevYear = (): void => {
+ /*istanbul ignore next*/
+ setCurrentYear(currentYear - 1);
+ };
+
+ /**
+ * Navigates to the next year.
+ */
+ const handleNextYear = (): void => {
+ /*istanbul ignore next*/
+ setCurrentYear(currentYear + 1);
+ };
+
+ /**
+ * Renders the days of the month for the calendar.
+ *
+ * @returns Array of JSX elements representing the days of each month.
+ */
+ const renderMonthDays = (): JSX.Element[] => {
+ const renderedMonths: JSX.Element[] = [];
+
+ for (let monthInx = 0; monthInx < 12; monthInx++) {
+ const monthStart = new Date(currentYear, monthInx, 1);
+ const monthEnd = new Date(currentYear, monthInx + 1, 0);
+
+ const startDate = new Date(monthStart);
+ const dayOfWeek = startDate.getDay();
+ const diff = startDate.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1);
+ startDate.setDate(diff);
+
+ const endDate = new Date(monthEnd);
+ const endDayOfWeek = endDate.getDay();
+ const diffEnd =
+ endDate.getDate() + (7 - endDayOfWeek) - (endDayOfWeek === 0 ? 7 : 0);
+ endDate.setDate(diffEnd);
+
+ const days = [];
+ let currentDate = startDate;
+ while (currentDate <= endDate) {
+ days.push(currentDate);
+ currentDate = new Date(
+ currentDate.getFullYear(),
+ currentDate.getMonth(),
+ currentDate.getDate() + 1,
+ );
+ }
+
+ const renderedDays = days.map((date, dayIndex) => {
+ const className = [
+ date.toLocaleDateString() === today.toLocaleDateString()
+ ? styles.day__today
+ : '',
+ date.getMonth() !== monthInx ? styles.day__outside : '',
+ selectedDate?.getTime() === date.getTime()
+ ? styles.day__selected
+ : '',
+ styles.day__yearly,
+ ].join(' ');
+
+ const eventsForCurrentDate = events?.filter((event) => {
+ return dayjs(event.startDate).isSame(date, 'day');
+ });
+
+ /*istanbul ignore next*/
+ const renderedEvents =
+ eventsForCurrentDate?.map((datas: InterfaceEventListCardProps) => {
+ const attendees: { _id: string }[] = [];
+ datas.attendees?.forEach((attendee: { _id: string }) => {
+ const r = {
+ _id: attendee._id,
+ };
+
+ attendees.push(r);
+ });
+
+ return (
+
+ );
+ }) || [];
+
+ /*istanbul ignore next*/
+ const toggleExpand = (index: string): void => {
+ if (expandedY === index) {
+ setExpandedY(null);
+ } else {
+ setExpandedY(index);
+ }
+ };
+
+ /*istanbul ignore next*/
+ return (
+
+ {date.getDate()}
+
+
+ {expandedY === `${monthInx}-${dayIndex}` && renderedEvents}
+
+ {renderedEvents && renderedEvents?.length > 0 && (
+
toggleExpand(`${monthInx}-${dayIndex}`)}
+ >
+ {expandedY === `${monthInx}-${dayIndex}` ? (
+
+ ) : (
+
+ )}
+
+ )}
+ {renderedEvents && renderedEvents?.length == 0 && (
+
toggleExpand(`${monthInx}-${dayIndex}`)}
+ >
+ {expandedY === `${monthInx}-${dayIndex}` ? (
+
+
+
+ No Event Available!
+
+
Close
+
+ ) : (
+
+ )}
+
+ )}
+
+
+ );
+ });
+
+ renderedMonths.push(
+
+
+
{months[monthInx]}
+
+ {weekdaysShorthand.map((weekday, index) => (
+
+ {weekday}
+
+ ))}
+
+
{renderedDays}
+
+
,
+ );
+ }
+
+ return renderedMonths;
+ };
+
+ /**
+ * Renders the yearly calendar with navigation buttons.
+ *
+ * @returns JSX.Element - The rendered yearly calendar component.
+ */
+ const renderYearlyCalendar = (): JSX.Element => {
+ return (
+
+
+
+
+
+
{currentYear}
+
+
+
+
+
+
+
+ );
+ };
+
+ return (
+
+
+
{renderYearlyCalendar()}
+
+
+ );
+};
+
+export default Calendar;
diff --git a/src/components/EventCalendar/constants.js b/src/components/EventCalendar/constants.js
new file mode 100644
index 0000000000..f2b770426c
--- /dev/null
+++ b/src/components/EventCalendar/constants.js
@@ -0,0 +1,56 @@
+export const holidays = [
+ { name: 'May Day / Labour Day', date: '05-01', month: 'May' }, // May 1st
+ { name: "Mother's Day", date: '05-08', month: 'May' }, // Second Sunday in May
+ { name: "Father's Day", date: '06-19', month: 'June' }, // Third Sunday in June
+ { name: 'Independence Day (US)', date: '07-04', month: 'July' }, // July 4th
+ { name: 'Oktoberfest', date: '09-21', month: 'September' }, // September 21st (starts in September, ends in October)
+ { name: 'Halloween', date: '10-31', month: 'October' }, // October 31st
+ { name: 'Diwali', date: '11-04', month: 'November' },
+ {
+ name: 'Remembrance Day / Veterans Day',
+ date: '11-11',
+ month: 'November',
+ },
+ { name: 'Christmas Day', date: '12-25', month: 'December' }, // December 25th
+];
+export const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+export const months = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+];
+export const hours = [
+ '12 AM',
+ '01 AM',
+ '02 AM',
+ '03 AM',
+ '04 AM',
+ '05 AM',
+ '06 AM',
+ '07 AM',
+ '08 AM',
+ '09 AM',
+ '10 AM',
+ '11 AM',
+ '12 PM',
+ '01 PM',
+ '02 PM',
+ '03 PM',
+ '04 PM',
+ '05 PM',
+ '06 PM',
+ '07 PM',
+ '08 PM',
+ '09 PM',
+ '10 PM',
+ '11 PM',
+];
diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.module.css b/src/components/EventDashboardScreen/EventDashboardScreen.module.css
new file mode 100644
index 0000000000..33c642dd95
--- /dev/null
+++ b/src/components/EventDashboardScreen/EventDashboardScreen.module.css
@@ -0,0 +1,198 @@
+.gap {
+ gap: 20px;
+}
+
+.mainContainer {
+ width: 50%;
+ flex-grow: 3;
+ padding: 20px;
+ max-height: 100%;
+ overflow: auto;
+}
+
+.containerHeight {
+ height: calc(100vh - 66px);
+}
+
+.colorLight {
+ background-color: #f1f3f6;
+}
+
+.pageContainer {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ padding: 1rem 1.5rem 0 calc(300px + 2rem + 1.5rem);
+}
+
+.expand {
+ padding-left: 4rem;
+ animation: moveLeft 0.5s ease-in-out;
+}
+.avatarStyle {
+ border-radius: 100%;
+}
+.profileContainer {
+ border: none;
+ padding: 2.1rem 0.5rem;
+ height: 52px;
+ border-radius: 8px 0px 0px 8px;
+ display: flex;
+ align-items: center;
+ background-color: white !important;
+ box-shadow:
+ 0 4px 4px 0 rgba(177, 177, 177, 0.2),
+ 0 6px 20px 0 rgba(151, 151, 151, 0.19);
+}
+.profileContainer:focus {
+ outline: none;
+ background-color: var(--bs-gray-100);
+}
+.imageContainer {
+ width: 56px;
+}
+.profileContainer .profileText {
+ flex: 1;
+ text-align: start;
+ overflow: hidden;
+ margin-right: 4px;
+}
+.angleDown {
+ margin-left: 4px;
+}
+.profileContainer .profileText .primaryText {
+ font-size: 1rem;
+ font-weight: 600;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2; /* number of lines to show */
+ -webkit-box-orient: vertical;
+ word-wrap: break-word;
+ white-space: normal;
+}
+.profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ font-weight: 400;
+ color: var(--bs-secondary);
+ display: block;
+ text-transform: capitalize;
+}
+
+.contract {
+ padding-left: calc(300px + 2rem + 1.5rem);
+ animation: moveRight 0.5s ease-in-out;
+}
+
+.collapseSidebarButton {
+ position: fixed;
+ height: 40px;
+ bottom: 0;
+ z-index: 9999;
+ width: calc(300px + 2rem);
+ background-color: rgba(245, 245, 245, 0.7);
+ color: black;
+ border: none;
+ border-radius: 0px;
+}
+
+.collapseSidebarButton:hover,
+.opendrawer:hover {
+ opacity: 1;
+ color: black !important;
+}
+.opendrawer {
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 100vh;
+ z-index: 9999;
+ background-color: rgba(245, 245, 245);
+ border: none;
+ border-radius: 0px;
+ margin-right: 20px;
+ color: black;
+}
+.profileDropdown {
+ background-color: transparent !important;
+}
+.profileDropdown .dropdown-toggle .btn .btn-normal {
+ display: none !important;
+ background-color: transparent !important;
+}
+.dropdownToggle {
+ background-image: url(/public/images/svg/angleDown.svg);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-color: azure;
+}
+
+.dropdownToggle::after {
+ border-top: none !important;
+ border-bottom: none !important;
+}
+
+.opendrawer:hover {
+ transition: background-color 0.5s ease;
+ background-color: var(--bs-primary);
+}
+.collapseSidebarButton:hover {
+ transition: background-color 0.5s ease;
+ background-color: var(--bs-primary);
+}
+
+@media (max-width: 1120px) {
+ .contract {
+ padding-left: calc(250px + 2rem + 1.5rem);
+ }
+ .collapseSidebarButton {
+ width: calc(250px + 2rem);
+ }
+}
+
+@media (max-height: 900px) {
+ .pageContainer {
+ padding: 1rem 1.5rem 0 calc(300px + 2rem);
+ }
+ .collapseSidebarButton {
+ height: 30px;
+ width: calc(300px + 1rem);
+ }
+}
+@media (max-height: 650px) {
+ .pageContainer {
+ padding: 1rem 1.5rem 0 calc(270px);
+ }
+ .collapseSidebarButton {
+ width: 250px;
+ height: 20px;
+ }
+ .opendrawer {
+ width: 30px;
+ }
+}
+
+/* For tablets */
+@media (max-width: 820px) {
+ .pageContainer {
+ padding-left: 2.5rem;
+ }
+
+ .opendrawer {
+ width: 25px;
+ }
+
+ .contract,
+ .expand {
+ animation: none;
+ }
+
+ .collapseSidebarButton {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+}
diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx b/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx
new file mode 100644
index 0000000000..6b48b034a9
--- /dev/null
+++ b/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx
@@ -0,0 +1,166 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { I18nextProvider } from 'react-i18next';
+import 'jest-location-mock';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import EventDashboardScreen from './EventDashboardScreen';
+import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import useLocalStorage from 'utils/useLocalstorage';
+const { setItem } = useLocalStorage();
+
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // Deprecated
+ removeListener: jest.fn(), // Deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+});
+
+let mockID: string | undefined = '123';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockID }),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '123',
+ image: null,
+ creator: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ },
+ name: 'Test Organization',
+ description: 'Testing this organization',
+ address: {
+ city: 'Mountain View',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Main Street',
+ line2: 'Apt 456',
+ postalCode: '94040',
+ sortingCode: 'XYZ-789',
+ state: 'CA',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: true,
+ members: [],
+ admins: [],
+ membershipRequests: [],
+ blockedUsers: [],
+ },
+ ],
+ },
+ },
+ },
+];
+const link = new StaticMockLink(MOCKS, true);
+
+const resizeWindow = (width: number): void => {
+ window.innerWidth = width;
+ fireEvent(window, new Event('resize'));
+};
+
+const clickToggleMenuBtn = (toggleButton: HTMLElement): void => {
+ fireEvent.click(toggleButton);
+};
+
+describe('Testing LeftDrawer in OrganizationScreen', () => {
+ test('should be redirected to / if IsLoggedIn is false', async () => {
+ setItem('IsLoggedIn', false);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ expect(window.location.pathname).toEqual('/');
+ });
+ test('should be redirected to / if ss is false', async () => {
+ setItem('IsLoggedIn', true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ test('Testing LeftDrawer in page functionality', async () => {
+ setItem('IsLoggedIn', true);
+ setItem('AdminFor', [
+ { _id: '6637904485008f171cf29924', __typename: 'Organization' },
+ ]);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ const toggleButton = screen.getByTestId('toggleMenuBtn') as HTMLElement;
+ const icon = toggleButton.querySelector('i');
+
+ // Resize window to a smaller width
+ resizeWindow(800);
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-right');
+ // Resize window back to a larger width
+
+ resizeWindow(1000);
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-left');
+
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-right');
+ });
+
+ test('should be redirected to / if orgId is undefined', async () => {
+ mockID = undefined;
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ expect(window.location.pathname).toEqual('/');
+ });
+});
diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.tsx b/src/components/EventDashboardScreen/EventDashboardScreen.tsx
new file mode 100644
index 0000000000..11b9ec936f
--- /dev/null
+++ b/src/components/EventDashboardScreen/EventDashboardScreen.tsx
@@ -0,0 +1,159 @@
+import LeftDrawerOrg from 'components/LeftDrawerOrg/LeftDrawerOrg';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import { Navigate, Outlet, useLocation, useParams } from 'react-router-dom';
+import { updateTargets } from 'state/action-creators';
+import { useAppDispatch } from 'state/hooks';
+import type { RootState } from 'state/reducers';
+import type { TargetsType } from 'state/reducers/routesReducer';
+import styles from './EventDashboardScreen.module.css';
+import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown';
+import useLocalStorage from 'utils/useLocalstorage';
+import type { InterfaceMapType } from 'utils/interfaces';
+
+/**
+ * The EventDashboardScreen component is the main dashboard view for event management.
+ * It includes navigation, a sidebar, and a profile dropdown.
+ *
+ * @returns JSX.Element - The rendered EventDashboardScreen component.
+ */
+const EventDashboardScreen = (): JSX.Element => {
+ const { getItem } = useLocalStorage();
+ const isLoggedIn = getItem('IsLoggedIn');
+ const adminFor = getItem('AdminFor');
+ const location = useLocation();
+ const titleKey: string | undefined = map[location.pathname.split('/')[2]];
+ const { t } = useTranslation('translation', { keyPrefix: titleKey });
+ const [hideDrawer, setHideDrawer] = useState(null);
+ const { orgId } = useParams();
+
+ // Redirect to home if orgId is not present or if user is not logged in
+ if (!orgId) {
+ return ;
+ }
+
+ if (isLoggedIn === false) return ;
+ if (adminFor === null) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ // Access targets from Redux store
+ const appRoutes: {
+ targets: TargetsType[];
+ } = useSelector((state: RootState) => state.appRoutes);
+ const { targets } = appRoutes;
+
+ const dispatch = useAppDispatch();
+
+ // Update targets when orgId changes
+ useEffect(() => {
+ dispatch(updateTargets(orgId));
+ }, [orgId]);
+
+ /**
+ * Handles window resize events to toggle the visibility of the sidebar drawer.
+ */
+ const handleResize = (): void => {
+ if (window.innerWidth <= 820 && !hideDrawer) {
+ setHideDrawer(true);
+ }
+ };
+
+ /**
+ * Toggles the visibility of the sidebar drawer.
+ */
+ const toggleDrawer = (): void => {
+ setHideDrawer(!hideDrawer);
+ };
+
+ useEffect(() => {
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [hideDrawer]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EventDashboardScreen;
+
+const map: InterfaceMapType = {
+ orgdash: 'dashboard',
+ orgpeople: 'organizationPeople',
+ requests: 'requests',
+ orgads: 'advertisement',
+ member: 'memberDetail',
+ orgevents: 'organizationEvents',
+ orgactionitems: 'organizationActionItems',
+ orgcontribution: 'orgContribution',
+ orgpost: 'orgPost',
+ orgfunds: 'funds',
+ orgfundcampaign: 'fundCampaign',
+ fundCampaignPledge: 'pledges',
+ orgsetting: 'orgSettings',
+ orgstore: 'addOnStore',
+ blockuser: 'blockUnblockUser',
+ orgvenues: 'organizationVenues',
+ event: 'eventManagement',
+};
diff --git a/src/components/EventListCard/EventListCard.module.css b/src/components/EventListCard/EventListCard.module.css
new file mode 100644
index 0000000000..1e47972a42
--- /dev/null
+++ b/src/components/EventListCard/EventListCard.module.css
@@ -0,0 +1,223 @@
+.cards h2 {
+ font-size: 15px;
+ color: #4e4c4c;
+ font-weight: 500;
+}
+.cards > h3 {
+ font-size: 17px;
+}
+.cards > p {
+ font-size: 14px;
+ margin-top: 0px;
+ margin-bottom: 7px;
+}
+.cards a {
+ color: #fff;
+ font-weight: 600;
+}
+.cards a:hover {
+ color: black;
+}
+.cards {
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s;
+ margin-bottom: 5px;
+}
+.dispflex {
+ display: flex;
+ cursor: pointer;
+ justify-content: space-between;
+ margin: 10px 5px 5px 0px;
+}
+.eventtitle {
+ margin-bottom: 0px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.iconContainer {
+ display: flex;
+ justify-content: flex-end;
+}
+.icon {
+ margin: 2px;
+}
+
+.cards {
+ width: 100%;
+ background: #5cacf7 !important;
+ padding: 2px 3px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 3px 2px #e8e8e8;
+ color: #737373;
+ box-sizing: border-box;
+}
+.cards:last-child:nth-last-child(odd) {
+ grid-column: auto / span 2;
+}
+
+.cards:first-child:nth-last-child(even),
+.cards:first-child:nth-last-child(even) ~ .box {
+ grid-column: auto / span 1;
+}
+
+.sidebarsticky > input {
+ text-decoration: none;
+ margin-bottom: 50px;
+ border-color: #e8e5e5;
+ width: 80%;
+ border-radius: 7px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ box-shadow: none;
+}
+.datediv {
+ display: flex;
+ flex-direction: row;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+.datebox {
+ width: 90%;
+ border-radius: 7px;
+ border-color: #e8e5e5;
+ outline: none;
+ box-shadow: none;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-right: 5px;
+ padding-left: 5px;
+}
+.datediv > div > p {
+ margin-bottom: 0.5rem;
+}
+
+.startDate {
+ margin-right: 0.25rem;
+}
+.endDate {
+ margin-left: 1.5rem;
+}
+
+.checkboxdiv > div label {
+ margin-right: 50px;
+}
+.checkboxdiv > label > input {
+ margin-left: 10px;
+}
+
+.dispflex > input {
+ width: 20%;
+ border: none;
+ box-shadow: none;
+ margin-top: 5px;
+}
+
+.checkboxContainer {
+ display: flex;
+ justify-content: space-between;
+}
+
+.checkboxdiv {
+ display: flex;
+ flex-direction: column;
+}
+
+.preview {
+ display: flex;
+ flex-direction: row;
+ font-weight: 700;
+ font-size: 16px;
+ color: #000000;
+ margin: 0;
+}
+.view {
+ margin-left: 2%;
+ font-weight: 600;
+ font-size: 16px;
+ color: #707070;
+}
+.flexdir {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ border: none;
+}
+
+.form_wrapper {
+ margin-top: 27px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ padding: 20px 30px;
+ background: #ffffff;
+ border-color: #e8e5e5;
+ border-width: 5px;
+ border-radius: 10px;
+ width: 40%;
+}
+
+.form_wrapper form {
+ display: flex;
+ align-items: left;
+ justify-content: left;
+ flex-direction: column;
+}
+.titlemodal {
+ color: #000000;
+ font-weight: 600;
+ font-size: 24px;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ border-bottom: 4px solid #31bb6b;
+ width: 50%;
+}
+.cancel > i {
+ margin-top: 5px;
+ transform: scale(1.2);
+ cursor: pointer;
+ color: #707070;
+}
+.modalbody {
+ width: 50px;
+}
+.list_box {
+ height: 70vh;
+ overflow-y: auto;
+ width: auto;
+}
+@media only screen and (max-width: 600px) {
+ .form_wrapper {
+ width: 90%;
+ top: 45%;
+ }
+ .checkboxContainer {
+ flex-direction: column;
+ }
+
+ .datediv {
+ flex-direction: column;
+ }
+
+ .datediv > div {
+ width: 100%;
+ margin-left: 0;
+ margin-bottom: 10px;
+ }
+
+ .datediv > div p {
+ margin-bottom: 5px;
+ }
+}
+
+.customButton {
+ width: 90%;
+ margin: 0 auto;
+}
diff --git a/src/components/EventListCard/EventListCard.test.tsx b/src/components/EventListCard/EventListCard.test.tsx
new file mode 100644
index 0000000000..afe81f436e
--- /dev/null
+++ b/src/components/EventListCard/EventListCard.test.tsx
@@ -0,0 +1,945 @@
+import React, { act } from 'react';
+import type { RenderResult } from '@testing-library/react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import type { InterfaceEventListCardProps } from './EventListCard';
+import EventListCard from './EventListCard';
+import i18n from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { BrowserRouter, MemoryRouter, Route, Routes } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { toast } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import useLocalStorage from 'utils/useLocalstorage';
+import { props } from './EventListCardProps';
+import { ERROR_MOCKS, MOCKS } from './EventListCardMocks';
+
+const { setItem } = useLocalStorage();
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(ERROR_MOCKS, true);
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.eventListCard ?? {},
+ ),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const renderEventListCard = (
+ props: InterfaceEventListCardProps,
+): RenderResult => {
+ const { key, ...restProps } = props; // Destructure the key and separate other props
+
+ return render(
+
+
+
+
+
+
+ }
+ />
+ }
+ />
+ Event Dashboard (Admin)}
+ />
+ Event Dashboard (User)}
+ />
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Testing Event List Card', () => {
+ const updateData = {
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: true,
+ recurring: false,
+ isRegisterable: true,
+ allDay: false,
+ location: 'New Delhi',
+ startDate: '03/18/2022',
+ endDate: '03/20/2022',
+ startTime: '09:00 AM',
+ endTime: '05:00 PM',
+ };
+
+ beforeAll(() => {
+ jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: 'orgId' }),
+ }));
+ });
+
+ afterAll(() => {
+ localStorage.clear();
+ jest.clearAllMocks();
+ });
+
+ test('Testing for event modal', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('eventModalCloseBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should navigate to "/" if orgId is not defined', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(window.location.pathname).toEqual('/');
+ });
+ });
+
+ test('Should render default text if event details are null', async () => {
+ renderEventListCard(props[0]);
+
+ await waitFor(() => {
+ expect(screen.getByText('Dogs Care')).toBeInTheDocument();
+ });
+ });
+
+ test('should render props and text elements test for the screen', async () => {
+ renderEventListCard(props[1]);
+
+ expect(screen.getByText(props[1].eventName)).toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateDescription')).toBeInTheDocument();
+ });
+
+ expect(screen.getByTestId('updateDescription')).toHaveValue(
+ props[1].eventDescription,
+ );
+ expect(screen.getByTestId('updateLocation')).toHaveValue(
+ props[1].eventLocation,
+ );
+
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should render truncated event name when length is more than 100', async () => {
+ const longEventName = 'a'.repeat(101);
+ renderEventListCard({ ...props[1], eventName: longEventName });
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateTitle')).toBeInTheDocument();
+ });
+
+ expect(screen.getByTestId('updateTitle')).toHaveValue(
+ `${longEventName.substring(0, 100)}...`,
+ );
+
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should render full event name when length is less than or equal to 100', async () => {
+ const shortEventName = 'a'.repeat(100);
+ renderEventListCard({ ...props[1], eventName: shortEventName });
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateTitle')).toBeInTheDocument();
+ });
+
+ expect(screen.getByTestId('updateTitle')).toHaveValue(shortEventName);
+
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should render truncated event description when length is more than 256', async () => {
+ const longEventDescription = 'a'.repeat(257);
+
+ renderEventListCard({
+ ...props[1],
+ eventDescription: longEventDescription,
+ });
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateDescription')).toBeInTheDocument();
+ });
+ expect(screen.getByTestId('updateDescription')).toHaveValue(
+ `${longEventDescription.substring(0, 256)}...`,
+ );
+
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should render full event description when length is less than or equal to 256', async () => {
+ const shortEventDescription = 'a'.repeat(256);
+
+ renderEventListCard({
+ ...props[1],
+ eventDescription: shortEventDescription,
+ });
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateDescription')).toBeInTheDocument();
+ });
+ expect(screen.getByTestId('updateDescription')).toHaveValue(
+ shortEventDescription,
+ );
+
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should navigate to event dashboard when clicked (For Admin)', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('showEventDashboardBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('showEventDashboardBtn'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('card')).not.toBeInTheDocument();
+ expect(screen.queryByText('Event Dashboard (Admin)')).toBeInTheDocument();
+ });
+ });
+
+ test('Should navigate to event dashboard when clicked (For User)', async () => {
+ setItem('userId', '123');
+ renderEventListCard(props[2]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('showEventDashboardBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('showEventDashboardBtn'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('card')).not.toBeInTheDocument();
+ expect(screen.queryByText('Event Dashboard (User)')).toBeInTheDocument();
+ });
+ });
+
+ test('Should update a non-recurring event', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ const eventTitle = screen.getByTestId('updateTitle');
+ fireEvent.change(eventTitle, { target: { value: '' } });
+ userEvent.type(eventTitle, updateData.title);
+
+ const eventDescription = screen.getByTestId('updateDescription');
+ fireEvent.change(eventDescription, { target: { value: '' } });
+ userEvent.type(eventDescription, updateData.description);
+
+ const eventLocation = screen.getByTestId('updateLocation');
+ fireEvent.change(eventLocation, { target: { value: '' } });
+ userEvent.type(eventLocation, updateData.location);
+
+ const startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: updateData.startDate },
+ });
+
+ const endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: updateData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('updateAllDay'));
+ userEvent.click(screen.getByTestId('updateIsPublic'));
+ userEvent.click(screen.getByTestId('updateRegistrable'));
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventUpdated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Should update a non all day non-recurring event', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ const eventTitle = screen.getByTestId('updateTitle');
+ fireEvent.change(eventTitle, { target: { value: '' } });
+ userEvent.type(eventTitle, updateData.title);
+
+ const eventDescription = screen.getByTestId('updateDescription');
+ fireEvent.change(eventDescription, { target: { value: '' } });
+ userEvent.type(eventDescription, updateData.description);
+
+ const eventLocation = screen.getByTestId('updateLocation');
+ fireEvent.change(eventLocation, { target: { value: '' } });
+ userEvent.type(eventLocation, updateData.location);
+
+ const startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: updateData.startDate },
+ });
+
+ const endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: updateData.endDate },
+ });
+
+ const startTimePicker = screen.getByLabelText(translations.startTime);
+ fireEvent.change(startTimePicker, {
+ target: { value: updateData.startTime },
+ });
+
+ const endTimePicker = screen.getByLabelText(translations.endTime);
+ fireEvent.change(endTimePicker, {
+ target: { value: updateData.endTime },
+ });
+
+ userEvent.click(screen.getByTestId('updateIsPublic'));
+ userEvent.click(screen.getByTestId('updateRegistrable'));
+
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventUpdated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should update a single event to be recurring', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ const eventTitle = screen.getByTestId('updateTitle');
+ fireEvent.change(eventTitle, { target: { value: '' } });
+ userEvent.type(eventTitle, updateData.title);
+
+ const eventDescription = screen.getByTestId('updateDescription');
+ fireEvent.change(eventDescription, { target: { value: '' } });
+ userEvent.type(eventDescription, updateData.description);
+
+ const eventLocation = screen.getByTestId('updateLocation');
+ fireEvent.change(eventLocation, { target: { value: '' } });
+ userEvent.type(eventLocation, updateData.location);
+
+ const startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: updateData.startDate },
+ });
+
+ const endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: updateData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('updateAllDay'));
+ userEvent.click(screen.getByTestId('updateRecurring'));
+ userEvent.click(screen.getByTestId('updateIsPublic'));
+ userEvent.click(screen.getByTestId('updateRegistrable'));
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventUpdated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should show different update options for a recurring event based on different conditions', async () => {
+ renderEventListCard(props[5]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('updateEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ // shows options to update thisInstance and thisAndFollowingInstances, and allInstances
+ await waitFor(() => {
+ expect(screen.getByTestId('update-thisInstance')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('update-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ expect(screen.getByTestId('update-allInstances')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('eventUpdateOptionsModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(translations.startDate)).toBeInTheDocument();
+ });
+
+ // change the event dates
+ let startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: updateData.startDate },
+ });
+
+ let endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: updateData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ // shows options to update thisInstance and thisAndFollowingInstances only
+ await waitFor(() => {
+ expect(screen.getByTestId('update-thisInstance')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('update-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByTestId('update-allInstances'),
+ ).not.toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('eventUpdateOptionsModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(translations.startDate)).toBeInTheDocument();
+ });
+
+ // reset the event dates to their original values
+ startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: '03/17/2022' },
+ });
+
+ endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: '03/17/2022' },
+ });
+
+ // now change the recurrence rule of the event
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customDailyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customDailyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceSubmitBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ // shows options to update thisAndFollowingInstances and allInstances only
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('update-thisInstance'),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.getByTestId('update-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ expect(screen.queryByTestId('update-allInstances')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('eventUpdateOptionsModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('eventModalCloseBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should show recurrenceRule as changed if the recurrence weekdays have changed', async () => {
+ renderEventListCard(props[4]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ // since the current recurrence weekDay for the current recurring event is "SATURDAY",
+ // let's first deselect it, and then we'll select a different day
+ // the recurrence rule should be marked as changed and we should see the option to update
+ // thisAndFollowingInstances and allInstances only
+ await waitFor(() => {
+ expect(screen.getAllByTestId('recurrenceWeekDay')[6]).toBeInTheDocument();
+ });
+
+ // deselect saturday, which is the 7th day in recurrenceWeekDay options
+ userEvent.click(screen.getAllByTestId('recurrenceWeekDay')[6]);
+
+ // select a different day, say wednesday, the 4th day in recurrenceWeekDay options
+ userEvent.click(screen.getAllByTestId('recurrenceWeekDay')[3]);
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ // shows options to update thisInstance and thisAndFollowingInstances, and allInstances
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('update-thisInstance'),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.getByTestId('update-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ expect(screen.getByTestId('update-allInstances')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('eventUpdateOptionsModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('eventModalCloseBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should update all instances of a recurring event', async () => {
+ renderEventListCard(props[6]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateTitle')).toBeInTheDocument();
+ });
+
+ const eventTitle = screen.getByTestId('updateTitle');
+ fireEvent.change(eventTitle, { target: { value: '' } });
+ userEvent.type(eventTitle, updateData.title);
+
+ const eventDescription = screen.getByTestId('updateDescription');
+ fireEvent.change(eventDescription, { target: { value: '' } });
+ userEvent.type(eventDescription, updateData.description);
+
+ const eventLocation = screen.getByTestId('updateLocation');
+ fireEvent.change(eventLocation, { target: { value: '' } });
+ userEvent.type(eventLocation, updateData.location);
+
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ // shows options to update thisInstance and thisAndFollowingInstances, and allInstances
+ await waitFor(() => {
+ expect(screen.getByTestId('update-thisInstance')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('update-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ expect(screen.getByTestId('update-allInstances')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('update-allInstances'));
+ userEvent.click(screen.getByTestId('recurringEventUpdateOptionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateEventBtn')).toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventUpdated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should update thisAndFollowingInstances of a recurring event', async () => {
+ renderEventListCard(props[5]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(translations.startDate)).toBeInTheDocument();
+ });
+
+ // change the event dates
+ const startDatePicker = screen.getByLabelText(translations.startDate);
+ fireEvent.change(startDatePicker, {
+ target: { value: updateData.startDate },
+ });
+
+ const endDatePicker = screen.getByLabelText(translations.endDate);
+ fireEvent.change(endDatePicker, {
+ target: { value: updateData.endDate },
+ });
+
+ // now change the recurrence rule of the event
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customDailyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customDailyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceSubmitBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('updateEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventUpdated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should render the delete modal', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('deleteEventModalBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteEventModalBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('eventDeleteModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('eventDeleteModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventDeleteModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('eventModalCloseBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('eventModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should call the delete event mutation when the "Yes" button is clicked', async () => {
+ renderEventListCard(props[1]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('deleteEventModalBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('deleteEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventDeleted);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('select different delete options on recurring events & then delete the recurring event', async () => {
+ renderEventListCard(props[4]);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('card')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('deleteEventModalBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteEventModalBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('delete-thisAndFollowingInstances'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('delete-thisAndFollowingInstances'));
+
+ userEvent.click(screen.getByTestId('delete-allInstances'));
+ userEvent.click(screen.getByTestId('delete-thisInstance'));
+
+ userEvent.click(screen.getByTestId('deleteEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventDeleted);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should show an error toast when the delete event mutation fails', async () => {
+ // Destructure key from props[1] and pass it separately to avoid spreading it
+ const { key, ...otherProps } = props[1];
+ render(
+
+
+
+
+
+
+ }
+ />
+ }
+ />
+
+
+
+
+
+ ,
+ );
+
+ userEvent.click(screen.getByTestId('card'));
+ userEvent.click(screen.getByTestId('deleteEventModalBtn'));
+ userEvent.click(screen.getByTestId('deleteEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ test('handle register should work properly', async () => {
+ setItem('userId', '456');
+
+ renderEventListCard(props[2]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('registerEventBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('registerEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ `Successfully registered for ${props[2].eventName}`,
+ );
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('eventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('should show already registered text when the user is registered for an event', async () => {
+ renderEventListCard(props[3]);
+
+ userEvent.click(screen.getByTestId('card'));
+
+ expect(
+ screen.getByText(translations.alreadyRegistered),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/EventListCard/EventListCard.tsx b/src/components/EventListCard/EventListCard.tsx
new file mode 100644
index 0000000000..ffa508ff7c
--- /dev/null
+++ b/src/components/EventListCard/EventListCard.tsx
@@ -0,0 +1,102 @@
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import styles from './EventListCard.module.css';
+import { Navigate, useParams } from 'react-router-dom';
+import EventListCardModals from './EventListCardModals';
+import type { InterfaceRecurrenceRule } from 'utils/recurrenceUtils';
+
+/**
+ * Props for the EventListCard component.
+ */
+export interface InterfaceEventListCardProps {
+ refetchEvents?: () => void;
+ userRole?: string;
+ key: string;
+ id: string;
+ eventLocation: string;
+ eventName: string;
+ eventDescription: string;
+ startDate: string;
+ endDate: string;
+ startTime: string | null;
+ endTime: string | null;
+ allDay: boolean;
+ recurring: boolean;
+ recurrenceRule: InterfaceRecurrenceRule | null;
+ isRecurringEventException: boolean;
+ isPublic: boolean;
+ isRegisterable: boolean;
+ registrants?: {
+ _id: string;
+ }[];
+ creator?: {
+ firstName: string;
+ lastName: string;
+ _id: string;
+ };
+}
+
+/**
+ * Component that displays an event card with a modal for event details.
+ *
+ * @param props - The props for the EventListCard component.
+ * @returns The rendered EventListCard component.
+ */
+function eventListCard(props: InterfaceEventListCardProps): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventListCard',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ const [eventModalIsOpen, setEventModalIsOpen] = useState(false);
+
+ const { orgId } = useParams();
+
+ // Redirect to home if orgId is not present
+ if (!orgId) {
+ return ;
+ }
+
+ /**
+ * Opens the event modal.
+ */
+ const showViewModal = (): void => {
+ setEventModalIsOpen(true);
+ };
+
+ /**
+ * Closes the event modal.
+ */
+ const hideViewModal = (): void => {
+ setEventModalIsOpen(false);
+ };
+
+ return (
+ <>
+
+
+
+ {props.eventName ? <>{props.eventName}> : <>Dogs Care>}
+
+
+
+
+
+ >
+ );
+}
+export {};
+export default eventListCard;
diff --git a/src/components/EventListCard/EventListCardMocks.ts b/src/components/EventListCard/EventListCardMocks.ts
new file mode 100644
index 0000000000..6312b5af51
--- /dev/null
+++ b/src/components/EventListCard/EventListCardMocks.ts
@@ -0,0 +1,201 @@
+import {
+ DELETE_EVENT_MUTATION,
+ REGISTER_EVENT,
+ UPDATE_EVENT_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+
+export const MOCKS = [
+ {
+ request: {
+ query: DELETE_EVENT_MUTATION,
+ variables: { id: '1' },
+ },
+ result: {
+ data: {
+ removeEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_EVENT_MUTATION,
+ variables: { id: '1', recurringEventDeleteType: 'thisInstance' },
+ },
+ result: {
+ data: {
+ removeEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: false,
+ recurring: false,
+ isRegisterable: true,
+ allDay: true,
+ startDate: '2022-03-18',
+ endDate: '2022-03-20',
+ location: 'New Delhi',
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: false,
+ recurring: false,
+ isRegisterable: true,
+ allDay: false,
+ startDate: '2022-03-18',
+ endDate: '2022-03-20',
+ location: 'New Delhi',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: false,
+ recurring: true,
+ recurringEventUpdateType: 'thisInstance',
+ isRegisterable: true,
+ allDay: true,
+ startDate: '2022-03-18',
+ endDate: '2022-03-20',
+ location: 'New Delhi',
+ recurrenceStartDate: '2022-03-18',
+ recurrenceEndDate: null,
+ frequency: 'WEEKLY',
+ weekDays: ['FRIDAY'],
+ interval: 1,
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ title: 'Updated title',
+ description: 'This is a new update',
+ isPublic: true,
+ recurring: true,
+ recurringEventUpdateType: 'allInstances',
+ isRegisterable: false,
+ allDay: true,
+ startDate: '2022-03-17',
+ endDate: '2022-03-17',
+ location: 'New Delhi',
+ recurrenceStartDate: '2022-03-17',
+ recurrenceEndDate: '2023-03-17',
+ frequency: 'MONTHLY',
+ weekDays: ['THURSDAY'],
+ interval: 1,
+ weekDayOccurenceInMonth: 3,
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ title: 'Shelter for Cats',
+ description: 'This is shelter for cat event',
+ isPublic: true,
+ recurring: true,
+ recurringEventUpdateType: 'thisAndFollowingInstances',
+ isRegisterable: false,
+ allDay: true,
+ startDate: '2022-03-18',
+ endDate: '2022-03-20',
+ location: 'India',
+ recurrenceStartDate: '2022-03-18',
+ recurrenceEndDate: null,
+ frequency: 'DAILY',
+ interval: 1,
+ },
+ },
+ result: {
+ data: {
+ updateEvent: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: REGISTER_EVENT,
+ variables: { eventId: '1' },
+ },
+ result: {
+ data: {
+ registerForEvent: [
+ {
+ _id: '123',
+ },
+ ],
+ },
+ },
+ },
+];
+
+export const ERROR_MOCKS = [
+ {
+ request: {
+ query: DELETE_EVENT_MUTATION,
+ variables: {
+ id: '1',
+ },
+ },
+ error: new Error('Something went wrong'),
+ },
+];
diff --git a/src/components/EventListCard/EventListCardModals.tsx b/src/components/EventListCard/EventListCardModals.tsx
new file mode 100644
index 0000000000..193890941c
--- /dev/null
+++ b/src/components/EventListCard/EventListCardModals.tsx
@@ -0,0 +1,834 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Form, Modal, Popover } from 'react-bootstrap';
+import styles from './EventListCard.module.css';
+import { DatePicker, TimePicker } from '@mui/x-date-pickers';
+import dayjs from 'dayjs';
+import type { Dayjs } from 'dayjs';
+import type { InterfaceEventListCardProps } from './EventListCard';
+import {
+ type InterfaceRecurrenceRuleState,
+ type RecurringEventMutationType,
+ Days,
+ Frequency,
+ allInstances,
+ getRecurrenceRuleText,
+ getWeekDayOccurenceInMonth,
+ recurringEventMutationOptions,
+ thisAndFollowingInstances,
+ thisInstance,
+ haveInstanceDatesChanged,
+ hasRecurrenceRuleChanged,
+} from 'utils/recurrenceUtils';
+import useLocalStorage from 'utils/useLocalstorage';
+import RecurrenceOptions from 'components/RecurrenceOptions/RecurrenceOptions';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ DELETE_EVENT_MUTATION,
+ REGISTER_EVENT,
+ UPDATE_EVENT_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import { errorHandler } from 'utils/errorHandler';
+
+enum Role {
+ USER = 'USER',
+ SUPERADMIN = 'SUPERADMIN',
+ ADMIN = 'ADMIN',
+}
+
+/**
+ * Converts a time string to a Dayjs object representing the current date with the specified time.
+ * @param time - A string representing the time in 'HH:mm:ss' format.
+ * @returns A Dayjs object with the current date and specified time.
+ */
+const timeToDayJs = (time: string): Dayjs => {
+ const dateTimeString = dayjs().format('YYYY-MM-DD') + ' ' + time;
+ return dayjs(dateTimeString, { format: 'YYYY-MM-DD HH:mm:ss' });
+};
+
+/**
+ * Properties for the `EventListCardModals` component.
+ * eventListCardProps - The properties of the event list card.
+ * eventModalIsOpen - Boolean indicating if the event modal is open.
+ * hideViewModal - Function to hide the event modal.
+ * t - Function for translation of text.
+ * tCommon - Function for translation of common text.
+ */
+interface InterfaceEventListCardModalProps {
+ eventListCardProps: InterfaceEventListCardProps;
+ eventModalIsOpen: boolean;
+ hideViewModal: () => void;
+ t: (key: string) => string;
+ tCommon: (key: string) => string;
+}
+
+/**
+ * The `EventListCardModals` component displays the modals related to events, such as viewing,
+ * updating, and deleting events.
+ * @param props - The properties for the component.
+ * @returns A JSX element containing the event modals.
+ */
+function EventListCardModals({
+ eventListCardProps,
+ eventModalIsOpen,
+ hideViewModal,
+ t,
+ tCommon,
+}: InterfaceEventListCardModalProps): JSX.Element {
+ const { refetchEvents } = eventListCardProps;
+
+ const { getItem } = useLocalStorage();
+ const userId = getItem('userId');
+
+ const { orgId } = useParams();
+ const navigate = useNavigate();
+
+ const [alldaychecked, setAllDayChecked] = useState(eventListCardProps.allDay);
+ const [recurringchecked, setRecurringChecked] = useState(
+ eventListCardProps.recurring,
+ );
+ const [publicchecked, setPublicChecked] = useState(
+ eventListCardProps.isPublic,
+ );
+ const [registrablechecked, setRegistrableChecked] = useState(
+ eventListCardProps.isRegisterable,
+ );
+ const [eventDeleteModalIsOpen, setEventDeleteModalIsOpen] = useState(false);
+ const [recurringEventUpdateModalIsOpen, setRecurringEventUpdateModalIsOpen] =
+ useState(false);
+ const [eventStartDate, setEventStartDate] = useState(
+ new Date(eventListCardProps.startDate),
+ );
+ const [eventEndDate, setEventEndDate] = useState(
+ new Date(eventListCardProps.endDate),
+ );
+
+ const [recurrenceRuleState, setRecurrenceRuleState] =
+ useState({
+ recurrenceStartDate: eventStartDate,
+ recurrenceEndDate: null,
+ frequency: Frequency.WEEKLY,
+ weekDays: [Days[eventStartDate.getDay()]],
+ interval: 1,
+ count: undefined,
+ weekDayOccurenceInMonth: undefined,
+ });
+
+ const {
+ recurrenceStartDate,
+ recurrenceEndDate,
+ frequency,
+ weekDays,
+ interval,
+ count,
+ weekDayOccurenceInMonth,
+ } = recurrenceRuleState;
+
+ const recurrenceRuleText = getRecurrenceRuleText(recurrenceRuleState);
+
+ const [formState, setFormState] = useState({
+ title: eventListCardProps.eventName,
+ eventdescrip: eventListCardProps.eventDescription,
+ location: eventListCardProps.eventLocation,
+ startTime: eventListCardProps.startTime?.split('.')[0] || '08:00:00',
+ endTime: eventListCardProps.endTime?.split('.')[0] || '08:00:00',
+ });
+
+ const [recurringEventDeleteType, setRecurringEventDeleteType] =
+ useState(thisInstance);
+
+ const [recurringEventUpdateType, setRecurringEventUpdateType] =
+ useState(thisInstance);
+
+ const [recurringEventUpdateOptions, setRecurringEventUpdateOptions] =
+ useState([
+ thisInstance,
+ thisAndFollowingInstances,
+ allInstances,
+ ]);
+
+ const [
+ shouldShowRecurringEventUpdateOptions,
+ setShouldShowRecurringEventUpdateOptions,
+ ] = useState(true);
+
+ useEffect(() => {
+ if (eventModalIsOpen) {
+ if (eventListCardProps.recurrenceRule) {
+ // get the recurrence rule
+ const { recurrenceRule } = eventListCardProps;
+
+ // set the recurrence rule state
+ setRecurrenceRuleState({
+ recurrenceStartDate: new Date(recurrenceRule.recurrenceStartDate),
+ recurrenceEndDate: recurrenceRule.recurrenceEndDate
+ ? new Date(recurrenceRule.recurrenceEndDate)
+ : null,
+ frequency: recurrenceRule.frequency,
+ weekDays: recurrenceRule.weekDays,
+ interval: recurrenceRule.interval,
+ count: recurrenceRule.count ?? undefined,
+ weekDayOccurenceInMonth:
+ recurrenceRule.weekDayOccurenceInMonth ?? undefined,
+ });
+ }
+ }
+ }, [eventModalIsOpen]);
+
+ // a state to specify whether the recurrence rule has changed
+ const [recurrenceRuleChanged, setRecurrenceRuleChanged] = useState(false);
+
+ // a state to specify whether the instance's startDate or endDate has changed
+ const [instanceDatesChanged, setInstanceDatesChanged] = useState(false);
+
+ // the `recurrenceRuleChanged` & `instanceDatesChanged` are required,
+ // because we will provide recurring event update options based on them, i.e.:
+ // - if the `instanceDatesChanged` is true, we'll not provide the option to update "allInstances"
+ // - if the `recurrenceRuleChanged` is true, we'll not provide the option to update "thisInstance"
+ // - if both are true, we'll only provide the option to update "thisAndFollowingInstances"
+ // updating recurring events is not very straightforward,
+ // find more info on the approach in this doc https://docs.talawa.io/docs/functionalities/recurring-events
+
+ useEffect(() => {
+ setInstanceDatesChanged(
+ haveInstanceDatesChanged(
+ eventListCardProps.startDate,
+ eventListCardProps.endDate,
+ dayjs(eventStartDate).format('YYYY-MM-DD'), // convert to date string
+ dayjs(eventEndDate).format('YYYY-MM-DD'), // convert to date string
+ ),
+ );
+ setRecurrenceRuleChanged(
+ hasRecurrenceRuleChanged(
+ eventListCardProps.recurrenceRule,
+ recurrenceRuleState,
+ ),
+ );
+ }, [eventStartDate, eventEndDate, recurrenceRuleState]);
+
+ useEffect(() => {
+ if (instanceDatesChanged) {
+ setRecurringEventUpdateType(thisInstance);
+ setRecurringEventUpdateOptions([thisInstance, thisAndFollowingInstances]);
+ setShouldShowRecurringEventUpdateOptions(true);
+ }
+
+ if (recurrenceRuleChanged) {
+ setRecurringEventUpdateType(thisAndFollowingInstances);
+ setRecurringEventUpdateOptions([thisAndFollowingInstances, allInstances]);
+ setShouldShowRecurringEventUpdateOptions(true);
+ }
+
+ if (recurrenceRuleChanged && instanceDatesChanged) {
+ setRecurringEventUpdateType(thisAndFollowingInstances);
+ setShouldShowRecurringEventUpdateOptions(false);
+ }
+
+ if (!recurrenceRuleChanged && !instanceDatesChanged) {
+ setRecurringEventUpdateType(thisInstance);
+ setRecurringEventUpdateOptions([
+ thisInstance,
+ thisAndFollowingInstances,
+ allInstances,
+ ]);
+ setShouldShowRecurringEventUpdateOptions(true);
+ }
+ }, [recurrenceRuleChanged, instanceDatesChanged]);
+
+ const [updateEvent] = useMutation(UPDATE_EVENT_MUTATION);
+
+ const updateEventHandler = async (): Promise => {
+ try {
+ const { data } = await updateEvent({
+ variables: {
+ id: eventListCardProps.id,
+ title: formState.title,
+ description: formState.eventdescrip,
+ isPublic: publicchecked,
+ recurring: recurringchecked,
+ recurringEventUpdateType: recurringchecked
+ ? recurringEventUpdateType
+ : undefined,
+ isRegisterable: registrablechecked,
+ allDay: alldaychecked,
+ startDate: dayjs(eventStartDate).format('YYYY-MM-DD'),
+ endDate: dayjs(eventEndDate).format('YYYY-MM-DD'),
+ location: formState.location,
+ startTime: !alldaychecked ? formState.startTime : undefined,
+ endTime: !alldaychecked ? formState.endTime : undefined,
+ recurrenceStartDate: recurringchecked
+ ? recurringEventUpdateType === thisAndFollowingInstances &&
+ (instanceDatesChanged || recurrenceRuleChanged)
+ ? dayjs(eventStartDate).format('YYYY-MM-DD')
+ : dayjs(recurrenceStartDate).format('YYYY-MM-DD')
+ : undefined,
+ recurrenceEndDate: recurringchecked
+ ? recurrenceEndDate
+ ? dayjs(recurrenceEndDate).format('YYYY-MM-DD')
+ : null
+ : undefined,
+ frequency: recurringchecked ? frequency : undefined,
+ weekDays:
+ recurringchecked &&
+ (frequency === Frequency.WEEKLY ||
+ (frequency === Frequency.MONTHLY && weekDayOccurenceInMonth))
+ ? weekDays
+ : undefined,
+ interval: recurringchecked ? interval : undefined,
+ count: recurringchecked ? count : undefined,
+ weekDayOccurenceInMonth: recurringchecked
+ ? weekDayOccurenceInMonth
+ : undefined,
+ },
+ });
+
+ if (data) {
+ toast.success(t('eventUpdated') as string);
+ setRecurringEventUpdateModalIsOpen(false);
+ hideViewModal();
+ if (refetchEvents) {
+ refetchEvents();
+ }
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ const handleEventUpdate = async (): Promise => {
+ if (!eventListCardProps.recurring) {
+ await updateEventHandler();
+ } else {
+ if (shouldShowRecurringEventUpdateOptions) {
+ setRecurringEventUpdateModalIsOpen(true);
+ } else {
+ await updateEventHandler();
+ }
+ }
+ };
+
+ const [deleteEvent] = useMutation(DELETE_EVENT_MUTATION);
+
+ const deleteEventHandler = async (): Promise => {
+ try {
+ const { data } = await deleteEvent({
+ variables: {
+ id: eventListCardProps.id,
+ recurringEventDeleteType: eventListCardProps.recurring
+ ? recurringEventDeleteType
+ : undefined,
+ },
+ });
+
+ if (data) {
+ toast.success(t('eventDeleted') as string);
+ setEventDeleteModalIsOpen(false);
+ hideViewModal();
+ if (refetchEvents) {
+ refetchEvents();
+ }
+ }
+ } catch (error: unknown) {
+ errorHandler(t, error);
+ }
+ };
+
+ const toggleDeleteModal = (): void => {
+ setEventDeleteModalIsOpen(!eventDeleteModalIsOpen);
+ };
+
+ const isInitiallyRegistered = eventListCardProps?.registrants?.some(
+ (registrant) => registrant._id === userId,
+ );
+ const [registerEventMutation] = useMutation(REGISTER_EVENT);
+ const [isRegistered, setIsRegistered] = useState(isInitiallyRegistered);
+
+ const registerEventHandler = async (): Promise => {
+ if (!isRegistered) {
+ try {
+ const { data } = await registerEventMutation({
+ variables: {
+ eventId: eventListCardProps.id,
+ },
+ });
+
+ if (data) {
+ toast.success(
+ `Successfully registered for ${eventListCardProps.eventName}`,
+ );
+ setIsRegistered(true);
+ hideViewModal();
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ }
+ };
+
+ const toggleRecurringEventUpdateModal = (): void => {
+ setRecurringEventUpdateModalIsOpen(!recurringEventUpdateModalIsOpen);
+ };
+
+ const openEventDashboard = (): void => {
+ const userPath = eventListCardProps.userRole === Role.USER ? 'user/' : '';
+ console.log(`/${userPath}event/${orgId}/${eventListCardProps.id}`);
+ navigate(`/${userPath}event/${orgId}/${eventListCardProps.id}`);
+ };
+
+ const popover = (
+
+ {recurrenceRuleText}
+
+ );
+
+ return (
+ <>
+ {/* preview modal */}
+
+
+ {t('eventDetails')}
+
+
+
+
+
+ 100
+ ? formState.title.substring(0, 100) + '...'
+ : formState.title
+ }
+ onChange={(e): void => {
+ setFormState({
+ ...formState,
+ title: e.target.value,
+ });
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+ {tCommon('description')}
+ 256
+ ? formState.eventdescrip.substring(0, 256) + '...'
+ : formState.eventdescrip
+ }
+ onChange={(e): void => {
+ setFormState({
+ ...formState,
+ eventdescrip: e.target.value,
+ });
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+ {tCommon('location')}
+ {
+ setFormState({
+ ...formState,
+ location: e.target.value,
+ });
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+
+
+ {
+ if (date) {
+ setEventStartDate(date?.toDate());
+ setEventEndDate(
+ eventEndDate < date?.toDate()
+ ? date?.toDate()
+ : eventEndDate,
+ );
+ if (!eventListCardProps.recurring) {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ recurrenceStartDate: date?.toDate(),
+ weekDays: [Days[date?.toDate().getDay()]],
+ weekDayOccurenceInMonth: weekDayOccurenceInMonth
+ ? /* istanbul ignore next */ getWeekDayOccurenceInMonth(
+ date?.toDate(),
+ )
+ : undefined,
+ });
+ }
+ }
+ }}
+ />
+
+
+ {
+ if (date) {
+ setEventEndDate(date?.toDate());
+ }
+ }}
+ minDate={dayjs(eventStartDate)}
+ />
+
+
+ {!alldaychecked && (
+
+
+ {
+ if (time) {
+ setFormState({
+ ...formState,
+ startTime: time?.format('HH:mm:ss'),
+ endTime:
+ timeToDayJs(formState.endTime) < time
+ ? time?.format('HH:mm:ss')
+ : /* istanbul ignore next */
+ formState.endTime,
+ });
+ }
+ }}
+ disabled={alldaychecked}
+ />
+
+
+ {
+ if (time) {
+ setFormState({
+ ...formState,
+ endTime: time?.format('HH:mm:ss'),
+ });
+ }
+ }}
+ minTime={timeToDayJs(formState.startTime)}
+ disabled={alldaychecked}
+ />
+
+
+ )}
+
+
+
+ {t('allDay')}?
+
{
+ setAllDayChecked(!alldaychecked);
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+
+
+ {t('recurringEvent')}:
+
{
+ setRecurringChecked(!recurringchecked);
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+
+
+
+
+ {t('isPublic')}?
+
{
+ setPublicChecked(!publicchecked);
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+
+
+ {t('isRegistrable')}?
+
{
+ setRegistrableChecked(!registrablechecked);
+ }}
+ disabled={
+ !(eventListCardProps.creator?._id === userId) &&
+ eventListCardProps.userRole === Role.USER
+ }
+ />
+
+
+
+
+ {/* Recurrence Options */}
+ {recurringchecked && (
+
+ )}
+
+
+
+ {(eventListCardProps.userRole !== Role.USER ||
+ eventListCardProps.creator?._id === userId) && (
+
+ {' '}
+ Show Event Dashboard{' '}
+
+ )}
+ {(eventListCardProps.userRole !== Role.USER ||
+ eventListCardProps.creator?._id === userId) && (
+
+ {t('editEvent')}
+
+ )}
+ {(eventListCardProps.userRole !== Role.USER ||
+ eventListCardProps.creator?._id === userId) && (
+
+ {t('deleteEvent')}
+
+ )}
+ {eventListCardProps.userRole === Role.USER &&
+ !(eventListCardProps.creator?._id === userId) &&
+ (isRegistered ? (
+
+ {t('alreadyRegistered')}
+
+ ) : (
+
+ {tCommon('register')}
+
+ ))}
+
+
+
+ {/* recurring event update options modal */}
+
+
+
+ {t('editEvent')}
+
+
+
+
+
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+
+ {/* delete modal */}
+
+
+
+ {t('deleteEvent')}
+
+
+
+ {!eventListCardProps.recurring && t('deleteEventMsg')}
+ {eventListCardProps.recurring && (
+ <>
+
+ >
+ )}
+
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+ >
+ );
+}
+
+export default EventListCardModals;
diff --git a/src/components/EventListCard/EventListCardProps.ts b/src/components/EventListCard/EventListCardProps.ts
new file mode 100644
index 0000000000..9aef474254
--- /dev/null
+++ b/src/components/EventListCard/EventListCardProps.ts
@@ -0,0 +1,194 @@
+import { Frequency, WeekDays } from 'utils/recurrenceUtils';
+import type { InterfaceEventListCardProps } from './EventListCard';
+
+export const props: InterfaceEventListCardProps[] = [
+ {
+ key: '',
+ id: '',
+ eventLocation: '',
+ eventName: '',
+ eventDescription: '',
+ startDate: '',
+ endDate: '',
+ startTime: '',
+ endTime: '',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: false,
+ isRegisterable: false,
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Dogs',
+ eventDescription: 'This is shelter for dogs event',
+ startDate: '2022-03-19',
+ endDate: '2022-03-26',
+ startTime: '02:00',
+ endTime: '06:00',
+ allDay: false,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ userRole: 'USER',
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Dogs',
+ eventDescription: 'This is shelter for dogs event',
+ startDate: '2022-03-19',
+ endDate: '2022-03-26',
+ startTime: '02:00',
+ endTime: '06:00',
+ allDay: true,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ creator: {
+ firstName: 'Joe',
+ lastName: 'David',
+ _id: '123',
+ },
+ registrants: [
+ {
+ _id: '234',
+ },
+ ],
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ userRole: 'USER',
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Dogs',
+ eventDescription: 'This is shelter for dogs event',
+ startDate: '2022-03-19',
+ endDate: '2022-03-26',
+ startTime: '02:00',
+ endTime: '06:00',
+ allDay: true,
+ recurring: false,
+ recurrenceRule: null,
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ creator: {
+ firstName: 'Joe',
+ lastName: 'David',
+ _id: '123',
+ },
+ registrants: [
+ {
+ _id: '456',
+ },
+ ],
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ userRole: 'ADMIN',
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Cats',
+ eventDescription: 'This is shelter for cat event',
+ startDate: '2022-03-19',
+ endDate: '2022-03-19',
+ startTime: '2:00',
+ endTime: '6:00',
+ allDay: false,
+ recurring: true,
+ recurrenceRule: {
+ recurrenceStartDate: '2022-03-19',
+ recurrenceEndDate: '2022-03-26',
+ frequency: Frequency.WEEKLY,
+ weekDays: [WeekDays.SATURDAY],
+ interval: 1,
+ count: null,
+ weekDayOccurenceInMonth: null,
+ },
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ userRole: 'ADMIN',
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Cats',
+ eventDescription: 'This is shelter for cat event',
+ startDate: '2022-03-17',
+ endDate: '2022-03-17',
+ startTime: null,
+ endTime: null,
+ allDay: true,
+ recurring: true,
+ recurrenceRule: {
+ recurrenceStartDate: '2022-03-17',
+ recurrenceEndDate: null,
+ frequency: Frequency.MONTHLY,
+ weekDays: [WeekDays.THURSDAY],
+ interval: 1,
+ count: null,
+ weekDayOccurenceInMonth: 3,
+ },
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+ {
+ userRole: 'ADMIN',
+ key: '123',
+ id: '1',
+ eventLocation: 'India',
+ eventName: 'Shelter for Cats',
+ eventDescription: 'This is shelter for cat event',
+ startDate: '2022-03-17',
+ endDate: '2022-03-17',
+ startTime: null,
+ endTime: null,
+ allDay: true,
+ recurring: true,
+ recurrenceRule: {
+ recurrenceStartDate: '2022-03-17',
+ recurrenceEndDate: '2023-03-17',
+ frequency: Frequency.MONTHLY,
+ weekDays: [WeekDays.THURSDAY],
+ interval: 1,
+ count: null,
+ weekDayOccurenceInMonth: 3,
+ },
+ isRecurringEventException: false,
+ isPublic: true,
+ isRegisterable: false,
+ refetchEvents: (): void => {
+ /* refetch function */
+ },
+ },
+];
diff --git a/src/components/EventManagement/Dashboard/EventDashboard.mocks.ts b/src/components/EventManagement/Dashboard/EventDashboard.mocks.ts
new file mode 100644
index 0000000000..f4f1a3025b
--- /dev/null
+++ b/src/components/EventManagement/Dashboard/EventDashboard.mocks.ts
@@ -0,0 +1,63 @@
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+
+export const MOCKS_WITH_TIME = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2024-01-01',
+ endDate: '2024-01-02',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ allDay: false,
+ location: 'India',
+ recurring: false,
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+ creator: {
+ _id: 'creator1',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_WITHOUT_TIME = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2024-01-01',
+ endDate: '2024-01-02',
+ startTime: null,
+ endTime: null,
+ allDay: true,
+ location: 'India',
+ recurring: false,
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+ creator: {
+ _id: 'creator1',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+ },
+ },
+ },
+];
diff --git a/src/components/EventManagement/Dashboard/EventDashboard.module.css b/src/components/EventManagement/Dashboard/EventDashboard.module.css
new file mode 100644
index 0000000000..37336002bb
--- /dev/null
+++ b/src/components/EventManagement/Dashboard/EventDashboard.module.css
@@ -0,0 +1,101 @@
+.eventContainer {
+ display: flex;
+ align-items: start;
+}
+
+.eventDetailsBox {
+ position: relative;
+ box-sizing: border-box;
+ background: #ffffff;
+ width: 66%;
+ padding: 0.3rem;
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 20px;
+ margin-bottom: 0;
+ margin-top: 20px;
+}
+.ctacards {
+ padding: 20px;
+ width: 100%;
+ display: flex;
+ background-color: #ffffff;
+ margin: 0 4px;
+ justify-content: space-between;
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
+ align-items: center;
+ border-radius: 20px;
+}
+.ctacards span {
+ color: rgb(181, 181, 181);
+ font-size: small;
+}
+/* .eventDetailsBox::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 6px;
+ background-color: #31bb6b;
+ border-radius: 20px;
+} */
+
+.time {
+ display: flex;
+ justify-content: space-between;
+ padding: 15px;
+ padding-bottom: 0px;
+ width: 33%;
+
+ box-sizing: border-box;
+ background: #ffffff;
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 20px;
+ margin-bottom: 0;
+ margin-top: 20px;
+ margin-left: 10px;
+}
+
+.startTime,
+.endTime {
+ display: flex;
+ font-size: 20px;
+}
+
+.to {
+ padding-right: 10px;
+}
+
+.startDate,
+.endDate {
+ color: #808080;
+ font-size: 14px;
+}
+
+.titlename {
+ font-weight: 600;
+ font-size: 25px;
+ padding: 15px;
+ padding-bottom: 0px;
+ width: 50%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.description {
+ color: #737373;
+ font-weight: 300;
+ font-size: 14px;
+ word-wrap: break-word;
+ padding: 15px;
+ padding-bottom: 0px;
+}
+
+.toporgloc {
+ font-size: 16px;
+ padding: 0.5rem;
+}
+
+.toporgloc span {
+ color: #737373;
+}
diff --git a/src/components/EventManagement/Dashboard/EventDashboard.test.tsx b/src/components/EventManagement/Dashboard/EventDashboard.test.tsx
new file mode 100644
index 0000000000..dc605a1604
--- /dev/null
+++ b/src/components/EventManagement/Dashboard/EventDashboard.test.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import type { RenderResult } from '@testing-library/react';
+import { render, act, fireEvent } from '@testing-library/react';
+import EventDashboard from './EventDashboard';
+import { BrowserRouter } from 'react-router-dom';
+import { ToastContainer } from 'react-toastify';
+import { MockedProvider } from '@apollo/react-testing';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import type { ApolloLink, DefaultOptions } from '@apollo/client';
+
+import { MOCKS_WITHOUT_TIME, MOCKS_WITH_TIME } from './EventDashboard.mocks';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const mockWithTime = new StaticMockLink(MOCKS_WITH_TIME, true);
+const mockWithoutTime = new StaticMockLink(MOCKS_WITHOUT_TIME, true);
+
+// We want to disable all forms of caching so that we do not need to define a custom merge function in testing for the network requests
+const defaultOptions: DefaultOptions = {
+ watchQuery: {
+ fetchPolicy: 'no-cache',
+ errorPolicy: 'ignore',
+ },
+ query: {
+ fetchPolicy: 'no-cache',
+ errorPolicy: 'all',
+ },
+};
+
+async function wait(ms = 500): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const mockID = 'event123';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ eventId: mockID }),
+}));
+
+const renderEventDashboard = (mockLink: ApolloLink): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Testing Event Dashboard Screen', () => {
+ test('The page should display event details correctly and also show the time if provided', async () => {
+ const { getByTestId } = renderEventDashboard(mockWithTime);
+
+ await wait();
+
+ expect(getByTestId('event-title')).toBeInTheDocument();
+ expect(getByTestId('event-description')).toBeInTheDocument();
+ expect(getByTestId('event-location')).toHaveTextContent('India');
+
+ expect(getByTestId('registrations-card')).toBeInTheDocument();
+ expect(getByTestId('attendees-card')).toBeInTheDocument();
+ expect(getByTestId('feedback-card')).toBeInTheDocument();
+ expect(getByTestId('feedback-rating')).toHaveTextContent('4/5');
+
+ const editButton = getByTestId('edit-event-button');
+ fireEvent.click(editButton);
+ expect(getByTestId('event-title')).toBeInTheDocument();
+ const closeButton = getByTestId('eventModalCloseBtn');
+ fireEvent.click(closeButton);
+ });
+
+ test('The page should display event details correctly and should not show the time if it is null', async () => {
+ const { getByTestId } = renderEventDashboard(mockWithoutTime);
+ await wait();
+
+ expect(getByTestId('event-title')).toBeInTheDocument();
+ expect(getByTestId('event-time')).toBeInTheDocument();
+ });
+
+ test('Should show loader while data is being fetched', async () => {
+ const { getByTestId, queryByTestId } = renderEventDashboard(mockWithTime);
+ expect(getByTestId('spinner')).toBeInTheDocument();
+ // Wait for loading to complete
+ await wait();
+
+ // Verify spinner is gone
+ expect(queryByTestId('spinner')).not.toBeInTheDocument();
+
+ // Verify content is visible
+ expect(getByTestId('event-title')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/EventManagement/Dashboard/EventDashboard.tsx b/src/components/EventManagement/Dashboard/EventDashboard.tsx
new file mode 100644
index 0000000000..d3552702c6
--- /dev/null
+++ b/src/components/EventManagement/Dashboard/EventDashboard.tsx
@@ -0,0 +1,188 @@
+import React, { useState } from 'react';
+import { Col, Row } from 'react-bootstrap';
+import styles from './EventDashboard.module.css';
+import { useTranslation } from 'react-i18next';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { useQuery } from '@apollo/client';
+import Loader from 'components/Loader/Loader';
+import { Edit } from '@mui/icons-material';
+import EventListCardModals from 'components/EventListCard/EventListCardModals';
+import type { InterfaceEventListCardProps } from 'components/EventListCard/EventListCard';
+import { formatDate } from 'utils/dateFormatter';
+
+/**
+ * Component that displays event details.
+ *
+ * @param props - The props for the EventDashboard component.
+ * @param eventId - The ID of the event to fetch and display.
+ * @returns The rendered EventDashboard component.
+ */
+const EventDashboard = (props: { eventId: string }): JSX.Element => {
+ const { eventId } = props;
+ const { t } = useTranslation(['translation', 'common']);
+ // const tEventManagement = (key: string): string => t(`eventManagement.${key}`);
+ const tEventList = (key: string): string => t(`eventListCard.${key}`);
+ const [eventModalIsOpen, setEventModalIsOpen] = useState(false);
+
+ const { data: eventData, loading: eventInfoLoading } = useQuery(
+ EVENT_DETAILS,
+ {
+ variables: { id: eventId },
+ },
+ );
+
+ /**
+ * Formats a time string (HH:MM) to a more readable format.
+ *
+ * @param timeString - The time string to format.
+ * @returns - The formatted time string.
+ */
+ function formatTime(timeString: string): string {
+ const [hours, minutes] = timeString.split(':').slice(0, 2);
+ return `${hours}:${minutes}`;
+ }
+
+ const showViewModal = (): void => {
+ setEventModalIsOpen(true);
+ };
+
+ const hideViewModal = (): void => {
+ setEventModalIsOpen(false);
+ };
+
+ if (eventInfoLoading) {
+ return ;
+ }
+
+ const eventListCardProps: InterfaceEventListCardProps = {
+ userRole: '',
+ key: eventData.event._id,
+ id: eventData.event._id,
+ eventLocation: eventData.event.location,
+ eventName: eventData.event.title,
+ eventDescription: eventData.event.description,
+ startDate: eventData.event.startDate,
+ endDate: eventData.event.endDate,
+ startTime: eventData.event.startTime,
+ endTime: eventData.event.endTime,
+ allDay: eventData.event.allDay,
+ recurring: eventData.event.recurring,
+ recurrenceRule: eventData.event.recurrenceRule,
+ isRecurringEventException: eventData.event.isRecurringEventException,
+ isPublic: eventData.event.isPublic,
+ isRegisterable: eventData.event.isRegisterable,
+ registrants: eventData.event.attendees,
+ creator: eventData.event.creator,
+ };
+
+ // Render event details
+ return (
+
+
+
+
+
+
+
+
+
+ {eventData.event.attendees.length}
+
+
+ No of Registrations
+
+
+
+
+
+
+
+ {eventData.event.attendees.length}
+
+
+ No of Attendees
+
+
+
+
+
+
+ 4/5
+
+ Average Feedback
+
+
+
+
+
+
+
+
+
+
+ {eventData.event.title}
+
+
+ {eventData.event.description}
+
+
+ Location: {eventData.event.location}
+
+
+ Registrants: {' '}
+ {eventData?.event?.attendees?.length}
+
+
+ Recurring Event: {' '}
+
+ {eventData.event.recurring ? 'Active' : 'Inactive'}
+
+
+
+
+
+
+ {eventData.event.startTime !== null
+ ? `${formatTime(eventData.event.startTime)}`
+ : ``}
+ {' '}
+
+ {formatDate(eventData.event.startDate)}{' '}
+
+
+
{t('to')}
+
+
+ {eventData.event.endTime !== null
+ ? `${formatTime(eventData.event.endTime)}`
+ : ``}
+ {' '}
+
+ {formatDate(eventData.event.endDate)}{' '}
+
+
+
+
+
+
+
+ );
+};
+
+export default EventDashboard;
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css
new file mode 100644
index 0000000000..9d1c32b766
--- /dev/null
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css
@@ -0,0 +1,22 @@
+.eventAgendaItemContainer h2 {
+ margin: 0.6rem 0;
+}
+
+.btnsContainer {
+ display: flex;
+ gap: 10px;
+}
+
+@media (max-width: 768px) {
+ .btnsContainer {
+ margin-bottom: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .createAgendaItemButton {
+ position: absolute;
+ top: 1rem;
+ right: 2rem;
+ }
+}
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx
new file mode 100644
index 0000000000..3bce7ad11e
--- /dev/null
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx
@@ -0,0 +1,214 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ waitFor,
+ act,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { MockedProvider } from '@apollo/client/testing';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import i18n from 'utils/i18nForTest';
+// import { toast } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+import EventAgendaItems from './EventAgendaItems';
+
+import {
+ MOCKS,
+ MOCKS_ERROR_QUERY,
+ // MOCKS_ERROR_MUTATION,
+} from './EventAgendaItemsMocks';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ eventId: '123' }),
+}));
+
+//temporarily fixes react-beautiful-dnd droppable method's depreciation error
+//needs to be fixed in React 19
+jest.spyOn(console, 'error').mockImplementation((message) => {
+ if (message.includes('Support for defaultProps will be removed')) {
+ return;
+ }
+ console.error(message);
+});
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR_QUERY, true);
+// const link3 = new StaticMockLink(MOCKS_ERROR_MUTATION, true);
+
+const translations = JSON.parse(
+ JSON.stringify(i18n.getDataByLanguage('en')?.translation.agendaItems),
+);
+
+describe('Testing Agenda Items Components', () => {
+ const formData = {
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ duration: '30',
+ relatedEventId: '123',
+ organizationId: '111',
+ sequence: 1,
+ categories: ['Category 1'],
+ attachments: [],
+ urls: [],
+ };
+ test('Component loads correctly', async () => {
+ window.location.assign('/event/111/123');
+ const { getByText } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.createAgendaItem)).toBeInTheDocument();
+ });
+ });
+
+ test('render error component on unsuccessful agenda item query', async () => {
+ window.location.assign('/event/111/123');
+ const { queryByText } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ queryByText(translations.createAgendaItem),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('opens and closes the create agenda item modal', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ { }
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createAgendaItemBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaItemBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('createAgendaItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaItemModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('createAgendaItemModalCloseBtn'),
+ );
+ });
+ test('creates new agenda item', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ { }
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createAgendaItemBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaItemBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('createAgendaItemModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.type(
+ screen.getByPlaceholderText(translations.enterTitle),
+ formData.title,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(translations.enterDescription),
+ formData.description,
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(translations.enterDuration),
+ formData.duration,
+ );
+ const categorySelect = screen.getByTestId('categorySelect');
+ userEvent.click(categorySelect);
+ await waitFor(() => {
+ const categoryOption = screen.getByText('Category 1');
+ userEvent.click(categoryOption);
+ });
+
+ userEvent.click(screen.getByTestId('createAgendaItemFormBtn'));
+
+ await waitFor(() => {
+ // expect(toast.success).toBeCalledWith(translations.agendaItemCreated);
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx
new file mode 100644
index 0000000000..b49ade4626
--- /dev/null
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx
@@ -0,0 +1,230 @@
+import React, { useState } from 'react';
+import type { ChangeEvent } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Button } from 'react-bootstrap';
+
+import { WarningAmberRounded } from '@mui/icons-material';
+import { toast } from 'react-toastify';
+
+import { useMutation, useQuery } from '@apollo/client';
+import {
+ AGENDA_ITEM_CATEGORY_LIST,
+ AgendaItemByEvent,
+} from 'GraphQl/Queries/Queries';
+import { CREATE_AGENDA_ITEM_MUTATION } from 'GraphQl/Mutations/mutations';
+
+import type {
+ InterfaceAgendaItemCategoryList,
+ InterfaceAgendaItemList,
+} from 'utils/interfaces';
+import AgendaItemsContainer from 'components/AgendaItems/AgendaItemsContainer';
+import AgendaItemsCreateModal from 'components/AgendaItems/AgendaItemsCreateModal';
+
+import styles from './EventAgendaItems.module.css';
+import Loader from 'components/Loader/Loader';
+
+/**
+ * Component to manage and display agenda items for a specific event.
+ *
+ * @param props - The component props.
+ * @param eventId - The ID of the event to manage agenda items for.
+ * @returns The rendered component.
+ */
+function EventAgendaItems(props: { eventId: string }): JSX.Element {
+ const { eventId } = props;
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'agendaItems',
+ });
+
+ // Extract organization ID from URL
+ const url: string = window.location.href;
+ const startIdx: number = url.indexOf('/event/') + '/event/'.length;
+ const orgId: string = url.slice(startIdx, url.indexOf('/', startIdx));
+
+ // State to manage the create agenda item modal visibility
+ const [agendaItemCreateModalIsOpen, setAgendaItemCreateModalIsOpen] =
+ useState(false);
+
+ // State to manage form values
+ const [formState, setFormState] = useState({
+ agendaItemCategoryIds: [''],
+ title: '',
+ description: '',
+ duration: '',
+ attachments: [''],
+ urls: [''],
+ });
+
+ // Query for agenda item categories
+ const {
+ data: agendaCategoryData,
+ loading: agendaCategoryLoading,
+ error: agendaCategoryError,
+ }: {
+ data: InterfaceAgendaItemCategoryList | undefined;
+ loading: boolean;
+ error?: Error | undefined;
+ } = useQuery(AGENDA_ITEM_CATEGORY_LIST, {
+ variables: { organizationId: orgId },
+ notifyOnNetworkStatusChange: true,
+ });
+
+ // Query for agenda items by event
+ const {
+ data: agendaItemData,
+ loading: agendaItemLoading,
+ error: agendaItemError,
+ refetch: refetchAgendaItem,
+ }: {
+ data: InterfaceAgendaItemList | undefined;
+ loading: boolean;
+ error?: unknown | undefined;
+ refetch: () => void;
+ } = useQuery(AgendaItemByEvent, {
+ variables: { relatedEventId: eventId }, //eventId
+ notifyOnNetworkStatusChange: true,
+ });
+
+ // Mutation for creating an agenda item
+ const [createAgendaItem] = useMutation(CREATE_AGENDA_ITEM_MUTATION);
+
+ /**
+ * Handler for creating a new agenda item.
+ *
+ * @param e - The form submit event.
+ */
+ const createAgendaItemHandler = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ await createAgendaItem({
+ variables: {
+ input: {
+ title: formState.title,
+ description: formState.description,
+ relatedEventId: eventId,
+ organizationId: orgId,
+ sequence: (agendaItemData?.agendaItemByEvent.length || 0) + 1 || 1, // Assign sequence based on current length
+ duration: formState.duration,
+ categories: formState.agendaItemCategoryIds,
+ attachments: formState.attachments,
+ urls: formState.urls,
+ },
+ },
+ });
+
+ // Reset form state and hide modal
+ setFormState({
+ title: '',
+ description: '',
+ duration: '',
+ agendaItemCategoryIds: [''],
+ attachments: [''],
+ urls: [''],
+ });
+ hideCreateModal();
+ refetchAgendaItem();
+ toast.success(t('agendaItemCreated') as string);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+
+ /**
+ * Toggles the visibility of the create agenda item modal.
+ */
+ const showCreateModal = (): void => {
+ setAgendaItemCreateModalIsOpen(!agendaItemCreateModalIsOpen);
+ };
+
+ /**
+ * Hides the create agenda item modal.
+ */
+ const hideCreateModal = (): void => {
+ setAgendaItemCreateModalIsOpen(!agendaItemCreateModalIsOpen);
+ };
+
+ // Show loader while data is loading
+ if (agendaItemLoading || agendaCategoryLoading) return ;
+
+ // Show error message if there is an error loading data
+ if (agendaItemError || agendaCategoryError) {
+ const errorMessage =
+ agendaCategoryError?.message ||
+ (agendaItemError as Error)?.message ||
+ 'Unknown error';
+
+ return (
+
+
+
+
+ Error occurred while loading{' '}
+ {agendaCategoryError ? 'Agenda Categories' : 'Agenda Items'} Data
+
+ {errorMessage}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {/* setSearchValue(e.target.value)}
+ value={searchValue}
+ data-testid="search"
+ /> */}
+
+
+
+ {t('createAgendaItem')}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default EventAgendaItems;
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItemsMocks.ts b/src/components/EventManagement/EventAgendaItems/EventAgendaItemsMocks.ts
new file mode 100644
index 0000000000..619a1e70f8
--- /dev/null
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItemsMocks.ts
@@ -0,0 +1,133 @@
+import { CREATE_AGENDA_ITEM_MUTATION } from 'GraphQl/Mutations/AgendaItemMutations';
+
+import { AgendaItemByEvent } from 'GraphQl/Queries/AgendaItemQueries';
+import { AGENDA_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/AgendaCategoryQueries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: AGENDA_ITEM_CATEGORY_LIST,
+ variables: { organizationId: '111' },
+ },
+ result: {
+ data: {
+ agendaItemCategoriesByOrganization: [
+ {
+ _id: 'agendaItemCategory1',
+ name: 'Category 1',
+ description: 'Test Description',
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: AgendaItemByEvent,
+ variables: { relatedEventId: '123' },
+ },
+ result: {
+ data: {
+ agendaItemByEvent: [
+ {
+ _id: 'agendaItem1',
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ duration: '30',
+ attachments: [],
+ createdBy: {
+ _id: 'user0',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ urls: [],
+ users: [],
+ sequence: 1,
+ categories: [
+ {
+ _id: 'agendaItemCategory1',
+ name: 'Category 1',
+ },
+ ],
+ organization: {
+ _id: '111',
+ name: 'Unity Foundation',
+ },
+ relatedEvent: {
+ _id: '123',
+ title: 'Aerobics for Everyone',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CREATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ input: {
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ duration: '30',
+ relatedEventId: '123',
+ organizationId: '111',
+ sequence: 1,
+ categories: ['Category 1'],
+ attachments: [],
+ urls: [],
+ },
+ },
+ },
+ result: {
+ data: {
+ createAgendaItem: {
+ _id: 'agendaItem1',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR_MUTATION = [
+ {
+ request: {
+ query: CREATE_AGENDA_ITEM_MUTATION,
+ variables: {
+ input: {
+ title: 'AgendaItem 1',
+ description: 'AgendaItem 1 Description',
+ duration: '30',
+ relatedEventId: '123',
+ organizationId: '111',
+ sequence: 1,
+ categories: ['agendaItemCategory1'],
+ attachments: [],
+ urls: [],
+ },
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+ {
+ request: {
+ query: AgendaItemByEvent,
+ variables: { relatedEventId: '123' },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+ {
+ request: {
+ query: AGENDA_ITEM_CATEGORY_LIST,
+ variables: { organizationId: '111' },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
+
+export const MOCKS_ERROR_QUERY = [];
diff --git a/src/components/EventManagement/EventAttendance/Attendance.mocks.ts b/src/components/EventManagement/EventAttendance/Attendance.mocks.ts
new file mode 100644
index 0000000000..2dc8c89571
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/Attendance.mocks.ts
@@ -0,0 +1,62 @@
+import { EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: {}, // Removed id since it's not required based on error
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: '6589386a2caa9d8d69087484',
+ firstName: 'Bruce',
+ lastName: 'Garza',
+ gender: null,
+ birthDate: null,
+ createdAt: '2023-04-13T10:23:17.742',
+ eventsAttended: [
+ {
+ __typename: 'Event',
+ _id: '660fdf7d2c1ef6c7db1649ad',
+ },
+ {
+ __typename: 'Event',
+ _id: '660fdd562c1ef6c7db1644f7',
+ },
+ ],
+ __typename: 'User',
+ },
+ {
+ _id: '6589386a2caa9d8d69087485',
+ firstName: 'Jane',
+ lastName: 'Smith',
+ gender: null,
+ birthDate: null,
+ createdAt: '2023-04-13T10:23:17.742',
+ eventsAttended: [
+ {
+ __typename: 'Event',
+ _id: '660fdf7d2c1ef6c7db1649ad',
+ },
+ ],
+ __typename: 'User',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: {},
+ },
+ error: new Error('An error occurred'),
+ },
+];
diff --git a/src/components/EventManagement/EventAttendance/AttendedEventList.test.tsx b/src/components/EventManagement/EventAttendance/AttendedEventList.test.tsx
new file mode 100644
index 0000000000..2d60081acf
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/AttendedEventList.test.tsx
@@ -0,0 +1,119 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import AttendedEventList from './AttendedEventList';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { formatDate } from 'utils/dateFormatter';
+
+const mockEvent = {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'This is a test event description',
+ startDate: '2023-05-01',
+ endDate: '2023-05-02',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: {
+ _id: 'recurringEvent123',
+ },
+ organization: {
+ _id: 'org456',
+ members: [
+ { _id: 'member1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'member2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+};
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: mockEvent,
+ },
+ },
+ },
+];
+
+describe('Testing AttendedEventList', () => {
+ const props = {
+ eventId: 'event123',
+ };
+
+ test('Component renders and displays event details correctly', async () => {
+ const { queryByText, queryByTitle } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(queryByText('Loading...')).toBeInTheDocument();
+
+ await waitFor(() => {
+ expect(queryByText('Test Event')).toBeInTheDocument();
+ expect(queryByText(formatDate(mockEvent.startDate))).toBeInTheDocument();
+ expect(queryByTitle('Event Date')).toBeInTheDocument();
+ });
+ });
+
+ test('Component handles error state gracefully', async () => {
+ const errorMock = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ const { queryByText } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(queryByText('Loading...')).not.toBeInTheDocument();
+ // The component doesn't explicitly render an error message, so we just check that the event details are not rendered
+ expect(queryByText('Test Event')).not.toBeInTheDocument();
+ });
+ });
+
+ test('Component renders link with correct URL', async () => {
+ const { container } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ const link = container.querySelector('a');
+ expect(link).not.toBeNull();
+ expect(link).toHaveAttribute('href', expect.stringContaining('/event/'));
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/AttendedEventList.tsx b/src/components/EventManagement/EventAttendance/AttendedEventList.tsx
new file mode 100644
index 0000000000..2d0286feb0
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/AttendedEventList.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { TableBody, TableCell, TableRow, Table } from '@mui/material';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { useQuery } from '@apollo/client';
+import { Link, useParams } from 'react-router-dom';
+import { formatDate } from 'utils/dateFormatter';
+import DateIcon from 'assets/svgs/cardItemDate.svg?react';
+interface InterfaceEventsAttended {
+ eventId: string;
+}
+/**
+ * Component to display a list of events attended by a member
+ * @param eventId - The ID of the event to display details for
+ * @returns A table row containing event details with a link to the event
+ */
+const AttendedEventList: React.FC = ({ eventId }) => {
+ const { orgId: currentOrg } = useParams();
+ const { data, loading, error } = useQuery(EVENT_DETAILS, {
+ variables: { id: eventId },
+ fetchPolicy: 'cache-first',
+ errorPolicy: 'all',
+ });
+
+ if (error || data?.error) {
+ return Error loading event details. Please try again later.
;
+ }
+
+ const event = data?.event ?? null;
+
+ if (loading) return Loading...
;
+ return (
+
+
+
+ {event && (
+
+
+
+
+
+
{event.title}
+
{formatDate(event.startDate)}
+
+
+
+
+ )}
+
+
+
+ );
+};
+export default AttendedEventList;
diff --git a/src/components/EventManagement/EventAttendance/EventAttendance.test.tsx b/src/components/EventManagement/EventAttendance/EventAttendance.test.tsx
new file mode 100644
index 0000000000..db44357d07
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventAttendance.test.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ waitFor,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import EventAttendance from './EventAttendance';
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18n from 'utils/i18nForTest';
+import { MOCKS } from './Attendance.mocks';
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(): Promise {
+ await waitFor(() => {
+ return Promise.resolve();
+ });
+}
+jest.mock('react-chartjs-2', () => ({
+ Line: () => null,
+ Bar: () => null,
+ Pie: () => null,
+}));
+
+const renderEventAttendance = (): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Event Attendance Component', () => {
+ beforeEach(() => {
+ jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ eventId: 'event123', orgId: 'org123' }),
+ }));
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('Component loads correctly with table headers', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('table-header-row')).toBeInTheDocument();
+ expect(screen.getByTestId('header-member-name')).toBeInTheDocument();
+ expect(screen.getByTestId('header-status')).toBeInTheDocument();
+ });
+ });
+
+ test('Renders attendee data correctly', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendee-name-0')).toBeInTheDocument();
+ expect(screen.getByTestId('attendee-name-1')).toHaveTextContent(
+ 'Jane Smith',
+ );
+ });
+ });
+
+ test('Search filters attendees by name correctly', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ const searchInput = screen.getByTestId('searchByName');
+ fireEvent.change(searchInput, { target: { value: 'Bruce' } });
+
+ await waitFor(() => {
+ const filteredAttendee = screen.getByTestId('attendee-name-0');
+ expect(filteredAttendee).toHaveTextContent('Bruce Garza');
+ });
+ });
+
+ test('Sort functionality changes attendee order', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ const sortDropdown = screen.getByTestId('sort-dropdown');
+ userEvent.click(sortDropdown);
+ userEvent.click(screen.getByText('Sort'));
+
+ await waitFor(() => {
+ const attendees = screen.getAllByTestId('attendee-name-0');
+ expect(attendees[0]).toHaveTextContent('Bruce Garza');
+ });
+ });
+
+ test('Date filter shows correct number of attendees', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ userEvent.click(screen.getByText('Filter: All'));
+ userEvent.click(screen.getByText('This Month'));
+
+ await waitFor(() => {
+ expect(screen.getByText('Attendees not Found')).toBeInTheDocument();
+ });
+ });
+ test('Statistics modal opens and closes correctly', async () => {
+ renderEventAttendance();
+ await wait();
+
+ expect(screen.queryByTestId('attendance-modal')).not.toBeInTheDocument();
+
+ const statsButton = screen.getByTestId('stats-modal');
+ userEvent.click(statsButton);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendance-modal')).toBeInTheDocument();
+ });
+
+ const closeButton = screen.getByTestId('close-button');
+ userEvent.click(closeButton);
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('attendance-modal')).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/EventAttendance.tsx b/src/components/EventManagement/EventAttendance/EventAttendance.tsx
new file mode 100644
index 0000000000..17f063f6b5
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventAttendance.tsx
@@ -0,0 +1,376 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { BiSearch as Search } from 'react-icons/bi';
+import {
+ Paper,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Tooltip,
+} from '@mui/material';
+import {
+ Button,
+ Dropdown,
+ DropdownButton,
+ Table,
+ FormControl,
+} from 'react-bootstrap';
+import styles from './EventsAttendance.module.css';
+import { useLazyQuery } from '@apollo/client';
+import { EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+import { useParams, Link } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { AttendanceStatisticsModal } from './EventStatistics';
+import AttendedEventList from './AttendedEventList';
+import type { InterfaceMember } from './InterfaceEvents';
+enum FilterPeriod {
+ ThisMonth = 'This Month',
+ ThisYear = 'This Year',
+ All = 'All',
+}
+/**
+ * Component to manage and display event attendance information
+ * Includes filtering and sorting functionality for attendees
+ * @returns JSX element containing the event attendance interface
+ */
+function EventAttendance(): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventAttendance',
+ });
+ const { eventId } = useParams<{ eventId: string }>();
+ const { orgId: currentUrl } = useParams();
+ const [filteredAttendees, setFilteredAttendees] = useState(
+ [],
+ );
+ const [sortOrder, setSortOrder] = useState<'ascending' | 'descending'>(
+ 'ascending',
+ );
+ const [filteringBy, setFilteringBy] = useState(
+ FilterPeriod.All,
+ );
+ const [show, setShow] = useState(false);
+
+ const sortAttendees = (attendees: InterfaceMember[]): InterfaceMember[] => {
+ return [...attendees].sort((a, b) => {
+ const nameA = `${a.firstName} ${a.lastName}`.toLowerCase();
+ const nameB = `${b.firstName} ${b.lastName}`.toLowerCase();
+ return sortOrder === 'ascending'
+ ? nameA.localeCompare(nameB)
+ : /*istanbul ignore next*/
+ nameB.localeCompare(nameA);
+ });
+ };
+
+ const filterAttendees = (attendees: InterfaceMember[]): InterfaceMember[] => {
+ const now = new Date();
+ return filteringBy === 'All'
+ ? attendees
+ : attendees.filter((attendee) => {
+ const attendeeDate = new Date(attendee.createdAt);
+ const isSameYear = attendeeDate.getFullYear() === now.getFullYear();
+ return filteringBy === 'This Month'
+ ? isSameYear && attendeeDate.getMonth() === now.getMonth()
+ : /*istanbul ignore next*/
+ isSameYear;
+ });
+ };
+
+ const filterAndSortAttendees = (
+ attendees: InterfaceMember[],
+ ): InterfaceMember[] => {
+ return sortAttendees(filterAttendees(attendees));
+ };
+ const searchEventAttendees = (value: string): void => {
+ const searchValueLower = value.toLowerCase().trim();
+
+ const filtered = (memberData?.event?.attendees ?? []).filter(
+ (attendee: InterfaceMember) => {
+ const fullName =
+ `${attendee.firstName} ${attendee.lastName}`.toLowerCase();
+ return (
+ attendee.firstName?.toLowerCase().includes(searchValueLower) ||
+ attendee.lastName?.toLowerCase().includes(searchValueLower) ||
+ attendee.email?.toLowerCase().includes(searchValueLower) ||
+ fullName.includes(searchValueLower)
+ );
+ },
+ );
+
+ const finalFiltered = filterAndSortAttendees(filtered);
+ setFilteredAttendees(finalFiltered);
+ };
+ const showModal = (): void => setShow(true);
+ const handleClose = (): void => setShow(false);
+
+ const statistics = useMemo(() => {
+ const totalMembers = filteredAttendees.length;
+ const membersAttended = filteredAttendees.filter(
+ (member) => member?.eventsAttended && member.eventsAttended.length > 0,
+ ).length;
+ const attendanceRate =
+ totalMembers > 0
+ ? Number(((membersAttended / totalMembers) * 100).toFixed(2))
+ : 0;
+
+ return { totalMembers, membersAttended, attendanceRate };
+ }, [filteredAttendees]);
+
+ const [getEventAttendees, { data: memberData, loading, error }] =
+ useLazyQuery(EVENT_ATTENDEES, {
+ variables: {
+ id: eventId,
+ },
+ fetchPolicy: 'cache-and-network',
+ nextFetchPolicy: 'cache-first',
+ errorPolicy: 'all',
+ notifyOnNetworkStatusChange: true,
+ });
+
+ useEffect(() => {
+ if (memberData?.event?.attendees) {
+ const updatedAttendees = filterAndSortAttendees(
+ memberData.event.attendees,
+ );
+ setFilteredAttendees(updatedAttendees);
+ }
+ }, [sortOrder, filteringBy, memberData]);
+
+ useEffect(() => {
+ getEventAttendees();
+ }, [eventId, getEventAttendees]);
+
+ if (loading) return {t('loading')}
;
+ /*istanbul ignore next*/
+ if (error) return {error.message}
;
+
+ return (
+
+
+
+
+
+ {t('historical_statistics')}
+
+
+
+
+ searchEventAttendees(e.target.value)}
+ />
+
+
+
+
+
+
+
+ Filter: {filteringBy}
+ >
+ }
+ onSelect={(eventKey) => setFilteringBy(eventKey as FilterPeriod)}
+ >
+ This Month
+ This Year
+ All
+
+
+
+ Sort
+ >
+ }
+ onSelect={
+ /*istanbul ignore next*/
+ (eventKey) => setSortOrder(eventKey as 'ascending' | 'descending')
+ }
+ >
+ Ascending
+ Descending
+
+
+
+ {/*
{totalMembers} */}
+
+
+
+
+
+ #
+
+
+ {t('Member Name')}
+
+
+ {t('Status')}
+
+
+ {t('Events Attended')}
+
+
+ {t('Task Assigned')}
+
+
+
+
+ {filteredAttendees.length === 0 ? (
+
+
+ {t('noAttendees')}
+
+
+ ) : (
+ filteredAttendees.map(
+ (member: InterfaceMember, index: number) => (
+
+
+ {index + 1}
+
+
+
+ {member.firstName} {member.lastName}
+
+
+
+ {member.__typename === 'User' ? t('Member') : t('Admin')}
+
+ (
+
+ ),
+ )}
+ >
+
+
+ {member.eventsAttended
+ ? member.eventsAttended.length
+ : /*istanbul ignore next*/
+ '0'}
+
+
+
+
+ {member.tagsAssignedWith ? (
+ /*istanbul ignore next*/
+ member.tagsAssignedWith.edges.map(
+ /*istanbul ignore next*/
+ (
+ edge: { node: { name: string } },
+ tagIndex: number,
+ ) => {edge.node.name}
,
+ )
+ ) : (
+ None
+ )}
+
+
+ ),
+ )
+ )}
+
+
+
+
+ );
+}
+
+export default EventAttendance;
diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.test.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.test.tsx
new file mode 100644
index 0000000000..03f4671a5e
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventStatistics.test.tsx
@@ -0,0 +1,358 @@
+import React from 'react';
+import { act, render, screen, waitFor } from '@testing-library/react';
+import { AttendanceStatisticsModal } from './EventStatistics';
+import { MockedProvider } from '@apollo/client/testing';
+import { EVENT_DETAILS, RECURRING_EVENTS } from 'GraphQl/Queries/Queries';
+import userEvent from '@testing-library/user-event';
+import { exportToCSV } from 'utils/chartToPdf';
+
+// Mock chart.js to avoid canvas errors
+jest.mock('react-chartjs-2', () => ({
+ Line: () => null,
+ Bar: () => null,
+}));
+// Mock react-router-dom
+jest.mock('react-router-dom', () => ({
+ useParams: () => ({
+ orgId: 'org123',
+ eventId: 'event123',
+ }),
+}));
+jest.mock('utils/chartToPdf', () => ({
+ exportToCSV: jest.fn(),
+}));
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2023-01-01',
+ endDate: '2023-01-02',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: { _id: 'base123' },
+ organization: {
+ _id: 'org123',
+ members: [
+ { _id: 'user1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'user2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [
+ {
+ _id: 'user1',
+ gender: 'MALE',
+ },
+ {
+ _id: 'user2',
+ gender: 'FEMALE',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: RECURRING_EVENTS,
+ variables: { baseRecurringEventId: 'base123' },
+ },
+ result: {
+ data: {
+ getRecurringEvents: [
+ {
+ _id: 'event123',
+ startDate: '2023-01-01',
+ title: 'Test Event 1',
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user2', gender: 'FEMALE' },
+ ],
+ },
+ {
+ _id: 'event456',
+ startDate: '2023-01-08',
+ title: 'Test Event 2',
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user3', gender: 'OTHER' },
+ ],
+ },
+ {
+ _id: 'event789',
+ startDate: '2023-01-15',
+ title: 'Test Event 3',
+ attendees: [
+ { _id: 'user2', gender: 'FEMALE' },
+ { _id: 'user3', gender: 'OTHER' },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const mockMemberData = [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ gender: 'MALE',
+ birthDate: new Date('1990-01-01'),
+ email: 'john@example.com' as `${string}@${string}.${string}`,
+ createdAt: '2023-01-01',
+ __typename: 'User',
+ tagsAssignedWith: {
+ edges: [],
+ },
+ },
+ {
+ _id: 'user2',
+ firstName: 'Jane',
+ lastName: 'Smith',
+ gender: 'FEMALE',
+ birthDate: new Date('1985-05-05'),
+ email: 'jane@example.com' as `${string}@${string}.${string}`,
+ createdAt: '2023-01-01',
+ __typename: 'User',
+ tagsAssignedWith: {
+ edges: [],
+ },
+ },
+];
+
+const mockStatistics = {
+ totalMembers: 2,
+ membersAttended: 1,
+ attendanceRate: 50,
+};
+
+describe('AttendanceStatisticsModal', () => {
+ test('renders modal with correct initial state', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendance-modal')).toBeInTheDocument();
+ expect(screen.getByTestId('gender-button')).toBeInTheDocument();
+ expect(screen.getByTestId('age-button')).toBeInTheDocument();
+ });
+ });
+
+ test('switches between gender and age demographics', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const genderButton = screen.getByTestId('gender-button');
+ const ageButton = screen.getByTestId('age-button');
+
+ userEvent.click(ageButton);
+ expect(ageButton).toHaveClass('btn-success');
+ expect(genderButton).toHaveClass('btn-light');
+
+ userEvent.click(genderButton);
+ expect(genderButton).toHaveClass('btn-success');
+ expect(ageButton).toHaveClass('btn-light');
+ });
+ });
+
+ test('handles data demographics export functionality', async () => {
+ const mockExportToCSV = jest.fn();
+ (exportToCSV as jest.Mock).mockImplementation(mockExportToCSV);
+
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('button', { name: 'Export Data' }),
+ ).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ const exportButton = screen.getByRole('button', { name: 'Export Data' });
+ await userEvent.click(exportButton);
+ });
+
+ await act(async () => {
+ const demographicsExport = screen.getByTestId('demographics-export');
+ await userEvent.click(demographicsExport);
+ });
+
+ expect(mockExportToCSV).toHaveBeenCalled();
+ });
+ test('handles data trends export functionality', async () => {
+ const mockExportToCSV = jest.fn();
+ (exportToCSV as jest.Mock).mockImplementation(mockExportToCSV);
+
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('button', { name: 'Export Data' }),
+ ).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ const exportButton = screen.getByRole('button', { name: 'Export Data' });
+ await userEvent.click(exportButton);
+ });
+
+ await act(async () => {
+ const demographicsExport = screen.getByTestId('trends-export');
+ await userEvent.click(demographicsExport);
+ });
+
+ expect(mockExportToCSV).toHaveBeenCalled();
+ });
+
+ test('displays recurring event data correctly', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+ });
+ test('handles pagination and today button correctly', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ // Wait for initial render
+ await waitFor(() => {
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+
+ // Test pagination
+ await act(async () => {
+ const nextButton = screen.getByAltText('right-arrow');
+ await userEvent.click(nextButton);
+ });
+
+ await act(async () => {
+ const prevButton = screen.getByAltText('left-arrow');
+ await userEvent.click(prevButton);
+ });
+
+ // Test today button
+ await act(async () => {
+ const todayButton = screen.getByTestId('today-button');
+ await userEvent.click(todayButton);
+ });
+
+ // Verify buttons are present and interactive
+ expect(screen.getByAltText('right-arrow')).toBeInTheDocument();
+ expect(screen.getByAltText('left-arrow')).toBeInTheDocument();
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+
+ test('handles pagination in recurring events view', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const nextButton = screen.getByAltText('right-arrow');
+ const prevButton = screen.getByAltText('left-arrow');
+
+ userEvent.click(nextButton);
+ userEvent.click(prevButton);
+ });
+ });
+
+ test('closes modal correctly', async () => {
+ const handleClose = jest.fn();
+ render(
+
+ key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const closeButton = screen.getByTestId('close-button');
+ userEvent.click(closeButton);
+ expect(handleClose).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
new file mode 100644
index 0000000000..5dda9e88a8
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
@@ -0,0 +1,583 @@
+import React, { useEffect, useState, useMemo, useCallback } from 'react';
+import {
+ Modal,
+ Button,
+ ButtonGroup,
+ Tooltip,
+ OverlayTrigger,
+ Dropdown,
+} from 'react-bootstrap';
+import 'react-datepicker/dist/react-datepicker.css';
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ BarElement,
+ Title,
+ Tooltip as ChartToolTip,
+ Legend,
+} from 'chart.js';
+import { Bar, Line } from 'react-chartjs-2';
+import { useParams } from 'react-router-dom';
+import { EVENT_DETAILS, RECURRING_EVENTS } from 'GraphQl/Queries/Queries';
+import { useLazyQuery } from '@apollo/client';
+import { exportToCSV } from 'utils/chartToPdf';
+import type { ChartOptions, TooltipItem } from 'chart.js';
+import type {
+ InterfaceAttendanceStatisticsModalProps,
+ InterfaceEvent,
+ InterfaceRecurringEvent,
+} from './InterfaceEvents';
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ BarElement,
+ Title,
+ ChartToolTip,
+ Legend,
+);
+/**
+ * Component to display statistical information about event attendance
+ * Shows metrics like total attendees, filtering options, and attendance trends
+ * @returns JSX element with event statistics dashboard
+ */
+
+export const AttendanceStatisticsModal: React.FC<
+ InterfaceAttendanceStatisticsModalProps
+> = ({ show, handleClose, statistics, memberData, t }): JSX.Element => {
+ const [selectedCategory, setSelectedCategory] = useState('Gender');
+ const { orgId, eventId } = useParams();
+ const [currentPage, setCurrentPage] = useState(0);
+ const eventsPerPage = 10;
+ const [loadEventDetails, { data: eventData }] = useLazyQuery(EVENT_DETAILS);
+ const [loadRecurringEvents, { data: recurringData }] =
+ useLazyQuery(RECURRING_EVENTS);
+ const isEventRecurring = eventData?.event?.recurring;
+ const currentEventIndex = useMemo(() => {
+ if (!recurringData?.getRecurringEvents || !eventId) return -1;
+ return recurringData.getRecurringEvents.findIndex(
+ (event: InterfaceEvent) => event._id === eventId,
+ );
+ }, [recurringData, eventId]);
+ useEffect(() => {
+ if (currentEventIndex >= 0) {
+ const newPage = Math.floor(currentEventIndex / eventsPerPage);
+ setCurrentPage(newPage);
+ }
+ }, [currentEventIndex, eventsPerPage]);
+ const filteredRecurringEvents = useMemo(
+ () => recurringData?.getRecurringEvents || [],
+ [recurringData],
+ );
+ const totalEvents = filteredRecurringEvents.length;
+ const totalPages = Math.ceil(totalEvents / eventsPerPage);
+
+ const paginatedRecurringEvents = useMemo(() => {
+ const startIndex = currentPage * eventsPerPage;
+ const endIndex = Math.min(startIndex + eventsPerPage, totalEvents);
+ return filteredRecurringEvents.slice(startIndex, endIndex);
+ }, [filteredRecurringEvents, currentPage, eventsPerPage, totalEvents]);
+
+ const attendeeCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) => event.attendees.length,
+ ),
+ [paginatedRecurringEvents],
+ );
+ const chartOptions: ChartOptions<'line'> = {
+ responsive: true,
+ maintainAspectRatio: false,
+ animation: false,
+ scales: { y: { beginAtZero: true } },
+ plugins: {
+ tooltip: {
+ callbacks: {
+ label:
+ /*istanbul ignore next*/
+ (context: TooltipItem<'line'>) => {
+ const label = context.dataset.label || '';
+ const value = context.parsed.y;
+ const isCurrentEvent =
+ paginatedRecurringEvents[context.dataIndex]._id === eventId;
+ return isCurrentEvent
+ ? `${label}: ${value} (Current Event)`
+ : `${label}: ${value}`;
+ },
+ },
+ },
+ },
+ };
+ const eventLabels = useMemo(
+ () =>
+ paginatedRecurringEvents.map((event: InterfaceEvent) => {
+ const date = (() => {
+ try {
+ const eventDate = new Date(event.startDate);
+ if (Number.isNaN(eventDate.getTime())) {
+ /*istanbul ignore next*/
+ console.error(`Invalid date for event: ${event._id}`);
+ /*istanbul ignore next*/
+ return 'Invalid date';
+ }
+ return eventDate.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ });
+ } catch (error) {
+ /*istanbul ignore next*/
+ console.error(
+ `Error formatting date for event: ${event._id}`,
+ error,
+ );
+ /*istanbul ignore next*/
+ return 'Invalid date';
+ }
+ })();
+ // Highlight the current event in the label
+ return event._id === eventId ? `→ ${date}` : date;
+ }),
+ [paginatedRecurringEvents, eventId],
+ );
+
+ const maleCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter((attendee) => attendee.gender === 'MALE')
+ .length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const femaleCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter((attendee) => attendee.gender === 'FEMALE')
+ .length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const otherCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter(
+ (attendee) =>
+ attendee.gender === 'OTHER' || attendee.gender === null,
+ ).length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const chartData = useMemo(
+ () => ({
+ labels: eventLabels,
+ datasets: [
+ {
+ label: 'Attendee Count',
+ data: attendeeCounts,
+ fill: true,
+ borderColor: '#008000',
+ pointRadius: paginatedRecurringEvents.map(
+ (event: InterfaceRecurringEvent) => (event._id === eventId ? 8 : 3),
+ ),
+ pointBackgroundColor: paginatedRecurringEvents.map(
+ (event: InterfaceRecurringEvent) =>
+ event._id === eventId ? '#008000' : 'transparent',
+ ),
+ },
+ {
+ label: 'Male Attendees',
+ data: maleCounts,
+ fill: false,
+ borderColor: '#0000FF',
+ },
+ {
+ label: 'Female Attendees',
+ data: femaleCounts,
+ fill: false,
+ borderColor: '#FF1493',
+ },
+ {
+ label: 'Other Attendees',
+ data: otherCounts,
+ fill: false,
+ borderColor: '#FFD700',
+ },
+ ],
+ }),
+ [eventLabels, attendeeCounts, maleCounts, femaleCounts, otherCounts],
+ );
+
+ const handlePreviousPage = useCallback(
+ /*istanbul ignore next*/
+ () => {
+ setCurrentPage((prevPage) => Math.max(prevPage - 1, 0));
+ },
+ [],
+ );
+
+ const handleNextPage = useCallback(
+ /*istanbul ignore next*/
+ () => {
+ if (currentPage < totalPages - 1) {
+ setCurrentPage((prevPage) => prevPage + 1);
+ }
+ },
+ [currentPage, totalPages],
+ );
+
+ const handleDateChange = useCallback((date: Date | null) => {
+ if (date) {
+ setCurrentPage(0);
+ }
+ }, []);
+ const categoryLabels = useMemo(
+ () =>
+ selectedCategory === 'Gender'
+ ? ['Male', 'Female', 'Other']
+ : ['Under 18', '18-40', 'Over 40'],
+ [selectedCategory],
+ );
+
+ const categoryData = useMemo(
+ () =>
+ selectedCategory === 'Gender'
+ ? [
+ memberData.filter((member) => member.gender === 'MALE').length,
+ memberData.filter((member) => member.gender === 'FEMALE').length,
+ memberData.filter(
+ (member) =>
+ member.gender === 'OTHER' ||
+ member.gender === null ||
+ member.gender === '',
+ ).length,
+ ]
+ : [
+ memberData.filter((member) => {
+ const today = new Date();
+ const birthDate = new Date(member.birthDate);
+ let age = today.getFullYear() - birthDate.getFullYear();
+ const monthDiff = today.getMonth() - birthDate.getMonth();
+ if (
+ monthDiff < 0 ||
+ (monthDiff === 0 && today.getDate() < birthDate.getDate())
+ ) {
+ /*istanbul ignore next*/
+ age--;
+ }
+ return age < 18;
+ }).length,
+ memberData.filter((member) => {
+ const age =
+ new Date().getFullYear() -
+ new Date(member.birthDate).getFullYear();
+ return age >= 18 && age <= 40;
+ }).length,
+ memberData.filter(
+ (member) =>
+ new Date().getFullYear() -
+ new Date(member.birthDate).getFullYear() >
+ 40,
+ ).length,
+ ],
+ [selectedCategory, memberData],
+ );
+
+ const handleCategoryChange = useCallback((category: string): void => {
+ setSelectedCategory(category);
+ }, []);
+
+ const exportTrendsToCSV = useCallback(() => {
+ const headers = [
+ 'Date',
+ 'Attendee Count',
+ 'Male Attendees',
+ 'Female Attendees',
+ 'Other Attendees',
+ ];
+ const data = [
+ headers,
+ ...eventLabels.map((label: string, index: number) => [
+ label,
+ attendeeCounts[index],
+ maleCounts[index],
+ femaleCounts[index],
+ otherCounts[index],
+ ]),
+ ];
+ exportToCSV(data, 'attendance_trends.csv');
+ }, [eventLabels, attendeeCounts, maleCounts, femaleCounts, otherCounts]);
+
+ const exportDemographicsToCSV = useCallback(() => {
+ const headers = [selectedCategory, 'Count'];
+ const data = [
+ headers,
+ ...categoryLabels.map((label, index) => [label, categoryData[index]]),
+ ];
+ exportToCSV(data, `${selectedCategory.toLowerCase()}_demographics.csv`);
+ }, [selectedCategory, categoryLabels, categoryData]);
+
+ /*istanbul ignore next*/
+ const handleExport = (eventKey: string | null): void => {
+ switch (eventKey) {
+ case 'trends':
+ try {
+ exportTrendsToCSV();
+ } catch (error) {
+ console.error('Failed to export trends:', error);
+ }
+ break;
+ case 'demographics':
+ try {
+ exportDemographicsToCSV();
+ } catch (error) {
+ console.error('Failed to export demographics:', error);
+ }
+ break;
+ default:
+ return;
+ }
+ };
+ useEffect(() => {
+ if (eventId) {
+ loadEventDetails({ variables: { id: eventId } });
+ }
+ }, [eventId, loadEventDetails]);
+ useEffect(() => {
+ if (eventId && orgId && eventData?.event?.baseRecurringEvent?._id) {
+ loadRecurringEvents({
+ variables: {
+ baseRecurringEventId: eventData?.event?.baseRecurringEvent?._id,
+ },
+ });
+ }
+ }, [eventId, orgId, eventData, loadRecurringEvents]);
+ return (
+
+
+
+ {t('historical_statistics')}
+
+
+
+
+
+ {isEventRecurring ? (
+
+
+
+
+
Previous Page}
+ >
+
+
+
+
+
handleDateChange(new Date())}
+ aria-label="Go to today"
+ >
+ Today
+
+
Next Page}
+ >
+ = totalPages - 1}
+ aria-label="Next page"
+ >
+
+
+
+
+
+ ) : (
+
+
+ {statistics.totalMembers}
+
+
+
+ )}
+
+
+ handleCategoryChange('Gender')}
+ >
+ Gender
+
+ handleCategoryChange('Age')}
+ >
+ Age
+
+
+
+
+
+
+
+
+
+
+ Export Data
+
+
+ {isEventRecurring && (
+
+ Trends
+
+ )}
+
+ Demographics
+
+
+
+
+ Close
+
+
+
+ );
+};
diff --git a/src/components/EventManagement/EventAttendance/EventsAttendance.module.css b/src/components/EventManagement/EventAttendance/EventsAttendance.module.css
new file mode 100644
index 0000000000..2ee236a4da
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventsAttendance.module.css
@@ -0,0 +1,35 @@
+.input {
+ display: flex;
+ width: 100%;
+ position: relative;
+}
+.customcell {
+ background-color: #31bb6b !important;
+ color: white !important;
+ font-size: medium !important;
+ font-weight: 500 !important;
+ padding-top: 10px !important;
+ padding-bottom: 10px !important;
+}
+
+.eventsAttended,
+.membername {
+ color: blue;
+}
+.actionBtn {
+ /* color:#39a440 !important; */
+ background-color: #ffffff !important;
+}
+.actionBtn:hover,
+.actionBtn:focus,
+.actionBtn:active {
+ color: #39a440 !important;
+}
+
+.table-body > .table-row {
+ background-color: #fff !important;
+}
+
+.table-body > .table-row:nth-child(2n) {
+ background: #afffe8 !important;
+}
diff --git a/src/components/EventManagement/EventAttendance/InterfaceEvents.ts b/src/components/EventManagement/EventAttendance/InterfaceEvents.ts
new file mode 100644
index 0000000000..7fc75ae4af
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/InterfaceEvents.ts
@@ -0,0 +1,82 @@
+export interface InterfaceAttendanceStatisticsModalProps {
+ show: boolean;
+ handleClose: () => void;
+ statistics: {
+ totalMembers: number;
+ membersAttended: number;
+ attendanceRate: number;
+ };
+ memberData: InterfaceMember[];
+ t: (key: string) => string;
+}
+
+export interface InterfaceMember {
+ createdAt: string;
+ firstName: string;
+ lastName: string;
+ email: `${string}@${string}.${string}`;
+ gender: string;
+ eventsAttended?: {
+ _id: string;
+ }[];
+ birthDate: Date;
+ __typename: string;
+ _id: string;
+ tagsAssignedWith: {
+ edges: {
+ cursor: string;
+ node: {
+ name: string;
+ };
+ }[];
+ };
+}
+
+export interface InterfaceEvent {
+ _id: string;
+ title: string;
+ description: string;
+ startDate: string;
+ endDate: string;
+ location: string;
+ startTime: string;
+ endTime: string;
+ allDay: boolean;
+ recurring: boolean;
+ recurrenceRule: {
+ recurrenceStartDate: string;
+ recurrenceEndDate?: string | null;
+ frequency: string;
+ weekDays: string[];
+ interval: number;
+ count?: number;
+ weekDayOccurenceInMonth?: number;
+ };
+ isRecurringEventException: boolean;
+ isPublic: boolean;
+ isRegisterable: boolean;
+ attendees: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ gender: string;
+ birthDate: string;
+ }[];
+ __typename: string;
+}
+
+export interface InterfaceRecurringEvent {
+ _id: string;
+ title: string;
+ startDate: string;
+ endDate: string;
+ frequency: InterfaceEvent['recurrenceRule']['frequency'];
+ interval: InterfaceEvent['recurrenceRule']['interval'];
+ attendees: {
+ _id: string;
+ gender: 'MALE' | 'FEMALE' | 'OTHER' | 'PREFER_NOT_TO_SAY';
+ }[];
+ isPublic: boolean;
+ isRegisterable: boolean;
+}
diff --git a/src/components/EventRegistrantsModal/AddOnSpotAttendee.test.tsx b/src/components/EventRegistrantsModal/AddOnSpotAttendee.test.tsx
new file mode 100644
index 0000000000..c0dc20d200
--- /dev/null
+++ b/src/components/EventRegistrantsModal/AddOnSpotAttendee.test.tsx
@@ -0,0 +1,177 @@
+import React from 'react';
+import { render, fireEvent, screen, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { BrowserRouter } from 'react-router-dom';
+import { SIGNUP_MUTATION } from 'GraphQl/Mutations/mutations';
+import AddOnSpotAttendee from './AddOnSpotAttendee';
+import userEvent from '@testing-library/user-event';
+import type { RenderResult } from '@testing-library/react';
+import { toast } from 'react-toastify';
+import { Provider } from 'react-redux';
+import { I18nextProvider } from 'react-i18next';
+import { store } from 'state/store';
+import i18nForTest from '../../utils/i18nForTest';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const mockProps = {
+ show: true,
+ handleClose: jest.fn(),
+ reloadMembers: jest.fn(),
+};
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ eventId: '123', orgId: '123' }),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: SIGNUP_MUTATION,
+ variables: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ phoneNo: '1234567890',
+ gender: 'Male',
+ password: '123456',
+ orgId: '123',
+ },
+ },
+ result: {
+ data: {
+ signUp: {
+ user: {
+ _id: '1',
+ },
+ accessToken: 'mock-access-token',
+ refreshToken: 'mock-refresh-token',
+ },
+ },
+ },
+ },
+];
+
+const ERROR_MOCKS = [
+ {
+ ...MOCKS[0],
+ error: new Error('Failed to add attendee'),
+ },
+];
+
+const renderAddOnSpotAttendee = (): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('AddOnSpotAttendee Component', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders the component with all form fields', async () => {
+ renderAddOnSpotAttendee();
+
+ expect(screen.getByText('On-spot Attendee')).toBeInTheDocument();
+ expect(screen.getByLabelText('First Name')).toBeInTheDocument();
+ expect(screen.getByLabelText('Last Name')).toBeInTheDocument();
+ expect(screen.getByLabelText('Email')).toBeInTheDocument();
+ expect(screen.getByLabelText('Phone No.')).toBeInTheDocument();
+ expect(screen.getByLabelText('Gender')).toBeInTheDocument();
+ });
+
+ it('handles form input changes correctly', async () => {
+ renderAddOnSpotAttendee();
+
+ const firstNameInput = screen.getByLabelText('First Name');
+ const lastNameInput = screen.getByLabelText('Last Name');
+ const emailInput = screen.getByLabelText('Email');
+
+ userEvent.type(firstNameInput, 'John');
+ userEvent.type(lastNameInput, 'Doe');
+ userEvent.type(emailInput, 'john@example.com');
+
+ expect(firstNameInput).toHaveValue('John');
+ expect(lastNameInput).toHaveValue('Doe');
+ expect(emailInput).toHaveValue('john@example.com');
+ });
+
+ it('submits form successfully and calls necessary callbacks', async () => {
+ renderAddOnSpotAttendee();
+
+ userEvent.type(screen.getByLabelText('First Name'), 'John');
+ userEvent.type(screen.getByLabelText('Last Name'), 'Doe');
+ userEvent.type(screen.getByLabelText('Email'), 'john@example.com');
+ userEvent.type(screen.getByLabelText('Phone No.'), '1234567890');
+ const genderSelect = screen.getByLabelText('Gender');
+ fireEvent.change(genderSelect, { target: { value: 'Male' } });
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalled();
+ expect(mockProps.reloadMembers).toHaveBeenCalled();
+ expect(mockProps.handleClose).toHaveBeenCalled();
+ });
+ });
+
+ it('displays error when organization ID is missing', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+ it('displays error when required fields are missing', async () => {
+ renderAddOnSpotAttendee();
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ it('handles mutation error appropriately', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ userEvent.type(screen.getByLabelText('First Name'), 'John');
+ userEvent.type(screen.getByLabelText('Last Name'), 'Doe');
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx b/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx
new file mode 100644
index 0000000000..6de839ce04
--- /dev/null
+++ b/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx
@@ -0,0 +1,209 @@
+import { SIGNUP_MUTATION } from 'GraphQl/Mutations/mutations';
+import React, { useState } from 'react';
+import { Modal, Form, Button, Spinner } from 'react-bootstrap';
+import { useParams } from 'react-router-dom';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import type {
+ InterfaceAddOnSpotAttendeeProps,
+ InterfaceFormData,
+} from 'utils/interfaces';
+import { useTranslation } from 'react-i18next';
+import { errorHandler } from 'utils/errorHandler';
+/**
+ * Modal component for adding on-spot attendees to an event
+ * @param show - Boolean to control modal visibility
+ * @param handleClose - Function to handle modal close
+ * @param reloadMembers - Function to refresh member list after adding attendee
+ * @returns Modal component with form for adding new attendee
+ */
+const AddOnSpotAttendee: React.FC = ({
+ show,
+ handleClose,
+ reloadMembers,
+}) => {
+ const [formData, setFormData] = useState({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phoneNo: '',
+ gender: '',
+ });
+ const { t } = useTranslation('translation', { keyPrefix: 'onSpotAttendee' });
+ const { t: tCommon } = useTranslation('common');
+ const { orgId } = useParams<{ orgId: string }>();
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [addSignUp] = useMutation(SIGNUP_MUTATION);
+ const validateForm = (): boolean => {
+ if (!formData.firstName || !formData.lastName || !formData.email) {
+ toast.error(t('invalidDetailsMessage'));
+ return false;
+ }
+ return true;
+ };
+
+ const resetForm = (): void => {
+ setFormData({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phoneNo: '',
+ gender: '',
+ });
+ };
+
+ const handleChange = (
+ e: React.ChangeEvent<
+ HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
+ >,
+ ): void => {
+ const target = e.target as
+ | HTMLInputElement
+ | HTMLSelectElement
+ | HTMLTextAreaElement;
+ setFormData((prev) => ({
+ ...prev,
+ [target.name]: target.value,
+ }));
+ };
+
+ const handleSubmit = async (
+ e: React.FormEvent,
+ ): Promise => {
+ e.preventDefault();
+
+ if (!validateForm()) {
+ return;
+ }
+
+ setIsSubmitting(true);
+
+ try {
+ const response = await addSignUp({
+ variables: {
+ ...formData,
+ password: '123456',
+ orgId,
+ },
+ });
+
+ if (response.data?.signUp) {
+ toast.success(t('attendeeAddedSuccess'));
+ resetForm();
+ reloadMembers();
+ handleClose();
+ }
+ } catch (error) {
+ /* istanbul ignore next */
+ errorHandler(t, error as Error);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+ return (
+ <>
+
+
+ {t('title')}
+
+
+
+ {t('phoneNumber')}
+
+
+
+
+ {tCommon('email')}
+
+
+
+
+ {tCommon('gender')}
+
+ {t('selectGender')}
+ {t('male')}
+ {t('female')}
+ {t('other')}
+
+
+
+
+ {isSubmitting ? (
+ <>
+
+ {t('addingAttendee')}
+ >
+ ) : (
+ 'Add'
+ )}
+
+
+
+
+ >
+ );
+};
+
+export default AddOnSpotAttendee;
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsModal.module.css b/src/components/EventRegistrantsModal/EventRegistrantsModal.module.css
new file mode 100644
index 0000000000..0f78d81c01
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsModal.module.css
@@ -0,0 +1,43 @@
+.loader,
+.loader:after {
+ border-radius: 50%;
+ width: 10em;
+ height: 10em;
+}
+.loader {
+ margin: 60px auto;
+ margin-top: 35vh !important;
+ font-size: 10px;
+ position: relative;
+ text-indent: -9999em;
+ border-top: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-right: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-left: 1.1em solid #febc59;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-animation: load8 1.1s infinite linear;
+ animation: load8 1.1s infinite linear;
+}
+
+@-webkit-keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx b/src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx
new file mode 100644
index 0000000000..8ca76393cd
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx
@@ -0,0 +1,403 @@
+import React from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { EventRegistrantsModal } from './EventRegistrantsModal';
+import { EVENT_ATTENDEES, MEMBERS_LIST } from 'GraphQl/Queries/Queries';
+import {
+ ADD_EVENT_ATTENDEE,
+ REMOVE_EVENT_ATTENDEE,
+} from 'GraphQl/Mutations/mutations';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+const queryMockWithoutRegistrant = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [],
+ },
+ },
+ },
+ },
+];
+
+const queryMockWithRegistrant = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ createdAt: '2023-01-01',
+ gender: 'Male',
+ birthDate: '1990-01-01',
+ eventsAttended: {
+ _id: 'event123',
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const queryMockOrgMembers = [
+ {
+ request: {
+ query: MEMBERS_LIST,
+ variables: { id: 'org123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: 'org123',
+ members: [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ image: null,
+ email: 'johndoe@example.com',
+ createdAt: '2023-01-01',
+ organizationsBlockedBy: [],
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+const queryMockWithoutOrgMembers = [
+ {
+ request: {
+ query: MEMBERS_LIST,
+ variables: { id: 'org123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: 'org123',
+ members: [],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const successfulAddRegistrantMock = [
+ {
+ request: {
+ query: ADD_EVENT_ATTENDEE,
+ variables: { userId: 'user1', eventId: 'event123' },
+ },
+ result: {
+ data: {
+ addEventAttendee: { _id: 'user1' },
+ },
+ },
+ },
+];
+
+const unsuccessfulAddRegistrantMock = [
+ {
+ request: {
+ query: ADD_EVENT_ATTENDEE,
+ variables: { userId: 'user1', eventId: 'event123' },
+ },
+ error: new Error('Oops'),
+ },
+];
+
+const successfulRemoveRegistrantMock = [
+ {
+ request: {
+ query: REMOVE_EVENT_ATTENDEE,
+ variables: { userId: 'user1', eventId: 'event123' },
+ },
+ result: {
+ data: {
+ removeEventAttendee: { _id: 'user1' },
+ },
+ },
+ },
+];
+
+const unsuccessfulRemoveRegistrantMock = [
+ {
+ request: {
+ query: REMOVE_EVENT_ATTENDEE,
+ variables: { userId: 'user1', eventId: 'event123' },
+ },
+ error: new Error('Oops'),
+ },
+];
+
+describe('Testing Event Registrants Modal', () => {
+ const props = {
+ show: true,
+ eventId: 'event123',
+ orgId: 'org123',
+ handleClose: jest.fn(),
+ };
+
+ test('The modal should be rendered, correct text must be displayed when there are no attendees and add attendee mutation must function properly', async () => {
+ const { queryByText, queryByLabelText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Event Registrants')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Registered Registrants')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(
+ queryByText('There are no registered attendees for this event.'),
+ ).toBeInTheDocument(),
+ );
+
+ // Get warning modal on blank button click
+ fireEvent.click(queryByText('Add Registrant') as Element);
+
+ await waitFor(() =>
+ expect(
+ queryByText('Please choose an user to add first!'),
+ ).toBeInTheDocument(),
+ );
+
+ // Choose a user to add as an attendee
+ const attendeeInput = queryByLabelText('Add an Registrant');
+ fireEvent.change(attendeeInput as Element, {
+ target: { value: 'John Doe' },
+ });
+ fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'ArrowDown' });
+ fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'Enter' });
+
+ fireEvent.click(queryByText('Add Registrant') as Element);
+
+ await waitFor(() =>
+ expect(queryByText('Adding the attendee...')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Attendee added Successfully')).toBeInTheDocument(),
+ );
+ });
+
+ test('Add attendee mutation must fail properly', async () => {
+ const { queryByText, queryByLabelText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(
+ queryByText('There are no registered attendees for this event.'),
+ ).toBeInTheDocument(),
+ );
+
+ // Choose a user to add as an attendee
+ const attendeeInput = queryByLabelText('Add an Registrant');
+ fireEvent.change(attendeeInput as Element, {
+ target: { value: 'John Doe' },
+ });
+ fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'ArrowDown' });
+ fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'Enter' });
+
+ fireEvent.click(queryByText('Add Registrant') as Element);
+
+ await waitFor(() =>
+ expect(queryByText('Adding the attendee...')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Error adding attendee')).toBeInTheDocument(),
+ );
+ });
+
+ test('Assigned attendees must be shown with badges and delete attendee mutation must function properly', async () => {
+ const { queryByText, queryByTestId } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Registered Registrants')).toBeInTheDocument(),
+ );
+
+ await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());
+
+ fireEvent.click(queryByTestId('CancelIcon') as Element);
+
+ await waitFor(() =>
+ expect(queryByText('Removing the attendee...')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Attendee removed Successfully')).toBeInTheDocument(),
+ );
+ });
+
+ test('Delete attendee mutation must fail properly', async () => {
+ const { queryByText, getByTestId } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Registered Registrants')).toBeInTheDocument(),
+ );
+
+ await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());
+
+ const deleteButton = getByTestId('CancelIcon');
+ fireEvent.click(deleteButton);
+
+ await waitFor(() =>
+ expect(queryByText('Removing the attendee...')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Error removing attendee')).toBeInTheDocument(),
+ );
+ });
+ test('Autocomplete functionality works correctly', async () => {
+ const { getByTitle, getByText, getByPlaceholderText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Wait for loading state to finish
+ await waitFor(() => {
+ const autocomplete = getByPlaceholderText(
+ 'Choose the user that you want to add',
+ );
+ expect(autocomplete).toBeInTheDocument();
+ });
+
+ // Test empty state with no options
+ const autocomplete = getByPlaceholderText(
+ 'Choose the user that you want to add',
+ );
+ fireEvent.change(autocomplete, { target: { value: 'NonexistentUser' } });
+
+ await waitFor(() => {
+ expect(getByText('No Registrations found')).toBeInTheDocument();
+ expect(getByText('Add Onspot Registration')).toBeInTheDocument();
+ });
+
+ // Test clicking "Add Onspot Registration"
+ fireEvent.click(getByText('Add Onspot Registration'));
+ expect(getByText('Add Onspot Registration')).toBeInTheDocument();
+ const closeButton = getByTitle('Close');
+ fireEvent.click(closeButton);
+ });
+});
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx b/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
new file mode 100644
index 0000000000..d48d3b7439
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
@@ -0,0 +1,207 @@
+import React, { useState } from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import { toast } from 'react-toastify';
+import { useMutation, useQuery } from '@apollo/client';
+import { EVENT_ATTENDEES, MEMBERS_LIST } from 'GraphQl/Queries/Queries';
+import {
+ ADD_EVENT_ATTENDEE,
+ REMOVE_EVENT_ATTENDEE,
+} from 'GraphQl/Mutations/mutations';
+import styles from 'components/EventRegistrantsModal/EventRegistrantsModal.module.css';
+import Avatar from '@mui/material/Avatar';
+import Chip from '@mui/material/Chip';
+import Stack from '@mui/material/Stack';
+import TextField from '@mui/material/TextField';
+import Autocomplete from '@mui/material/Autocomplete';
+import { useTranslation } from 'react-i18next';
+import AddOnSpotAttendee from './AddOnSpotAttendee';
+
+// Props for the EventRegistrantsModal component
+type ModalPropType = {
+ show: boolean;
+ eventId: string;
+ orgId: string;
+ handleClose: () => void;
+};
+
+// User information interface
+interface InterfaceUser {
+ _id: string;
+ firstName: string;
+ lastName: string;
+}
+
+/**
+ * Modal component for managing event registrants.
+ * Allows adding and removing attendees from an event.
+ *
+ * @param show - Whether the modal is visible or not.
+ * @param eventId - The ID of the event.
+ * @param orgId - The ID of the organization.
+ * @param handleClose - Function to close the modal.
+ * @returns JSX element representing the modal.
+ */
+export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
+ const { eventId, orgId, handleClose, show } = props;
+ const [member, setMember] = useState(null);
+ const [open, setOpen] = useState(false);
+
+ // Hooks for mutation operations
+ const [addRegistrantMutation] = useMutation(ADD_EVENT_ATTENDEE);
+ const [removeRegistrantMutation] = useMutation(REMOVE_EVENT_ATTENDEE);
+
+ // Translation hooks
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventRegistrantsModal',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // Query hooks to fetch event attendees and organization members
+ const {
+ data: attendeesData,
+ loading: attendeesLoading,
+ refetch: attendeesRefetch,
+ } = useQuery(EVENT_ATTENDEES, {
+ variables: { id: eventId },
+ });
+
+ const { data: memberData, loading: memberLoading } = useQuery(MEMBERS_LIST, {
+ variables: { id: orgId },
+ });
+
+ // Function to add a new registrant to the event
+ const addRegistrant = (): void => {
+ if (member == null) {
+ toast.warning('Please choose an user to add first!');
+ return;
+ }
+ toast.warn('Adding the attendee...');
+ addRegistrantMutation({
+ variables: {
+ userId: member._id,
+ eventId: eventId,
+ },
+ })
+ .then(() => {
+ toast.success(
+ tCommon('addedSuccessfully', { item: 'Attendee' }) as string,
+ );
+ attendeesRefetch(); // Refresh the list of attendees
+ })
+ .catch((err) => {
+ toast.error(t('errorAddingAttendee') as string);
+ toast.error(err.message);
+ });
+ };
+
+ // Function to remove a registrant from the event
+ const deleteRegistrant = (userId: string): void => {
+ toast.warn('Removing the attendee...');
+ removeRegistrantMutation({
+ variables: {
+ userId,
+ eventId: eventId,
+ },
+ })
+ .then(() => {
+ toast.success(
+ tCommon('removedSuccessfully', { item: 'Attendee' }) as string,
+ );
+ attendeesRefetch(); // Refresh the list of attendees
+ })
+ .catch((err) => {
+ toast.error(t('errorRemovingAttendee') as string);
+ toast.error(err.message);
+ });
+ };
+
+ // Show a loading screen if data is still being fetched
+ if (attendeesLoading || memberLoading) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+ setOpen(false)
+ }
+ reloadMembers={
+ /*istanbul ignore next */
+ () => {
+ attendeesRefetch();
+ }
+ }
+ />
+
+ Event Registrants
+
+
+ Registered Registrants
+ {attendeesData.event.attendees.length == 0
+ ? `There are no registered attendees for this event.`
+ : null}
+
+ {attendeesData.event.attendees.map((attendee: InterfaceUser) => (
+ {`${attendee.firstName[0]}${attendee.lastName[0]}`}
+ }
+ label={`${attendee.firstName} ${attendee.lastName}`}
+ variant="outlined"
+ key={attendee._id}
+ onDelete={(): void => deleteRegistrant(attendee._id)}
+ />
+ ))}
+
+
+
+ {
+ setMember(newMember);
+ }}
+ noOptionsText={
+
+
No Registrations found
+
{
+ setOpen(true);
+ }}
+ >
+ Add Onspot Registration
+
+
+ }
+ options={memberData.organizations[0].members}
+ getOptionLabel={(member: InterfaceUser): string =>
+ `${member.firstName} ${member.lastName}`
+ }
+ renderInput={(params): React.ReactNode => (
+
+ )}
+ />
+
+
+
+
+ Add Registrant
+
+
+
+ >
+ );
+};
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css
new file mode 100644
index 0000000000..59b31333af
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css
@@ -0,0 +1,13 @@
+button .iconWrapper {
+ width: 36px;
+ padding-right: 4px;
+ margin-right: 4px;
+ transform: translateY(4px);
+}
+
+button .iconWrapperSm {
+ width: 36px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx
new file mode 100644
index 0000000000..d1707e8520
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { EventRegistrantsWrapper } from './EventRegistrantsWrapper';
+import { EVENT_ATTENDEES, MEMBERS_LIST } from 'GraphQl/Queries/Queries';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+const queryMock = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MEMBERS_LIST,
+ variables: { id: 'org123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: 'org123',
+ members: [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@palisadoes.com',
+ image: '',
+ createdAt: '12/12/22',
+ organizationsBlockedBy: [],
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+
+describe('Testing Event Registrants Wrapper', () => {
+ const props = {
+ eventId: 'event123',
+ orgId: 'org123',
+ };
+
+ test('The button should work properly', async () => {
+ const { queryByText, queryByRole } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Open the modal
+ fireEvent.click(queryByText('Show Registrants') as Element);
+
+ await waitFor(() =>
+ expect(queryByText('Event Registrants')).toBeInTheDocument(),
+ );
+
+ // Close the modal
+ fireEvent.click(queryByRole('button', { name: /close/i }) as HTMLElement);
+ await waitFor(() =>
+ expect(queryByText('Event Registrants')).not.toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx
new file mode 100644
index 0000000000..b198fcdd6d
--- /dev/null
+++ b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx
@@ -0,0 +1,60 @@
+import React, { useState } from 'react';
+import { EventRegistrantsModal } from './EventRegistrantsModal';
+import { Button } from 'react-bootstrap';
+import IconComponent from 'components/IconComponent/IconComponent';
+import styles from './EventRegistrantsWrapper.module.css';
+
+// Props for the EventRegistrantsWrapper component
+type PropType = {
+ eventId: string;
+ orgId: string;
+};
+
+/**
+ * Wrapper component that displays a button to show the event registrants modal.
+ *
+ * @param eventId - The ID of the event.
+ * @param orgId - The ID of the organization.
+ * @returns JSX element representing the wrapper with a button to show the modal.
+ */
+export const EventRegistrantsWrapper = ({
+ eventId,
+ orgId,
+}: PropType): JSX.Element => {
+ // State to control the visibility of the modal
+ const [showModal, setShowModal] = useState(false);
+
+ return (
+ <>
+ {/* Button to open the event registrants modal */}
+ {
+ setShowModal(true); // Show the modal when button is clicked
+ }}
+ >
+
+
+
+ Show Registrants
+
+
+ {/* Render the EventRegistrantsModal if showModal is true */}
+ {showModal && (
+ {
+ setShowModal(false); // Hide the modal when closed
+ }}
+ eventId={eventId}
+ orgId={orgId}
+ />
+ )}
+ >
+ );
+};
diff --git a/src/components/EventStats/EventStats.module.css b/src/components/EventStats/EventStats.module.css
new file mode 100644
index 0000000000..44ba75a0a8
--- /dev/null
+++ b/src/components/EventStats/EventStats.module.css
@@ -0,0 +1,35 @@
+.stackEvents {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ margin: 10px 20px;
+ padding: 10px 20px;
+ overflow: hidden;
+ gap: 2px;
+ column-gap: 2px;
+}
+
+@media screen and (min-width: 801px) {
+ .stackEvents {
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: flex-start;
+ padding: 0 2rem;
+ margin: 0 40px;
+ gap: 5px;
+ column-gap: 4px;
+ }
+}
+
+@media screen and (min-width: 768px) and (max-width: 800px) {
+ .stackEvents {
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: flex-start;
+ padding: 0 2rem;
+ margin: 0 20px;
+ gap: 5px;
+ column-gap: 4px;
+ }
+}
diff --git a/src/components/EventStats/EventStats.test.tsx b/src/components/EventStats/EventStats.test.tsx
new file mode 100644
index 0000000000..e2496fa5af
--- /dev/null
+++ b/src/components/EventStats/EventStats.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { EventStats } from './EventStats';
+import { BrowserRouter } from 'react-router-dom';
+import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
+
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+// These modules are used by the Feedback component
+jest.mock('@mui/x-charts/PieChart', () => ({
+ pieArcLabelClasses: jest.fn(),
+ PieChart: jest.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: jest.fn(),
+}));
+
+const mockData = [
+ {
+ request: {
+ query: EVENT_FEEDBACKS,
+ variables: {
+ id: 'eventStats123',
+ },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'eventStats123',
+ feedback: [
+ {
+ _id: 'feedback1',
+ review: 'review1',
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+ },
+ },
+];
+
+describe('Testing Event Stats', () => {
+ const props = {
+ eventId: 'eventStats123',
+ show: true,
+ handleClose: jest.fn(),
+ };
+
+ test('The stats should be rendered properly', async () => {
+ const { queryByText } = render(
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Event Statistics')).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventStats/EventStats.tsx b/src/components/EventStats/EventStats.tsx
new file mode 100644
index 0000000000..ce1e55e4bc
--- /dev/null
+++ b/src/components/EventStats/EventStats.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import { Modal } from 'react-bootstrap';
+import { FeedbackStats } from './Statistics/Feedback';
+import { ReviewStats } from './Statistics/Review';
+import { AverageRating } from './Statistics/AverageRating';
+import styles from './Loader.module.css';
+import eventStatsStyles from './EventStats.module.css';
+import { useQuery } from '@apollo/client';
+import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
+
+// Props for the EventStats component
+type ModalPropType = {
+ show: boolean;
+ eventId: string;
+ handleClose: () => void;
+};
+
+/**
+ * Component that displays event statistics in a modal.
+ * Shows feedback, reviews, and average rating for the event.
+ *
+ * @param show - Whether the modal is visible or not.
+ * @param handleClose - Function to close the modal.
+ * @param eventId - The ID of the event.
+ * @returns JSX element representing the event statistics modal.
+ */
+export const EventStats = ({
+ show,
+ handleClose,
+ eventId,
+}: ModalPropType): JSX.Element => {
+ // Query to fetch event feedback data
+ const { data, loading } = useQuery(EVENT_FEEDBACKS, {
+ variables: { id: eventId },
+ });
+
+ // Show a loading screen while data is being fetched
+ if (loading) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+
+ Event Statistics
+
+
+ {/* Render feedback statistics */}
+
+
+ {/* Render review statistics and average rating */}
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/EventStats/EventStatsWrapper.module.css b/src/components/EventStats/EventStatsWrapper.module.css
new file mode 100644
index 0000000000..f5f42546c3
--- /dev/null
+++ b/src/components/EventStats/EventStatsWrapper.module.css
@@ -0,0 +1,13 @@
+button .iconWrapper {
+ width: 32px;
+ padding-right: 4px;
+ margin-right: 4px;
+ transform: translateY(4px);
+}
+
+button .iconWrapperSm {
+ width: 32px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/EventStats/EventStatsWrapper.test.tsx b/src/components/EventStats/EventStatsWrapper.test.tsx
new file mode 100644
index 0000000000..0e64ac13cc
--- /dev/null
+++ b/src/components/EventStats/EventStatsWrapper.test.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { EventStatsWrapper } from './EventStatsWrapper';
+import { BrowserRouter } from 'react-router-dom';
+import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
+
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+jest.mock('@mui/x-charts/PieChart', () => ({
+ pieArcLabelClasses: jest.fn(),
+ PieChart: jest.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: jest.fn(),
+}));
+
+const mockData = [
+ {
+ request: {
+ query: EVENT_FEEDBACKS,
+ variables: {
+ id: 'eventStats123',
+ },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'eventStats123',
+ feedback: [
+ {
+ _id: 'feedback1',
+ review: 'review1',
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+ },
+ },
+];
+
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+// These modules are used by the Feedback component
+jest.mock('@mui/x-charts/PieChart', () => ({
+ pieArcLabelClasses: jest.fn(),
+ PieChart: jest.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: jest.fn(),
+}));
+
+describe('Testing Event Stats Wrapper', () => {
+ const props = {
+ eventId: 'eventStats123',
+ };
+
+ test('The button to open and close the modal should work properly', async () => {
+ const { queryByText, queryByRole } = render(
+
+
+
+
+ ,
+ );
+
+ // Open the modal
+ fireEvent.click(queryByText('View Event Statistics') as Element);
+
+ await waitFor(() =>
+ expect(queryByText('Event Statistics')).toBeInTheDocument(),
+ );
+
+ // Close the modal
+ fireEvent.click(queryByRole('button', { name: /close/i }) as HTMLElement);
+ await waitFor(() =>
+ expect(queryByText('Event Statistics')).not.toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventStats/EventStatsWrapper.tsx b/src/components/EventStats/EventStatsWrapper.tsx
new file mode 100644
index 0000000000..fb701a00da
--- /dev/null
+++ b/src/components/EventStats/EventStatsWrapper.tsx
@@ -0,0 +1,48 @@
+import React, { useState } from 'react';
+import { EventStats } from './EventStats';
+import { Button } from 'react-bootstrap';
+import IconComponent from 'components/IconComponent/IconComponent';
+import styles from './EventStatsWrapper.module.css';
+
+// Props for the EventStatsWrapper component
+type PropType = {
+ eventId: string;
+};
+
+/**
+ * Wrapper component that displays a button to show event statistics.
+ *
+ * @param eventId - The ID of the event.
+ * @returns JSX element representing the wrapper with a button to view event statistics.
+ */
+export const EventStatsWrapper = ({ eventId }: PropType): JSX.Element => {
+ // State to control the visibility of the EventStats component
+ const [showModal, setShowModal] = useState(false);
+
+ return (
+ <>
+ {/* Button to open the event statistics view */}
+ {
+ setShowModal(true); // Show the EventStats component when button is clicked
+ }}
+ >
+
+
+
+ View Event Statistics
+
+
+ {/* Render the EventStats component if showModal is true */}
+ setShowModal(false)} // Hide the EventStats component when closed
+ key={eventId || 'eventStatsDetails'} // Use eventId as key for the component
+ eventId={eventId}
+ />
+ >
+ );
+};
diff --git a/src/components/EventStats/Loader.module.css b/src/components/EventStats/Loader.module.css
new file mode 100644
index 0000000000..0f78d81c01
--- /dev/null
+++ b/src/components/EventStats/Loader.module.css
@@ -0,0 +1,43 @@
+.loader,
+.loader:after {
+ border-radius: 50%;
+ width: 10em;
+ height: 10em;
+}
+.loader {
+ margin: 60px auto;
+ margin-top: 35vh !important;
+ font-size: 10px;
+ position: relative;
+ text-indent: -9999em;
+ border-top: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-right: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
+ border-left: 1.1em solid #febc59;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-animation: load8 1.1s infinite linear;
+ animation: load8 1.1s infinite linear;
+}
+
+@-webkit-keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/EventStats/Statistics/AverageRating.test.tsx b/src/components/EventStats/Statistics/AverageRating.test.tsx
new file mode 100644
index 0000000000..01cf6461e3
--- /dev/null
+++ b/src/components/EventStats/Statistics/AverageRating.test.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { AverageRating } from './AverageRating';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+
+const props = {
+ data: {
+ event: {
+ _id: '123',
+ feedback: [
+ {
+ _id: 'feedback1',
+ review: 'review1',
+ rating: 5,
+ },
+ {
+ _id: 'feedback2',
+ review: 'review2',
+ rating: 5,
+ },
+ {
+ _id: 'feedback3',
+ review: null,
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+};
+
+describe('Testing Average Rating Card', () => {
+ test('The component should be rendered and the Score should be shown', async () => {
+ const { queryByText } = render(
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Average Review Score')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Rated 5.00 / 5')).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventStats/Statistics/AverageRating.tsx b/src/components/EventStats/Statistics/AverageRating.tsx
new file mode 100644
index 0000000000..9f1a157e01
--- /dev/null
+++ b/src/components/EventStats/Statistics/AverageRating.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import Card from 'react-bootstrap/Card';
+import Rating from '@mui/material/Rating';
+import FavoriteIcon from '@mui/icons-material/Favorite';
+import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
+import Typography from '@mui/material/Typography';
+
+// Props for the AverageRating component
+type ModalPropType = {
+ data: {
+ event: {
+ _id: string;
+ averageFeedbackScore: number;
+ feedback: FeedbackType[];
+ };
+ };
+};
+
+// Type representing individual feedback
+type FeedbackType = {
+ _id: string;
+ rating: number;
+ review: string | null;
+};
+
+/**
+ * Component that displays the average rating for an event.
+ * Shows a rating value and a star rating icon.
+ *
+ * @param data - Data containing the average feedback score to be displayed.
+ * @returns JSX element representing the average rating card with a star rating.
+ */
+export const AverageRating = ({ data }: ModalPropType): JSX.Element => {
+ return (
+ <>
+
+
+
+ Average Review Score
+
+
+ Rated {data.event.averageFeedbackScore.toFixed(2)} / 5
+
+ }
+ size="medium"
+ emptyIcon={ }
+ sx={{
+ '& .MuiRating-iconFilled': {
+ color: '#ff6d75', // Color for filled stars
+ },
+ '& .MuiRating-iconHover': {
+ color: '#ff3d47', // Color for star on hover
+ },
+ }}
+ />
+
+
+ >
+ );
+};
diff --git a/src/components/EventStats/Statistics/Feedback.test.tsx b/src/components/EventStats/Statistics/Feedback.test.tsx
new file mode 100644
index 0000000000..9abdee4c57
--- /dev/null
+++ b/src/components/EventStats/Statistics/Feedback.test.tsx
@@ -0,0 +1,104 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { FeedbackStats } from './Feedback';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+
+// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
+jest.mock('@mui/x-charts/PieChart', () => ({
+ pieArcLabelClasses: jest.fn(),
+ PieChart: jest.fn().mockImplementation(() => <>Test>),
+ pieArcClasses: jest.fn(),
+}));
+
+const nonEmptyProps = {
+ data: {
+ event: {
+ _id: '123',
+ feedback: [
+ {
+ _id: 'feedback1',
+ review: 'review1',
+ rating: 5,
+ },
+ {
+ _id: 'feedback2',
+ review: 'review2',
+ rating: 5,
+ },
+ {
+ _id: 'feedback3',
+ review: null,
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+};
+
+const emptyProps = {
+ data: {
+ event: {
+ _id: '123',
+ feedback: [],
+ averageFeedbackScore: 5,
+ },
+ },
+};
+
+describe('Testing Feedback Statistics Card', () => {
+ test('The component should be rendered and the feedback should be shown if present', async () => {
+ const { queryByText } = render(
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Feedback Analysis')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(
+ queryByText('3 people have filled feedback for this event.'),
+ ).toBeInTheDocument(),
+ );
+
+ await waitFor(() => {
+ expect(queryByText('Test')).toBeInTheDocument();
+ });
+ });
+
+ test('The component should be rendered and message should be shown if no feedback is present', async () => {
+ const { queryByText } = render(
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(queryByText('Feedback Analysis')).toBeInTheDocument(),
+ );
+
+ await waitFor(() =>
+ expect(
+ queryByText('Please ask attendees to submit feedback for insights!'),
+ ).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventStats/Statistics/Feedback.tsx b/src/components/EventStats/Statistics/Feedback.tsx
new file mode 100644
index 0000000000..a6a255828f
--- /dev/null
+++ b/src/components/EventStats/Statistics/Feedback.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import {
+ PieChart,
+ pieArcClasses,
+ pieArcLabelClasses,
+} from '@mui/x-charts/PieChart';
+import Card from 'react-bootstrap/Card';
+
+// Props for the FeedbackStats component
+type ModalPropType = {
+ data: {
+ event: {
+ _id: string;
+ averageFeedbackScore: number | null;
+ feedback: FeedbackType[];
+ };
+ };
+};
+
+// Type representing individual feedback
+type FeedbackType = {
+ _id: string;
+ rating: number;
+ review: string | null;
+};
+
+/**
+ * Component that displays a pie chart of feedback ratings for an event.
+ * Shows how many people gave each rating.
+ *
+ * @param data - Data containing event feedback to be displayed in the chart.
+ * @returns JSX element representing the feedback analysis card with a pie chart.
+ */
+export const FeedbackStats = ({ data }: ModalPropType): JSX.Element => {
+ // Colors for the pie chart slices, from green (high ratings) to red (low ratings)
+ const ratingColors = [
+ '#57bb8a', // Green
+ '#94bd77',
+ '#d4c86a',
+ '#e9b861',
+ '#e79a69',
+ '#dd776e', // Red
+ ];
+
+ // Count the number of feedbacks for each rating
+ const count: Record = {};
+
+ data.event.feedback.forEach((feedback: FeedbackType) => {
+ if (feedback.rating in count) count[feedback.rating]++;
+ else count[feedback.rating] = 1;
+ });
+
+ // Prepare data for the pie chart
+ const chartData = [];
+ for (let rating = 0; rating <= 5; rating++) {
+ if (rating in count)
+ chartData.push({
+ id: rating,
+ value: count[rating],
+ label: `${rating} (${count[rating]})`,
+ color: ratingColors[5 - rating],
+ });
+ }
+
+ return (
+ <>
+
+
+
+ Feedback Analysis
+
+
+ {data.event.feedback.length} people have filled feedback for this
+ event.
+
+ {data.event.feedback.length ? (
+
+ `${item.id} (${item.value})`,
+ innerRadius: 30,
+ outerRadius: 120,
+ paddingAngle: 2,
+ cornerRadius: 5,
+ startAngle: 0,
+ highlightScope: { faded: 'global', highlighted: 'item' },
+ faded: { innerRadius: 30, additionalRadius: -30 },
+ },
+ ]}
+ sx={{
+ [`& .${pieArcClasses.faded}`]: {
+ fill: 'gray',
+ },
+ [`& .${pieArcLabelClasses.root}`]: {
+ fill: 'black',
+ fontSize: '15px',
+ },
+ [`& .${pieArcLabelClasses.faded}`]: {
+ display: 'none',
+ },
+ }}
+ width={380}
+ height={380}
+ />
+ ) : (
+ <>Please ask attendees to submit feedback for insights!>
+ )}
+
+
+ >
+ );
+};
diff --git a/src/components/EventStats/Statistics/Review.test.tsx b/src/components/EventStats/Statistics/Review.test.tsx
new file mode 100644
index 0000000000..9093444ab2
--- /dev/null
+++ b/src/components/EventStats/Statistics/Review.test.tsx
@@ -0,0 +1,95 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { ReviewStats } from './Review';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { ToastContainer } from 'react-toastify';
+
+const nonEmptyReviewProps = {
+ data: {
+ event: {
+ _id: '123',
+ feedback: [
+ {
+ _id: 'feedback1',
+ review: 'review1',
+ rating: 5,
+ },
+ {
+ _id: 'feedback2',
+ review: 'review2',
+ rating: 5,
+ },
+ {
+ _id: 'feedback3',
+ review: null,
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+};
+
+const emptyReviewProps = {
+ data: {
+ event: {
+ _id: '123',
+ feedback: [
+ {
+ _id: 'feedback3',
+ review: null,
+ rating: 5,
+ },
+ ],
+ averageFeedbackScore: 5,
+ },
+ },
+};
+
+describe('Testing Review Statistics Card', () => {
+ test('The component should be rendered and the reviews should be shown if present', async () => {
+ const { queryByText } = render(
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => expect(queryByText('Reviews')).toBeInTheDocument());
+
+ await waitFor(() =>
+ expect(queryByText('Filled by 2 people.')).toBeInTheDocument(),
+ );
+
+ await waitFor(() => expect(queryByText('review2')).toBeInTheDocument());
+ });
+
+ test('The component should be rendered and message should be shown if no review is present', async () => {
+ const { queryByText } = render(
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => expect(queryByText('Reviews')).toBeInTheDocument());
+
+ await waitFor(() =>
+ expect(
+ queryByText('Waiting for people to talk about the event...'),
+ ).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/src/components/EventStats/Statistics/Review.tsx b/src/components/EventStats/Statistics/Review.tsx
new file mode 100644
index 0000000000..343a8107c3
--- /dev/null
+++ b/src/components/EventStats/Statistics/Review.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import Card from 'react-bootstrap/Card';
+import Rating from '@mui/material/Rating';
+
+// Props for the ReviewStats component
+type ModalPropType = {
+ data: {
+ event: {
+ _id: string;
+ averageFeedbackScore: number | null;
+ feedback: FeedbackType[];
+ };
+ };
+};
+
+// Type representing individual feedback
+type FeedbackType = {
+ _id: string;
+ rating: number;
+ review: string | null;
+};
+
+/**
+ * Component that displays reviews for an event.
+ * Shows a list of reviews with ratings and text.
+ *
+ * @param data - Data containing event feedback to be displayed.
+ * @returns JSX element representing the reviews card.
+ */
+export const ReviewStats = ({ data }: ModalPropType): JSX.Element => {
+ // Filter out feedback that has a review
+ const reviews = data.event.feedback.filter(
+ (feedback: FeedbackType) => feedback.review != null,
+ );
+
+ return (
+ <>
+
+
+
+ Reviews
+
+ Filled by {reviews.length} people.
+ {reviews.length ? (
+ reviews.map((review) => (
+
+ ))
+ ) : (
+ <>Waiting for people to talk about the event...>
+ )}
+
+
+ >
+ );
+};
diff --git a/src/components/HolidayCards/HolidayCard.module.css b/src/components/HolidayCards/HolidayCard.module.css
new file mode 100644
index 0000000000..c7686ab870
--- /dev/null
+++ b/src/components/HolidayCards/HolidayCard.module.css
@@ -0,0 +1,12 @@
+.card {
+ background-color: #b4dcb7;
+ font-size: 14px;
+ font-weight: 400;
+ display: flex;
+ padding: 8px 4px;
+ border-radius: 5px;
+ margin-bottom: 4px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
diff --git a/src/components/HolidayCards/HolidayCard.tsx b/src/components/HolidayCards/HolidayCard.tsx
new file mode 100644
index 0000000000..56ac86405d
--- /dev/null
+++ b/src/components/HolidayCards/HolidayCard.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import styles from './HolidayCard.module.css';
+
+// Props for the HolidayCard component
+interface InterfaceHolidayList {
+ holidayName: string;
+}
+
+/**
+ * Component that displays a card with the name of a holiday.
+ *
+ * @param props - Contains the holidayName to be displayed on the card.
+ * @returns JSX element representing a card with the holiday name.
+ */
+const HolidayCard = (props: InterfaceHolidayList): JSX.Element => {
+ /*istanbul ignore next*/
+ return {props?.holidayName}
;
+};
+
+export default HolidayCard;
diff --git a/src/components/IconComponent/IconComponent.test.tsx b/src/components/IconComponent/IconComponent.test.tsx
new file mode 100644
index 0000000000..3ba6ccd84d
--- /dev/null
+++ b/src/components/IconComponent/IconComponent.test.tsx
@@ -0,0 +1,105 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import IconComponent from './IconComponent';
+
+const screenTestIdMap: Record> = {
+ MyOrganizations: {
+ name: 'My Organizations',
+ testId: 'Icon-Component-MyOrganizationsIcon',
+ },
+ Dashboard: {
+ name: 'Dashboard',
+ testId: 'Icon-Component-DashboardIcon',
+ },
+ People: {
+ name: 'People',
+ testId: 'Icon-Component-PeopleIcon',
+ },
+ Tags: {
+ name: 'Tags',
+ testId: 'Icon-Component-TagsIcon',
+ },
+ Tag: {
+ name: 'Tag',
+ testId: 'Icon-Component-TagIcon',
+ },
+ Requests: {
+ name: 'Requests',
+ testId: 'Icon-Component-RequestsIcon',
+ },
+ Events: {
+ name: 'Events',
+ testId: 'Icon-Component-EventsIcon',
+ },
+ ActionItems: {
+ name: 'Action Items',
+ testId: 'Icon-Component-ActionItemIcon',
+ },
+ Posts: {
+ name: 'Posts',
+ testId: 'Icon-Component-PostsIcon',
+ },
+ BlockUnblock: {
+ name: 'Block/Unblock',
+ testId: 'Block/Icon-Component-UnblockIcon',
+ },
+ Plugins: {
+ name: 'Plugins',
+ testId: 'Icon-Component-PluginsIcon',
+ },
+ Settings: {
+ name: 'Settings',
+ testId: 'Icon-Component-SettingsIcon',
+ },
+ ListEventRegistrants: {
+ name: 'List Event Registrants',
+ testId: 'Icon-Component-List-Event-Registrants',
+ },
+ CheckInRegistrants: {
+ name: 'Check In Registrants',
+ testId: 'Icon-Component-Check-In-Registrants',
+ },
+ Advertisement: {
+ name: 'Advertisement',
+ testId: 'Icon-Component-Advertisement',
+ },
+ Funds: {
+ name: 'Funds',
+ testId: 'Icon-Component-Funds',
+ },
+ Venues: {
+ name: 'Venues',
+ testId: 'Icon-Component-Venues',
+ },
+ Donate: {
+ name: 'Donate',
+ testId: 'Icon-Component-Donate',
+ },
+ Campaigns: {
+ name: 'Campaigns',
+ testId: 'Icon-Component-Campaigns',
+ },
+ MyPledges: {
+ name: 'My Pledges',
+ testId: 'Icon-Component-My-Pledges',
+ },
+ Volunteer: {
+ name: 'Volunteer',
+ testId: 'Icon-Component-Volunteer',
+ },
+ default: {
+ name: 'default',
+ testId: 'Icon-Component-DefaultIcon',
+ },
+};
+
+describe('Testing Collapsible Dropdown component', () => {
+ it('Renders the correct icon according to the component', () => {
+ for (const component in screenTestIdMap) {
+ render( );
+ expect(
+ screen.getByTestId(screenTestIdMap[component].testId),
+ ).toBeInTheDocument();
+ }
+ });
+});
diff --git a/src/components/IconComponent/IconComponent.tsx b/src/components/IconComponent/IconComponent.tsx
new file mode 100644
index 0000000000..8430aca131
--- /dev/null
+++ b/src/components/IconComponent/IconComponent.tsx
@@ -0,0 +1,157 @@
+import {
+ QuestionMarkOutlined,
+ ContactPageOutlined,
+ NewspaperOutlined,
+} from '@mui/icons-material';
+import ActionItemIcon from 'assets/svgs/actionItem.svg?react';
+import BlockUserIcon from 'assets/svgs/blockUser.svg?react';
+import CheckInRegistrantsIcon from 'assets/svgs/checkInRegistrants.svg?react';
+import DashboardIcon from 'assets/svgs/dashboard.svg?react';
+import EventsIcon from 'assets/svgs/events.svg?react';
+import FundsIcon from 'assets/svgs/funds.svg?react';
+import ListEventRegistrantsIcon from 'assets/svgs/listEventRegistrants.svg?react';
+import OrganizationsIcon from 'assets/svgs/organizations.svg?react';
+import PeopleIcon from 'assets/svgs/people.svg?react';
+import TagsIcon from 'assets/svgs/tags.svg?react';
+import TagIcon from 'assets/svgs/tag.svg?react';
+import PluginsIcon from 'assets/svgs/plugins.svg?react';
+import PostsIcon from 'assets/svgs/posts.svg?react';
+import SettingsIcon from 'assets/svgs/settings.svg?react';
+import VenueIcon from 'assets/svgs/venues.svg?react';
+import RequestsIcon from 'assets/svgs/requests.svg?react';
+import { MdOutlineVolunteerActivism } from 'react-icons/md';
+
+import React from 'react';
+
+export interface InterfaceIconComponent {
+ name: string;
+ fill?: string;
+ height?: string;
+ width?: string;
+}
+/**
+ * Renders an icon based on the provided name.
+ *
+ * @param props - Contains the name of the icon and optional styles (fill, height, width).
+ * @returns JSX element representing the icon.
+ */
+const iconComponent = (props: InterfaceIconComponent): JSX.Element => {
+ switch (props.name) {
+ case 'My Organizations':
+ return (
+
+ );
+ case 'Dashboard':
+ return (
+
+ );
+ case 'People':
+ return ;
+ case 'Tags':
+ return ;
+ case 'Tag':
+ return ;
+ case 'Requests':
+ return (
+
+ );
+ case 'Events':
+ return ;
+ case 'Action Items':
+ return (
+
+ );
+ case 'Posts':
+ return ;
+ case 'Block/Unblock':
+ return (
+
+ );
+ case 'Plugins':
+ return (
+
+ );
+ case 'Settings':
+ return (
+
+ );
+ case 'List Event Registrants':
+ return (
+
+ );
+ case 'Check In Registrants':
+ return (
+
+ );
+ case 'Advertisement':
+ return (
+
+ );
+ case 'Funds':
+ return (
+
+ );
+ case 'Venues':
+ return (
+
+ );
+ case 'Donate':
+ return (
+
+ );
+ case 'Campaigns':
+ return (
+
+ );
+ case 'My Pledges':
+ return (
+
+ );
+ case 'Volunteer':
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+};
+
+export default iconComponent;
diff --git a/src/components/InfiniteScrollLoader/InfiniteScrollLoader.module.css b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.module.css
new file mode 100644
index 0000000000..a5b609ae75
--- /dev/null
+++ b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.module.css
@@ -0,0 +1,23 @@
+.simpleLoader {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ margin: 1rem 0;
+ border: 4px solid transparent;
+ border-top-color: var(--bs-gray-400);
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/InfiniteScrollLoader/InfiniteScrollLoader.test.tsx b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.test.tsx
new file mode 100644
index 0000000000..1e179e0de0
--- /dev/null
+++ b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.test.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import InfiniteScrollLoader from './InfiniteScrollLoader';
+
+describe('Testing InfiniteScrollLoader component', () => {
+ test('Component should be rendered properly', () => {
+ render( );
+
+ expect(screen.getByTestId('infiniteScrollLoader')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('infiniteScrollLoaderSpinner'),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx
new file mode 100644
index 0000000000..7846889cdb
--- /dev/null
+++ b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import styles from './InfiniteScrollLoader.module.css';
+
+/**
+ * A Loader for infinite scroll.
+ */
+
+const InfiniteScrollLoader = (): JSX.Element => {
+ return (
+
+ );
+};
+
+export default InfiniteScrollLoader;
diff --git a/src/components/LeftDrawer/LeftDrawer.module.css b/src/components/LeftDrawer/LeftDrawer.module.css
new file mode 100644
index 0000000000..86948b9930
--- /dev/null
+++ b/src/components/LeftDrawer/LeftDrawer.module.css
@@ -0,0 +1,239 @@
+.leftDrawer {
+ width: calc(300px + 2rem);
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ z-index: 100;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem 1rem 0 1rem;
+ background-color: #f6f8fc;
+ transition: 0.5s;
+ font-family: var(--bs-leftDrawer-font-family);
+}
+
+.activeDrawer {
+ width: calc(300px + 2rem);
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ animation: comeToRightBigScreen 0.5s ease-in-out;
+}
+
+.inactiveDrawer {
+ position: fixed;
+ top: 0;
+ left: calc(-300px - 2rem);
+ bottom: 0;
+ animation: goToLeftBigScreen 0.5s ease-in-out;
+}
+
+.leftDrawer .talawaLogo {
+ width: 100%;
+ height: 65px;
+}
+
+.leftDrawer .talawaText {
+ font-size: 20px;
+ text-align: center;
+ font-weight: 500;
+}
+
+.leftDrawer .titleHeader {
+ margin: 2rem 0 1rem 0;
+ font-weight: 600;
+}
+
+.leftDrawer .optionList button {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ text-align: start;
+ margin-bottom: 0.8rem;
+ border-radius: 16px;
+ outline: none;
+ border: none;
+}
+
+.leftDrawer .optionList button .iconWrapper {
+ width: 36px;
+}
+
+.leftDrawer .profileContainer {
+ border: none;
+ width: 100%;
+ padding: 2.1rem 0.5rem;
+ height: 52px;
+ display: flex;
+ align-items: center;
+ background-color: var(--bs-white);
+}
+
+.leftDrawer .profileContainer:focus {
+ outline: none;
+ background-color: var(--bs-gray-100);
+}
+
+.leftDrawer .imageContainer {
+ width: 68px;
+}
+
+.leftDrawer .profileContainer img {
+ height: 52px;
+ width: 52px;
+ border-radius: 50%;
+}
+
+.leftDrawer .profileContainer .profileText {
+ flex: 1;
+ text-align: start;
+}
+
+.leftDrawer .profileContainer .profileText .primaryText {
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+
+.leftDrawer .profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ font-weight: 400;
+ color: var(--bs-secondary);
+ display: block;
+ text-transform: capitalize;
+}
+
+@media (max-width: 1120px) {
+ .leftDrawer {
+ width: calc(250px + 2rem);
+ padding: 1rem 1rem 0 1rem;
+ }
+}
+
+/* For tablets */
+@media (max-width: 820px) {
+ .hideElemByDefault {
+ display: none;
+ }
+
+ .leftDrawer {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+
+ .inactiveDrawer {
+ opacity: 0;
+ left: 0;
+ z-index: -1;
+ animation: closeDrawer 0.4s ease-in-out;
+ }
+
+ .activeDrawer {
+ display: flex;
+ z-index: 100;
+ animation: openDrawer 0.6s ease-in-out;
+ }
+
+ .logout {
+ margin-bottom: 2.5rem !important;
+ }
+}
+
+@keyframes goToLeftBigScreen {
+ from {
+ left: 0;
+ }
+
+ to {
+ opacity: 0.1;
+ left: calc(-300px - 2rem);
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes goToLeftBigScreen {
+ from {
+ left: 0;
+ }
+
+ to {
+ opacity: 0.1;
+ left: calc(-300px - 2rem);
+ }
+}
+
+@keyframes comeToRightBigScreen {
+ from {
+ opacity: 0.4;
+ left: calc(-300px - 2rem);
+ }
+
+ to {
+ opacity: 1;
+ left: 0;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes comeToRightBigScreen {
+ from {
+ opacity: 0.4;
+ left: calc(-300px - 2rem);
+ }
+
+ to {
+ opacity: 1;
+ left: 0;
+ }
+}
+
+@keyframes closeDrawer {
+ from {
+ left: 0;
+ opacity: 1;
+ }
+
+ to {
+ left: -1000px;
+ opacity: 0;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes closeDrawer {
+ from {
+ left: 0;
+ opacity: 1;
+ }
+
+ to {
+ left: -1000px;
+ opacity: 0;
+ }
+}
+
+@keyframes openDrawer {
+ from {
+ opacity: 0;
+ left: -1000px;
+ }
+
+ to {
+ left: 0;
+ opacity: 1;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes openDrawer {
+ from {
+ opacity: 0;
+ left: -1000px;
+ }
+
+ to {
+ left: 0;
+ opacity: 1;
+ }
+}
diff --git a/src/components/LeftDrawer/LeftDrawer.test.tsx b/src/components/LeftDrawer/LeftDrawer.test.tsx
new file mode 100644
index 0000000000..6701d2d4bb
--- /dev/null
+++ b/src/components/LeftDrawer/LeftDrawer.test.tsx
@@ -0,0 +1,226 @@
+import React, { act } from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter } from 'react-router-dom';
+
+import i18nForTest from 'utils/i18nForTest';
+import type { InterfaceLeftDrawerProps } from './LeftDrawer';
+import LeftDrawer from './LeftDrawer';
+import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { MockedProvider } from '@apollo/react-testing';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+const props = {
+ hideDrawer: true,
+ setHideDrawer: jest.fn(),
+};
+
+const resizeWindow = (width: number): void => {
+ window.innerWidth = width;
+ fireEvent(window, new Event('resize'));
+};
+
+const propsOrg: InterfaceLeftDrawerProps = {
+ ...props,
+};
+
+const MOCKS = [
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ result: {},
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+beforeEach(() => {
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ setItem(
+ 'UserImage',
+ 'https://api.dicebear.com/5.x/initials/svg?seed=John%20Doe',
+ );
+});
+
+afterEach(() => {
+ jest.clearAllMocks();
+ localStorage.clear();
+});
+
+describe('Testing Left Drawer component for SUPERADMIN', () => {
+ test('Component should be rendered properly', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ expect(screen.getByText('My Organizations')).toBeInTheDocument();
+ expect(screen.getByText('Users')).toBeInTheDocument();
+ expect(screen.getByText('Community Profile')).toBeInTheDocument();
+ expect(screen.getByText('Talawa Admin Portal')).toBeInTheDocument();
+
+ const orgsBtn = screen.getByTestId(/orgsBtn/i);
+ const rolesBtn = screen.getByTestId(/rolesBtn/i);
+ const communityProfileBtn = screen.getByTestId(/communityProfileBtn/i);
+
+ await act(async () => {
+ orgsBtn.click();
+ });
+
+ expect(
+ orgsBtn.className.includes('text-white btn btn-success'),
+ ).toBeTruthy();
+ expect(rolesBtn.className.includes('text-secondary btn')).toBeTruthy();
+ expect(
+ communityProfileBtn.className.includes('text-secondary btn'),
+ ).toBeTruthy();
+
+ await act(async () => {
+ userEvent.click(rolesBtn);
+ });
+
+ expect(global.window.location.pathname).toContain('/users');
+
+ await act(async () => {
+ userEvent.click(communityProfileBtn);
+ });
+ });
+
+ test('Testing Drawer when hideDrawer is null', async () => {
+ const tempProps: InterfaceLeftDrawerProps = {
+ ...props,
+ hideDrawer: false,
+ };
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ });
+ });
+
+ test('Testing Drawer when hideDrawer is false', async () => {
+ const tempProps: InterfaceLeftDrawerProps = {
+ ...props,
+ hideDrawer: false,
+ };
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ });
+ });
+
+ test('Testing Drawer when the screen size is less than or equal to 820px', async () => {
+ const tempProps: InterfaceLeftDrawerProps = {
+ ...props,
+ hideDrawer: false,
+ };
+ resizeWindow(800);
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ expect(screen.getByText('My Organizations')).toBeInTheDocument();
+ expect(screen.getByText('Talawa Admin Portal')).toBeInTheDocument();
+
+ const orgsBtn = screen.getByTestId(/orgsBtn/i);
+
+ await act(async () => {
+ orgsBtn.click();
+ });
+
+ expect(
+ orgsBtn.className.includes('text-white btn btn-success'),
+ ).toBeTruthy();
+ });
+});
+
+describe('Testing Left Drawer component for ADMIN', () => {
+ test('Components should be rendered properly', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ expect(screen.getByText('My Organizations')).toBeInTheDocument();
+ expect(screen.getByText('Talawa Admin Portal')).toBeInTheDocument();
+
+ expect(screen.getAllByText(/admin/i)).toHaveLength(1);
+
+ const orgsBtn = screen.getByTestId(/orgsBtn/i);
+
+ await act(async () => {
+ orgsBtn.click();
+ });
+
+ expect(
+ orgsBtn.className.includes('text-white btn btn-success'),
+ ).toBeTruthy();
+
+ // These screens aren't meant for admins, so they should not be present
+ expect(screen.queryByTestId(/rolesBtn/i)).toBeNull();
+
+ await act(async () => {
+ userEvent.click(orgsBtn);
+ });
+
+ expect(global.window.location.pathname).toContain('/orglist');
+ });
+});
diff --git a/src/components/LeftDrawer/LeftDrawer.tsx b/src/components/LeftDrawer/LeftDrawer.tsx
new file mode 100644
index 0000000000..4628603ba1
--- /dev/null
+++ b/src/components/LeftDrawer/LeftDrawer.tsx
@@ -0,0 +1,137 @@
+import React from 'react';
+import Button from 'react-bootstrap/Button';
+import { useTranslation } from 'react-i18next';
+import { NavLink } from 'react-router-dom';
+import OrganizationsIcon from 'assets/svgs/organizations.svg?react';
+import RolesIcon from 'assets/svgs/roles.svg?react';
+import SettingsIcon from 'assets/svgs/settings.svg?react';
+import TalawaLogo from 'assets/svgs/talawa.svg?react';
+import styles from './LeftDrawer.module.css';
+import useLocalStorage from 'utils/useLocalstorage';
+
+export interface InterfaceLeftDrawerProps {
+ hideDrawer: boolean | null; // Controls the visibility of the drawer
+ setHideDrawer: React.Dispatch>; // Function to set the visibility state
+}
+
+/**
+ * LeftDrawer component for displaying navigation options.
+ *
+ * @param hideDrawer - Determines if the drawer should be hidden or shown.
+ * @param setHideDrawer - Function to update the visibility state of the drawer.
+ * @returns JSX element for the left navigation drawer.
+ */
+const leftDrawer = ({
+ hideDrawer,
+ setHideDrawer,
+}: InterfaceLeftDrawerProps): JSX.Element => {
+ const { t } = useTranslation('translation', { keyPrefix: 'leftDrawer' });
+ const { t: tCommon } = useTranslation('common');
+
+ const { getItem } = useLocalStorage();
+ const superAdmin = getItem('SuperAdmin');
+
+ /**
+ * Handles link click to hide the drawer on smaller screens.
+ */
+ const handleLinkClick = (): void => {
+ if (window.innerWidth <= 820) {
+ setHideDrawer(true);
+ }
+ };
+
+ return (
+ <>
+
+
+
{tCommon('talawaAdminPortal')}
+
+ {tCommon('menu')}
+
+
+
+ {({ isActive }) => (
+
+
+
+
+ {t('my organizations')}
+
+ )}
+
+ {superAdmin && (
+ <>
+
+ {({ isActive }) => (
+
+
+
+
+ {tCommon('users')}
+
+ )}
+
+
+ {({ isActive }) => (
+
+
+
+
+ {t('communityProfile')}
+
+ )}
+
+ >
+ )}
+
+
+ >
+ );
+};
+
+export default leftDrawer;
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css b/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css
new file mode 100644
index 0000000000..6296b1aa73
--- /dev/null
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css
@@ -0,0 +1,362 @@
+.leftDrawer {
+ width: calc(300px + 2rem);
+ min-height: 100%;
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ z-index: 100;
+ display: flex;
+ flex-direction: column;
+ padding: 0.8rem 0rem 0 1rem;
+ background-color: #f6f8fc;
+ transition: 0.5s;
+ font-family: var(--bs-leftDrawer-font-family);
+}
+
+.avatarContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 1rem 0;
+ width: 75%;
+ height: 75%;
+}
+
+.activeDrawer {
+ width: calc(300px + 2rem);
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ animation: comeToRightBigScreen 0.5s ease-in-out;
+}
+
+.inactiveDrawer {
+ position: fixed;
+ top: 0;
+ left: calc(-300px - 2rem);
+ bottom: 0;
+ animation: goToLeftBigScreen 0.5s ease-in-out;
+}
+
+.leftDrawer .brandingContainer {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+}
+
+.leftDrawer .organizationContainer button {
+ position: relative;
+ margin: 0.7rem 0;
+ padding: 2.5rem 0.1rem;
+ border-radius: 16px;
+}
+
+.leftDrawer .talawaLogo {
+ width: 50px;
+ height: 50px;
+ margin-right: 0.3rem;
+}
+
+.leftDrawer .talawaText {
+ font-size: 18px;
+ font-weight: 500;
+}
+
+.leftDrawer .titleHeader {
+ font-weight: 600;
+ margin: 0.6rem 0rem;
+}
+
+.leftDrawer .optionList {
+ height: 100%;
+ overflow-y: scroll;
+ padding-bottom: 1.5rem;
+}
+
+.leftDrawer .optionList button {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ text-align: start;
+ margin-bottom: 0.8rem;
+ border-radius: 16px;
+ font-size: 16px;
+ padding: 0.6rem;
+ padding-left: 0.8rem;
+ outline: none;
+ border: none;
+}
+
+.leftDrawer button .iconWrapper {
+ width: 36px;
+}
+
+.leftDrawer .optionList .collapseBtn {
+ height: 48px;
+ border: none;
+}
+
+.leftDrawer button .iconWrapperSm {
+ width: 36px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.leftDrawer .organizationContainer .profileContainer {
+ background-color: #e0e9ff;
+ padding-right: 10px;
+}
+
+.leftDrawer .profileContainer {
+ border: none;
+ width: 100%;
+ height: 52px;
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ background-color: var(--bs-white);
+}
+
+.leftDrawer .profileContainer:focus {
+ outline: none;
+}
+
+.leftDrawer .imageContainer {
+ width: 68px;
+ margin-left: 0.75rem;
+ margin-right: 8px;
+}
+
+.leftDrawer .profileContainer img {
+ height: 52px;
+ width: 52px;
+ border-radius: 50%;
+}
+
+.leftDrawer .profileContainer .profileText {
+ flex: 1;
+ text-align: start;
+ overflow: hidden;
+}
+
+.leftDrawer .profileContainer .profileText .primaryText {
+ font-size: 1.1rem;
+ font-weight: 600;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2; /* number of lines to show */
+ -webkit-box-orient: vertical;
+ word-wrap: break-word;
+ white-space: normal;
+}
+
+.leftDrawer .profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ font-weight: 400;
+ /* color: var(--bs-secondary); */
+ display: block;
+ text-transform: capitalize;
+}
+
+@media (max-width: 1120px) {
+ .leftDrawer {
+ width: calc(250px + 2rem);
+ padding: 1rem 0rem 0 1rem;
+ }
+}
+
+/* For tablets */
+@media (max-height: 900px) {
+ .leftDrawer {
+ width: calc(300px + 1rem);
+ }
+ .leftDrawer .talawaLogo {
+ width: 38px;
+ height: 38px;
+ margin-right: 0.4rem;
+ }
+ .leftDrawer .talawaText {
+ font-size: 1rem;
+ }
+ .leftDrawer .organizationContainer button {
+ margin: 0.6rem 0;
+ padding: 2.2rem 0.1rem;
+ }
+ .leftDrawer .optionList button {
+ margin-bottom: 0.05rem;
+ font-size: 16px;
+ padding-left: 0.8rem;
+ }
+ .leftDrawer .profileContainer .profileText .primaryText {
+ font-size: 1rem;
+ }
+ .leftDrawer .profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ }
+}
+@media (max-height: 650px) {
+ .leftDrawer {
+ padding: 0.5rem 0rem 0 0.8rem;
+ width: calc(250px);
+ }
+ .leftDrawer .talawaText {
+ font-size: 0.8rem;
+ }
+ .leftDrawer .organizationContainer button {
+ margin: 0.2rem 0;
+ padding: 1.6rem 0rem;
+ }
+ .leftDrawer .titleHeader {
+ font-size: 16px;
+ }
+ .leftDrawer .optionList button {
+ margin-bottom: 0.05rem;
+ font-size: 14px;
+ padding: 0.4rem;
+ padding-left: 0.8rem;
+ }
+ .leftDrawer .profileContainer .profileText .primaryText {
+ font-size: 0.8rem;
+ }
+ .leftDrawer .profileContainer .profileText .secondaryText {
+ font-size: 0.6rem;
+ }
+ .leftDrawer .imageContainer {
+ width: 40px;
+ margin-left: 0.75rem;
+ margin-right: 12px;
+ }
+ .leftDrawer .imageContainer img {
+ width: 40px;
+ height: 100%;
+ }
+}
+
+@media (max-width: 820px) {
+ .hideElemByDefault {
+ display: none;
+ }
+
+ .leftDrawer {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+
+ .inactiveDrawer {
+ opacity: 0;
+ left: 0;
+ z-index: -1;
+ animation: closeDrawer 0.2s ease-in-out;
+ }
+
+ .activeDrawer {
+ display: flex;
+ z-index: 100;
+ animation: openDrawer 0.4s ease-in-out;
+ }
+
+ .logout {
+ margin-bottom: 2.5rem;
+ }
+}
+
+@keyframes goToLeftBigScreen {
+ from {
+ left: 0;
+ }
+
+ to {
+ opacity: 0.1;
+ left: calc(-300px - 2rem);
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes goToLeftBigScreen {
+ from {
+ left: 0;
+ }
+
+ to {
+ opacity: 0.1;
+ left: calc(-300px - 2rem);
+ }
+}
+
+@keyframes comeToRightBigScreen {
+ from {
+ opacity: 0.4;
+ left: calc(-300px - 2rem);
+ }
+
+ to {
+ opacity: 1;
+ left: 0;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes comeToRightBigScreen {
+ from {
+ opacity: 0.4;
+ left: calc(-300px - 2rem);
+ }
+
+ to {
+ opacity: 1;
+ left: 0;
+ }
+}
+
+@keyframes closeDrawer {
+ from {
+ left: 0;
+ opacity: 1;
+ }
+
+ to {
+ left: -1000px;
+ opacity: 0;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes closeDrawer {
+ from {
+ left: 0;
+ opacity: 1;
+ }
+
+ to {
+ left: -1000px;
+ opacity: 0;
+ }
+}
+
+@keyframes openDrawer {
+ from {
+ opacity: 0;
+ left: -1000px;
+ }
+
+ to {
+ left: 0;
+ opacity: 1;
+ }
+}
+
+/* Webkit prefix for older browser compatibility */
+@-webkit-keyframes openDrawer {
+ from {
+ opacity: 0;
+ left: -1000px;
+ }
+
+ to {
+ left: 0;
+ opacity: 1;
+ }
+}
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx b/src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx
new file mode 100644
index 0000000000..71f3593499
--- /dev/null
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx
@@ -0,0 +1,474 @@
+import React, { act } from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter, MemoryRouter } from 'react-router-dom';
+
+import i18nForTest from 'utils/i18nForTest';
+import type { InterfaceLeftDrawerProps } from './LeftDrawerOrg';
+import LeftDrawerOrg from './LeftDrawerOrg';
+import { Provider } from 'react-redux';
+import { MockedProvider } from '@apollo/react-testing';
+import { store } from 'state/store';
+import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+const props: InterfaceLeftDrawerProps = {
+ orgId: '123',
+ targets: [
+ {
+ name: 'Admin Profile',
+ url: '/member/123',
+ },
+ {
+ name: 'Dashboard',
+ url: '/orgdash/123',
+ },
+ {
+ name: 'People',
+ url: '/orgpeople/123',
+ },
+ {
+ name: 'Events',
+ url: '/orgevents/123',
+ },
+ {
+ name: 'Posts',
+ url: '/orgpost/123',
+ },
+ {
+ name: 'Block/Unblock',
+ url: '/blockuser/123',
+ },
+ {
+ name: 'Plugins',
+ subTargets: [
+ {
+ name: 'Plugin Store',
+ url: '/orgstore/123',
+ icon: 'fa-store',
+ },
+ ],
+ },
+ {
+ name: 'Settings',
+ url: '/orgsetting/123',
+ },
+ {
+ name: 'All Organizations',
+ url: '/orglist/123',
+ },
+ ],
+ hideDrawer: false,
+ setHideDrawer: jest.fn(),
+};
+
+const MOCKS = [
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ result: {
+ data: {
+ revokeRefreshTokenForUser: true,
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '123',
+ image: null,
+ creator: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ },
+ name: 'Test Organization',
+ description: 'Testing this organization',
+ address: {
+ city: 'Delhi',
+ countryCode: 'IN',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '110001',
+ sortingCode: 'ABC-123',
+ state: 'Delhi',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: true,
+ members: [
+ {
+ _id: 'john123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ },
+ {
+ _id: 'jane123',
+ firstName: 'Jane',
+ lastName: 'Doe',
+ email: 'JaneDoe@example.com',
+ },
+ ],
+ admins: [
+ {
+ _id: 'john123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ createdAt: '12-03-2024',
+ },
+ ],
+ membershipRequests: [],
+ blockedUsers: [],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const MOCKS_WITH_IMAGE = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '123',
+ image:
+ 'https://api.dicebear.com/5.x/initials/svg?seed=Test%20Organization',
+ creator: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ },
+ name: 'Test Organization',
+ description: 'Testing this organization',
+ address: {
+ city: 'Delhi',
+ countryCode: 'IN',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '110001',
+ sortingCode: 'ABC-123',
+ state: 'Delhi',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: true,
+ members: [
+ {
+ _id: 'john123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ },
+ {
+ _id: 'jane123',
+ firstName: 'Jane',
+ lastName: 'Doe',
+ email: 'JaneDoe@example.com',
+ },
+ ],
+ admins: [
+ {
+ _id: 'john123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'JohnDoe@example.com',
+ createdAt: '12-03-2024',
+ },
+ ],
+ membershipRequests: [],
+ blockedUsers: [],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const MOCKS_EMPTY = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [],
+ },
+ },
+ },
+];
+
+const MOCKS_EMPTY_ORGID = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '' },
+ },
+ result: {
+ data: {
+ organizations: [],
+ },
+ },
+ },
+];
+
+const defaultScreens = [
+ 'Dashboard',
+ 'People',
+ 'Events',
+ 'Posts',
+ 'Block/Unblock',
+ 'Plugins',
+ 'Settings',
+ 'All Organizations',
+];
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const resizeWindow = (width: number): void => {
+ window.innerWidth = width;
+ fireEvent(window, new Event('resize'));
+};
+
+beforeEach(() => {
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ setItem(
+ 'UserImage',
+ 'https://api.dicebear.com/5.x/initials/svg?seed=John%20Doe',
+ );
+});
+
+afterEach(() => {
+ jest.clearAllMocks();
+ localStorage.clear();
+});
+
+const link = new StaticMockLink(MOCKS, true);
+const linkImage = new StaticMockLink(MOCKS_WITH_IMAGE, true);
+const linkEmpty = new StaticMockLink(MOCKS_EMPTY, true);
+const linkEmptyOrgId = new StaticMockLink(MOCKS_EMPTY_ORGID, true);
+
+describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
+ test('Component should be rendered properly', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ defaultScreens.map((screenName) => {
+ expect(screen.getByText(screenName)).toBeInTheDocument();
+ });
+ });
+
+ test('Testing Profile Page & Organization Detail Modal', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(screen.getByTestId(/orgBtn/i)).toBeInTheDocument();
+ });
+
+ test('Should not show org not found error when viewing admin profile', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ setItem('id', '123');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(
+ screen.queryByText(/Error occured while loading Organization data/i),
+ ).not.toBeInTheDocument();
+ });
+
+ test('Testing Menu Buttons', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByText('Dashboard'));
+ expect(global.window.location.pathname).toContain('/orgdash/123');
+ });
+
+ test('Testing when screen size is less than 820px', async () => {
+ setItem('SuperAdmin', true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ resizeWindow(800);
+ expect(screen.getAllByText(/Dashboard/i)[0]).toBeInTheDocument();
+ expect(screen.getAllByText(/People/i)[0]).toBeInTheDocument();
+
+ const peopelBtn = screen.getByTestId(/People/i);
+ userEvent.click(peopelBtn);
+ await wait();
+ expect(window.location.pathname).toContain('/orgpeople/123');
+ });
+
+ test('Testing when image is present for Organization', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ });
+
+ test('Testing when Organization does not exists', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(
+ screen.getByText(/Error occured while loading Organization data/i),
+ ).toBeInTheDocument();
+ });
+
+ test('Testing Drawer when hideDrawer is null', () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+
+ test('Testing Drawer when hideDrawer is true', () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+});
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx b/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
new file mode 100644
index 0000000000..a687351c98
--- /dev/null
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
@@ -0,0 +1,214 @@
+import { useQuery } from '@apollo/client';
+import { WarningAmberOutlined } from '@mui/icons-material';
+import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
+import CollapsibleDropdown from 'components/CollapsibleDropdown/CollapsibleDropdown';
+import IconComponent from 'components/IconComponent/IconComponent';
+import React, { useEffect, useMemo, useState } from 'react';
+import Button from 'react-bootstrap/Button';
+import { useTranslation } from 'react-i18next';
+import { NavLink, useLocation } from 'react-router-dom';
+import type { TargetsType } from 'state/reducers/routesReducer';
+import type { InterfaceQueryOrganizationsListObject } from 'utils/interfaces';
+import AngleRightIcon from 'assets/svgs/angleRight.svg?react';
+import TalawaLogo from 'assets/svgs/talawa.svg?react';
+import styles from './LeftDrawerOrg.module.css';
+import Avatar from 'components/Avatar/Avatar';
+import useLocalStorage from 'utils/useLocalstorage';
+
+export interface InterfaceLeftDrawerProps {
+ orgId: string;
+ targets: TargetsType[];
+ hideDrawer: boolean | null;
+ setHideDrawer: React.Dispatch>;
+}
+
+/**
+ * LeftDrawerOrg component for displaying organization details and navigation options.
+ *
+ * @param orgId - ID of the current organization.
+ * @param targets - List of navigation targets.
+ * @param hideDrawer - Determines if the drawer should be hidden or shown.
+ * @param setHideDrawer - Function to update the visibility state of the drawer.
+ * @returns JSX element for the left navigation drawer with organization details.
+ */
+const leftDrawerOrg = ({
+ targets,
+ orgId,
+ hideDrawer,
+ setHideDrawer,
+}: InterfaceLeftDrawerProps): JSX.Element => {
+ const { t: tCommon } = useTranslation('common');
+ const { t: tErrors } = useTranslation('errors');
+ const location = useLocation();
+ const { getItem } = useLocalStorage();
+ const userId = getItem('id');
+ const getIdFromPath = (pathname: string): string => {
+ if (!pathname) return '';
+ const segments = pathname.split('/');
+ // Index 2 (third segment) represents the ID in paths like /member/{userId}
+ return segments.length > 2 ? segments[2] : '';
+ };
+ const [isProfilePage, setIsProfilePage] = useState(false);
+ const [showDropdown, setShowDropdown] = useState(false);
+ const [organization, setOrganization] = useState<
+ InterfaceQueryOrganizationsListObject | undefined
+ >(undefined);
+ const {
+ data,
+ loading,
+ }: {
+ data:
+ | { organizations: InterfaceQueryOrganizationsListObject[] }
+ | undefined;
+ loading: boolean;
+ } = useQuery(ORGANIZATIONS_LIST, {
+ variables: { id: orgId },
+ });
+
+ // Get the ID from the current path
+ const pathId = useMemo(
+ () => getIdFromPath(location.pathname),
+ [location.pathname],
+ );
+ // Check if the current page is admin profile page
+ useEffect(() => {
+ // if param id is equal to userId, then it is a profile page
+ setIsProfilePage(pathId === userId);
+ }, [location, userId]);
+
+ // Set organization data when query data is available
+ useEffect(() => {
+ let isMounted = true;
+ if (data && isMounted) {
+ setOrganization(data?.organizations[0]);
+ } else {
+ setOrganization(undefined);
+ }
+ return () => {
+ isMounted = false;
+ };
+ }, [data]);
+
+ /**
+ * Handles link click to hide the drawer on smaller screens.
+ */
+ const handleLinkClick = (): void => {
+ if (window.innerWidth <= 820) {
+ setHideDrawer(true);
+ }
+ };
+
+ return (
+ <>
+
+ {/* Branding Section */}
+
+
+
+ {tCommon('talawaAdminPortal')}
+
+
+
+ {/* Organization Section */}
+
+ {loading ? (
+ <>
+
+ >
+ ) : organization == undefined ? (
+ !isProfilePage && (
+
+
+
+
+ {tErrors('errorLoading', { entity: 'Organization' })}
+
+ )
+ ) : (
+
+
+ {organization.image ? (
+
+ ) : (
+
+ )}
+
+
+ {organization.name}
+
+ {organization.address.city}
+
+
+
+
+ )}
+
+
+ {/* Options List */}
+
+ {tCommon('menu')}
+
+
+ {targets.map(({ name, url }, index) => {
+ return url ? (
+
+ {({ isActive }) => (
+
+
+
+
+ {tCommon(name)}
+
+ )}
+
+ ) : (
+
+ );
+ })}
+
+
+ >
+ );
+};
+
+export default leftDrawerOrg;
diff --git a/src/components/Loader/Loader.module.css b/src/components/Loader/Loader.module.css
new file mode 100644
index 0000000000..aad512e826
--- /dev/null
+++ b/src/components/Loader/Loader.module.css
@@ -0,0 +1,25 @@
+.spinner_wrapper {
+ height: 100vh;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.spinnerXl {
+ width: 6rem;
+ height: 6rem;
+ border-width: 0.5rem;
+}
+
+.spinnerLg {
+ height: 4rem;
+ width: 4rem;
+ border-width: 0.3rem;
+}
+
+.spinnerSm {
+ height: 2rem;
+ width: 2rem;
+ border-width: 0.2rem;
+}
diff --git a/src/components/Loader/Loader.test.tsx b/src/components/Loader/Loader.test.tsx
new file mode 100644
index 0000000000..c512b480e3
--- /dev/null
+++ b/src/components/Loader/Loader.test.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import Loader from './Loader';
+
+describe('Testing Loader component', () => {
+ test('Component should be rendered properly', () => {
+ render( );
+
+ expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
+ expect(screen.getByTestId('spinner')).toBeInTheDocument();
+ });
+
+ test('Component should render on custom sizes', () => {
+ render( );
+
+ expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
+ expect(screen.getByTestId('spinner')).toBeInTheDocument();
+ });
+
+ test('Component should render with large size', () => {
+ render( );
+
+ expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
+ expect(screen.getByTestId('spinner')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/Loader/Loader.tsx b/src/components/Loader/Loader.tsx
new file mode 100644
index 0000000000..b59c735175
--- /dev/null
+++ b/src/components/Loader/Loader.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import styles from './Loader.module.css';
+import { Spinner } from 'react-bootstrap';
+
+interface InterfaceLoaderProps {
+ styles?: StyleSheet | string; // Custom styles for the spinner wrapper
+ size?: 'sm' | 'lg' | 'xl'; // Size of the spinner
+}
+
+/**
+ * Loader component for displaying a loading spinner.
+ *
+ * @param styles - Optional custom styles for the spinner wrapper.
+ * @param size - Size of the spinner. Can be 'sm', 'lg', or 'xl'.
+ * @returns JSX element for a loading spinner.
+ */
+const Loader = (props: InterfaceLoaderProps): JSX.Element => {
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default Loader;
diff --git a/src/components/LoginPortalToggle/LoginPortalToggle.module.css b/src/components/LoginPortalToggle/LoginPortalToggle.module.css
new file mode 100644
index 0000000000..5983e20905
--- /dev/null
+++ b/src/components/LoginPortalToggle/LoginPortalToggle.module.css
@@ -0,0 +1,34 @@
+.navLinkClass {
+ display: inline-block;
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ line-height: 1.4;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ color: var(--bs-gray-900);
+ border-radius: 0.3rem;
+ width: 100%;
+ box-sizing: border-box;
+ border: 1px solid var(--bs-gray-700);
+ font-weight: 500;
+ transition: all 0.25s ease;
+}
+
+.navLinkClass:hover {
+ color: var(--bs-white);
+ background-color: var(--bs-gray);
+ border-color: var(--bs-gray);
+}
+
+.activeLink {
+ color: var(--bs-white);
+ border: 1px solid var(--bs-primary);
+ background-color: var(--bs-primary);
+}
+
+.activeLink:hover {
+ color: var(--bs-white);
+ background-color: var(--bs-primary);
+ border: 1px solid var(--bs-primary);
+}
diff --git a/src/components/LoginPortalToggle/LoginPortalToggle.test.tsx b/src/components/LoginPortalToggle/LoginPortalToggle.test.tsx
new file mode 100644
index 0000000000..327e6cccea
--- /dev/null
+++ b/src/components/LoginPortalToggle/LoginPortalToggle.test.tsx
@@ -0,0 +1,33 @@
+import React, { act } from 'react';
+import { render } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import LoginPortalToggle from './LoginPortalToggle';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing LoginPortalToggle component', () => {
+ test('Component Should be rendered properly', async () => {
+ const mockOnToggle = jest.fn();
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ });
+});
diff --git a/src/components/LoginPortalToggle/LoginPortalToggle.tsx b/src/components/LoginPortalToggle/LoginPortalToggle.tsx
new file mode 100644
index 0000000000..76305b02d3
--- /dev/null
+++ b/src/components/LoginPortalToggle/LoginPortalToggle.tsx
@@ -0,0 +1,58 @@
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import styles from './LoginPortalToggle.module.css';
+import Col from 'react-bootstrap/Col';
+import Row from 'react-bootstrap/Row';
+import { NavLink } from 'react-router-dom';
+
+interface InterfaceLoginPortalToggleProps {
+ onToggle: (role: 'admin' | 'user') => void; // Callback function for role change
+}
+
+/**
+ * Component for toggling between admin and user login portals.
+ *
+ * @param onToggle - Callback function to handle role changes ('admin' or 'user').
+ * @returns JSX element for login portal toggle.
+ */
+function loginPortalToggle({
+ onToggle,
+}: InterfaceLoginPortalToggleProps): JSX.Element {
+ const { t: tCommon } = useTranslation('common');
+ const [activeRole, setActiveRole] = useState<'admin' | 'user'>('admin'); // Default role is 'admin'
+
+ /**
+ * Handles navigation link click and updates the active role.
+ *
+ * @param role - The role to be activated ('admin' or 'user').
+ */
+ const handleNavLinkClick = (role: 'admin' | 'user'): void => {
+ onToggle(role); // Invoke the callback with the new role
+ setActiveRole(role); // Update the active role
+ };
+
+ return (
+
+
+ handleNavLinkClick('admin')}
+ >
+ {tCommon('admin')}
+
+
+
+ handleNavLinkClick('user')}
+ >
+ {tCommon('user')}
+
+
+
+ );
+}
+
+export default loginPortalToggle;
diff --git a/src/components/MemberDetail/EventsAttendedByMember.test.tsx b/src/components/MemberDetail/EventsAttendedByMember.test.tsx
new file mode 100644
index 0000000000..23ae0efa3b
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedByMember.test.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import EventsAttendedByMember from './EventsAttendedByMember';
+import { BrowserRouter } from 'react-router-dom';
+
+const mockEventData = {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2023-01-01',
+ endDate: '2023-01-02',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: { _id: 'base123' },
+ organization: {
+ _id: 'org123',
+ members: [
+ { _id: 'user1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'user2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user2', gender: 'FEMALE' },
+ ],
+ },
+};
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: mockEventData,
+ },
+ },
+];
+
+const errorMocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ error: new Error('An error occurred'),
+ },
+];
+
+describe('EventsAttendedByMember', () => {
+ test('renders loading state initially', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByTestId('loading')).toBeInTheDocument();
+ expect(screen.getByText('Loading event details...')).toBeInTheDocument();
+ });
+
+ test('renders error state when query fails', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ const errorMessage = await screen.findByTestId('error');
+ expect(errorMessage).toBeInTheDocument();
+ expect(
+ screen.getByText('Unable to load event details. Please try again later.'),
+ ).toBeInTheDocument();
+ });
+
+ test('renders event card with correct data when query succeeds', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await screen.findByTestId('EventsAttendedCard');
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(screen.getByText('Test Location')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedByMember.tsx b/src/components/MemberDetail/EventsAttendedByMember.tsx
new file mode 100644
index 0000000000..ce926d84eb
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedByMember.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import { useQuery } from '@apollo/client';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import EventAttendedCard from './EventsAttendedCardItem';
+import { Spinner } from 'react-bootstrap';
+/**
+ * Component to display events attended by a specific member
+ * @param eventsId - ID of the event to fetch and display details for
+ * @returns Event card component with event details
+ */
+interface InterfaceEventsAttendedByMember {
+ eventsId: string;
+}
+
+function EventsAttendedByMember({
+ eventsId,
+}: InterfaceEventsAttendedByMember): JSX.Element {
+ const {
+ data: events,
+ loading,
+ error,
+ } = useQuery(EVENT_DETAILS, {
+ variables: { id: eventsId },
+ });
+
+ if (loading)
+ return (
+
+
+
Loading event details...
+
+ );
+ if (error)
+ return (
+
+
Unable to load event details. Please try again later.
+
+ );
+
+ const { organization, _id, startDate, title, location } = events.event;
+
+ return (
+
+ );
+}
+
+export default EventsAttendedByMember;
diff --git a/src/components/MemberDetail/EventsAttendedCardItem.test.tsx b/src/components/MemberDetail/EventsAttendedCardItem.test.tsx
new file mode 100644
index 0000000000..afbb19eeea
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedCardItem.test.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+import EventAttendedCard from './EventsAttendedCardItem';
+
+interface InterfaceEventAttendedCardProps {
+ type: 'Event';
+ title: string;
+ startdate: string;
+ time: string;
+ location: string;
+ orgId: string;
+ eventId: string;
+}
+
+describe('EventAttendedCard', () => {
+ const mockProps: InterfaceEventAttendedCardProps = {
+ type: 'Event' as const,
+ title: 'Test Event',
+ startdate: '2023-05-15',
+ time: '14:00',
+ location: 'Test Location',
+ orgId: 'org123',
+ eventId: 'event456',
+ };
+
+ const renderComponent = (props = mockProps): void => {
+ render(
+
+
+ ,
+ );
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders event details correctly', () => {
+ renderComponent();
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(screen.getByText('MAY')).toBeInTheDocument();
+ expect(screen.getByText('15')).toBeInTheDocument();
+ expect(screen.getByText('Test Location')).toBeInTheDocument();
+ });
+
+ it('renders link with correct path', () => {
+ renderComponent();
+ const link = screen.getByRole('link');
+ expect(link).toHaveAttribute('href', '/event/org123/event456');
+ });
+
+ it('renders location icon', () => {
+ renderComponent();
+ expect(screen.getByTestId('LocationOnIcon')).toBeInTheDocument();
+ });
+
+ it('renders chevron right icon', () => {
+ renderComponent();
+ expect(screen.getByTestId('ChevronRightIcon')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedCardItem.tsx b/src/components/MemberDetail/EventsAttendedCardItem.tsx
new file mode 100644
index 0000000000..cfed19e4dd
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedCardItem.tsx
@@ -0,0 +1,77 @@
+import React from 'react';
+import dayjs from 'dayjs';
+import { Card, Row, Col } from 'react-bootstrap';
+import { MdChevronRight, MdLocationOn } from 'react-icons/md';
+import { Link } from 'react-router-dom';
+/**
+ * Card component to display individual event attendance information
+ * Shows event details including title, date, location and organization
+ * @param orgId - Organization ID
+ * @param eventId - Event ID
+ * @param startdate - Event start date
+ * @param title - Event title
+ * @param location - Event location
+ * @returns Card component with formatted event information
+ */
+export interface InterfaceCardItem {
+ title: string;
+ time?: string;
+ startdate?: string;
+ creator?: string;
+ location?: string;
+ eventId?: string;
+ orgId?: string;
+}
+
+const EventAttendedCard = (props: InterfaceCardItem): JSX.Element => {
+ const { title, startdate, location, orgId, eventId } = props;
+
+ return (
+
+
+
+
+
+ {startdate && dayjs(startdate).isValid() ? (
+ <>
+
+ {dayjs(startdate).format('MMM').toUpperCase()}
+
+
+ {dayjs(startdate).format('D')}
+
+ >
+ ) : (
+ /*istanbul ignore next*/
+
Date N/A
+ )}
+
+
+
+ {title}
+
+
+ {location}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default EventAttendedCard;
diff --git a/src/components/MemberDetail/EventsAttendedMemberModal.test.tsx b/src/components/MemberDetail/EventsAttendedMemberModal.test.tsx
new file mode 100644
index 0000000000..ebdc3fff4c
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedMemberModal.test.tsx
@@ -0,0 +1,125 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { BrowserRouter } from 'react-router-dom';
+import EventsAttendedMemberModal from './EventsAttendedMemberModal';
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ i18n: { changeLanguage: () => Promise.resolve() },
+ }),
+}));
+
+jest.mock('./customTableCell', () => ({
+ CustomTableCell: ({ eventId }: { eventId: string }) => (
+
+ {`Event ${eventId}`}
+ 2024-03-14
+ Yes
+ 5
+
+ ),
+}));
+
+const mockEvents = Array.from({ length: 6 }, (_, index) => ({
+ _id: `${index + 1}`,
+ name: `Event ${index + 1}`,
+ date: '2024-03-14',
+ isRecurring: true,
+ attendees: 5,
+}));
+
+describe('EventsAttendedMemberModal', () => {
+ const defaultProps = {
+ eventsAttended: mockEvents,
+ setShow: jest.fn(),
+ show: true,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('renders modal with correct title when show is true', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('Events Attended List')).toBeInTheDocument();
+ expect(screen.getByText('Showing 1 - 5 of 6 Events')).toBeInTheDocument();
+ });
+
+ test('displays empty state message when no events', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('noeventsAttended')).toBeInTheDocument();
+ });
+
+ test('renders correct number of events per page', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ const eventRows = screen.getAllByTestId('event-row');
+ expect(eventRows).toHaveLength(5);
+ expect(screen.getByText('Event 1')).toBeInTheDocument();
+ expect(screen.getByText('Event 5')).toBeInTheDocument();
+ });
+
+ test('handles pagination correctly', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name: 'Go to next page' }));
+ expect(screen.getByText('Event 6')).toBeInTheDocument();
+ });
+
+ test('closes modal when close button is clicked', () => {
+ const mockSetShow = jest.fn();
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name: 'Close' }));
+ expect(mockSetShow).toHaveBeenCalledWith(false);
+ expect(mockSetShow).toHaveBeenCalledTimes(1);
+ });
+
+ test('displays correct pagination info', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('Showing 1 - 5 of 6 Events')).toBeInTheDocument();
+ fireEvent.click(screen.getByRole('button', { name: 'Go to next page' }));
+ expect(screen.getByText('Showing 6 - 6 of 6 Events')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedMemberModal.tsx b/src/components/MemberDetail/EventsAttendedMemberModal.tsx
new file mode 100644
index 0000000000..69913998b1
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedMemberModal.tsx
@@ -0,0 +1,131 @@
+import React, { useState, useMemo } from 'react';
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Paper,
+ Pagination,
+} from '@mui/material';
+import { Modal } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import styles from '../../screens/MemberDetail/MemberDetail.module.css';
+import { CustomTableCell } from './customTableCell';
+/**
+ * Modal component to display paginated list of events attended by a member
+ * @param eventsAttended - Array of events attended by the member
+ * @param setShow - Function to control modal visibility
+ * @param show - Boolean to control modal visibility
+ * @param eventsPerPage - Number of events to display per page
+ * @returns Modal component with paginated events list
+ */
+interface InterfaceEvent {
+ _id: string;
+ name: string;
+ date: string;
+ isRecurring: boolean;
+ attendees: number;
+}
+
+interface InterfaceEventsAttendedMemberModalProps {
+ eventsAttended: InterfaceEvent[];
+ setShow: (show: boolean) => void;
+ show: boolean;
+ eventsPerPage?: number;
+}
+
+const EventsAttendedMemberModal: React.FC<
+ InterfaceEventsAttendedMemberModalProps
+> = ({ eventsAttended, setShow, show, eventsPerPage = 5 }) => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'memberDetail',
+ });
+ const [page, setPage] = useState(1);
+
+ const handleClose = (): void => {
+ setShow(false);
+ };
+
+ const handleChangePage = (
+ event: React.ChangeEvent,
+ newPage: number,
+ ): void => {
+ setPage(newPage);
+ };
+
+ const totalPages = useMemo(
+ () => Math.ceil(eventsAttended.length / eventsPerPage),
+ [eventsAttended.length, eventsPerPage],
+ );
+
+ const paginatedEvents = useMemo(
+ () =>
+ eventsAttended.slice((page - 1) * eventsPerPage, page * eventsPerPage),
+ [eventsAttended, page, eventsPerPage],
+ );
+
+ return (
+
+
+ Events Attended List
+
+
+ {eventsAttended.length === 0 ? (
+ {t('noeventsAttended')}
+ ) : (
+ <>
+
+ Showing {(page - 1) * eventsPerPage + 1} -{' '}
+ {Math.min(page * eventsPerPage, eventsAttended.length)} of{' '}
+ {eventsAttended.length} Events
+
+
+
+
+
+
+ Event Name
+
+
+ Date of Event
+
+
+ Recurring Event
+
+
+ Attendees
+
+
+
+
+ {paginatedEvents.map((event) => (
+
+ ))}
+
+
+
+
+
+
{
+ if (type === 'page') return `Go to page ${page}`;
+ return `Go to ${type} page`;
+ }}
+ />
+
+
+ >
+ )}
+
+
+ );
+};
+
+export default EventsAttendedMemberModal;
diff --git a/src/components/MemberDetail/customTableCell.test.tsx b/src/components/MemberDetail/customTableCell.test.tsx
new file mode 100644
index 0000000000..bc296a74f3
--- /dev/null
+++ b/src/components/MemberDetail/customTableCell.test.tsx
@@ -0,0 +1,171 @@
+import React from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { BrowserRouter } from 'react-router-dom';
+import { CustomTableCell } from './customTableCell';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'This is a test event description',
+ startDate: '2023-05-01',
+ endDate: '2023-05-02',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: {
+ _id: 'recurringEvent123',
+ },
+ organization: {
+ _id: 'org456',
+ members: [
+ { _id: 'member1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'member2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+ },
+ },
+ },
+ },
+];
+
+describe('CustomTableCell', () => {
+ it('renders event details correctly', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => screen.getByTestId('custom-row'));
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(screen.getByText('May 1, 2023')).toBeInTheDocument();
+ expect(screen.getByText('Yes')).toBeInTheDocument();
+ expect(screen.getByText('2')).toBeInTheDocument();
+
+ const link = screen.getByRole('link', { name: 'Test Event' });
+ expect(link).toHaveAttribute('href', '/event/org456/event123');
+ });
+
+ it('displays loading state', () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
+ });
+
+ // it('displays error state', async () => {
+ // const errorMock = [
+ // {
+ // request: {
+ // query: EVENT_DETAILS,
+ // variables: { id: 'event123' },
+ // },
+ // error: new Error('An error occurred'),
+ // },
+ // ];
+
+ // render(
+ //
+ //
+ // ,
+ // );
+
+ // await waitFor(
+ // () => {
+ // expect(
+ // screen.getByText('Error loading event details'),
+ // ).toBeInTheDocument();
+ // },
+ // { timeout: 2000 },
+ // );
+
+ // // Check if the error message from toast has been called
+ // expect(toast.error).toHaveBeenCalledWith('An error occurred');
+ // });
+
+ // it('displays no event found message', async () => {
+ // const noEventMock = [
+ // {
+ // request: {
+ // query: EVENT_DETAILS,
+ // variables: { id: 'event123' },
+ // },
+ // result: {
+ // data: {
+ // event: {
+ // _id: null,
+ // title: null,
+ // startDate: null,
+ // description: null,
+ // endDate: null,
+ // startTime: null,
+ // endTime: null,
+ // allDay: false,
+ // location: null,
+ // recurring: null,
+ // organization: {
+ // _id: null,
+ // members: [],
+ // },
+ // baseRecurringEvent: {
+ // _id: 'recurringEvent123',
+ // },
+ // attendees: [],
+ // },
+ // },
+ // },
+ // },
+ // ];
+
+ // render(
+ //
+ //
+ // ,
+ // );
+
+ // await waitFor(() => screen.getByText('No event found'));
+ // expect(screen.getByText('No event found')).toBeInTheDocument();
+ // });
+});
diff --git a/src/components/MemberDetail/customTableCell.tsx b/src/components/MemberDetail/customTableCell.tsx
new file mode 100644
index 0000000000..b8cc2bdd98
--- /dev/null
+++ b/src/components/MemberDetail/customTableCell.tsx
@@ -0,0 +1,78 @@
+import { useQuery } from '@apollo/client';
+import { CircularProgress, TableCell, TableRow } from '@mui/material';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import React from 'react';
+import styles from '../../screens/MemberDetail/MemberDetail.module.css';
+import { Link } from 'react-router-dom';
+/**
+ * Custom table cell component to display event details
+ * @param eventId - ID of the event to fetch and display
+ * @returns TableRow component with event information
+ */
+
+export const CustomTableCell: React.FC<{ eventId: string }> = ({ eventId }) => {
+ const { data, loading, error } = useQuery(EVENT_DETAILS, {
+ variables: {
+ id: eventId,
+ },
+ errorPolicy: 'all',
+ fetchPolicy: 'cache-first',
+ nextFetchPolicy: 'cache-and-network',
+ pollInterval: 30000,
+ });
+
+ if (loading)
+ return (
+
+
+
+
+
+ );
+ /*istanbul ignore next*/
+ if (error) {
+ return (
+
+
+ {`Unable to load event details. Please try again later.`}
+
+
+ );
+ }
+ const event = data?.event;
+ /*istanbul ignore next*/
+ if (!event) {
+ return (
+
+
+ Event not found or has been deleted
+
+
+ );
+ }
+
+ return (
+
+
+
+ {event.title}
+
+
+
+ {new Date(event.startDate).toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ timeZone: 'UTC',
+ })}
+
+ {event.recurring ? 'Yes' : 'No'}
+
+ {event.attendees?.length ?? 0}
+
+
+ );
+};
diff --git a/src/components/MemberRequestCard/MemberRequestCard.module.css b/src/components/MemberRequestCard/MemberRequestCard.module.css
new file mode 100644
index 0000000000..fdc99eba83
--- /dev/null
+++ b/src/components/MemberRequestCard/MemberRequestCard.module.css
@@ -0,0 +1,57 @@
+.memberlist {
+ margin-top: -1px;
+}
+.memberimg {
+ border-radius: 10px;
+ margin-left: 10px;
+}
+.singledetails {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+.singledetails p {
+ margin-bottom: -5px;
+}
+.singledetails_data_left {
+ margin-top: 10px;
+ margin-left: 10px;
+ color: #707070;
+}
+.singledetails_data_right {
+ justify-content: right;
+ margin-top: 10px;
+ text-align: right;
+ color: #707070;
+}
+.membername {
+ font-size: 16px;
+ font-weight: bold;
+}
+.memberfont {
+ margin-top: 3px;
+}
+.memberfont > span {
+ width: 80%;
+}
+.memberfontcreated {
+ margin-top: 18px;
+}
+.memberfontcreatedbtn {
+ margin-top: 33px;
+ border-radius: 7px;
+ border-color: #e8e5e5;
+ background-color: #f7f7f7;
+ padding-right: 10px;
+ padding-left: 10px;
+ justify-content: flex-end;
+ float: right;
+ text-align: right;
+ box-shadow: none;
+}
+#grid_wrapper {
+ align-items: left;
+}
+.peoplelistdiv {
+ margin-right: 50px;
+}
diff --git a/src/components/MemberRequestCard/MemberRequestCard.test.tsx b/src/components/MemberRequestCard/MemberRequestCard.test.tsx
new file mode 100644
index 0000000000..a38a046ea2
--- /dev/null
+++ b/src/components/MemberRequestCard/MemberRequestCard.test.tsx
@@ -0,0 +1,122 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+
+import {
+ ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ REJECT_ORGANIZATION_REQUEST_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import MemberRequestCard from './MemberRequestCard';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const MOCKS = [
+ {
+ request: {
+ query: ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: REJECT_ORGANIZATION_REQUEST_MUTATION,
+ variables: { userid: '234' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '2',
+ },
+ ],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Member Request Card', () => {
+ const props = {
+ id: '1',
+ memberName: 'John Doe',
+ memberLocation: 'India',
+ joinDate: '18/03/2022',
+ memberImage: 'image',
+ email: 'johndoe@gmail.com',
+ };
+
+ global.alert = jest.fn();
+
+ it('should render props and text elements test for the page component', async () => {
+ global.confirm = (): boolean => true;
+
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText(/Accept/i));
+ userEvent.click(screen.getByText(/Reject/i));
+
+ expect(screen.getByAltText(/userImage/i)).toBeInTheDocument();
+ expect(screen.getByText(/Joined:/i)).toBeInTheDocument();
+ expect(screen.getByText(props.memberName)).toBeInTheDocument();
+ expect(screen.getByText(props.memberLocation)).toBeInTheDocument();
+ expect(screen.getByText(props.joinDate)).toBeInTheDocument();
+ expect(screen.getByText(props.email)).toBeInTheDocument();
+ });
+
+ it('should render text elements when props value is not passed', async () => {
+ global.confirm = (): boolean => false;
+
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText(/Accept/i));
+ userEvent.click(screen.getByText(/Reject/i));
+
+ expect(screen.getByAltText(/userImage/i)).toBeInTheDocument();
+ expect(screen.getByText(/Joined:/i)).toBeInTheDocument();
+ expect(screen.queryByText(props.memberName)).not.toBeInTheDocument();
+ expect(screen.getByText(props.memberLocation)).toBeInTheDocument();
+ expect(screen.getByText(props.joinDate)).toBeInTheDocument();
+ expect(screen.getByText(props.email)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberRequestCard/MemberRequestCard.tsx b/src/components/MemberRequestCard/MemberRequestCard.tsx
new file mode 100644
index 0000000000..c45b698506
--- /dev/null
+++ b/src/components/MemberRequestCard/MemberRequestCard.tsx
@@ -0,0 +1,138 @@
+import React from 'react';
+import styles from './MemberRequestCard.module.css';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import Button from 'react-bootstrap/Button';
+import { useMutation } from '@apollo/client';
+import {
+ ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ REJECT_ORGANIZATION_REQUEST_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import defaultImg from 'assets/images/blank.png';
+import { errorHandler } from 'utils/errorHandler';
+
+interface InterfaceMemberRequestCardProps {
+ id: string; // Unique identifier for the member
+ memberName: string; // Name of the member
+ memberLocation: string; // Location of the member
+ joinDate: string; // Date when the member joined
+ memberImage: string; // URL for the member's image
+ email: string; // Email of the member
+}
+
+/**
+ * Component for displaying and managing member requests.
+ *
+ * @param props - Properties for the member request card.
+ * @returns JSX element for member request card.
+ */
+function MemberRequestCard(
+ props: InterfaceMemberRequestCardProps,
+): JSX.Element {
+ const [acceptMutation] = useMutation(ACCEPT_ORGANIZATION_REQUEST_MUTATION);
+ const [rejectMutation] = useMutation(REJECT_ORGANIZATION_REQUEST_MUTATION);
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'membershipRequest',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ /**
+ * Handles accepting a member request.
+ * Displays a success message and reloads the page.
+ */
+ const addMember = async (): Promise => {
+ try {
+ await acceptMutation({
+ variables: {
+ id: props.id,
+ },
+ });
+
+ /* istanbul ignore next */
+ toast.success(t('memberAdded') as string);
+ /* istanbul ignore next */
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ /**
+ * Handles rejecting a member request.
+ * Confirms rejection and reloads the page if confirmed.
+ */
+ const rejectMember = async (): Promise => {
+ const sure = window.confirm('Are you sure you want to Reject Request ?');
+ if (sure) {
+ try {
+ await rejectMutation({
+ variables: {
+ userid: props.id,
+ },
+ });
+
+ /* istanbul ignore next */
+ window.location.reload();
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ }
+ };
+
+ return (
+ <>
+
+
+ {props.memberImage ? (
+
+ ) : (
+
+ )}
+
+
+
+ {props.memberName ? <>{props.memberName}> : <>Dogs Care>}
+
+
{props.memberLocation}
+
{props.email}
+
+
+
+ {tCommon('joined')}: {props.joinDate}
+
+
+ {t('accept')}
+
+
+ {t('reject')}
+
+
+
+
+
+
+ >
+ );
+}
+export default MemberRequestCard;
diff --git a/src/components/NotFound/NotFound.module.css b/src/components/NotFound/NotFound.module.css
new file mode 100644
index 0000000000..a209bc9f48
--- /dev/null
+++ b/src/components/NotFound/NotFound.module.css
@@ -0,0 +1,22 @@
+.section {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.error {
+ font-size: 1.2rem;
+ font-weight: 500;
+}
+
+@media (min-width: 440px) and (max-width: 570px) {
+ .section {
+ margin-left: 50px;
+ }
+ .error {
+ font-size: 1.1rem;
+ font-style: oblique;
+ }
+}
diff --git a/src/components/NotFound/NotFound.test.tsx b/src/components/NotFound/NotFound.test.tsx
new file mode 100644
index 0000000000..54c0bcfe4a
--- /dev/null
+++ b/src/components/NotFound/NotFound.test.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+
+import { render, screen } from '@testing-library/react';
+import NotFound from './NotFound';
+
+describe('Tesing the NotFound Component', () => {
+ it('renders the component with the correct title for posts', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText(/Not Found!/i)).toBeInTheDocument();
+ });
+
+ it('renders the component with the correct title for users', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText(/Not Found!/i)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/NotFound/NotFound.tsx b/src/components/NotFound/NotFound.tsx
new file mode 100644
index 0000000000..6b5332bbcf
--- /dev/null
+++ b/src/components/NotFound/NotFound.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+import styles from './NotFound.module.css';
+
+interface InterfaceNotFoundProps {
+ title: string; // Title of the page or resource not found
+ keyPrefix: string; // Translation key prefix
+}
+
+/**
+ * Component to display a "Not Found" message.
+ *
+ * @param title - Title of the page or resource that was not found.
+ * @param keyPrefix - Prefix for translation keys.
+ * @returns JSX element for the "Not Found" page.
+ */
+function notFound(props: InterfaceNotFoundProps): JSX.Element {
+ const key = props.keyPrefix.toString();
+ const { t } = useTranslation('translation', {
+ keyPrefix: key,
+ });
+ return (
+ <>
+
+ {t(`${props.title} not found!`)}
+
+ >
+ );
+}
+
+export default notFound;
diff --git a/src/components/OrgAdminListCard/OrgAdminListCard.test.tsx b/src/components/OrgAdminListCard/OrgAdminListCard.test.tsx
new file mode 100644
index 0000000000..7baea946d2
--- /dev/null
+++ b/src/components/OrgAdminListCard/OrgAdminListCard.test.tsx
@@ -0,0 +1,108 @@
+import React, { act } from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import type { RenderResult } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { REMOVE_ADMIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import OrgAdminListCard from './OrgAdminListCard';
+import i18nForTest from 'utils/i18nForTest';
+import { MemoryRouter, Route, Routes } from 'react-router-dom';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const MOCKS = [
+ {
+ request: {
+ query: REMOVE_ADMIN_MUTATION,
+ variables: { userid: '456', orgid: '987' },
+ },
+ result: {
+ data: {
+ removeAdmin: {
+ _id: '456',
+ },
+ },
+ },
+ },
+];
+const link = new StaticMockLink(MOCKS, true);
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const renderOrgAdminListCard = (props: {
+ toggleRemoveModal: () => boolean;
+ id: string | undefined;
+}): RenderResult => {
+ return render(
+
+
+
+
+ }
+ />
+ orgListScreen}
+ />
+
+
+
+ ,
+ );
+};
+jest.mock('i18next-browser-languagedetector', () => ({
+ init: jest.fn(),
+ type: 'languageDetector',
+ detect: jest.fn(() => 'en'),
+ cacheUserLanguage: jest.fn(),
+}));
+describe('Testing Organization Admin List Card', () => {
+ global.alert = jest.fn();
+
+ beforeEach(() => {
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value: { reload: jest.fn() },
+ });
+ });
+
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ test('should render props and text elements test for the page component', async () => {
+ const props = {
+ toggleRemoveModal: () => true,
+ id: '456',
+ };
+
+ renderOrgAdminListCard(props);
+
+ await wait();
+
+ userEvent.click(screen.getByTestId(/removeAdminBtn/i));
+
+ await wait(2000);
+ });
+
+ test('Should not render text elements when props value is not passed', async () => {
+ const props = {
+ toggleRemoveModal: () => true,
+ id: undefined,
+ };
+
+ renderOrgAdminListCard(props);
+
+ await waitFor(() => {
+ const orgListScreen = screen.getByTestId('orgListScreen');
+ expect(orgListScreen).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/OrgAdminListCard/OrgAdminListCard.tsx b/src/components/OrgAdminListCard/OrgAdminListCard.tsx
new file mode 100644
index 0000000000..f8fc454823
--- /dev/null
+++ b/src/components/OrgAdminListCard/OrgAdminListCard.tsx
@@ -0,0 +1,83 @@
+import React from 'react';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import { REMOVE_ADMIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useTranslation } from 'react-i18next';
+import { Navigate, useParams } from 'react-router-dom';
+import { errorHandler } from 'utils/errorHandler';
+
+interface InterfaceOrgPeopleListCardProps {
+ id: string | undefined;
+ toggleRemoveModal: () => void;
+}
+/**
+ * Component to confirm and handle the removal of an admin.
+ *
+ * @param id - ID of the admin to be removed.
+ * @param toggleRemoveModal - Function to toggle the visibility of the modal.
+ * @returns JSX element for the removal confirmation modal.
+ */
+function orgAdminListCard(props: InterfaceOrgPeopleListCardProps): JSX.Element {
+ if (!props.id) {
+ return ;
+ }
+ const { orgId: currentUrl } = useParams();
+ const [remove] = useMutation(REMOVE_ADMIN_MUTATION);
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgAdminListCard',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ /**
+ * Function to remove the admin from the organization
+ * and display a success message.
+ */
+ const removeAdmin = async (): Promise => {
+ try {
+ const { data } = await remove({
+ variables: {
+ userid: props.id,
+ orgid: currentUrl,
+ },
+ });
+ if (data) {
+ toast.success(t('adminRemoved') as string);
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+ return (
+ <>
+
+
+ {t('removeAdmin')}
+
+
+
+
+ {t('removeAdminMsg')}
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+ >
+ );
+}
+export default orgAdminListCard;
diff --git a/src/components/OrgContriCards/OrgContriCards.module.css b/src/components/OrgContriCards/OrgContriCards.module.css
new file mode 100644
index 0000000000..d20b696621
--- /dev/null
+++ b/src/components/OrgContriCards/OrgContriCards.module.css
@@ -0,0 +1,22 @@
+.cards {
+ width: 45%;
+ background: #fcfcfc;
+ margin: 10px 20px;
+ padding: 20px 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 3px 5px #c9c9c9;
+ margin-right: 40px;
+ color: #737373;
+}
+.cards > h2 {
+ font-size: 19px;
+}
+.cards > h3 {
+ font-size: 17px;
+}
+.cards > p {
+ font-size: 14px;
+ margin-top: -5px;
+ margin-bottom: 7px;
+}
diff --git a/src/components/OrgContriCards/OrgContriCards.test.tsx b/src/components/OrgContriCards/OrgContriCards.test.tsx
new file mode 100644
index 0000000000..4f202cd355
--- /dev/null
+++ b/src/components/OrgContriCards/OrgContriCards.test.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
+import { I18nextProvider } from 'react-i18next';
+
+import OrgContriCards from './OrgContriCards';
+import i18nForTest from 'utils/i18nForTest';
+import { BACKEND_URL } from 'Constant/constant';
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ uri: BACKEND_URL,
+});
+
+describe('Testing the Organization Contributions Cards', () => {
+ const props = {
+ key: '123',
+ id: '123',
+ userName: 'John Doe',
+ contriDate: '06/03/2022',
+ contriAmount: '500',
+ contriTransactionId: 'QW56DA88',
+ userEmail: 'johndoe@gmail.com',
+ };
+
+ it('should render props and text elements test for the page component', () => {
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('Date:')).toBeInTheDocument();
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
+ expect(screen.getByText('06/03/2022')).toBeInTheDocument();
+ expect(screen.getByText('500')).toBeInTheDocument();
+ expect(screen.getByText('QW56DA88')).toBeInTheDocument();
+ expect(screen.getByText('johndoe@gmail.com')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrgContriCards/OrgContriCards.tsx b/src/components/OrgContriCards/OrgContriCards.tsx
new file mode 100644
index 0000000000..6635be09b8
--- /dev/null
+++ b/src/components/OrgContriCards/OrgContriCards.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import { useTranslation } from 'react-i18next';
+
+import styles from './OrgContriCards.module.css';
+
+/**
+ * Props for the OrgContriCards component
+ */
+interface InterfaceOrgContriCardsProps {
+ key: string;
+ id: string;
+ userName: string;
+ contriDate: string;
+ contriAmount: string;
+ contriTransactionId: string;
+ userEmail: string;
+}
+
+/**
+ * Component to display organization contribution cards
+ *
+ * This component shows the contribution details of a user in a card format. It includes
+ * the user's name, email, contribution date, transaction ID, and the contribution amount.
+ *
+ * @param props - The properties passed to the component
+ * @returns JSX.Element representing a contribution card
+ */
+function orgContriCards(props: InterfaceOrgContriCardsProps): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgContriCards',
+ });
+
+ return (
+ <>
+
+
+ {props.userName}
+ {props.userEmail}
+
+ {t('date')}:{props.contriDate}
+
+
+ {t('transactionId')}: {props.contriTransactionId}
+
+
+ {t('amount')}: $ {props.contriAmount}
+
+
+
+ >
+ );
+}
+export default orgContriCards;
diff --git a/src/components/OrgDelete/OrgDelete.test.tsx b/src/components/OrgDelete/OrgDelete.test.tsx
new file mode 100644
index 0000000000..b9b9ca2572
--- /dev/null
+++ b/src/components/OrgDelete/OrgDelete.test.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import type { NormalizedCacheObject } from '@apollo/client';
+import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
+import { I18nextProvider } from 'react-i18next';
+
+import OrgDelete from './OrgDelete';
+import i18nForTest from 'utils/i18nForTest';
+import { BACKEND_URL } from 'Constant/constant';
+
+const client: ApolloClient = new ApolloClient({
+ cache: new InMemoryCache(),
+ uri: BACKEND_URL,
+});
+
+describe('Testing Organization People List Card', () => {
+ test('should render props and text elements test for the page component', () => {
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('Delete Org')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrgDelete/OrgDelete.tsx b/src/components/OrgDelete/OrgDelete.tsx
new file mode 100644
index 0000000000..e596445e5c
--- /dev/null
+++ b/src/components/OrgDelete/OrgDelete.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+/**
+ * Component for displaying organization deletion message
+ *
+ * This component renders a message related to deleting an organization.
+ *
+ * @returns JSX.Element representing the organization deletion message
+ */
+function orgDelete(): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgDelete',
+ });
+
+ return (
+ <>
+ {/* Container for the organization deletion message */}
+
+ {t('deleteOrg')}
+
+ >
+ );
+}
+export default orgDelete;
diff --git a/src/components/OrgListCard/OrgListCard.module.css b/src/components/OrgListCard/OrgListCard.module.css
new file mode 100644
index 0000000000..430ea318d6
--- /dev/null
+++ b/src/components/OrgListCard/OrgListCard.module.css
@@ -0,0 +1,141 @@
+.orgCard {
+ background-color: var(--bs-white);
+ margin: 0.5rem;
+ height: calc(120px + 2rem);
+ padding: 1rem;
+ border-radius: 8px;
+ outline: 1px solid var(--bs-gray-200);
+ position: relative;
+}
+
+.orgCard .innerContainer {
+ display: flex;
+}
+
+.orgCard .innerContainer .orgImgContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ overflow: hidden;
+ border-radius: 4px;
+}
+
+.orgCard .innerContainer .orgImgContainer {
+ width: 125px;
+ height: 120px;
+ object-fit: contain;
+}
+
+.orgCard .innerContainer .orgImgContainer {
+ width: 125px;
+ height: 120px;
+ background-color: var(--bs-gray-200);
+}
+
+.orgCard .innerContainer .content {
+ flex: 1;
+ margin-left: 1rem;
+ width: 70%;
+ margin-top: 0.7rem;
+}
+
+.orgCard button {
+ position: absolute;
+ bottom: 1rem;
+ right: 1rem;
+ z-index: 1;
+}
+
+.flaskIcon {
+ margin-top: 4px;
+}
+
+.manageBtn {
+ display: flex;
+ justify-content: space-around;
+ width: 8rem;
+}
+
+.orgName {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 1rem;
+}
+
+.orgdesc {
+ font-size: 0.9rem;
+ color: var(--bs-gray-600);
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+ max-width: 20rem;
+}
+
+.orgadmin {
+ font-size: 0.9rem;
+}
+
+.orgmember {
+ font-size: 0.9rem;
+}
+
+.address {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+ align-items: center;
+}
+
+.address h6 {
+ font-size: 0.9rem;
+ color: var(--bs-gray-600);
+}
+
+@media (max-width: 580px) {
+ .orgCard {
+ height: unset;
+ margin: 0.5rem 0;
+ padding: 1.25rem 1.5rem;
+ }
+
+ .orgCard .innerContainer {
+ flex-direction: column;
+ }
+
+ .orgCard .innerContainer .orgImgContainer {
+ margin-bottom: 0.8rem;
+ }
+
+ .orgCard .innerContainer .orgImgContainer img {
+ height: auto;
+ width: 100%;
+ }
+
+ .orgCard .innerContainer .content {
+ margin-left: 0;
+ }
+
+ .orgCard button {
+ bottom: 0;
+ right: 0;
+ position: relative;
+ margin-left: auto;
+ display: block;
+ }
+
+ .flaskIcon {
+ margin-bottom: 6px;
+ }
+
+ .manageBtn {
+ display: flex;
+ justify-content: space-around;
+ width: 100%;
+ }
+}
diff --git a/src/components/OrgListCard/OrgListCard.test.tsx b/src/components/OrgListCard/OrgListCard.test.tsx
new file mode 100644
index 0000000000..4072265ea4
--- /dev/null
+++ b/src/components/OrgListCard/OrgListCard.test.tsx
@@ -0,0 +1,149 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import i18nForTest from 'utils/i18nForTest';
+import type { InterfaceOrgListCardProps } from './OrgListCard';
+import OrgListCard from './OrgListCard';
+import userEvent from '@testing-library/user-event';
+import { BrowserRouter } from 'react-router-dom';
+import { IS_SAMPLE_ORGANIZATION_QUERY } from 'GraphQl/Queries/Queries';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { MockedProvider } from '@apollo/react-testing';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem, removeItem } = useLocalStorage();
+
+const MOCKS = [
+ {
+ request: {
+ query: IS_SAMPLE_ORGANIZATION_QUERY,
+ variables: {
+ isSampleOrganizationId: 'xyz',
+ },
+ },
+ result: {
+ data: {
+ isSampleOrganization: true,
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const props: InterfaceOrgListCardProps = {
+ data: {
+ _id: 'xyz',
+ name: 'Dogs Care',
+ image: 'https://api.dicebear.com/5.x/initials/svg?seed=John%20Doe',
+ address: {
+ city: 'Sample City',
+ countryCode: 'US',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Sample Street',
+ line2: 'Apartment 456',
+ postalCode: '12345',
+ sortingCode: 'ABC-123',
+ state: 'Sample State',
+ },
+ admins: [
+ {
+ _id: '123',
+ },
+ {
+ _id: '456',
+ },
+ ],
+ members: [],
+ createdAt: '04/07/2019',
+ creator: {
+ _id: 'abc',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+};
+
+describe('Testing the Super Dash List', () => {
+ test('should render props and text elements test for the page component', async () => {
+ removeItem('id');
+ setItem('id', '123'); // Means the user is an admin
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(screen.getByAltText(/Dogs Care image/i)).toBeInTheDocument();
+ expect(screen.getByText(/Admins:/i)).toBeInTheDocument();
+ expect(screen.getByText(/Members:/i)).toBeInTheDocument();
+ expect(screen.getByText('Dogs Care')).toBeInTheDocument();
+ expect(screen.getByText(/Sample City/i)).toBeInTheDocument();
+ expect(screen.getByText(/123 Sample Street/i)).toBeInTheDocument();
+ expect(screen.getByTestId(/manageBtn/i)).toBeInTheDocument();
+ expect(screen.getByTestId(/flaskIcon/i)).toBeInTheDocument();
+ userEvent.click(screen.getByTestId(/manageBtn/i));
+ removeItem('id');
+ });
+
+ test('Testing if the props data is not provided', () => {
+ window.location.assign('/orgdash');
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(window.location).toBeAt('/orgdash');
+ });
+
+ test('Testing if component is rendered properly when image is null', () => {
+ const imageNullProps = {
+ ...props,
+ ...{ data: { ...props.data, ...{ image: null } } },
+ };
+ render(
+
+
+
+
+
+
+ ,
+ );
+ expect(screen.getByTestId(/emptyContainerForImage/i)).toBeInTheDocument();
+ });
+
+ test('Testing if user is redirected to orgDash screen', () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+ userEvent.click(screen.getByTestId('manageBtn'));
+ });
+});
diff --git a/src/components/OrgListCard/OrgListCard.tsx b/src/components/OrgListCard/OrgListCard.tsx
new file mode 100644
index 0000000000..10365d2364
--- /dev/null
+++ b/src/components/OrgListCard/OrgListCard.tsx
@@ -0,0 +1,140 @@
+import React from 'react';
+import FlaskIcon from 'assets/svgs/flask.svg?react';
+import Button from 'react-bootstrap/Button';
+import { useTranslation } from 'react-i18next';
+import styles from './OrgListCard.module.css';
+import { useNavigate } from 'react-router-dom';
+import type {
+ InterfaceOrgConnectionInfoType,
+ InterfaceQueryOrganizationsListObject,
+} from 'utils/interfaces';
+import {
+ IS_SAMPLE_ORGANIZATION_QUERY,
+ ORGANIZATIONS_LIST,
+} from 'GraphQl/Queries/Queries';
+import { useQuery } from '@apollo/client';
+import { Tooltip } from '@mui/material';
+import Avatar from 'components/Avatar/Avatar';
+
+/**
+ * Props for the OrgListCard component
+ */
+export interface InterfaceOrgListCardProps {
+ data: InterfaceOrgConnectionInfoType;
+}
+
+/**
+ * Component for displaying a list card for an organization
+ *
+ * This component renders a card that displays information about an organization,
+ * including its name, address, members, and admins. It also provides a button
+ * to manage the organization, navigating to the organization's dashboard.
+ *
+ * @param props - The properties passed to the component
+ * @returns JSX.Element representing an organization list card
+ */
+function orgListCard(props: InterfaceOrgListCardProps): JSX.Element {
+ // Destructure data from props
+ const { _id, admins, image, address, members, name } = props.data;
+
+ // Query to check if the organization is a sample organization
+ const { data } = useQuery(IS_SAMPLE_ORGANIZATION_QUERY, {
+ variables: {
+ isSampleOrganizationId: _id,
+ },
+ });
+
+ // Use navigate hook from react-router-dom to navigate to the organization dashboard
+ const navigate = useNavigate();
+
+ // Query to get the organization list
+ const {
+ data: userData,
+ }: {
+ data?: {
+ organizations: InterfaceQueryOrganizationsListObject[];
+ };
+ } = useQuery(ORGANIZATIONS_LIST, {
+ variables: { id: _id },
+ });
+
+ // Handle click event to navigate to the organization dashboard
+ function handleClick(): void {
+ const url = '/orgdash/' + _id;
+
+ // Dont change the below two lines
+ navigate(url);
+ }
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgListCard',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ return (
+ <>
+ {/* Container for the organization card */}
+
+
+ {/* Container for the organization image */}
+
+ {image ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Tooltip for the organization name */}
+
+ {name}
+
+ {/* Description of the organization */}
+
+ {userData?.organizations[0].description}
+
+ {/* Display the organization address if available */}
+ {address && address.city && (
+
+
+ {address.line1},
+ {address.city},
+ {address.countryCode}
+
+
+ )}
+ {/* Display the number of admins and members */}
+
+ {tCommon('admins')}: {admins.length}
+ {tCommon('members')}: {members.length}
+
+
+
+ {/* Button to manage the organization */}
+
+ {/* Show flask icon if the organization is a sample organization */}
+ {data && data?.isSampleOrganization && (
+
+ )}
+ {' '}
+ {t('manage')}
+
+
+ >
+ );
+}
+export default orgListCard;
diff --git a/src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx b/src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx
new file mode 100644
index 0000000000..7cee31107f
--- /dev/null
+++ b/src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx
@@ -0,0 +1,81 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+
+import OrgPeopleListCard from './OrgPeopleListCard';
+import { REMOVE_MEMBER_MUTATION } from 'GraphQl/Mutations/mutations';
+import i18nForTest from 'utils/i18nForTest';
+import { BrowserRouter } from 'react-router-dom';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const MOCKS = [
+ {
+ request: {
+ query: REMOVE_MEMBER_MUTATION,
+ variable: { userid: '123', orgid: '456' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ },
+ ],
+ },
+ },
+ },
+];
+const link = new StaticMockLink(MOCKS, true);
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Organization People List Card', () => {
+ const props = {
+ toggleRemoveModal: () => true,
+ id: '1',
+ };
+ global.alert = jest.fn();
+
+ test('should render props and text elements test for the page component', async () => {
+ global.confirm = (): boolean => true;
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId(/removeMemberBtn/i));
+ });
+
+ test('Should not render modal when id is undefined', async () => {
+ global.confirm = (): boolean => false;
+
+ render(
+
+
+
+ true} />
+
+
+ ,
+ );
+
+ await wait();
+
+ expect(window.location.pathname).toEqual('/orglist');
+ });
+});
diff --git a/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx b/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx
new file mode 100644
index 0000000000..dca72b84e7
--- /dev/null
+++ b/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import { useTranslation } from 'react-i18next';
+import { REMOVE_MEMBER_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useParams, Navigate } from 'react-router-dom';
+import { errorHandler } from 'utils/errorHandler';
+import styles from '../../style/app.module.css';
+import { Close } from '@mui/icons-material';
+
+/**
+ * Props for the OrgPeopleListCard component
+ */
+interface InterfaceOrgPeopleListCardProps {
+ id: string | undefined;
+ toggleRemoveModal: () => void;
+}
+
+/**
+ * Component for displaying a modal to remove a member from an organization
+ *
+ * This component shows a modal that confirms the removal of a member from the organization.
+ * It performs the removal action and displays success or error messages.
+ *
+ * @param props - The properties passed to the component
+ * @returns JSX.Element representing the organization people list card modal
+ */
+function orgPeopleListCard(
+ props: InterfaceOrgPeopleListCardProps,
+): JSX.Element {
+ // Get the current organization ID from the URL parameters
+ const { orgId: currentUrl } = useParams();
+
+ // If the member ID is not provided, navigate to the organization list
+ if (!props.id) {
+ return ;
+ }
+
+ // Mutation to remove a member from the organization
+ const [remove] = useMutation(REMOVE_MEMBER_MUTATION);
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgPeopleListCard',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // Function to remove a member and handle success or error
+ const removeMember = async (): Promise => {
+ try {
+ const { data } = await remove({
+ variables: {
+ userid: props.id,
+ orgid: currentUrl,
+ },
+ });
+ // If the mutation is successful, show a success message and reload the page
+ /* istanbul ignore next */
+ if (data) {
+ toast.success(t('memberRemoved') as string);
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ return (
+
+ {/* Modal to confirm member removal */}
+
+
+ {t('removeMember')}
+ {/* Button to close the modal */}
+
+
+
+
+ {t('removeMemberMsg')}
+
+ {/* Button to cancel the removal action */}
+
+ {tCommon('no')}
+
+ {/* Button to confirm the removal action */}
+
+ {tCommon('yes')}
+
+
+
+
+ );
+}
+export {};
+export default orgPeopleListCard;
diff --git a/src/components/OrgPostCard/OrgPostCard.module.css b/src/components/OrgPostCard/OrgPostCard.module.css
new file mode 100644
index 0000000000..c7ff8073d2
--- /dev/null
+++ b/src/components/OrgPostCard/OrgPostCard.module.css
@@ -0,0 +1,278 @@
+.cards h2 {
+ font-size: 20px;
+}
+.cards > h3 {
+ font-size: 17px;
+}
+.card {
+ width: 100%;
+ height: 20rem;
+ margin-bottom: 2rem;
+}
+.postimage {
+ border-radius: 0px;
+ width: 100%;
+ height: 12rem;
+ max-width: 100%;
+ max-height: 12rem;
+ object-fit: cover;
+ position: relative;
+ color: black;
+}
+.preview {
+ display: flex;
+ position: relative;
+ width: 100%;
+ margin-top: 10px;
+ justify-content: center;
+}
+.preview img {
+ width: 400px;
+ height: auto;
+}
+.preview video {
+ width: 400px;
+ height: auto;
+}
+.nopostimage {
+ border-radius: 0px;
+ width: 100%;
+ height: 12rem;
+ max-height: 12rem;
+ object-fit: cover;
+ position: relative;
+}
+.cards:hover {
+ filter: brightness(0.8);
+}
+.cards:hover::before {
+ opacity: 0.5;
+}
+.knowMoreText {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ opacity: 0;
+ color: white;
+ padding: 10px;
+ font-weight: bold;
+ font-size: 1.5rem;
+ transition: opacity 0.3s ease-in-out;
+}
+
+.cards:hover .knowMoreText {
+ opacity: 1;
+}
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(
+ 0,
+ 0,
+ 0,
+ 0.9
+ ); /* Dark grey modal background with transparency */
+ z-index: 9999;
+}
+
+.modalContent {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #fff;
+ padding: 20px;
+ max-width: 800px;
+ max-height: 600px;
+ overflow: auto;
+}
+
+.modalImage {
+ flex: 1;
+ margin-right: 20px;
+ width: 25rem;
+ height: 15rem;
+}
+.nomodalImage {
+ flex: 1;
+ margin-right: 20px;
+ width: 100%;
+ height: 15rem;
+}
+
+.modalImage img,
+.modalImage video {
+ border-radius: 0px;
+ width: 100%;
+ height: 25rem;
+ max-width: 25rem;
+ max-height: 15rem;
+ object-fit: cover;
+ position: relative;
+}
+.modalInfo {
+ flex: 1;
+}
+.title {
+ font-size: 16px;
+ color: #000;
+ font-weight: 600;
+}
+.text {
+ font-size: 13px;
+ color: #000;
+ font-weight: 300;
+}
+.author {
+ color: #737373;
+ font-weight: 100;
+ font-size: 13px;
+}
+.closeButton {
+ position: relative;
+ bottom: 5rem;
+ right: 10px;
+ padding: 4px;
+ background-color: red; /* Red close button color */
+ color: #fff;
+ border: none;
+ cursor: pointer;
+}
+.closeButtonP {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ background: transparent;
+ transform: scale(1.2);
+ cursor: pointer;
+ border: none;
+ color: #707070;
+ font-weight: 600;
+ font-size: 16px;
+ cursor: pointer;
+}
+.cards:hover::after {
+ opacity: 1;
+ mix-blend-mode: normal;
+}
+.cards > p {
+ font-size: 14px;
+ margin-top: 0px;
+ margin-bottom: 7px;
+}
+.cards a {
+ color: #737373;
+ font-weight: 600;
+}
+.cards a:hover {
+ color: black;
+}
+.infodiv {
+ margin-bottom: 7px;
+ width: 15rem;
+ text-align: justify;
+ word-wrap: break-word;
+}
+.infodiv > p {
+ margin: 0;
+}
+.dispflex {
+ display: flex;
+ justify-content: space-between;
+}
+.iconContainer {
+ display: flex;
+}
+.icon {
+ transform: scale(0.75);
+ cursor: pointer;
+}
+/* .cards {
+ width: 75%;
+ background: #fcfcfc;
+ margin: 10px 40px;
+ padding: 20px 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 3px 5px #c9c9c9;
+ margin-right: 30px;
+ color: #737373;
+ box-sizing: border-box;
+} */
+.cards:last-child:nth-last-child(odd) {
+ grid-column: auto / span 2;
+}
+.cards:first-child:nth-last-child(even),
+.cards:first-child:nth-last-child(even) ~ .box {
+ grid-column: auto / span 1;
+}
+.toggleClickBtn {
+ color: #31bb6b;
+ cursor: pointer;
+ border: none;
+ font-size: 12px;
+ background-color: white;
+}
+.toggleClickBtnNone {
+ display: none;
+}
+/* Menu Modal Styles */
+.menuModal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(0, 0, 0, 0.7); /* Dark grey modal background */
+ z-index: 9999;
+}
+
+.menuContent {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #fff;
+ padding-top: 20px;
+ max-width: 700px;
+ max-height: 500px;
+ overflow: hidden;
+ position: relative;
+}
+
+.menuOptions {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+}
+
+.menuOptions li {
+ padding: 10px;
+ border-bottom: 1px solid #ccc;
+ padding-left: 100px;
+ padding-right: 100px;
+ cursor: pointer;
+}
+
+.moreOptionsButton {
+ position: relative;
+ bottom: 5rem;
+ right: 10px;
+ padding: 2px;
+ background-color: transparent;
+ color: #000;
+ border: none;
+ cursor: pointer;
+}
+.list {
+ color: red;
+ cursor: pointer;
+}
diff --git a/src/components/OrgPostCard/OrgPostCard.test.tsx b/src/components/OrgPostCard/OrgPostCard.test.tsx
new file mode 100644
index 0000000000..7105e5e8f2
--- /dev/null
+++ b/src/components/OrgPostCard/OrgPostCard.test.tsx
@@ -0,0 +1,788 @@
+import React from 'react';
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import OrgPostCard from './OrgPostCard';
+import { I18nextProvider } from 'react-i18next';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import {
+ DELETE_POST_MUTATION,
+ UPDATE_POST_MUTATION,
+ TOGGLE_PINNED_POST,
+} from 'GraphQl/Mutations/mutations';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import convertToBase64 from 'utils/convertToBase64';
+import { BrowserRouter } from 'react-router-dom';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+const MOCKS = [
+ {
+ request: {
+ query: DELETE_POST_MUTATION,
+ variables: { id: '12' },
+ },
+ result: {
+ data: {
+ removePost: {
+ _id: '12',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_POST_MUTATION,
+ variables: {
+ id: '12',
+ title: 'updated title',
+ text: 'This is a updated text',
+ },
+ },
+ result: {
+ data: {
+ updatePost: {
+ _id: '12',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: TOGGLE_PINNED_POST,
+ variables: {
+ id: '12',
+ },
+ },
+ result: {
+ data: {
+ togglePostPin: {
+ _id: '12',
+ },
+ },
+ },
+ },
+];
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+jest.mock('i18next-browser-languagedetector', () => ({
+ init: jest.fn(),
+ type: 'languageDetector',
+ detect: jest.fn(() => 'en'),
+ cacheUserLanguage: jest.fn(),
+}));
+const link = new StaticMockLink(MOCKS, true);
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+describe('Testing Organization Post Card', () => {
+ const originalLocation = window.location;
+
+ beforeAll(() => {
+ Object.defineProperty(window, 'location', {
+ configurable: true,
+ value: {
+ reload: jest.fn(),
+ },
+ });
+ });
+
+ afterAll(() => {
+ Object.defineProperty(window, 'location', {
+ configurable: true,
+ value: originalLocation,
+ });
+ });
+
+ const props = {
+ id: '12',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo: 'Time change',
+ postAuthor: 'John Doe',
+ postPhoto: 'test.png',
+ postVideo: 'test.mp4',
+ pinned: false,
+ };
+
+ jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+ }));
+ jest.mock('react', () => ({
+ ...jest.requireActual('react'),
+ useRef: jest.fn(),
+ }));
+ global.alert = jest.fn();
+
+ test('Opens post on image click', () => {
+ const { getByTestId, getByAltText } = render(
+
+
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+
+ expect(getByTestId('card-text')).toBeInTheDocument();
+ expect(getByTestId('card-title')).toBeInTheDocument();
+ expect(getByAltText('image')).toBeInTheDocument();
+ });
+ test('renders with default props', () => {
+ const { getByAltText, getByTestId } = render(
+
+
+
+
+ ,
+ );
+ expect(getByTestId('card-text')).toBeInTheDocument();
+ expect(getByTestId('card-title')).toBeInTheDocument();
+ expect(getByAltText('image')).toBeInTheDocument();
+ });
+ test('toggles "Read more" button', () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+ const toggleButton = getByTestId('toggleBtn');
+ fireEvent.click(toggleButton);
+ expect(toggleButton).toHaveTextContent('hide');
+ fireEvent.click(toggleButton);
+ expect(toggleButton).toHaveTextContent('Read more');
+ });
+ test('opens and closes edit modal', async () => {
+ setItem('id', '123');
+
+ render(
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ const createOrgBtn = screen.getByTestId('modalOrganizationHeader');
+ expect(createOrgBtn).toBeInTheDocument();
+ userEvent.click(createOrgBtn);
+ userEvent.click(screen.getByTestId('closeOrganizationModal'));
+ });
+ test('Should render text elements when props value is not passed', async () => {
+ global.confirm = (): boolean => false;
+ render(
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByAltText('image'));
+ expect(screen.getByAltText('Post Image')).toBeInTheDocument();
+ });
+ test('Testing post updating after post is updated', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+ fireEvent.change(getByTestId('updateTitle'), {
+ target: { value: 'updated title' },
+ });
+ fireEvent.change(getByTestId('updateText'), {
+ target: { value: 'This is a updated text' },
+ });
+ const postVideoUrlInput = screen.queryByTestId('postVideoUrl');
+ if (postVideoUrlInput) {
+ fireEvent.change(getByTestId('postVideoUrl'), {
+ target: { value: 'This is a updated video' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/video/i));
+ const input = getByTestId('postVideoUrl');
+ const file = new File(['test-video'], 'test.mp4', { type: 'video/mp4' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+ await waitFor(() => {
+ convertToBase64(file);
+ });
+
+ userEvent.click(screen.getByTestId('closePreview'));
+ }
+ const imageUrlInput = screen.queryByTestId('postImageUrl');
+ if (imageUrlInput) {
+ fireEvent.change(getByTestId('postImageUrl'), {
+ target: { value: 'This is a updated image' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/image/i));
+ const input = getByTestId('postImageUrl');
+ const file = new File(['test-image'], 'test.jpg', { type: 'image/jpeg' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+
+ // Simulate the asynchronous base64 conversion function
+ await waitFor(() => {
+ convertToBase64(file); // Replace with the expected base64-encoded image
+ });
+ document.getElementById = jest.fn(() => input);
+ const clearImageButton = getByTestId('closeimage');
+ fireEvent.click(clearImageButton);
+ }
+ userEvent.click(screen.getByTestId('updatePostBtn'));
+
+ await waitFor(
+ () => {
+ expect(window.location.reload).toHaveBeenCalled();
+ },
+ { timeout: 2500 },
+ );
+ });
+ test('Testing post updating functionality fail case', async () => {
+ const props2 = {
+ id: '',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo: 'Time change',
+ postAuthor: 'John Doe',
+ postPhoto: 'test.png',
+ postVideo: 'test.mp4',
+ pinned: true,
+ };
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+ fireEvent.change(getByTestId('updateTitle'), {
+ target: { value: 'updated title' },
+ });
+ fireEvent.change(getByTestId('updateText'), {
+ target: { value: 'This is a updated text' },
+ });
+ const postVideoUrlInput = screen.queryByTestId('postVideoUrl');
+ if (postVideoUrlInput) {
+ fireEvent.change(getByTestId('postVideoUrl'), {
+ target: { value: 'This is a updated video' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/video/i));
+ const input = getByTestId('postVideoUrl');
+ const file = new File(['test-video'], 'test.mp4', { type: 'video/mp4' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+ await waitFor(() => {
+ convertToBase64(file);
+ });
+
+ userEvent.click(screen.getByTestId('closePreview'));
+ }
+ const imageUrlInput = screen.queryByTestId('postImageUrl');
+ if (imageUrlInput) {
+ fireEvent.change(getByTestId('postImageUrl'), {
+ target: { value: 'This is a updated image' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/image/i));
+ const input = getByTestId('postImageUrl');
+ const file = new File(['test-image'], 'test.jpg', { type: 'image/jpeg' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+
+ // Simulate the asynchronous base64 conversion function
+ await waitFor(() => {
+ convertToBase64(file); // Replace with the expected base64-encoded image
+ });
+ document.getElementById = jest.fn(() => input);
+ const clearImageButton = getByTestId('closeimage');
+ fireEvent.click(clearImageButton);
+ }
+ userEvent.click(screen.getByTestId('updatePostBtn'));
+
+ await waitFor(
+ () => {
+ expect(window.location.reload).toHaveBeenCalled();
+ },
+ { timeout: 2500 },
+ );
+ });
+ test('Testing pin post functionality', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('pinpostBtn'));
+
+ await waitFor(
+ () => {
+ expect(window.location.reload).toHaveBeenCalled();
+ },
+ { timeout: 3000 },
+ );
+ });
+ test('Testing pin post functionality fail case', async () => {
+ const props2 = {
+ id: '',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo: 'Time change',
+ postAuthor: 'John Doe',
+ postPhoto: 'test.png',
+ postVideo: 'test.mp4',
+ pinned: true,
+ };
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('pinpostBtn'));
+ });
+ test('Testing post delete functionality', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('deletePostModalBtn'));
+ fireEvent.click(screen.getByTestId('deletePostBtn'));
+
+ await waitFor(
+ () => {
+ expect(window.location.reload).toHaveBeenCalled();
+ },
+ { timeout: 3000 },
+ );
+ });
+ test('Testing post delete functionality fail case', async () => {
+ const props2 = {
+ id: '',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo: 'Time change',
+ postAuthor: 'John Doe',
+ postPhoto: 'test.png',
+ postVideo: 'test.mp4',
+ pinned: true,
+ };
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('deletePostModalBtn'));
+ fireEvent.click(screen.getByTestId('deletePostBtn'));
+ });
+ test('Testing close functionality of primary modal', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('closeiconbtn'));
+
+ //Primary Modal is closed
+ expect(screen.queryByTestId('moreiconbtn')).not.toBeInTheDocument();
+ });
+ test('Testing close functionality of secondary modal', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('closebtn'));
+
+ //Secondary Modal is closed
+ expect(screen.queryByTestId('deletePostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('editPostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
+ });
+ test('renders without "Read more" button when postInfo length is less than or equal to 43', () => {
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.queryByTestId('toggleBtn')).not.toBeInTheDocument();
+ });
+ test('renders with "Read more" button when postInfo length is more than 43', () => {
+ const props2 = {
+ id: '12',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo:
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum.', // Length is greater than 43
+ postAuthor: 'John Doe',
+ postPhoto: 'photoLink',
+ postVideo: 'videoLink',
+ pinned: false,
+ };
+ render(
+
+
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+
+ expect(screen.getByTestId('toggleBtn')).toBeInTheDocument();
+ });
+ test('updates state variables correctly when handleEditModal is called', () => {
+ const link2 = new StaticMockLink(MOCKS, true);
+ render(
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ expect(screen.queryByTestId('editPostModalBtn')).toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ //Primary Modal is closed
+ expect(screen.queryByTestId('closeiconbtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('moreiconbtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('toggleBtn')).not.toBeInTheDocument();
+
+ //Secondary Modal is closed
+ expect(screen.queryByTestId('deletePostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('editPostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
+ });
+ test('updates state variables correctly when handleDeleteModal is called', () => {
+ const link2 = new StaticMockLink(MOCKS, true);
+ render(
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ expect(screen.queryByTestId('deletePostModalBtn')).toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('deletePostModalBtn'));
+
+ //Primary Modal is closed
+ expect(screen.queryByTestId('closeiconbtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('moreiconbtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('toggleBtn')).not.toBeInTheDocument();
+
+ //Secondary Modal is closed
+ expect(screen.queryByTestId('deletePostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('editPostModalBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
+ });
+ test('clears postvideo state and resets file input value', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ const postVideoUrlInput = screen.queryByTestId('postVideoUrl');
+
+ if (postVideoUrlInput) {
+ fireEvent.change(getByTestId('postVideoUrl'), {
+ target: { value: '' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/video/i));
+ const input = getByTestId('postVideoUrl');
+ const file = new File(['test-video'], 'test.mp4', { type: 'video/mp4' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+ await waitFor(() => {
+ convertToBase64(file);
+ });
+
+ userEvent.click(screen.getByTestId('closePreview'));
+ }
+ });
+ test('clears postimage state and resets file input value', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ const imageUrlInput = screen.queryByTestId('postImageUrl');
+
+ if (imageUrlInput) {
+ fireEvent.change(getByTestId('postImageUrl'), {
+ target: { value: '' },
+ });
+ userEvent.click(screen.getByPlaceholderText(/image/i));
+ const input = getByTestId('postImageUrl');
+ const file = new File(['test-image'], 'test.jpg', { type: 'image/jpeg' });
+ Object.defineProperty(input, 'files', {
+ value: [file],
+ });
+ fireEvent.change(input);
+
+ // Simulate the asynchronous base64 conversion function
+ await waitFor(() => {
+ convertToBase64(file); // Replace with the expected base64-encoded image
+ });
+ document.getElementById = jest.fn(() => input);
+ const clearImageButton = getByTestId('closeimage');
+ fireEvent.click(clearImageButton);
+ }
+ });
+ test('clears postitle state and resets file input value', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ fireEvent.change(getByTestId('updateTitle'), {
+ target: { value: '' },
+ });
+
+ userEvent.click(screen.getByTestId('updatePostBtn')); // Should not update post
+
+ expect(screen.getByTestId('updateTitle')).toHaveValue('');
+ expect(screen.getByTestId('closeOrganizationModal')).toBeInTheDocument();
+ expect(screen.getByTestId('updatePostBtn')).toBeInTheDocument();
+ });
+ test('clears postinfo state and resets file input value', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByAltText('image'));
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+
+ fireEvent.change(getByTestId('updateText'), {
+ target: { value: '' },
+ });
+
+ userEvent.click(screen.getByTestId('updatePostBtn')); // Should not update post
+
+ expect(screen.getByTestId('updateText')).toHaveValue('');
+ expect(screen.getByTestId('closeOrganizationModal')).toBeInTheDocument();
+ expect(screen.getByTestId('updatePostBtn')).toBeInTheDocument();
+ });
+ test('Testing create organization modal', async () => {
+ setItem('id', '123');
+
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByAltText('image'));
+
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+
+ userEvent.click(screen.getByTestId('editPostModalBtn'));
+ const createOrgBtn = screen.getByTestId('modalOrganizationHeader');
+ expect(createOrgBtn).toBeInTheDocument();
+ userEvent.click(createOrgBtn);
+ userEvent.click(screen.getByTestId('closeOrganizationModal'));
+ });
+ test('should toggle post pin when pin button is clicked', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+ userEvent.click(screen.getByAltText('image'));
+
+ userEvent.click(screen.getByTestId('moreiconbtn'));
+ const pinButton = getByTestId('pinpostBtn');
+ fireEvent.click(pinButton);
+ await waitFor(() => {
+ expect(MOCKS[2].request.variables).toEqual({
+ id: '12',
+ });
+ });
+ });
+ test('testing video play and pause on mouse enter and leave events', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ const card = getByTestId('cardVid');
+
+ HTMLVideoElement.prototype.play = jest.fn();
+ HTMLVideoElement.prototype.pause = jest.fn();
+
+ fireEvent.mouseEnter(card);
+ expect(HTMLVideoElement.prototype.play).toHaveBeenCalled();
+
+ fireEvent.mouseLeave(card);
+ expect(HTMLVideoElement.prototype.pause).toHaveBeenCalled();
+ });
+ test('for rendering when no image and no video is available', async () => {
+ const props2 = {
+ id: '',
+ postID: '123',
+ postTitle: 'Event Info',
+ postInfo: 'Time change',
+ postAuthor: 'John Doe',
+ postPhoto: '',
+ postVideo: '',
+ pinned: true,
+ };
+
+ const { getByAltText } = render(
+
+
+
+
+ ,
+ );
+
+ expect(getByAltText('image not found')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrgPostCard/OrgPostCard.tsx b/src/components/OrgPostCard/OrgPostCard.tsx
new file mode 100644
index 0000000000..b7cc419d12
--- /dev/null
+++ b/src/components/OrgPostCard/OrgPostCard.tsx
@@ -0,0 +1,611 @@
+import { useMutation } from '@apollo/client';
+import { Close, MoreVert, PushPin } from '@mui/icons-material';
+import {
+ DELETE_POST_MUTATION,
+ TOGGLE_PINNED_POST,
+ UPDATE_POST_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import AboutImg from 'assets/images/defaultImg.png';
+import type { ChangeEvent } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
+import { Form, Button, Card, Modal } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import convertToBase64 from 'utils/convertToBase64';
+import { errorHandler } from 'utils/errorHandler';
+import type { InterfacePostForm } from 'utils/interfaces';
+import styles from './OrgPostCard.module.css';
+interface InterfaceOrgPostCardProps {
+ postID: string;
+ id: string;
+ postTitle: string;
+ postInfo: string;
+ postAuthor: string;
+ postPhoto: string | null;
+ postVideo: string | null;
+ pinned: boolean;
+}
+export default function OrgPostCard(
+ props: InterfaceOrgPostCardProps,
+): JSX.Element {
+ const {
+ postID, // Destructure the key prop from props
+ // ...rest // Spread the rest of the props
+ } = props;
+ const [postformState, setPostFormState] = useState({
+ posttitle: '',
+ postinfo: '',
+ postphoto: '',
+ postvideo: '',
+ pinned: false,
+ });
+ const [postPhotoUpdated, setPostPhotoUpdated] = useState(false);
+ const [postVideoUpdated, setPostVideoUpdated] = useState(false);
+ const [togglePost, setPostToggle] = useState('Read more');
+ const [showEditModal, setShowEditModal] = useState(false);
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [menuVisible, setMenuVisible] = useState(false);
+ const [playing, setPlaying] = useState(false);
+ const videoRef = useRef(null);
+ const [toggle] = useMutation(TOGGLE_PINNED_POST);
+ const togglePostPin = async (id: string, pinned: boolean): Promise => {
+ try {
+ const { data } = await toggle({
+ variables: {
+ id,
+ },
+ });
+ if (data) {
+ setModalVisible(false);
+ setMenuVisible(false);
+ toast.success(`${pinned ? 'Post unpinned' : 'Post pinned'}`);
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ }
+ };
+ const toggleShowEditModal = (): void => {
+ const { postTitle, postInfo, postPhoto, postVideo, pinned } = props;
+ setPostFormState({
+ posttitle: postTitle,
+ postinfo: postInfo,
+ postphoto: postPhoto,
+ postvideo: postVideo,
+ pinned: pinned,
+ });
+ setPostPhotoUpdated(false);
+ setPostVideoUpdated(false);
+ setShowEditModal((prev) => !prev);
+ };
+ const toggleShowDeleteModal = (): void => setShowDeleteModal((prev) => !prev);
+ const handleVideoPlay = (): void => {
+ setPlaying(true);
+ videoRef.current?.play();
+ };
+ const handleVideoPause = (): void => {
+ setPlaying(false);
+ videoRef.current?.pause();
+ };
+ const handleCardClick = (): void => {
+ setModalVisible(true);
+ };
+ const handleMoreOptionsClick = (): void => {
+ setMenuVisible(true);
+ };
+ const clearImageInput = (): void => {
+ setPostFormState({
+ ...postformState,
+ postphoto: '',
+ });
+ setPostPhotoUpdated(true);
+ const fileInput = document.getElementById(
+ 'postImageUrl',
+ ) as HTMLInputElement;
+ if (fileInput) {
+ fileInput.value = '';
+ }
+ };
+ const clearVideoInput = (): void => {
+ setPostFormState({
+ ...postformState,
+ postvideo: '',
+ });
+ setPostVideoUpdated(true);
+ const fileInput = document.getElementById(
+ 'postVideoUrl',
+ ) as HTMLInputElement;
+ if (fileInput) {
+ fileInput.value = '';
+ }
+ };
+ function handletoggleClick(): void {
+ if (togglePost === 'Read more') {
+ setPostToggle('hide');
+ } else {
+ setPostToggle('Read more');
+ }
+ }
+ function handleEditModal(): void {
+ const { postPhoto, postVideo } = props;
+ setModalVisible(false);
+ setMenuVisible(false);
+ setShowEditModal(true);
+ setPostFormState({
+ ...postformState,
+ postphoto: postPhoto,
+ postvideo: postVideo,
+ });
+ }
+ function handleDeleteModal(): void {
+ setModalVisible(false);
+ setMenuVisible(false);
+ setShowDeleteModal(true);
+ }
+ useEffect(() => {
+ setPostFormState({
+ posttitle: props.postTitle,
+ postinfo: props.postInfo,
+ postphoto: props.postPhoto,
+ postvideo: props.postVideo,
+ pinned: props.pinned,
+ });
+ }, []);
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgPostCard',
+ });
+ const { t: tCommon } = useTranslation('common');
+ const [deletePostMutation] = useMutation(DELETE_POST_MUTATION);
+ const [updatePostMutation] = useMutation(UPDATE_POST_MUTATION);
+ const deletePost = async (): Promise => {
+ try {
+ const { data } = await deletePostMutation({
+ variables: {
+ id: props.id,
+ },
+ });
+ if (data) {
+ toast.success(t('postDeleted') as string);
+ toggleShowDeleteModal();
+ setTimeout(() => {
+ window.location.reload();
+ });
+ }
+ } catch (error: unknown) {
+ errorHandler(t, error);
+ }
+ };
+ const handleInputEvent = (
+ e: ChangeEvent,
+ ): void => {
+ const { name, value } = e.target;
+ setPostFormState((prevPostFormState) => ({
+ ...prevPostFormState,
+ [name]: value,
+ }));
+ };
+ const updatePostHandler = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ const { data } = await updatePostMutation({
+ variables: {
+ id: props.id,
+ title: postformState.posttitle,
+ text: postformState.postinfo,
+ ...(postPhotoUpdated && {
+ imageUrl: postformState.postphoto,
+ }),
+ ...(postVideoUpdated && {
+ videoUrl: postformState.postvideo,
+ }),
+ },
+ });
+ if (data) {
+ toast.success(t('postUpdated') as string);
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+ return (
+ <>
+
+
+ {props.postVideo && (
+
+
+
+
+
+ {props.pinned && (
+
+ )}
+
+ {props.postTitle}
+
+
+ {props.postInfo}
+
+
+ {props.postAuthor}
+
+
+
+ )}
+ {props.postPhoto ? (
+
+
+
+ {props.pinned && (
+
+ )}
+
+ {props.postTitle}
+
+ {props.postInfo}
+ {props.postAuthor}
+
+
+ ) : !props.postVideo ? (
+
+
+
+
+ {props.pinned && (
+
+ )}
+
+ {props.postTitle}
+
+
+ {props.postInfo && props.postInfo.length > 20
+ ? props.postInfo.substring(0, 20) + '...'
+ : props.postInfo}
+ {' '}
+
+ {props.postAuthor}
+
+
+
+
+ ) : (
+ ''
+ )}
+
+ {modalVisible && (
+
+
+ {props.postPhoto && (
+
+
+
+ )}
+ {props.postVideo && (
+
+
+
+
+
+ )}
+ {!props.postPhoto && !props.postVideo && (
+
+ {' '}
+
+
+ )}
+
+
+ {t('author')}: {props.postAuthor}
+
+
+ {togglePost === 'Read more' ? (
+
+ {props.postInfo.length > 43
+ ? props.postInfo.substring(0, 40) + '...'
+ : props.postInfo}
+
+ ) : (
+
{props.postInfo}
+ )}
+
43
+ ? styles.toggleClickBtn
+ : styles.toggleClickBtnNone
+ }`}
+ onClick={handletoggleClick}
+ >
+ {togglePost}
+
+
+
+
+
+
+
setModalVisible(false)}
+ data-testid="closeiconbtn"
+ >
+
+
+
+
+ )}
+ {menuVisible && (
+
+
+
+
+ {tCommon('edit')}
+
+
+ {t('deletePost')}
+
+ =>
+ togglePostPin(props.id, props.pinned)
+ }
+ >
+ {!props.pinned ? 'Pin post' : 'Unpin post'}
+
+ setMenuVisible(false)}
+ data-testid="closebtn"
+ >
+ {tCommon('close')}
+
+
+
+
+ )}
+
+
+
+ {t('deletePost')}
+
+
+
+
+ {t('deletePostMsg')}
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+
+
+ {t('editPost')}
+
+
+
+ >
+ );
+}
diff --git a/src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx b/src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx
new file mode 100644
index 0000000000..39d4884e8b
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx
@@ -0,0 +1,208 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18n from 'utils/i18nForTest';
+import type { ApolloLink } from '@apollo/client';
+import { MOCKS, MOCKS_ERROR } from './OrgActionItemCategoryMocks';
+import type { InterfaceActionItemCategoryModal } from './CategoryModal';
+import CategoryModal from './CategoryModal';
+import { toast } from 'react-toastify';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const link1 = new StaticMockLink(MOCKS);
+const link3 = new StaticMockLink(MOCKS_ERROR);
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.orgActionItemCategories ?? {},
+ ),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const categoryProps: InterfaceActionItemCategoryModal[] = [
+ {
+ isOpen: true,
+ hide: jest.fn(),
+ refetchCategories: jest.fn(),
+ orgId: 'orgId',
+ mode: 'create',
+ category: {
+ _id: 'categoryId',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2044-01-01',
+ creator: { _id: 'userId', firstName: 'John', lastName: 'Doe' },
+ },
+ },
+ {
+ isOpen: true,
+ hide: jest.fn(),
+ refetchCategories: jest.fn(),
+ orgId: 'orgId',
+ mode: 'edit',
+ category: {
+ _id: 'categoryId',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2044-01-01',
+ creator: { _id: 'userId', firstName: 'John', lastName: 'Doe' },
+ },
+ },
+];
+
+const renderCategoryModal = (
+ link: ApolloLink,
+ props: InterfaceActionItemCategoryModal,
+): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+const fillFormAndSubmit = async (
+ name: string,
+ isDisabled: boolean,
+): Promise => {
+ const nameInput = screen.getByLabelText('Name *');
+ const isDisabledSwitch = screen.getByTestId('isDisabledSwitch');
+ const submitBtn = screen.getByTestId('formSubmitButton');
+
+ fireEvent.change(nameInput, { target: { value: name } });
+ if (isDisabled) {
+ userEvent.click(isDisabledSwitch);
+ }
+ userEvent.click(submitBtn);
+};
+
+describe('Testing Action Item Category Modal', () => {
+ it('should populate form fields with correct values in edit mode', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ await waitFor(() =>
+ expect(
+ screen.getByText(translations.categoryDetails),
+ ).toBeInTheDocument(),
+ );
+
+ expect(screen.getByLabelText('Name *')).toHaveValue('Category 1');
+ expect(screen.getByTestId('isDisabledSwitch')).not.toBeChecked();
+ });
+
+ it('should update name when input value changes', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ const nameInput = screen.getByLabelText('Name *');
+ expect(nameInput).toHaveValue('Category 1');
+ fireEvent.change(nameInput, { target: { value: 'Category 2' } });
+ expect(nameInput).toHaveValue('Category 2');
+ });
+
+ it('should update isDisabled when switch is toggled', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ const isDisabledSwitch = screen.getByTestId('isDisabledSwitch');
+ expect(isDisabledSwitch).not.toBeChecked();
+ userEvent.click(isDisabledSwitch);
+ expect(isDisabledSwitch).toBeChecked();
+ });
+
+ it('should edit category', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ await fillFormAndSubmit('Category 2', true);
+
+ await waitFor(() => {
+ expect(categoryProps[1].refetchCategories).toHaveBeenCalled();
+ expect(categoryProps[1].hide).toHaveBeenCalled();
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfulUpdation,
+ );
+ });
+ });
+
+ it('Edit only Name', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ await fillFormAndSubmit('Category 2', false);
+
+ await waitFor(() => {
+ expect(categoryProps[1].refetchCategories).toHaveBeenCalled();
+ expect(categoryProps[1].hide).toHaveBeenCalled();
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfulUpdation,
+ );
+ });
+ });
+
+ it('Edit only isDisabled', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ await fillFormAndSubmit('Category 1', true);
+
+ await waitFor(() => {
+ expect(categoryProps[1].refetchCategories).toHaveBeenCalled();
+ expect(categoryProps[1].hide).toHaveBeenCalled();
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfulUpdation,
+ );
+ });
+ });
+
+ it('Error in updating category', async () => {
+ renderCategoryModal(link3, categoryProps[1]);
+ await fillFormAndSubmit('Category 2', true);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('Mock Graphql Error');
+ });
+ });
+
+ it('should create category', async () => {
+ renderCategoryModal(link1, categoryProps[0]);
+ await fillFormAndSubmit('Category 2', true);
+
+ await waitFor(() => {
+ expect(categoryProps[0].refetchCategories).toHaveBeenCalled();
+ expect(categoryProps[0].hide).toHaveBeenCalled();
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfulCreation,
+ );
+ });
+ });
+
+ it('Error in creating category', async () => {
+ renderCategoryModal(link3, categoryProps[0]);
+ await fillFormAndSubmit('Category 2', true);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('Mock Graphql Error');
+ });
+ });
+
+ it('Try to edit without changing any field', async () => {
+ renderCategoryModal(link1, categoryProps[1]);
+ const submitBtn = screen.getByTestId('formSubmitButton');
+ userEvent.click(submitBtn);
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(translations.sameNameConflict);
+ });
+ });
+});
diff --git a/src/components/OrgSettings/ActionItemCategories/CategoryModal.tsx b/src/components/OrgSettings/ActionItemCategories/CategoryModal.tsx
new file mode 100644
index 0000000000..43018db0ab
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/CategoryModal.tsx
@@ -0,0 +1,208 @@
+import React, { type ChangeEvent, type FC, useEffect, useState } from 'react';
+import { Button, Form, Modal } from 'react-bootstrap';
+import styles from './OrgActionItemCategories.module.css';
+import { useTranslation } from 'react-i18next';
+import type { InterfaceActionItemCategoryInfo } from 'utils/interfaces';
+import { useMutation } from '@apollo/client';
+import {
+ CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+} from 'GraphQl/Mutations/ActionItemCategoryMutations';
+import { toast } from 'react-toastify';
+import { FormControl, TextField } from '@mui/material';
+
+/**
+ * Props for the `CategoryModal` component.
+ *
+ *
+ * isOpen - The state of the modal.
+ * hide - The function to hide the modal.
+ * refetchCategories - The function to refetch the categories.
+ * orgId - The organization ID.
+ * category - The category to be edited.
+ * mode - The mode of the modal.
+ * @returns The `CategoryModal` component.
+ */
+export interface InterfaceActionItemCategoryModal {
+ isOpen: boolean;
+ hide: () => void;
+ refetchCategories: () => void;
+ orgId: string;
+ category: InterfaceActionItemCategoryInfo | null;
+ mode: 'create' | 'edit';
+}
+
+/**
+ * A modal component for creating and editing action item categories.
+ *
+ * @param props - The properties passed to the component.
+ * @returns The `CategoryModal` component.
+ */
+const CategoryModal: FC = ({
+ category,
+ hide,
+ isOpen,
+ mode,
+ refetchCategories,
+ orgId,
+}) => {
+ const { t: tCommon } = useTranslation('common');
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgActionItemCategories',
+ });
+
+ const [formState, setFormState] = useState({
+ name: category?.name ?? '',
+ isDisabled: category?.isDisabled ?? false,
+ });
+
+ const { name, isDisabled } = formState;
+
+ useEffect(() => {
+ setFormState({
+ name: category?.name ?? '',
+ isDisabled: category?.isDisabled ?? false,
+ });
+ }, [category]);
+
+ // Mutations for creating and updating categories
+ const [createActionItemCategory] = useMutation(
+ CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ );
+
+ const [updateActionItemCategory] = useMutation(
+ UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+ );
+
+ /**
+ * Handles category creation.
+ *
+ * @param e - The form submission event.
+ */
+ const handleCreate = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ await createActionItemCategory({
+ variables: {
+ name,
+ isDisabled,
+ organizationId: orgId,
+ },
+ });
+
+ refetchCategories();
+ hide();
+ toast.success(t('successfulCreation'));
+ } catch (error: unknown) {
+ toast.error((error as Error).message);
+ }
+ };
+
+ /**
+ * Handles category update.
+ *
+ * @param e - The form submission event.
+ */
+ const handleEdit = async (e: ChangeEvent): Promise => {
+ e.preventDefault();
+ if (name === category?.name && isDisabled === category?.isDisabled) {
+ toast.error(t('sameNameConflict')); // Show error if the name is the same
+ } else {
+ try {
+ const updatedFields: { [key: string]: string | boolean } = {};
+ if (name != category?.name) {
+ updatedFields.name = name;
+ }
+ if (isDisabled != category?.isDisabled) {
+ updatedFields.isDisabled = isDisabled;
+ }
+
+ await updateActionItemCategory({
+ variables: {
+ actionItemCategoryId: category?._id,
+ ...updatedFields,
+ },
+ });
+
+ setFormState({
+ name: '',
+ isDisabled: false,
+ });
+ refetchCategories();
+ hide();
+ toast.success(t('successfulUpdation'));
+ } catch (error: unknown) {
+ toast.error((error as Error).message);
+ }
+ }
+ };
+
+ return (
+
+
+ {t('categoryDetails')}
+
+
+
+
+
+
+ {tCommon('disabled')}
+
+ setFormState({
+ ...formState,
+ isDisabled: !isDisabled,
+ })
+ }
+ />
+
+
+
+ {mode === 'create'
+ ? tCommon('create')
+ : t('updateActionItemCategory')}
+
+
+
+
+ );
+};
+
+export default CategoryModal;
diff --git a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.module.css b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.module.css
new file mode 100644
index 0000000000..919421b0f2
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.module.css
@@ -0,0 +1,138 @@
+/* Button Styles */
+.addButton {
+ /* Position and size of the button */
+ width: 7em;
+ position: absolute;
+ right: 1rem;
+ top: 1rem;
+}
+
+/* Modal Styles */
+.createModal {
+ /* Position and size of the modal */
+ margin-top: 20vh;
+ margin-left: 13vw;
+ max-width: 80vw;
+}
+
+.icon {
+ /* Size and color of the icon */
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.message {
+ /* Centering the content of the modal */
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.titlemodal {
+ /* Styling for the modal title */
+ color: #707070;
+ font-weight: 600;
+ font-size: 32px;
+ width: 65%;
+ margin-bottom: 0px;
+}
+
+.modalCloseBtn {
+ /* Styling for the modal close button */
+ width: 40px;
+ height: 40px;
+ padding: 1rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+/* Input Styles */
+.noOutline input {
+ /* Removing the outline from the input */
+ outline: none;
+}
+
+/* Header and Action Item Categories Styles */
+.btnsContainer {
+ /* Styling for the container of the buttons */
+ display: flex;
+ margin: 0.5rem 0 1.5rem 0;
+}
+
+.btnsContainer .input {
+ /* Styling for the input field */
+ flex: 1;
+ min-width: 18rem;
+ position: relative;
+}
+
+.btnsContainer input {
+ /* Styling for the input border */
+ outline: 1px solid var(--bs-gray-400);
+}
+
+.btnsContainer .input button {
+ /* Styling for the button in the input field */
+ width: 52px;
+}
+
+.inputField {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ background-color: white;
+ box-shadow: 0 1px 1px #31bb6b;
+}
+
+.inputField > button {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+/* Dropdown Styles */
+.dropdown {
+ /* Styling for the dropdown */
+ background-color: white;
+ border: 1px solid #31bb6b;
+ position: relative;
+ display: inline-block;
+ color: #31bb6b;
+}
+
+/* Datagrid Styles */
+.rowBackground {
+ /* Styling for the row background */
+ background-color: var(--bs-white);
+ max-height: 120px;
+}
+
+.tableHeader {
+ /* Styling for the table header */
+ background-color: var(--bs-primary);
+ color: var(--bs-white);
+ font-size: 1rem;
+}
+
+.chipIcon {
+ /* Styling for the chip icon */
+ height: 0.9rem !important;
+}
+
+.chip {
+ /* Styling for the chip */
+ height: 1.5rem !important;
+}
+
+.active {
+ /* Styling for the active state */
+ background-color: #31bb6a50 !important;
+}
+
+.pending {
+ /* Styling for the pending state */
+ background-color: #ffd76950 !important;
+ color: #bb952bd0 !important;
+ border-color: #bb952bd0 !important;
+}
diff --git a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx
new file mode 100644
index 0000000000..d3698bf346
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx
@@ -0,0 +1,241 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18n from 'utils/i18nForTest';
+import type { ApolloLink } from '@apollo/client';
+import { MOCKS, MOCKS_EMPTY, MOCKS_ERROR } from './OrgActionItemCategoryMocks';
+import OrgActionItemCategories from './OrgActionItemCategories';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+ return {
+ DateTimePicker: jest.requireActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ ).DesktopDateTimePicker,
+ };
+});
+
+const link1 = new StaticMockLink(MOCKS);
+const link2 = new StaticMockLink(MOCKS_EMPTY);
+const link3 = new StaticMockLink(MOCKS_ERROR);
+const t = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.orgActionItemCategories ?? {},
+ ),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const renderActionItemCategories = (
+ link: ApolloLink,
+ orgId: string,
+): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Testing Organisation Action Item Categories', () => {
+ it('should render the Action Item Categories Screen', async () => {
+ renderActionItemCategories(link1, 'orgId');
+ await waitFor(() => {
+ expect(screen.getByTestId('searchByName')).toBeInTheDocument();
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.getByText('Category 2')).toBeInTheDocument();
+ });
+ });
+
+ it('Sort the Categories (asc/desc) by createdAt', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const sortBtn = await screen.findByTestId('sort');
+ expect(sortBtn).toBeInTheDocument();
+
+ // Sort by createdAt_DESC
+ fireEvent.click(sortBtn);
+ await waitFor(() => {
+ expect(screen.getByTestId('createdAt_DESC')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('createdAt_DESC'));
+ await waitFor(() => {
+ expect(screen.getAllByTestId('categoryName')[0]).toHaveTextContent(
+ 'Category 1',
+ );
+ });
+
+ // Sort by createdAt_ASC
+ fireEvent.click(sortBtn);
+ await waitFor(() => {
+ expect(screen.getByTestId('createdAt_ASC')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('createdAt_ASC'));
+ await waitFor(() => {
+ expect(screen.getAllByTestId('categoryName')[0]).toHaveTextContent(
+ 'Category 2',
+ );
+ });
+ });
+
+ it('Filter the categories by status (All/Disabled)', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const filterBtn = await screen.findByTestId('filter');
+ expect(filterBtn).toBeInTheDocument();
+
+ // Filter by All
+ fireEvent.click(filterBtn);
+ await waitFor(() => {
+ expect(screen.getByTestId('statusAll')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('statusAll'));
+
+ await waitFor(() => {
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.getByText('Category 2')).toBeInTheDocument();
+ });
+
+ // Filter by Disabled
+ fireEvent.click(filterBtn);
+ await waitFor(() => {
+ expect(screen.getByTestId('statusDisabled')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('statusDisabled'));
+ await waitFor(() => {
+ expect(screen.queryByText('Category 1')).toBeNull();
+ expect(screen.getByText('Category 2')).toBeInTheDocument();
+ });
+ });
+
+ it('Filter the categories by status (Active)', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const filterBtn = await screen.findByTestId('filter');
+ expect(filterBtn).toBeInTheDocument();
+
+ fireEvent.click(filterBtn);
+ await waitFor(() => {
+ expect(screen.getByTestId('statusActive')).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByTestId('statusActive'));
+ await waitFor(() => {
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.queryByText('Category 2')).toBeNull();
+ });
+ });
+
+ it('open and closes Create Category modal', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const addCategoryBtn = await screen.findByTestId(
+ 'createActionItemCategoryBtn',
+ );
+ expect(addCategoryBtn).toBeInTheDocument();
+ userEvent.click(addCategoryBtn);
+
+ await waitFor(() => expect(screen.getAllByText(t.create)).toHaveLength(2));
+ userEvent.click(screen.getByTestId('actionItemCategoryModalCloseBtn'));
+ await waitFor(() =>
+ expect(
+ screen.queryByTestId('actionItemCategoryModalCloseBtn'),
+ ).toBeNull(),
+ );
+ });
+
+ it('open and closes Edit Category modal', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const editCategoryBtn = await screen.findByTestId('editCategoryBtn1');
+ await waitFor(() => expect(editCategoryBtn).toBeInTheDocument());
+ userEvent.click(editCategoryBtn);
+
+ await waitFor(() =>
+ expect(screen.getByText(t.updateActionItemCategory)).toBeInTheDocument(),
+ );
+ userEvent.click(screen.getByTestId('actionItemCategoryModalCloseBtn'));
+ await waitFor(() =>
+ expect(
+ screen.queryByTestId('actionItemCategoryModalCloseBtn'),
+ ).toBeNull(),
+ );
+ });
+
+ it('Search categories by name', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const searchInput = await screen.findByTestId('searchByName');
+ expect(searchInput).toBeInTheDocument();
+
+ userEvent.type(searchInput, 'Category 1');
+ userEvent.click(screen.getByTestId('searchBtn'));
+ await waitFor(() => {
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.queryByText('Category 2')).toBeNull();
+ });
+ });
+
+ it('Search categories by name and clear the input by backspace', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const searchInput = await screen.findByTestId('searchByName');
+ expect(searchInput).toBeInTheDocument();
+
+ // Clear the search input by backspace
+ userEvent.type(searchInput, 'A{backspace}');
+ await waitFor(() => {
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.getByText('Category 2')).toBeInTheDocument();
+ });
+ });
+
+ it('Search categories by name on press of ENTER', async () => {
+ renderActionItemCategories(link1, 'orgId');
+
+ const searchInput = await screen.findByTestId('searchByName');
+ expect(searchInput).toBeInTheDocument();
+
+ userEvent.type(searchInput, 'Category 1');
+ userEvent.type(searchInput, '{enter}');
+ await waitFor(() => {
+ expect(screen.getByText('Category 1')).toBeInTheDocument();
+ expect(screen.queryByText('Category 2')).toBeNull();
+ });
+ });
+
+ it('should render Empty Action Item Categories Screen', async () => {
+ renderActionItemCategories(link2, 'orgId');
+ await waitFor(() => {
+ expect(screen.getByTestId('searchByName')).toBeInTheDocument();
+ expect(screen.getByText(t.noActionItemCategories)).toBeInTheDocument();
+ });
+ });
+
+ it('should render the Action Item Categories Screen with error', async () => {
+ renderActionItemCategories(link3, 'orgId');
+ await waitFor(() => {
+ expect(screen.getByTestId('errorMsg')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.tsx b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.tsx
new file mode 100644
index 0000000000..49cf47dd49
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.tsx
@@ -0,0 +1,418 @@
+import type { FC } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
+import { Button, Dropdown, Form } from 'react-bootstrap';
+import styles from './OrgActionItemCategories.module.css';
+import { useTranslation } from 'react-i18next';
+
+import { useQuery } from '@apollo/client';
+import { ACTION_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/Queries';
+import type { InterfaceActionItemCategoryInfo } from 'utils/interfaces';
+import Loader from 'components/Loader/Loader';
+import {
+ Circle,
+ Search,
+ Sort,
+ WarningAmberRounded,
+ FilterAltOutlined,
+} from '@mui/icons-material';
+import {
+ DataGrid,
+ type GridCellParams,
+ type GridColDef,
+} from '@mui/x-data-grid';
+import dayjs from 'dayjs';
+import { Chip, Stack } from '@mui/material';
+import CategoryModal from './CategoryModal';
+
+enum ModalState {
+ SAME = 'same',
+ DELETE = 'delete',
+}
+
+enum CategoryStatus {
+ Active = 'active',
+ Disabled = 'disabled',
+}
+
+interface InterfaceActionItemCategoryProps {
+ orgId: string;
+}
+
+const dataGridStyle = {
+ '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': {
+ outline: 'none !important',
+ },
+ '&.MuiDataGrid-root .MuiDataGrid-columnHeader:focus-within': {
+ outline: 'none',
+ },
+ '& .MuiDataGrid-row:hover': {
+ backgroundColor: 'transparent',
+ },
+ '& .MuiDataGrid-row.Mui-hovered': {
+ backgroundColor: 'transparent',
+ },
+ '& .MuiDataGrid-root': {
+ borderRadius: '0.5rem',
+ },
+ '& .MuiDataGrid-main': {
+ borderRadius: '0.5rem',
+ },
+};
+
+/**
+ * Represents the component for managing organization action item categories.
+ * This component allows creating, updating, enabling, and disabling action item categories.
+ */
+const OrgActionItemCategories: FC = ({
+ orgId,
+}) => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgActionItemCategories',
+ });
+ const { t: tCommon } = useTranslation('common');
+ const { t: tErrors } = useTranslation('errors');
+
+ const [category, setCategory] =
+ useState(null);
+ const [searchTerm, setSearchTerm] = useState('');
+ const [searchValue, setSearchValue] = useState('');
+ const [sortBy, setSortBy] = useState<'createdAt_ASC' | 'createdAt_DESC'>(
+ 'createdAt_DESC',
+ );
+ const [status, setStatus] = useState(null);
+ const [categories, setCategories] = useState<
+ InterfaceActionItemCategoryInfo[]
+ >([]);
+ const [modalMode, setModalMode] = useState<'edit' | 'create'>('create');
+ const [modalState, setModalState] = useState<{
+ [key in ModalState]: boolean;
+ }>({
+ [ModalState.SAME]: false,
+ [ModalState.DELETE]: false,
+ });
+
+ // Query to fetch action item categories
+ const {
+ data: catData,
+ loading: catLoading,
+ error: catError,
+ refetch: refetchCategories,
+ }: {
+ data?: {
+ actionItemCategoriesByOrganization: InterfaceActionItemCategoryInfo[];
+ };
+ loading: boolean;
+ error?: Error | undefined;
+ refetch: () => void;
+ } = useQuery(ACTION_ITEM_CATEGORY_LIST, {
+ variables: {
+ organizationId: orgId,
+ where: {
+ name_contains: searchTerm,
+ is_disabled: !status ? undefined : status === CategoryStatus.Disabled,
+ },
+ orderBy: sortBy,
+ },
+ });
+
+ const openModal = (modal: ModalState): void =>
+ setModalState((prevState) => ({ ...prevState, [modal]: true }));
+
+ const closeModal = (modal: ModalState): void =>
+ setModalState((prevState) => ({ ...prevState, [modal]: false }));
+
+ const handleOpenModal = useCallback(
+ (
+ category: InterfaceActionItemCategoryInfo | null,
+ mode: 'edit' | 'create',
+ ): void => {
+ setCategory(category);
+ setModalMode(mode);
+ openModal(ModalState.SAME);
+ },
+ [openModal],
+ );
+
+ useEffect(() => {
+ if (catData && catData.actionItemCategoriesByOrganization) {
+ setCategories(catData.actionItemCategoriesByOrganization);
+ }
+ }, [catData]);
+
+ // Show loader while data is being fetched
+ if (catLoading) {
+ return ;
+ }
+
+ // Show error message if there's an error
+ if (catError) {
+ return (
+
+
+
+ {tErrors('errorLoading', { entity: 'Action Item Categories' })}
+
+ {`${catError.message}`}
+
+
+ );
+ }
+
+ const columns: GridColDef[] = [
+ {
+ field: 'id',
+ headerName: 'Sr. No.',
+ flex: 1,
+ minWidth: 100,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return {params.row.id}
;
+ },
+ },
+ {
+ field: 'categoryName',
+ headerName: 'Category',
+ flex: 2,
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ {params.row.name}
+
+ );
+ },
+ },
+ {
+ field: 'status',
+ headerName: 'Status',
+ flex: 1,
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ return (
+ }
+ label={params.row.isDisabled ? 'Disabled' : 'Active'}
+ variant="outlined"
+ color="primary"
+ className={`${styles.chip} ${params.row.isDisabled ? styles.pending : styles.active}`}
+ />
+ );
+ },
+ },
+ {
+ field: 'createdBy',
+ headerName: 'Created By',
+ flex: 1,
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ return params.row.creator.firstName + ' ' + params.row.creator.lastName;
+ },
+ },
+ {
+ field: 'createdOn',
+ headerName: 'Created On',
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ flex: 1,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ {dayjs(params.row.createdAt).format('DD/MM/YYYY')}
+
+ );
+ },
+ },
+ {
+ field: 'action',
+ headerName: 'Action',
+ flex: 2,
+ align: 'center',
+ minWidth: 100,
+ headerAlign: 'center',
+ sortable: false,
+ headerClassName: `${styles.tableHeader}`,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ handleOpenModal(
+ params.row as InterfaceActionItemCategoryInfo,
+ 'edit',
+ )
+ }
+ >
+
+
+ );
+ },
+ },
+ ];
+
+ return (
+
+ {/* Header with search, filter and Create Button */}
+
+
+
setSearchValue(e.target.value)}
+ onKeyUp={(e) => {
+ if (e.key === 'Enter') {
+ setSearchTerm(searchValue);
+ } else if (e.key === 'Backspace' && searchValue === '') {
+ setSearchTerm('');
+ }
+ }}
+ data-testid="searchByName"
+ />
+ setSearchTerm(searchValue)}
+ data-testid="searchBtn"
+ >
+
+
+
+
+
+
+
+
+ {tCommon('sort')}
+
+
+ setSortBy('createdAt_DESC')}
+ data-testid="createdAt_DESC"
+ >
+ {tCommon('createdLatest')}
+
+ setSortBy('createdAt_ASC')}
+ data-testid="createdAt_ASC"
+ >
+ {tCommon('createdEarliest')}
+
+
+
+
+
+
+ {t('status')}
+
+
+ setStatus(null)}
+ data-testid="statusAll"
+ >
+ {tCommon('all')}
+
+ setStatus(CategoryStatus.Active)}
+ data-testid="statusActive"
+ >
+ {tCommon('active')}
+
+ setStatus(CategoryStatus.Disabled)}
+ data-testid="statusDisabled"
+ >
+ {tCommon('disabled')}
+
+
+
+
+
+ handleOpenModal(null, 'create')}
+ style={{ marginTop: '11px' }}
+ data-testid="createActionItemCategoryBtn"
+ >
+
+ {tCommon('create')}
+
+
+
+
+
+ {/* Table with Action Item Categories */}
+
row._id}
+ slots={{
+ noRowsOverlay: () => (
+
+ {t('noActionItemCategories')}
+
+ ),
+ }}
+ sx={dataGridStyle}
+ getRowClassName={() => `${styles.rowBackground}`}
+ autoHeight
+ rowHeight={65}
+ rows={categories.map((category, index) => ({
+ id: index + 1,
+ ...category,
+ }))}
+ columns={columns}
+ isRowSelectable={() => false}
+ />
+
+ closeModal(ModalState.SAME)}
+ refetchCategories={refetchCategories}
+ category={category}
+ orgId={orgId}
+ mode={modalMode}
+ />
+
+ );
+};
+
+export default OrgActionItemCategories;
diff --git a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategoryMocks.ts b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategoryMocks.ts
new file mode 100644
index 0000000000..10310a01e4
--- /dev/null
+++ b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategoryMocks.ts
@@ -0,0 +1,288 @@
+import {
+ CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+
+import { ACTION_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/Queries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '' },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [
+ {
+ _id: 'categoryId1',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2024-08-26',
+ creator: {
+ _id: 'creatorId1',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ {
+ _id: 'categoryId2',
+ name: 'Category 2',
+ isDisabled: true,
+ createdAt: '2024-08-25',
+ creator: {
+ _id: 'creatorId2',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '' },
+ orderBy: 'createdAt_ASC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [
+ {
+ _id: 'categoryId2',
+ name: 'Category 2',
+ isDisabled: true,
+ createdAt: '2024-08-25',
+ creator: {
+ _id: 'creatorId2',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+ {
+ _id: 'categoryId1',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2024-08-26',
+ creator: {
+ _id: 'creatorId1',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '', is_disabled: false },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [
+ {
+ _id: 'categoryId1',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2024-08-26',
+ creator: {
+ _id: 'creatorId1',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '', is_disabled: true },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [
+ {
+ _id: 'categoryId2',
+ name: 'Category 2',
+ isDisabled: true,
+ createdAt: '2024-08-25',
+ creator: {
+ _id: 'creatorId2',
+ firstName: 'John',
+ lastName: 'Doe',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: 'Category 1' },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [
+ {
+ _id: 'categoryId1',
+ name: 'Category 1',
+ isDisabled: false,
+ createdAt: '2024-08-26',
+ creator: {
+ _id: 'creatorId1',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 2',
+ isDisabled: true,
+ organizationId: 'orgId',
+ },
+ },
+ result: {
+ data: {
+ createActionItemCategory: {
+ _id: 'categoryId3',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 2',
+ isDisabled: true,
+ actionItemCategoryId: 'categoryId',
+ },
+ },
+ result: {
+ data: {
+ updateActionItemCategory: {
+ _id: 'categoryId',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 2',
+ isDisabled: false,
+ actionItemCategoryId: 'categoryId',
+ },
+ },
+ result: {
+ data: {
+ updateActionItemCategory: {
+ _id: 'categoryId',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 1',
+ isDisabled: true,
+ actionItemCategoryId: 'categoryId',
+ },
+ },
+ result: {
+ data: {
+ updateActionItemCategory: {
+ _id: 'categoryId',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_EMPTY = [
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '' },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ result: {
+ data: {
+ actionItemCategoriesByOrganization: [],
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR = [
+ {
+ request: {
+ query: ACTION_ITEM_CATEGORY_LIST,
+ variables: {
+ organizationId: 'orgId',
+ where: { name_contains: '' },
+ orderBy: 'createdAt_DESC',
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+ {
+ request: {
+ query: CREATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 2',
+ isDisabled: true,
+ organizationId: 'orgId',
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+ {
+ request: {
+ query: UPDATE_ACTION_ITEM_CATEGORY_MUTATION,
+ variables: {
+ name: 'Category 2',
+ isDisabled: true,
+ actionItemCategoryId: 'categoryId',
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx
new file mode 100644
index 0000000000..da92dfd201
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx
@@ -0,0 +1,125 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import AgendaCategoryCreateModal from './AgendaCategoryCreateModal';
+
+const mockFormState = {
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: 'Test User',
+};
+const mockHideCreateModal = jest.fn();
+const mockSetFormState = jest.fn();
+const mockCreateAgendaCategoryHandler = jest.fn();
+const mockT = (key: string): string => key;
+
+describe('AgendaCategoryCreateModal', () => {
+ test('renders modal correctly', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('agendaCategoryDetails')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('createAgendaCategoryFormSubmitBtn'),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByTestId('createAgendaCategoryModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+ test('tests the condition for formState.name and formState.description', () => {
+ const mockFormState = {
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: 'Test User',
+ };
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ const nameInput = screen.getByLabelText('name');
+ fireEvent.change(nameInput, {
+ target: { value: 'New name' },
+ });
+ const descriptionInput = screen.getByLabelText('description');
+ fireEvent.change(descriptionInput, {
+ target: { value: 'New description' },
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ name: 'New name',
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ description: 'New description',
+ });
+ });
+ test('calls createAgendaCategoryHandler when form is submitted', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ fireEvent.submit(screen.getByTestId('createAgendaCategoryFormSubmitBtn'));
+ expect(mockCreateAgendaCategoryHandler).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.tsx
new file mode 100644
index 0000000000..57a3057b3f
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.tsx
@@ -0,0 +1,105 @@
+import React from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import type { ChangeEvent } from 'react';
+import styles from './OrganizationAgendaCategory.module.css';
+
+/**
+ * InterfaceFormStateType is an object containing the form state
+ */
+interface InterfaceFormStateType {
+ name: string;
+ description: string;
+ createdBy: string;
+}
+
+/**
+ * InterfaceAgendaCategoryCreateModalProps is an object containing the props for AgendaCategoryCreateModal component
+ */
+interface InterfaceAgendaCategoryCreateModalProps {
+ agendaCategoryCreateModalIsOpen: boolean;
+ hideCreateModal: () => void;
+ formState: InterfaceFormStateType;
+ setFormState: (state: React.SetStateAction) => void;
+ createAgendaCategoryHandler: (
+ e: ChangeEvent,
+ ) => Promise;
+ t: (key: string) => string;
+}
+
+/**
+ * AgendaCategoryCreateModal component is used to create the agenda category details like name, description
+ * @param agendaCategoryCreateModalIsOpen - boolean value to check if the modal is open or not
+ * @param hideCreateModal - function to hide the modal
+ * @param formState - object containing the form state
+ * @param setFormState - function to set the form state
+ * @param createAgendaCategoryHandler - function to create the agenda category
+ * @param t - i18n function to translate the text
+ * @returns returns the AgendaCategoryCreateModal component
+ */
+const AgendaCategoryCreateModal: React.FC<
+ InterfaceAgendaCategoryCreateModalProps
+> = ({
+ agendaCategoryCreateModalIsOpen,
+ hideCreateModal,
+ formState,
+ setFormState,
+ createAgendaCategoryHandler,
+ t,
+}) => {
+ return (
+
+
+ {t('agendaCategoryDetails')}
+
+
+
+
+
+
+ {t('name')}
+
+ setFormState({ ...formState, name: e.target.value })
+ }
+ />
+
+
+ {t('description')}
+
+ setFormState({ ...formState, description: e.target.value })
+ }
+ />
+
+
+ {t('createAgendaCategory')}
+
+
+
+
+ );
+};
+
+export default AgendaCategoryCreateModal;
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryDeleteModal.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryDeleteModal.tsx
new file mode 100644
index 0000000000..a16186bac7
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryDeleteModal.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import styles from './OrganizationAgendaCategory.module.css';
+
+/**
+ * InterfaceAgendaCategoryDeleteModalProps is an object containing the props for AgendaCategoryDeleteModal component
+ */
+interface InterfaceAgendaCategoryDeleteModalProps {
+ agendaCategoryDeleteModalIsOpen: boolean;
+ toggleDeleteModal: () => void;
+ deleteAgendaCategoryHandler: () => Promise;
+ t: (key: string) => string;
+ tCommon: (key: string) => string;
+}
+
+/**
+ * AgendaCategoryDeleteModal component is used to delete the agenda category
+ * @param agendaCategoryDeleteModalIsOpen - boolean value to check if the modal is open or not
+ * @param toggleDeleteModal - function to toggle the modal
+ * @param deleteAgendaCategoryHandler - function to delete the agenda category
+ * @param t - i18n function to translate the text
+ * @param tCommon - i18n function to translate the text
+ * @returns returns the AgendaCategoryDeleteModal component
+ */
+const AgendaCategoryDeleteModal: React.FC<
+ InterfaceAgendaCategoryDeleteModalProps
+> = ({
+ agendaCategoryDeleteModalIsOpen,
+ toggleDeleteModal,
+ deleteAgendaCategoryHandler,
+ t,
+ tCommon,
+}) => {
+ return (
+
+
+
+ {t('deleteAgendaCategory')}
+
+
+
+ {t('deleteAgendaCategoryMsg')}
+
+
+
+ {tCommon('no')}
+
+
+ {tCommon('yes')}
+
+
+
+ );
+};
+
+export default AgendaCategoryDeleteModal;
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryPreviewModal.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryPreviewModal.tsx
new file mode 100644
index 0000000000..794db27ad3
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryPreviewModal.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+
+import styles from './OrganizationAgendaCategory.module.css';
+
+/**
+ * InterfaceFormStateType is an object containing the form state
+ */
+interface InterfaceFormStateType {
+ name: string;
+ description: string;
+ createdBy: string;
+}
+
+/**
+ * InterfaceAgendaCategoryPreviewModalProps is an object containing the props for AgendaCategoryPreviewModal component
+ */
+interface InterfaceAgendaCategoryPreviewModalProps {
+ agendaCategoryPreviewModalIsOpen: boolean;
+ hidePreviewModal: () => void;
+ showUpdateModal: () => void;
+ toggleDeleteModal: () => void;
+ formState: InterfaceFormStateType;
+
+ t: (key: string) => string;
+}
+
+/**
+ * AgendaCategoryPreviewModal component is used to preview the agenda category details like name, description, createdBy
+ * @param agendaCategoryPreviewModalIsOpen - boolean value to check if the modal is open or not
+ * @param hidePreviewModal - function to hide the modal
+ * @param showUpdateModal - function to show the update modal
+ * @param toggleDeleteModal - function to toggle the delete modal
+ * @param formState - object containing the form state
+ * @param t - i18n function to translate the text
+ * @returns returns the AgendaCategoryPreviewModal component
+ */
+const AgendaCategoryPreviewModal: React.FC<
+ InterfaceAgendaCategoryPreviewModalProps
+> = ({
+ agendaCategoryPreviewModalIsOpen,
+ hidePreviewModal,
+ showUpdateModal,
+ toggleDeleteModal,
+ formState,
+ t,
+}) => {
+ return (
+
+
+ {t('agendaCategoryDetails')}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AgendaCategoryPreviewModal;
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx
new file mode 100644
index 0000000000..168b97abd3
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx
@@ -0,0 +1,151 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import AgendaCategoryUpdateModal from './AgendaCategoryUpdateModal';
+
+const mockFormState = {
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: 'Test User',
+};
+const mockHideUpdateModal = jest.fn();
+const mockSetFormState = jest.fn();
+const mockUpdateAgendaCategoryHandler = jest.fn();
+const mockT = (key: string): string => key;
+
+describe('AgendaCategoryUpdateModal', () => {
+ test('renders modal correctly', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('updateAgendaCategory')).toBeInTheDocument();
+ expect(screen.getByTestId('editAgendaCategoryBtn')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('updateAgendaCategoryModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+
+ test('calls hideUpdateModal when close button is clicked', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn'));
+ expect(mockHideUpdateModal).toHaveBeenCalledTimes(1);
+ });
+
+ test('tests the condition for formState.name and formState.description', () => {
+ const mockFormState = {
+ name: 'Test Name',
+ description: 'Test Description',
+ createdBy: 'Test User',
+ };
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ const nameInput = screen.getByLabelText('name');
+ fireEvent.change(nameInput, {
+ target: { value: 'New name' },
+ });
+ const descriptionInput = screen.getByLabelText('description');
+ fireEvent.change(descriptionInput, {
+ target: { value: 'New description' },
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ name: 'New name',
+ });
+ expect(mockSetFormState).toHaveBeenCalledWith({
+ ...mockFormState,
+ description: 'New description',
+ });
+ });
+
+ test('calls updateAgendaCategoryHandler when form is submitted', () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ fireEvent.submit(screen.getByTestId('editAgendaCategoryBtn'));
+ expect(mockUpdateAgendaCategoryHandler).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.tsx
new file mode 100644
index 0000000000..508f1db69b
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import type { ChangeEvent } from 'react';
+
+import styles from './OrganizationAgendaCategory.module.css';
+
+/**
+ * InterfaceFormStateType is an object containing the form state
+ */
+interface InterfaceFormStateType {
+ name: string;
+ description: string;
+ createdBy: string;
+}
+
+/**
+ * InterfaceAgendaCategoryUpdateModalProps is an object containing the props for AgendaCategoryUpdateModal component
+ */
+interface InterfaceAgendaCategoryUpdateModalProps {
+ agendaCategoryUpdateModalIsOpen: boolean;
+ hideUpdateModal: () => void;
+ formState: InterfaceFormStateType;
+ setFormState: (state: React.SetStateAction) => void;
+ updateAgendaCategoryHandler: (
+ e: ChangeEvent,
+ ) => Promise;
+ t: (key: string) => string;
+}
+
+/**
+ * AgendaCategoryUpdateModal component is used to update the agenda category details like name, description
+ * @param agendaCategoryUpdateModalIsOpen - boolean value to check if the modal is open or not
+ * @param hideUpdateModal - function to hide the modal
+ * @param formState - object containing the form state
+ * @param setFormState - function to set the form state
+ * @param updateAgendaCategoryHandler - function to update the agenda category
+ * @param t - i18n function to translate the text
+ * @returns returns the AgendaCategoryUpdateModal component
+ */
+const AgendaCategoryUpdateModal: React.FC<
+ InterfaceAgendaCategoryUpdateModalProps
+> = ({
+ agendaCategoryUpdateModalIsOpen,
+ hideUpdateModal,
+ formState,
+ setFormState,
+ updateAgendaCategoryHandler,
+ t,
+}) => {
+ return (
+
+
+ {t('updateAgendaCategory')}
+
+
+
+
+
+
+ {t('name')}
+
+ setFormState({ ...formState, name: e.target.value })
+ }
+ />
+
+
+ {t('description')}
+
+ setFormState({ ...formState, description: e.target.value })
+ }
+ />
+
+
+ {t('update')}
+
+
+
+
+ );
+};
+
+export default AgendaCategoryUpdateModal;
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.module.css b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.module.css
new file mode 100644
index 0000000000..13e187f8b5
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.module.css
@@ -0,0 +1,171 @@
+.agendaCategoryContainer {
+ height: 90vh;
+}
+
+.agendaCategoryModal {
+ max-width: 80vw;
+ margin-top: 2vh;
+ margin-left: 13vw;
+}
+
+.btnsContainer {
+ display: flex;
+ gap: 10px;
+}
+
+.btnsContainer .btnsBlock {
+ display: flex;
+ gap: 10px;
+}
+
+.btnsContainer button {
+ display: flex;
+ align-items: center;
+}
+
+.container {
+ min-height: 100vh;
+}
+
+.datediv {
+ display: flex;
+ flex-direction: row;
+}
+
+.datebox {
+ width: 90%;
+ border-radius: 7px;
+ outline: none;
+ box-shadow: none;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-right: 5px;
+ padding-left: 5px;
+ margin-right: 5px;
+ margin-left: 5px;
+}
+
+.dropdown {
+ display: block;
+}
+
+.dropdownToggle {
+ margin-bottom: 0;
+ display: flex;
+}
+
+.dropdownModalToggle {
+ width: 50%;
+}
+
+.errorIcon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.greenregbtn {
+ margin: 1rem 0 0;
+ margin-top: 15px;
+ border: 1px solid var(--bs-gray-300);
+ box-shadow: 0 2px 2px var(--bs-gray-300);
+ padding: 10px 10px;
+ border-radius: 5px;
+ background-color: var(--bs-primary);
+ width: 100%;
+ font-size: 16px;
+ color: var(--bs-white);
+ outline: none;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.2s,
+ box-shadow 0.2s;
+ width: 100%;
+}
+
+h2 {
+ margin-top: 0.5rem;
+}
+
+hr {
+ border: none;
+ height: 1px;
+ background-color: var(--bs-gray-500);
+ margin: 1rem;
+}
+
+.iconContainer {
+ display: flex;
+ justify-content: flex-end;
+}
+.icon {
+ margin: 1px;
+}
+
+.message {
+ margin-top: 25%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.organizationAgendaCategoryContainer h2 {
+ margin: 0.6rem 0;
+}
+
+.preview {
+ display: flex;
+ flex-direction: row;
+ font-weight: 900;
+ font-size: 16px;
+ color: rgb(80, 80, 80);
+}
+
+.removeFilterIcon {
+ cursor: pointer;
+}
+
+.searchForm {
+ display: inline;
+}
+
+.titlemodal {
+ color: var(--bs-gray-600);
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid var(--bs-primary);
+ width: 65%;
+}
+
+.view {
+ margin-left: 2%;
+ font-weight: 600;
+ font-size: 16px;
+ color: var(--bs-gray-600);
+}
+
+@media (max-width: 767px) {
+ .btnsContainer {
+ margin-bottom: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .btnsContainer .btnsBlock .dropdownToggle {
+ flex-grow: 1;
+ }
+
+ .btnsContainer button {
+ width: 100%;
+ }
+
+ .createAgendaCategoryButton {
+ position: absolute;
+ top: 1rem;
+ right: 2rem;
+ }
+}
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx
new file mode 100644
index 0000000000..56cb450647
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx
@@ -0,0 +1,237 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ waitFor,
+ act,
+ waitForElementToBeRemoved,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import { MockedProvider } from '@apollo/client/testing';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import i18n from 'utils/i18nForTest';
+import { toast } from 'react-toastify';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+import OrganizationAgendaCategory from './OrganizationAgendaCategory';
+import {
+ MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY,
+ MOCKS_ERROR_MUTATION,
+} from './OrganizationAgendaCategoryErrorMocks';
+import { MOCKS } from './OrganizationAgendaCategoryMocks';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: '123' }),
+}));
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(
+ MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY,
+ true,
+);
+const link3 = new StaticMockLink(MOCKS_ERROR_MUTATION, true);
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.organizationAgendaCategory ??
+ {},
+ ),
+ ),
+};
+
+describe('Testing Agenda Categories Component', () => {
+ const formData = {
+ name: 'Category',
+ description: 'Test Description',
+ createdBy: 'Test User',
+ };
+ test('Component loads correctly', async () => {
+ const { getByText } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.createAgendaCategory)).toBeInTheDocument();
+ });
+ });
+
+ test('render error component on unsuccessful agenda category list query', async () => {
+ const { queryByText } = render(
+
+
+
+
+ { }
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ queryByText(translations.createAgendaCategory),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('opens and closes the create agenda category modal', async () => {
+ render(
+
+
+
+
+
+ { }
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createAgendaCategoryBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('createAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaCategoryModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('createAgendaCategoryModalCloseBtn'),
+ );
+ });
+ test('creates new agenda cagtegory', async () => {
+ render(
+
+
+
+
+
+ { }
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createAgendaCategoryBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('createAgendaCategoryBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('createAgendaCategoryModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+
+ userEvent.type(
+ screen.getByPlaceholderText(translations.name),
+ formData.name,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(translations.description),
+ formData.description,
+ );
+ userEvent.click(screen.getByTestId('createAgendaCategoryFormSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.agendaCategoryCreated,
+ );
+ });
+ });
+
+ // test('toasts error on unsuccessful creation', async () => {
+ // render(
+ //
+ //
+ //
+ //
+ //
+ // { }
+ //
+ //
+ //
+ //
+ // ,
+ // );
+
+ // await wait();
+
+ // await waitFor(() => {
+ // expect(screen.getByTestId('createAgendaCategoryBtn')).toBeInTheDocument();
+ // });
+ // userEvent.click(screen.getByTestId('createAgendaCategoryBtn'));
+
+ // await waitFor(() => {
+ // return expect(
+ // screen.findByTestId('createAgendaCategoryModalCloseBtn'),
+ // ).resolves.toBeInTheDocument();
+ // });
+
+ // userEvent.type(
+ // screen.getByPlaceholderText(translations.name),
+ // formData.name,
+ // );
+
+ // userEvent.type(
+ // screen.getByPlaceholderText(translations.description),
+ // formData.description,
+ // );
+ // userEvent.click(screen.getByTestId('createAgendaCategoryFormSubmitBtn'));
+
+ // await waitFor(() => {
+ // expect(toast.error).toBeCalledWith();
+ // });
+ // });
+});
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.tsx b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.tsx
new file mode 100644
index 0000000000..884371d862
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.tsx
@@ -0,0 +1,187 @@
+import React, { useState } from 'react';
+import type { ChangeEvent, FC } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Button } from 'react-bootstrap';
+
+import { WarningAmberRounded } from '@mui/icons-material';
+import { toast } from 'react-toastify';
+
+import { useMutation, useQuery } from '@apollo/client';
+import { AGENDA_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/Queries';
+import { CREATE_AGENDA_ITEM_CATEGORY_MUTATION } from 'GraphQl/Mutations/mutations';
+
+import type { InterfaceAgendaItemCategoryList } from 'utils/interfaces';
+import AgendaCategoryContainer from 'components/AgendaCategory/AgendaCategoryContainer';
+import AgendaCategoryCreateModal from './AgendaCategoryCreateModal';
+import styles from './OrganizationAgendaCategory.module.css';
+import Loader from 'components/Loader/Loader';
+
+interface InterfaceAgendaCategoryProps {
+ orgId: string;
+}
+
+/**
+ * Component for managing and displaying agenda item categories within an organization.
+ *
+ * This component allows users to view, create, and manage agenda item categories. It includes functionality for displaying categories, handling creation, and managing modal visibility.
+ *
+ * @returns The rendered component.
+ */
+
+const organizationAgendaCategory: FC = ({
+ orgId,
+}) => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'organizationAgendaCategory',
+ });
+
+ // State for managing modal visibility and form data
+ const [agendaCategoryCreateModalIsOpen, setAgendaCategoryCreateModalIsOpen] =
+ useState(false);
+
+ const [formState, setFormState] = useState({
+ name: '',
+ description: '',
+ createdBy: '',
+ });
+
+ /**
+ * Query to fetch agenda item categories for the organization.
+ */
+ const {
+ data: agendaCategoryData,
+ loading: agendaCategoryLoading,
+ error: agendaCategoryError,
+ refetch: refetchAgendaCategory,
+ }: {
+ data: InterfaceAgendaItemCategoryList | undefined;
+ loading: boolean;
+ error?: unknown | undefined;
+ refetch: () => void;
+ } = useQuery(AGENDA_ITEM_CATEGORY_LIST, {
+ variables: { organizationId: orgId },
+ notifyOnNetworkStatusChange: true,
+ });
+
+ /**
+ * Mutation to create a new agenda item category.
+ */
+ const [createAgendaCategory] = useMutation(
+ CREATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ );
+
+ /**
+ * Handler function to create a new agenda item category.
+ *
+ * @param e - The form submit event.
+ * @returns A promise that resolves when the agenda item category is created.
+ */
+ const createAgendaCategoryHandler = async (
+ e: ChangeEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ await createAgendaCategory({
+ variables: {
+ input: {
+ organizationId: orgId,
+ name: formState.name,
+ description: formState.description,
+ },
+ },
+ });
+ toast.success(t('agendaCategoryCreated') as string);
+ setFormState({ name: '', description: '', createdBy: '' });
+ refetchAgendaCategory();
+ hideCreateModal();
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+
+ /**
+ * Toggles the visibility of the create agenda item category modal.
+ */
+ const showCreateModal = (): void => {
+ setAgendaCategoryCreateModalIsOpen(!agendaCategoryCreateModalIsOpen);
+ };
+
+ /**
+ * Hides the create agenda item category modal.
+ */
+ const hideCreateModal = (): void => {
+ setAgendaCategoryCreateModalIsOpen(!agendaCategoryCreateModalIsOpen);
+ };
+
+ if (agendaCategoryLoading) return ;
+
+ if (agendaCategoryError) {
+ return (
+
+
+
+
+ Error occured while loading{' '}
+ {agendaCategoryError && 'Agenda Categories'}
+ Data
+
+ {agendaCategoryError && (agendaCategoryError as Error).message}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {/* setSearchValue(e.target.value)}
+ value={searchValue}
+ data-testid="searchAgendaCategories"
+ /> */}
+
+
+
+
+ {t('createAgendaCategory')}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default organizationAgendaCategory;
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryErrorMocks.ts b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryErrorMocks.ts
new file mode 100644
index 0000000000..10809c3da8
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryErrorMocks.ts
@@ -0,0 +1,51 @@
+import { CREATE_AGENDA_ITEM_CATEGORY_MUTATION } from 'GraphQl/Mutations/AgendaCategoryMutations';
+
+import { AGENDA_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/AgendaCategoryQueries';
+
+export const MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY = [
+ {
+ request: {
+ query: AGENDA_ITEM_CATEGORY_LIST,
+ variables: { organizationId: '123' },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
+
+export const MOCKS_ERROR_MUTATION = [
+ {
+ request: {
+ query: AGENDA_ITEM_CATEGORY_LIST,
+ variables: { organizationId: '123' },
+ },
+ result: {
+ data: {
+ agendaItemCategoriesByOrganization: [
+ {
+ _id: 'agendaItemCategory1',
+ name: 'Category',
+ description: 'Test Description',
+ createdBy: {
+ _id: 'user1',
+ firstName: 'Harve',
+ lastName: 'Lance',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CREATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ input: {
+ organizationId: '123',
+ name: 'Category',
+ description: 'Test Description',
+ },
+ },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryMocks.ts b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryMocks.ts
new file mode 100644
index 0000000000..434b0dcad2
--- /dev/null
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategoryMocks.ts
@@ -0,0 +1,47 @@
+import { CREATE_AGENDA_ITEM_CATEGORY_MUTATION } from 'GraphQl/Mutations/AgendaCategoryMutations';
+
+import { AGENDA_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/AgendaCategoryQueries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: AGENDA_ITEM_CATEGORY_LIST,
+ variables: { organizationId: '123' },
+ },
+ result: {
+ data: {
+ agendaItemCategoriesByOrganization: [
+ {
+ _id: 'agendaItemCategory1',
+ name: 'Category',
+ description: 'Test Description',
+ createdBy: {
+ _id: 'user1',
+ firstName: 'Harve',
+ lastName: 'Lance',
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CREATE_AGENDA_ITEM_CATEGORY_MUTATION,
+ variables: {
+ input: {
+ organizationId: '123',
+ name: 'Category',
+ description: 'Test Description',
+ },
+ },
+ },
+ result: {
+ data: {
+ createAgendaCategory: {
+ _id: 'agendaItemCategory1',
+ },
+ },
+ },
+ },
+];
diff --git a/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.module.css b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.module.css
new file mode 100644
index 0000000000..2b15a2ac0c
--- /dev/null
+++ b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.module.css
@@ -0,0 +1,25 @@
+.settingsBody {
+ margin: 2.5rem 0;
+}
+
+.cardHeader {
+ padding: 1.25rem 1rem 1rem 1rem;
+ border-bottom: 1px solid var(--bs-gray-200);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.cardHeader .cardTitle {
+ font-size: 1.2rem;
+ font-weight: 600;
+}
+
+.cardBody {
+ min-height: 180px;
+}
+
+.cardBody .textBox {
+ margin: 0 0 3rem 0;
+ color: var(--bs-secondary);
+}
diff --git a/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx
new file mode 100644
index 0000000000..77ffe65c08
--- /dev/null
+++ b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx
@@ -0,0 +1,307 @@
+import React, { act } from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { render, screen, waitFor } from '@testing-library/react';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+
+import {
+ DELETE_ORGANIZATION_MUTATION,
+ REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18nForTest from 'utils/i18nForTest';
+import DeleteOrg from './DeleteOrg';
+import { ToastContainer, toast } from 'react-toastify';
+import { IS_SAMPLE_ORGANIZATION_QUERY } from 'GraphQl/Queries/Queries';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 1000): Promise {
+ await act(async () => {
+ await new Promise((resolve) => setTimeout(resolve, ms));
+ });
+}
+
+const MOCKS = [
+ {
+ request: {
+ query: IS_SAMPLE_ORGANIZATION_QUERY,
+ variables: {
+ isSampleOrganizationId: '123',
+ },
+ },
+ result: {
+ data: {
+ isSampleOrganization: true,
+ },
+ },
+ },
+ {
+ request: {
+ query: REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+ },
+ result: {
+ data: {
+ removeSampleOrganization: true,
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_ORGANIZATION_MUTATION,
+ variables: {
+ id: '456',
+ },
+ },
+ result: {
+ data: {
+ removeOrganization: {
+ _id: '456',
+ },
+ },
+ },
+ },
+];
+
+const MOCKS_WITH_ERROR = [
+ {
+ request: {
+ query: IS_SAMPLE_ORGANIZATION_QUERY,
+ variables: {
+ isSampleOrganizationId: '123',
+ },
+ },
+ result: {
+ data: {
+ isSampleOrganization: true,
+ },
+ },
+ },
+ {
+ request: {
+ query: DELETE_ORGANIZATION_MUTATION,
+ variables: {
+ id: '456',
+ },
+ },
+ error: new Error('Failed to delete organization'),
+ },
+ {
+ request: {
+ query: REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+ },
+ error: new Error('Failed to delete sample organization'),
+ },
+];
+
+const mockNavgatePush = jest.fn();
+let mockURL = '123';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockURL }),
+ useNavigate: () => mockNavgatePush,
+}));
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_WITH_ERROR, true);
+
+afterEach(() => {
+ localStorage.clear();
+});
+
+describe('Delete Organization Component', () => {
+ test('should be able to Toggle Delete Organization Modal', async () => {
+ mockURL = '456';
+ setItem('SuperAdmin', true);
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ act(() => {
+ screen.getByTestId(/openDeleteModalBtn/i).click();
+ });
+ expect(await screen.findByTestId(/orgDeleteModal/i)).toBeInTheDocument();
+ act(() => {
+ screen.getByTestId(/closeDelOrgModalBtn/i).click();
+ });
+ await waitFor(() => {
+ expect(screen.queryByTestId(/orgDeleteModal/i)).not.toBeInTheDocument();
+ });
+ });
+
+ test('should be able to Toggle Delete Organization Modal When Organization is Sample Organization', async () => {
+ mockURL = '123';
+ setItem('SuperAdmin', true);
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await wait();
+ act(() => {
+ screen.getByTestId(/openDeleteModalBtn/i).click();
+ });
+ expect(screen.getByTestId(/orgDeleteModal/i)).toBeInTheDocument();
+ act(() => {
+ screen.getByTestId(/closeDelOrgModalBtn/i).click();
+ });
+ await waitFor(() => {
+ expect(screen.queryByTestId(/orgDeleteModal/i)).not.toBeInTheDocument();
+ });
+ });
+
+ test('Delete organization functionality should work properly', async () => {
+ mockURL = '456';
+ setItem('SuperAdmin', true);
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ screen.debug();
+ act(() => {
+ screen.getByTestId('openDeleteModalBtn').click();
+ });
+ screen.debug();
+ expect(await screen.findByTestId('orgDeleteModal')).toBeInTheDocument();
+ const deleteButton = await screen.findByTestId('deleteOrganizationBtn');
+ act(() => {
+ deleteButton.click();
+ });
+ });
+
+ test('Delete organization functionality should work properly for sample org', async () => {
+ mockURL = '123';
+ setItem('SuperAdmin', true);
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('openDeleteModalBtn')).toBeInTheDocument();
+ });
+ act(() => {
+ screen.getByTestId('openDeleteModalBtn').click();
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('orgDeleteModal')).toBeInTheDocument();
+ });
+ const deleteButton = await screen.findByTestId('deleteOrganizationBtn');
+ act(() => {
+ deleteButton.click();
+ });
+ await wait(2000);
+ expect(mockNavgatePush).toHaveBeenCalledWith('/orglist');
+ });
+
+ test('Error handling for IS_SAMPLE_ORGANIZATION_QUERY mock', async () => {
+ mockURL = '123';
+ setItem('SuperAdmin', true);
+ jest.spyOn(toast, 'error');
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('openDeleteModalBtn')).toBeInTheDocument();
+ });
+ act(() => {
+ screen.getByTestId('openDeleteModalBtn').click();
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('orgDeleteModal')).toBeInTheDocument();
+ });
+ act(() => {
+ screen.getByTestId('deleteOrganizationBtn').click();
+ });
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(
+ 'Failed to delete sample organization',
+ );
+ });
+ });
+
+ test('Error handling for DELETE_ORGANIZATION_MUTATION mock', async () => {
+ mockURL = '456';
+ setItem('SuperAdmin', true);
+ jest.spyOn(toast, 'error');
+
+ await act(async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('openDeleteModalBtn')).toBeInTheDocument();
+ });
+ act(() => {
+ screen.getByTestId('openDeleteModalBtn').click();
+ });
+ await waitFor(() => {
+ expect(screen.getByTestId('orgDeleteModal')).toBeInTheDocument();
+ });
+ act(() => {
+ screen.getByTestId('deleteOrganizationBtn').click();
+ });
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith('Failed to delete organization');
+ });
+ });
+});
diff --git a/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.tsx b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.tsx
new file mode 100644
index 0000000000..8edf11ca22
--- /dev/null
+++ b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.tsx
@@ -0,0 +1,150 @@
+import React, { useState } from 'react';
+import { Button, Card, Modal } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { useMutation, useQuery } from '@apollo/client';
+import { errorHandler } from 'utils/errorHandler';
+import { toast } from 'react-toastify';
+import {
+ DELETE_ORGANIZATION_MUTATION,
+ REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import { IS_SAMPLE_ORGANIZATION_QUERY } from 'GraphQl/Queries/Queries';
+import styles from './DeleteOrg.module.css';
+import { useNavigate, useParams } from 'react-router-dom';
+import useLocalStorage from 'utils/useLocalstorage';
+
+/**
+ * A component for deleting an organization.
+ *
+ * It displays a card with a delete button. When the delete button is clicked,
+ * a modal appears asking for confirmation. Depending on the type of organization
+ * (sample or regular), it performs the delete operation and shows appropriate
+ * success or error messages.
+ *
+ * @returns JSX.Element - The rendered component with delete functionality.
+ */
+function deleteOrg(): JSX.Element {
+ // Translation hook for localization
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'deleteOrg',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // Get the current organization ID from the URL
+ const { orgId: currentUrl } = useParams();
+ // Navigation hook for redirecting
+ const navigate = useNavigate();
+ // State to control the visibility of the delete confirmation modal
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+
+ // Hook for accessing local storage
+ const { getItem } = useLocalStorage();
+ // Check if the user has super admin privileges
+ const canDelete = getItem('SuperAdmin');
+
+ /**
+ * Toggles the visibility of the delete confirmation modal.
+ */
+ const toggleDeleteModal = (): void => setShowDeleteModal(!showDeleteModal);
+
+ // GraphQL mutations for deleting organizations
+ const [del] = useMutation(DELETE_ORGANIZATION_MUTATION);
+ const [removeSampleOrganization] = useMutation(
+ REMOVE_SAMPLE_ORGANIZATION_MUTATION,
+ );
+
+ // Query to check if the organization is a sample organization
+ const { data } = useQuery(IS_SAMPLE_ORGANIZATION_QUERY, {
+ variables: {
+ isSampleOrganizationId: currentUrl,
+ },
+ });
+
+ /**
+ * Deletes the organization. It handles both sample and regular organizations.
+ * Displays success or error messages based on the operation result.
+ */
+ const deleteOrg = async (): Promise => {
+ if (data && data.isSampleOrganization) {
+ // If it's a sample organization, use a specific mutation
+ removeSampleOrganization()
+ .then(() => {
+ toast.success(t('successfullyDeletedSampleOrganization') as string);
+ setTimeout(() => {
+ navigate('/orglist');
+ }, 1000);
+ })
+ .catch((error) => {
+ toast.error(error.message);
+ });
+ } else {
+ // For regular organizations, use a different mutation
+ try {
+ await del({
+ variables: {
+ id: currentUrl,
+ },
+ });
+ navigate('/orglist');
+ } catch (error) {
+ errorHandler(t, error);
+ }
+ }
+ };
+
+ return (
+ <>
+ {canDelete && (
+
+
+
{t('deleteOrganization')}
+
+
+ {t('longDelOrgMsg')}
+
+ {data && data.isSampleOrganization
+ ? t('deleteSampleOrganization')
+ : t('deleteOrganization')}
+
+
+
+ )}
+ {/* Delete Organization Modal */}
+ {canDelete && (
+
+
+ {t('deleteOrganization')}
+
+ {t('deleteMsg')}
+
+
+ {tCommon('cancel')}
+
+
+ {t('confirmDelete')}
+
+
+
+ )}
+ >
+ );
+}
+
+export default deleteOrg;
diff --git a/src/components/OrgSettings/General/GeneralSettings.tsx b/src/components/OrgSettings/General/GeneralSettings.tsx
new file mode 100644
index 0000000000..4dbca1b6eb
--- /dev/null
+++ b/src/components/OrgSettings/General/GeneralSettings.tsx
@@ -0,0 +1,73 @@
+import React, { type FC } from 'react';
+import { Card, Col, Form, Row } from 'react-bootstrap';
+import styles from 'screens/OrgSettings/OrgSettings.module.css';
+import OrgProfileFieldSettings from './OrgProfileFieldSettings/OrgProfileFieldSettings';
+import ChangeLanguageDropDown from 'components/ChangeLanguageDropdown/ChangeLanguageDropDown';
+import DeleteOrg from './DeleteOrg/DeleteOrg';
+import OrgUpdate from './OrgUpdate/OrgUpdate';
+import { useTranslation } from 'react-i18next';
+
+/**
+ * Props for the `GeneralSettings` component.
+ */
+interface InterfaceGeneralSettingsProps {
+ orgId: string;
+}
+
+/**
+ * A component for displaying general settings for an organization.
+ *
+ * @param props - The properties passed to the component.
+ * @returns The `GeneralSettings` component.
+ */
+const GeneralSettings: FC = ({ orgId }) => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgSettings',
+ });
+
+ return (
+
+
+
+
+
{t('updateOrganization')}
+
+
+ {/* Render organization update component */}
+
+
+
+
+
+
+
+
+
+
+
+ {t('changeLanguage')}
+
+ {/* Render language change dropdown component */}
+
+
+
+
+
+
+
+
+
{t('manageCustomFields')}
+
+
+ {/* Render organization profile field settings component */}
+
+
+
+
+
+ );
+};
+
+export default GeneralSettings;
diff --git a/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.module.css b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.module.css
new file mode 100644
index 0000000000..851f70ce39
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.module.css
@@ -0,0 +1,24 @@
+.customDataTable {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.customDataTable th,
+.customDataTable td {
+ padding: 8px;
+ text-align: left;
+}
+
+.customDataTable th {
+ background-color: #f2f2f2;
+}
+form {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.saveButton {
+ width: 10em;
+ align-self: self-end;
+}
diff --git a/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx
new file mode 100644
index 0000000000..8db8773381
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx
@@ -0,0 +1,280 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import OrgProfileFieldSettings from './OrgProfileFieldSettings';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import {
+ ADD_CUSTOM_FIELD,
+ REMOVE_CUSTOM_FIELD,
+} from 'GraphQl/Mutations/mutations';
+import { ORGANIZATION_CUSTOM_FIELDS } from 'GraphQl/Queries/Queries';
+import { ToastContainer, toast } from 'react-toastify';
+
+const MOCKS = [
+ {
+ request: {
+ query: ADD_CUSTOM_FIELD,
+ variables: {
+ type: '',
+ name: '',
+ },
+ },
+ result: {
+ data: {
+ addOrganizationCustomField: {
+ name: 'Custom Field Name',
+ type: 'string',
+ },
+ },
+ },
+ },
+
+ {
+ request: {
+ query: REMOVE_CUSTOM_FIELD,
+ variables: { customFieldId: 'adsdasdsa334343yiu423434' },
+ },
+ result: {
+ data: {
+ removeOrganizationCustomField: {
+ type: '',
+ name: '',
+ },
+ },
+ },
+ },
+
+ {
+ request: {
+ query: ORGANIZATION_CUSTOM_FIELDS,
+ variables: {},
+ },
+ result: {
+ data: {
+ customFieldsByOrganization: [
+ {
+ _id: 'adsdasdsa334343yiu423434',
+ type: 'fieldType',
+ name: 'fieldName',
+ },
+ ],
+ },
+ },
+ },
+];
+
+const ERROR_MOCKS = [
+ {
+ request: {
+ query: ADD_CUSTOM_FIELD,
+ variables: {
+ type: '',
+ name: '',
+ },
+ },
+ error: new Error('Failed to add custom field'),
+ },
+ {
+ request: {
+ query: REMOVE_CUSTOM_FIELD,
+ variables: {
+ customFieldId: 'adsdasdsa334343yiu423434',
+ },
+ },
+ error: new Error('Failed to remove custom field'),
+ },
+ {
+ request: {
+ query: ORGANIZATION_CUSTOM_FIELDS,
+ variables: {},
+ },
+ result: {
+ data: {
+ customFieldsByOrganization: [
+ {
+ _id: 'adsdasdsa334343yiu423434',
+ type: 'fieldType',
+ name: 'fieldName',
+ },
+ ],
+ },
+ },
+ },
+];
+
+const ORGANIZATION_CUSTOM_FIELDS_ERROR_MOCKS = [
+ {
+ request: {
+ query: ORGANIZATION_CUSTOM_FIELDS,
+ variables: {},
+ },
+ error: new Error('Failed to fetch custom field'),
+ },
+];
+
+const NO_C_FIELD_MOCK = [
+ {
+ request: {
+ query: ADD_CUSTOM_FIELD,
+ variables: {
+ type: 'fieldType',
+ name: 'fieldName',
+ },
+ },
+ result: {
+ data: {
+ addOrganizationCustomField: {
+ name: 'Custom Field Name',
+ type: 'string',
+ },
+ },
+ },
+ },
+
+ {
+ request: {
+ query: ORGANIZATION_CUSTOM_FIELDS,
+ variables: {},
+ },
+ result: {
+ data: {
+ customFieldsByOrganization: [],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(NO_C_FIELD_MOCK, true);
+const link3 = new StaticMockLink(ERROR_MOCKS, true);
+const link4 = new StaticMockLink(ORGANIZATION_CUSTOM_FIELDS_ERROR_MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Save Button', () => {
+ test('Testing Failure Case For Fetching Custom field', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ expect(
+ screen.queryByText('Failed to fetch custom field'),
+ ).toBeInTheDocument();
+ });
+ test('Saving Organization Custom Field', async () => {
+ render(
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('saveChangesBtn'));
+ await wait();
+ expect(screen.queryByText('Field added successfully')).toBeInTheDocument();
+ });
+
+ test('Testing Failure Case For Saving Custom Field', async () => {
+ render(
+
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByTestId('saveChangesBtn'));
+ await wait();
+ expect(
+ screen.queryByText('Failed to add custom field'),
+ ).toBeInTheDocument();
+ await wait();
+ userEvent.type(screen.getByTestId('customFieldInput'), 'Age{enter}');
+ await wait();
+ expect(
+ screen.queryByText('Failed to add custom field'),
+ ).toBeInTheDocument();
+ });
+
+ test('Testing Typing Organization Custom Field Name', async () => {
+ const { getByTestId } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const fieldNameInput = getByTestId('customFieldInput');
+ userEvent.type(fieldNameInput, 'Age');
+ });
+ test('When No Custom Data is Present', async () => {
+ const { getByText } = render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ expect(getByText('No custom fields available')).toBeInTheDocument();
+ });
+ test('Testing Remove Custom Field Button', async () => {
+ render(
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('removeCustomFieldBtn'));
+ await wait();
+ expect(
+ screen.queryByText('Field removed successfully'),
+ ).toBeInTheDocument();
+ });
+
+ test('Testing Failure Case For Removing Custom Field', async () => {
+ const toastSpy = jest.spyOn(toast, 'error');
+ render(
+
+
+
+
+
+ ,
+ );
+ await wait();
+ userEvent.click(screen.getByTestId('removeCustomFieldBtn'));
+ await wait();
+ expect(toastSpy).toHaveBeenCalledWith('Failed to remove custom field');
+ });
+});
diff --git a/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.tsx b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.tsx
new file mode 100644
index 0000000000..dcb6992e21
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.tsx
@@ -0,0 +1,191 @@
+import { useMutation, useQuery } from '@apollo/client';
+import React, { useState } from 'react';
+import { FaTrash } from 'react-icons/fa';
+import { Form } from 'react-bootstrap';
+import Button from 'react-bootstrap/Button';
+import {
+ ADD_CUSTOM_FIELD,
+ REMOVE_CUSTOM_FIELD,
+} from 'GraphQl/Mutations/mutations';
+import { ORGANIZATION_CUSTOM_FIELDS } from 'GraphQl/Queries/Queries';
+import styles from './OrgProfileFieldSettings.module.css';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import EditOrgCustomFieldDropDown from 'components/EditCustomFieldDropDown/EditCustomFieldDropDown';
+import { useParams } from 'react-router-dom';
+import type { InterfaceCustomFieldData } from 'utils/interfaces';
+
+/**
+ * Component for managing organization profile field settings
+ *
+ * This component allows adding and removing custom fields for an organization.
+ * It displays existing custom fields and provides a form to add new fields.
+ *
+ * @returns JSX.Element representing the organization profile field settings
+ */
+const OrgProfileFieldSettings = (): JSX.Element => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgProfileField',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ // State to hold the custom field data
+ const [customFieldData, setCustomFieldData] =
+ useState({
+ type: '',
+ name: '',
+ });
+
+ // Get the current organization ID from the URL parameters
+ const { orgId: currentOrgId } = useParams();
+
+ // Mutation to add a custom field
+ const [addCustomField] = useMutation(ADD_CUSTOM_FIELD);
+
+ // Mutation to remove a custom field
+ const [removeCustomField] = useMutation(REMOVE_CUSTOM_FIELD);
+
+ // Query to fetch custom fields for the organization
+ const { loading, error, data, refetch } = useQuery(
+ ORGANIZATION_CUSTOM_FIELDS,
+ {
+ variables: {
+ customFieldsByOrganizationId: currentOrgId,
+ },
+ },
+ );
+
+ // Function to handle saving a new custom field
+ const handleSave = async (): Promise => {
+ try {
+ await addCustomField({
+ variables: {
+ organizationId: currentOrgId,
+ ...customFieldData,
+ },
+ });
+ toast.success(t('fieldSuccessMessage') as string);
+ setCustomFieldData({ type: '', name: '' });
+ refetch();
+ } catch (error) {
+ toast.error((error as Error).message);
+ }
+ };
+
+ // Function to handle removing a custom field
+ const handleRemove = async (customFieldId: string): Promise => {
+ try {
+ await removeCustomField({
+ variables: {
+ organizationId: currentOrgId,
+ customFieldId: customFieldId,
+ },
+ });
+
+ toast.success(t('fieldRemovalSuccess') as string);
+ refetch();
+ } catch (error) {
+ toast.error((error as Error).message);
+ }
+ };
+
+ // Render loading or error messages if needed
+ if (loading) return {tCommon('loading')}
;
+ if (error) return {error.message}
;
+
+ return (
+
+ {/* Display existing custom fields or a message if there are none */}
+ {data.customFieldsByOrganization.length === 0 ? (
+
{t('noCustomField')}
+ ) : (
+
+
+ {data.customFieldsByOrganization.map(
+ (
+ field: {
+ _id: string;
+ name: string;
+ type: string;
+ },
+ index: number,
+ ) => (
+
+ {field.name}
+ {field.type}
+
+ {/* Button to remove a custom field */}
+ handleRemove(field._id)}
+ title={t('Remove Custom Field')}
+ data-testid="removeCustomFieldBtn"
+ >
+
+
+
+
+ ),
+ )}
+
+
+ )}
+
+
+
+
+ {/* Form to add a new custom field */}
+
+
+
+
+
+ );
+};
+
+export default OrgProfileFieldSettings;
diff --git a/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.module.css b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.module.css
new file mode 100644
index 0000000000..fca7ac5e5b
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.module.css
@@ -0,0 +1,13 @@
+.message {
+ height: 420px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.icon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
diff --git a/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx
new file mode 100644
index 0000000000..6304bb3ec9
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx
@@ -0,0 +1,242 @@
+import React, { act } from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18nForTest from 'utils/i18nForTest';
+import OrgUpdate from './OrgUpdate';
+import {
+ MOCKS,
+ MOCKS_ERROR_ORGLIST,
+ MOCKS_ERROR_UPDATE_ORGLIST,
+} from './OrgUpdateMocks';
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 500): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Organization Update', () => {
+ const props = {
+ orgId: '123',
+ };
+
+ const formData = {
+ name: 'Palisadoes Organization',
+ description: 'This is a updated description',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ displayImage: new File(['hello'], 'hello.png', { type: 'image/png' }),
+ userRegistrationRequired: false,
+ isVisible: true,
+ };
+
+ global.alert = jest.fn();
+
+ test('should render props and text elements test for the page component along with mock data', async () => {
+ act(() => {
+ render(
+
+
+
+
+ ,
+ );
+ });
+ await wait();
+ // Check labels are present or not
+ expect(screen.getByText('Name')).toBeInTheDocument();
+ expect(screen.getByText('Description')).toBeInTheDocument();
+ expect(screen.getByText('Address')).toBeInTheDocument();
+ expect(screen.getByText('Display Image:')).toBeInTheDocument();
+ expect(screen.getByText(/Registration/)).toBeInTheDocument();
+ expect(screen.getByText('Visible in Search:')).toBeInTheDocument();
+
+ // Get the input fields, and btns
+ const name = screen.getByPlaceholderText(/Enter Organization Name/i);
+ const des = screen.getByPlaceholderText(/Description/i);
+ const city = screen.getByPlaceholderText(/City/i);
+ const countryCode = screen.getByTestId('countrycode');
+ const line1 = screen.getByPlaceholderText(/Line 1/i);
+ const line2 = screen.getByPlaceholderText(/Line 2/i);
+ const dependentLocality =
+ screen.getByPlaceholderText(/Dependent Locality/i);
+ const sortingCode = screen.getByPlaceholderText(/Sorting code/i);
+ const postalCode = screen.getByPlaceholderText(/Postal Code/i);
+ const userRegistrationRequired =
+ screen.getByPlaceholderText(/Registration/i);
+ const isVisible = screen.getByPlaceholderText(/Visible/i);
+
+ // Checking if form fields got updated according to the mock data
+ expect(name).toHaveValue('Palisadoes');
+ expect(des).toHaveValue('Equitable Access to STEM Education Jobs');
+ expect(city).toHaveValue('Kingston');
+ expect(countryCode).toHaveValue('JM');
+ expect(dependentLocality).toHaveValue('Sample Dependent Locality');
+ expect(line1).toHaveValue('123 Jamaica Street');
+ expect(line2).toHaveValue('Apartment 456');
+ expect(postalCode).toHaveValue('JM12345');
+ expect(sortingCode).toHaveValue('ABC-123');
+ expect(userRegistrationRequired).toBeChecked();
+ expect(isVisible).not.toBeChecked();
+ });
+
+ test('Should Update organization properly', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+ ,
+ );
+ });
+
+ await wait();
+
+ // Get the input fields, and btns
+ const name = screen.getByPlaceholderText(/Enter Organization Name/i);
+ const des = screen.getByPlaceholderText(/Description/i);
+
+ const city = screen.getByPlaceholderText(/City/i);
+ const countryCode = screen.getByTestId('countrycode');
+ const line1 = screen.getByPlaceholderText(/Line 1/i);
+ const line2 = screen.getByPlaceholderText(/Line 2/i);
+ const dependentLocality =
+ screen.getByPlaceholderText(/Dependent Locality/i);
+ const sortingCode = screen.getByPlaceholderText(/Sorting code/i);
+ const postalCode = screen.getByPlaceholderText(/Postal Code/i);
+ const displayImage = screen.getByPlaceholderText(/Display Image/i);
+ const userRegistrationRequired =
+ screen.getByPlaceholderText(/Registration/i);
+ const isVisible = screen.getByPlaceholderText(/Visible/i);
+ const saveChangesBtn = screen.getByText(/Save Changes/i);
+
+ // Emptying the text fields to add updated data
+ fireEvent.change(name, { target: { value: '' } });
+ fireEvent.change(des, { target: { value: '' } });
+ fireEvent.change(city, { target: { value: '' } });
+ fireEvent.change(line1, { target: { value: '' } });
+ fireEvent.change(line2, { target: { value: '' } });
+ fireEvent.change(postalCode, { target: { value: '' } });
+ fireEvent.change(sortingCode, { target: { value: '' } });
+ fireEvent.change(dependentLocality, { target: { value: '' } });
+
+ // Mocking filling form behaviour
+ userEvent.type(name, formData.name);
+ userEvent.type(des, formData.description);
+ userEvent.type(city, formData.address.city);
+ userEvent.selectOptions(countryCode, formData.address.countryCode);
+ userEvent.type(line1, formData.address.line1);
+ userEvent.type(line2, formData.address.line2);
+ userEvent.type(postalCode, formData.address.postalCode);
+ userEvent.type(dependentLocality, formData.address.dependentLocality);
+ userEvent.type(sortingCode, formData.address.sortingCode);
+ userEvent.upload(displayImage, formData.displayImage);
+ userEvent.click(userRegistrationRequired);
+ userEvent.click(isVisible);
+
+ await wait();
+ userEvent.click(saveChangesBtn);
+
+ // Checking if the form got update accordingly
+ expect(name).toHaveValue(formData.name);
+ expect(des).toHaveValue(formData.description);
+ expect(city).toHaveValue(formData.address.city);
+ expect(countryCode).toHaveValue(formData.address.countryCode);
+ expect(dependentLocality).toHaveValue(formData.address.dependentLocality);
+ expect(line1).toHaveValue(formData.address.line1);
+ expect(line2).toHaveValue(formData.address.line2);
+ expect(postalCode).toHaveValue(formData.address.postalCode);
+ expect(sortingCode).toHaveValue(formData.address.sortingCode);
+ expect(displayImage).toBeTruthy();
+ expect(userRegistrationRequired).not.toBeChecked();
+ expect(isVisible).toBeChecked();
+ });
+
+ test('Should render error occured text when Organization Could not be found', async () => {
+ act(() => {
+ render(
+
+
+
+
+ ,
+ );
+ });
+ await wait();
+ expect(screen.getByText(/Mock Graphql Error/i)).toBeInTheDocument();
+ });
+
+ test('Should show error occured toast when Organization could not be updated', async () => {
+ await act(async () => {
+ render(
+
+
+
+
+ ,
+ );
+ });
+
+ await wait();
+
+ // Get the input fields, and btns
+ const name = screen.getByPlaceholderText(/Enter Organization Name/i);
+ const des = screen.getByPlaceholderText(/Description/i);
+ const city = screen.getByPlaceholderText(/City/i);
+ const countryCode = screen.getByTestId('countrycode');
+ const line1 = screen.getByPlaceholderText(/Line 1/i);
+ const line2 = screen.getByPlaceholderText(/Line 2/i);
+ const dependentLocality =
+ screen.getByPlaceholderText(/Dependent Locality/i);
+ const sortingCode = screen.getByPlaceholderText(/Sorting code/i);
+ const postalCode = screen.getByPlaceholderText(/Postal Code/i);
+ const displayImage = screen.getByPlaceholderText(/Display Image/i);
+ const userRegistrationRequired =
+ screen.getByPlaceholderText(/Registration/i);
+ const isVisible = screen.getByPlaceholderText(/Visible/i);
+ const saveChangesBtn = screen.getByText(/Save Changes/i);
+
+ // Emptying the text fields to add updated data
+ fireEvent.change(name, { target: { value: '' } });
+ fireEvent.change(des, { target: { value: '' } });
+ fireEvent.change(city, { target: { value: '' } });
+ fireEvent.change(line1, { target: { value: '' } });
+ fireEvent.change(line2, { target: { value: '' } });
+ fireEvent.change(postalCode, { target: { value: '' } });
+ fireEvent.change(sortingCode, { target: { value: '' } });
+ fireEvent.change(dependentLocality, { target: { value: '' } });
+
+ // Mocking filling form behaviour
+ userEvent.type(name, formData.name);
+ userEvent.type(des, formData.description);
+ userEvent.type(city, formData.address.city);
+ userEvent.selectOptions(countryCode, formData.address.countryCode);
+ userEvent.type(line1, formData.address.line1);
+ userEvent.type(line2, formData.address.line2);
+ userEvent.type(postalCode, formData.address.postalCode);
+ userEvent.type(dependentLocality, formData.address.dependentLocality);
+ userEvent.type(sortingCode, formData.address.sortingCode);
+ userEvent.upload(displayImage, formData.displayImage);
+ userEvent.click(userRegistrationRequired);
+ userEvent.click(isVisible);
+
+ await wait();
+ userEvent.click(saveChangesBtn);
+ });
+});
diff --git a/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.tsx b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.tsx
new file mode 100644
index 0000000000..6e0be28a56
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.tsx
@@ -0,0 +1,359 @@
+import React, { useState, useEffect } from 'react';
+import { useMutation, useQuery } from '@apollo/client';
+import Button from 'react-bootstrap/Button';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+
+import type { ApolloError } from '@apollo/client';
+import { WarningAmberRounded } from '@mui/icons-material';
+import { UPDATE_ORGANIZATION_MUTATION } from 'GraphQl/Mutations/mutations';
+import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
+import Loader from 'components/Loader/Loader';
+import { Col, Form, Row } from 'react-bootstrap';
+import convertToBase64 from 'utils/convertToBase64';
+import { errorHandler } from 'utils/errorHandler';
+import styles from './OrgUpdate.module.css';
+import type {
+ InterfaceQueryOrganizationsListObject,
+ InterfaceAddress,
+} from 'utils/interfaces';
+import { countryOptions } from 'utils/formEnumFields';
+
+interface InterfaceOrgUpdateProps {
+ orgId: string;
+}
+
+/**
+ * Component for updating organization details.
+ *
+ * This component allows users to update the organization's name, description, address,
+ * visibility settings, and upload an image. It uses GraphQL mutations and queries to
+ * fetch and update data.
+ *
+ * @param props - Component props containing the organization ID.
+ * @returns The rendered component.
+ */
+function orgUpdate(props: InterfaceOrgUpdateProps): JSX.Element {
+ const { orgId } = props;
+
+ const [formState, setFormState] = useState<{
+ orgName: string;
+ orgDescrip: string;
+ address: InterfaceAddress;
+ orgImage: string | null;
+ }>({
+ orgName: '',
+ orgDescrip: '',
+ address: {
+ city: '',
+ countryCode: '',
+ dependentLocality: '',
+ line1: '',
+ line2: '',
+ postalCode: '',
+ sortingCode: '',
+ state: '',
+ },
+ orgImage: null,
+ });
+
+ const handleInputChange = (fieldName: string, value: string): void => {
+ setFormState((prevState) => ({
+ ...prevState,
+ address: {
+ ...prevState.address,
+ [fieldName]: value,
+ },
+ }));
+ };
+
+ const [userRegistrationRequiredChecked, setuserRegistrationRequiredChecked] =
+ React.useState(false);
+ const [visiblechecked, setVisibleChecked] = React.useState(false);
+
+ const [login] = useMutation(UPDATE_ORGANIZATION_MUTATION);
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'orgUpdate',
+ });
+ const { t: tCommon } = useTranslation('common');
+
+ const {
+ data,
+ loading,
+ refetch,
+ error,
+ }: {
+ data?: {
+ organizations: InterfaceQueryOrganizationsListObject[];
+ };
+ loading: boolean;
+ refetch: (variables: { id: string }) => void;
+ error?: ApolloError;
+ } = useQuery(ORGANIZATIONS_LIST, {
+ variables: { id: orgId },
+ notifyOnNetworkStatusChange: true,
+ });
+
+ // Update form state when data changes
+ useEffect(() => {
+ let isMounted = true;
+ if (data && isMounted) {
+ setFormState({
+ ...formState,
+ orgName: data.organizations[0].name,
+ orgDescrip: data.organizations[0].description,
+ address: data.organizations[0].address,
+ });
+ setuserRegistrationRequiredChecked(
+ data.organizations[0].userRegistrationRequired,
+ );
+ setVisibleChecked(data.organizations[0].visibleInSearch);
+ }
+ return () => {
+ isMounted = false;
+ };
+ }, [data, orgId]);
+
+ /**
+ * Handles the save button click event.
+ * Updates the organization with the form data.
+ */
+ const onSaveChangesClicked = async (): Promise => {
+ try {
+ const { data } = await login({
+ variables: {
+ id: orgId,
+ name: formState.orgName,
+ description: formState.orgDescrip,
+ address: {
+ city: formState.address.city,
+ countryCode: formState.address.countryCode,
+ dependentLocality: formState.address.dependentLocality,
+ line1: formState.address.line1,
+ line2: formState.address.line2,
+ postalCode: formState.address.postalCode,
+ sortingCode: formState.address.sortingCode,
+ state: formState.address.state,
+ },
+ userRegistrationRequired: userRegistrationRequiredChecked,
+ visibleInSearch: visiblechecked,
+ file: formState.orgImage,
+ },
+ });
+ // istanbul ignore next
+ if (data) {
+ refetch({ id: orgId });
+ toast.success(t('successfulUpdated') as string);
+ }
+ } catch (error: unknown) {
+ errorHandler(t, error);
+ }
+ };
+
+ if (loading) {
+ return ;
+ }
+
+ if (error) {
+ return (
+
+
+
+ Error occured while loading Organization Data
+
+ {`${error.message}`}
+
+
+ );
+ }
+
+ return (
+ <>
+
+
{tCommon('name')}
+
{
+ setFormState({
+ ...formState,
+ orgName: e.target.value,
+ });
+ }}
+ />
+ {tCommon('description')}
+ {
+ setFormState({
+ ...formState,
+ orgDescrip: e.target.value,
+ });
+ }}
+ />
+ {tCommon('address')}
+
+
+ {
+ const countryCode = e.target.value;
+ handleInputChange('countryCode', countryCode);
+ }}
+ >
+
+ Select a country
+
+ {countryOptions.map((country) => (
+
+ {country.label}
+
+ ))}
+
+
+
+ handleInputChange('city', e.target.value)}
+ />
+
+
+
+
+ handleInputChange('state', e.target.value)}
+ />
+
+
+
+ handleInputChange('dependentLocality', e.target.value)
+ }
+ />
+
+
+
+
+ handleInputChange('line1', e.target.value)}
+ />
+
+
+ handleInputChange('line2', e.target.value)}
+ />
+
+
+
+
+
+ handleInputChange('postalCode', e.target.value)
+ }
+ />
+
+
+
+ handleInputChange('sortingCode', e.target.value)
+ }
+ />
+
+
+
+
+
+ {t('userRegistrationRequired')}:
+
+
+ setuserRegistrationRequiredChecked(
+ !userRegistrationRequiredChecked,
+ )
+ }
+ />
+
+
+
+ {t('isVisibleInSearch')}:
+
+ setVisibleChecked(!visiblechecked)}
+ />
+
+
+ {tCommon('displayImage')}:
+ => {
+ const target = e.target as HTMLInputElement;
+ const file = target.files && target.files[0];
+ /* istanbul ignore else */
+ if (file)
+ setFormState({
+ ...formState,
+ orgImage: await convertToBase64(file),
+ });
+ }}
+ data-testid="organisationImage"
+ />
+
+
+ {tCommon('saveChanges')}
+
+
+
+
+ >
+ );
+}
+export default orgUpdate;
diff --git a/src/components/OrgSettings/General/OrgUpdate/OrgUpdateMocks.ts b/src/components/OrgSettings/General/OrgUpdate/OrgUpdateMocks.ts
new file mode 100644
index 0000000000..4c7f704719
--- /dev/null
+++ b/src/components/OrgSettings/General/OrgUpdate/OrgUpdateMocks.ts
@@ -0,0 +1,204 @@
+import { UPDATE_ORGANIZATION_MUTATION } from 'GraphQl/Mutations/mutations';
+import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '123',
+ image: null,
+ name: 'Palisadoes',
+ description: 'Equitable Access to STEM Education Jobs',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: false,
+ creator: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@example.com',
+ },
+ members: {
+ _id: '123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@gmail.com',
+ },
+ admins: [
+ {
+ _id: '123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@gmail.com',
+ createdAt: '12-03-2024',
+ },
+ ],
+ membershipRequests: {
+ _id: '456',
+ user: {
+ firstName: 'Sam',
+ lastName: 'Smith',
+ email: 'samsmith@gmail.com',
+ },
+ },
+ blockedUsers: [],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_ORGANIZATION_MUTATION,
+ variables: {
+ id: '123',
+ name: 'Updated Organization',
+ description: 'This is an updated test organization',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ image: new File(['hello'], 'hello.png', { type: 'image/png' }),
+ userRegistrationRequired: true,
+ visibleInSearch: false,
+ },
+ },
+ result: {
+ data: {
+ updateOrganization: {
+ _id: '123',
+ name: 'Updated Organization',
+ description: 'This is an updated test organization',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: false,
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR_ORGLIST = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ error: new Error('Mock Graphql Error'),
+ },
+];
+
+export const MOCKS_ERROR_UPDATE_ORGLIST = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '123',
+ image: null,
+ name: 'Palisadoes',
+ description: 'Equitable Access to STEM Education Jobs',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ userRegistrationRequired: true,
+ visibleInSearch: false,
+ creator: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@example.com',
+ },
+ members: {
+ _id: '123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@gmail.com',
+ },
+ admins: [
+ {
+ _id: '123',
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'johndoe@gmail.com',
+ createdAt: '12-03-2024',
+ },
+ ],
+ membershipRequests: {
+ _id: '456',
+ user: {
+ firstName: 'Sam',
+ lastName: 'Smith',
+ email: 'samsmith@gmail.com',
+ },
+ },
+ blockedUsers: [],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_ORGANIZATION_MUTATION,
+ variables: {
+ id: '123',
+ name: 'Updated Organization',
+ description: 'This is an updated test organization',
+ address: {
+ city: 'Kingston',
+ countryCode: 'JM',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Jamaica Street',
+ line2: 'Apartment 456',
+ postalCode: 'JM12345',
+ sortingCode: 'ABC-123',
+ state: 'Kingston Parish',
+ },
+ image: new File(['hello'], 'hello.png', { type: 'image/png' }),
+ userRegistrationRequired: true,
+ visibleInSearch: false,
+ },
+ },
+ erorr: new Error('Mock Graphql Updating Organization Error'),
+ },
+];
diff --git a/src/components/OrganizationCard/OrganizationCard.module.css b/src/components/OrganizationCard/OrganizationCard.module.css
new file mode 100644
index 0000000000..6c65b8258b
--- /dev/null
+++ b/src/components/OrganizationCard/OrganizationCard.module.css
@@ -0,0 +1,46 @@
+.alignimg {
+ border-radius: 50%;
+ background-blend-mode: darken;
+ height: 65px;
+ width: 65px;
+}
+
+.box {
+ color: #ffbd59;
+}
+
+.box :hover {
+ color: #ffbd59;
+}
+
+.first_box {
+ display: flex;
+ flex-direction: row;
+ padding-bottom: 10px;
+ padding-top: 10px;
+}
+
+.second_box {
+ padding-left: 20px;
+ padding-top: 10px;
+}
+
+.second_box > h4 {
+ font-size: 10;
+ font-weight: bold;
+ text-decoration: none;
+ color: black;
+}
+
+.second_box > h5 {
+ text-decoration: none;
+ font-size: 10;
+ font-weight: 100;
+ color: #969696;
+}
+
+.deco {
+ border: 1px solid #dfdfdf;
+ width: 65vw;
+ height: 0px !important;
+}
diff --git a/src/components/OrganizationCard/OrganizationCard.test.tsx b/src/components/OrganizationCard/OrganizationCard.test.tsx
new file mode 100644
index 0000000000..e4abf3a1fd
--- /dev/null
+++ b/src/components/OrganizationCard/OrganizationCard.test.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import OrganizationCard from './OrganizationCard';
+
+describe('Testing the Organization Card', () => {
+ test('should render props and text elements test for the page component', () => {
+ const props = {
+ id: '123',
+ image: 'https://via.placeholder.com/80',
+ firstName: 'John',
+ lastName: 'Doe',
+ name: 'Sample',
+ };
+
+ render( );
+
+ expect(screen.getByText(props.name)).toBeInTheDocument();
+ expect(screen.getByText(/Owner:/i)).toBeInTheDocument();
+ expect(screen.getByText(props.firstName)).toBeInTheDocument();
+ expect(screen.getByText(props.lastName)).toBeInTheDocument();
+ });
+
+ test('Should render text elements when props value is not passed', () => {
+ const props = {
+ id: '123',
+ image: '',
+ firstName: 'John',
+ lastName: 'Doe',
+ name: 'Sample',
+ };
+
+ render( );
+
+ expect(screen.getByText(props.name)).toBeInTheDocument();
+ expect(screen.getByText(/Owner:/i)).toBeInTheDocument();
+ expect(screen.getByText(props.firstName)).toBeInTheDocument();
+ expect(screen.getByText(props.lastName)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationCard/OrganizationCard.tsx b/src/components/OrganizationCard/OrganizationCard.tsx
new file mode 100644
index 0000000000..ae513eff5d
--- /dev/null
+++ b/src/components/OrganizationCard/OrganizationCard.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import styles from './OrganizationCard.module.css';
+
+interface InterfaceOrganizationCardProps {
+ image: string;
+ id: string;
+ name: string;
+ lastName: string;
+ firstName: string;
+}
+
+/**
+ * Component to display an organization's card with its image and owner details.
+ *
+ * @param props - Properties for the organization card.
+ * @returns JSX element representing the organization card.
+ */
+function OrganizationCard(props: InterfaceOrganizationCardProps): JSX.Element {
+ const uri = '/superorghome/i=' + props.id;
+
+ return (
+
+
+
+ {props.image ? (
+
+ ) : (
+
+ )}
+
+
{props.name}
+
+ Owner:
+ {props.firstName}
+
+
+ {props.lastName}
+
+
+
+
+
+
+
+ );
+}
+
+export default OrganizationCard;
diff --git a/src/components/OrganizationCardStart/OrganizationCardStart.module.css b/src/components/OrganizationCardStart/OrganizationCardStart.module.css
new file mode 100644
index 0000000000..6c65b8258b
--- /dev/null
+++ b/src/components/OrganizationCardStart/OrganizationCardStart.module.css
@@ -0,0 +1,46 @@
+.alignimg {
+ border-radius: 50%;
+ background-blend-mode: darken;
+ height: 65px;
+ width: 65px;
+}
+
+.box {
+ color: #ffbd59;
+}
+
+.box :hover {
+ color: #ffbd59;
+}
+
+.first_box {
+ display: flex;
+ flex-direction: row;
+ padding-bottom: 10px;
+ padding-top: 10px;
+}
+
+.second_box {
+ padding-left: 20px;
+ padding-top: 10px;
+}
+
+.second_box > h4 {
+ font-size: 10;
+ font-weight: bold;
+ text-decoration: none;
+ color: black;
+}
+
+.second_box > h5 {
+ text-decoration: none;
+ font-size: 10;
+ font-weight: 100;
+ color: #969696;
+}
+
+.deco {
+ border: 1px solid #dfdfdf;
+ width: 65vw;
+ height: 0px !important;
+}
diff --git a/src/components/OrganizationCardStart/OrganizationCardStart.test.tsx b/src/components/OrganizationCardStart/OrganizationCardStart.test.tsx
new file mode 100644
index 0000000000..dd65c8649e
--- /dev/null
+++ b/src/components/OrganizationCardStart/OrganizationCardStart.test.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import OrganizationCardStart from './OrganizationCardStart';
+
+describe('Testing the Organization Cards', () => {
+ test('should render props and text elements test for the page component', () => {
+ const props = {
+ id: '123',
+ image: 'https://via.placeholder.com/80',
+ name: 'Sample',
+ };
+
+ render( );
+
+ expect(screen.getByText(props.name)).toBeInTheDocument();
+ });
+
+ test('Should render text elements when props value is not passed', () => {
+ const props = {
+ id: '123',
+ image: '',
+ name: 'Sample',
+ };
+
+ render( );
+
+ expect(screen.getByText(props.name)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationCardStart/OrganizationCardStart.tsx b/src/components/OrganizationCardStart/OrganizationCardStart.tsx
new file mode 100644
index 0000000000..298fb13db9
--- /dev/null
+++ b/src/components/OrganizationCardStart/OrganizationCardStart.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import styles from './OrganizationCardStart.module.css';
+
+interface InterfaceOrganizationCardStartProps {
+ image: string;
+ id: string;
+ name: string;
+}
+
+/**
+ * Component to display a simplified card for an organization.
+ *
+ * @param image - URL of the organization's image.
+ * @param id - Unique identifier for the organization.
+ * @param name - Name of the organization.
+ * @returns JSX element representing the organization card.
+ */
+function organizationCardStart(
+ props: InterfaceOrganizationCardStartProps,
+): JSX.Element {
+ const uri = '/orghome/i=' + props.id;
+
+ return (
+ <>
+
+
+
+ {props.image ? (
+
+ ) : (
+
+ )}
+
+
{props.name}
+
+
+
+
+
+
+ >
+ );
+}
+
+export default organizationCardStart;
diff --git a/src/components/OrganizationDashCards/CardItem.module.css b/src/components/OrganizationDashCards/CardItem.module.css
new file mode 100644
index 0000000000..bfb85cb1bb
--- /dev/null
+++ b/src/components/OrganizationDashCards/CardItem.module.css
@@ -0,0 +1,81 @@
+.cardItem {
+ position: relative;
+ display: flex;
+ align-items: center;
+ border: 1px solid var(--bs-gray-200);
+ border-radius: 8px;
+ margin-top: 20px;
+}
+
+.cardItem .iconWrapper {
+ position: relative;
+ height: 40px;
+ width: 40px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.cardItem .iconWrapper .themeOverlay {
+ background: var(--bs-primary);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ opacity: 0.12;
+ border-radius: 50%;
+}
+
+.cardItem .iconWrapper .dangerOverlay {
+ background: var(--bs-danger);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ opacity: 0.12;
+ border-radius: 50%;
+}
+
+.cardItem .title {
+ font-size: 1rem;
+ flex: 1;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+ margin-left: 3px;
+}
+
+.cardItem .location {
+ font-size: 0.9rem;
+ color: var(--bs-primary);
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+}
+
+.cardItem .time {
+ font-size: 0.9rem;
+ color: var(--bs-secondary);
+}
+
+.cardItem .creator {
+ font-size: 1rem;
+ color: rgb(33, 208, 21);
+}
+
+.rightCard {
+ display: flex;
+ gap: 7px;
+ min-width: 170px;
+ justify-content: center;
+ flex-direction: column;
+ margin-left: 10px;
+ overflow-x: hidden;
+ width: 210px;
+}
diff --git a/src/components/OrganizationDashCards/CardItem.test.tsx b/src/components/OrganizationDashCards/CardItem.test.tsx
new file mode 100644
index 0000000000..31f3474607
--- /dev/null
+++ b/src/components/OrganizationDashCards/CardItem.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import CardItem from './CardItem';
+import type { InterfaceCardItem } from './CardItem';
+import dayjs from 'dayjs';
+
+describe('Testing the Organization Card', () => {
+ test('Should render props and text elements For event card', () => {
+ const props: InterfaceCardItem = {
+ type: 'Event',
+ title: 'Event Title',
+ startdate: '2023-09-13',
+ enddate: '2023-09-14',
+ location: 'Event Location',
+ };
+
+ render( );
+
+ expect(screen.getByText(/Event Title/i)).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ `${dayjs(props.startdate).format('MMM D, YYYY')} - ${dayjs(
+ props.enddate,
+ ).format('MMM D, YYYY')}`,
+ ),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/Event Location/i)).toBeInTheDocument();
+ });
+
+ test('Should render props and text elements for Post card', () => {
+ const props: InterfaceCardItem = {
+ type: 'Post',
+ title: 'Post Title',
+ time: '2023-09-03',
+ creator: {
+ email: 'johndoe@example.com',
+ firstName: 'John',
+ lastName: 'Doe',
+ __typename: 'User',
+ _id: '1',
+ },
+ };
+
+ render( );
+
+ expect(screen.getByText(/Post Title/i)).toBeInTheDocument();
+ expect(
+ screen.getByText(dayjs(props.time).format('MMM D, YYYY')),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/John Doe/i)).toBeInTheDocument();
+ });
+
+ test('Should render props and text elements for Membership Request card', () => {
+ const props: InterfaceCardItem = {
+ type: 'MembershipRequest',
+ title: 'Membership Request Title',
+ };
+
+ render( );
+ expect(screen.getByText(/Membership Request Title/i)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationDashCards/CardItem.tsx b/src/components/OrganizationDashCards/CardItem.tsx
new file mode 100644
index 0000000000..a7cfaa0f57
--- /dev/null
+++ b/src/components/OrganizationDashCards/CardItem.tsx
@@ -0,0 +1,123 @@
+import React from 'react';
+import EventsIcon from 'assets/svgs/cardItemEvent.svg?react';
+import PostsIcon from 'assets/svgs/post.svg?react';
+import MarkerIcon from 'assets/svgs/cardItemLocation.svg?react';
+import DateIcon from 'assets/svgs/cardItemDate.svg?react';
+import UserIcon from 'assets/svgs/user.svg?react';
+import dayjs from 'dayjs';
+import styles from './CardItem.module.css';
+import { PersonAddAlt1Rounded } from '@mui/icons-material';
+
+/**
+ * Interface for the CardItem component's props.
+ */
+export interface InterfaceCardItem {
+ type: 'Event' | 'Post' | 'MembershipRequest';
+ title: string;
+ time?: string;
+ startdate?: string;
+ enddate?: string;
+ creator?: any;
+ location?: string;
+}
+
+/**
+ * Component to display a card item with various types such as Event, Post, or MembershipRequest.
+ *
+ * @param props - Props for the CardItem component.
+ * @returns JSX element representing the card item.
+ */
+const cardItem = (props: InterfaceCardItem): JSX.Element => {
+ const { creator, type, title, startdate, time, enddate, location } = props;
+ return (
+ <>
+
+
+
+ {type == 'Event' ? (
+
+ ) : type == 'Post' ? (
+
+ ) : (
+ type == 'MembershipRequest' && (
+
+ )
+ )}
+
+
+
+ {creator && (
+
+ {' '}
+ {' '}
+
+ {creator.firstName} {creator.lastName}
+
+
+ )}
+
+ {title && (
+
+ {title}
+
+ )}
+
+ {location && (
+
+ {' '}
+ {location}
+
+ )}
+ {type == 'Event' && startdate && (
+
+ {type === 'Event' && (
+
+ )}{' '}
+ {dayjs(startdate).format('MMM D, YYYY')} -{' '}
+ {dayjs(enddate).format('MMM D, YYYY')}
+
+ )}
+ {type == 'Post' && time && (
+
+ {type === 'Post' && (
+
+ )}{' '}
+ {dayjs(time).format('MMM D, YYYY')}
+
+ )}
+
+
+ >
+ );
+};
+
+export default cardItem;
diff --git a/src/components/OrganizationDashCards/CardItemLoading.tsx b/src/components/OrganizationDashCards/CardItemLoading.tsx
new file mode 100644
index 0000000000..c7c666afb6
--- /dev/null
+++ b/src/components/OrganizationDashCards/CardItemLoading.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import styles from './CardItem.module.css';
+
+/**
+ * CardItemLoading component is a loading state for the card item. It is used when the data is being fetched.
+ * @returns JSX.Element
+ */
+const cardItemLoading = (): JSX.Element => {
+ return (
+ <>
+
+ >
+ );
+};
+
+export default cardItemLoading;
diff --git a/src/components/OrganizationDashCards/DashboardCard.test.tsx b/src/components/OrganizationDashCards/DashboardCard.test.tsx
new file mode 100644
index 0000000000..71e5e1fed0
--- /dev/null
+++ b/src/components/OrganizationDashCards/DashboardCard.test.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import DashboardCard from './DashboardCard';
+
+describe('Testing the Dashboard Card', () => {
+ test('should render props and text elements For event card', () => {
+ const props = {
+ icon: ,
+ title: 'Example Title',
+ count: 100,
+ };
+
+ render( );
+
+ expect(screen.getByText(/Example Title/i)).toBeInTheDocument();
+ expect(screen.getByText(/100/i)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationDashCards/DashboardCard.tsx b/src/components/OrganizationDashCards/DashboardCard.tsx
new file mode 100644
index 0000000000..4a29eafc57
--- /dev/null
+++ b/src/components/OrganizationDashCards/DashboardCard.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { Card, Row } from 'react-bootstrap';
+import Col from 'react-bootstrap/Col';
+import styles from './Dashboardcard.module.css';
+
+/** Dashboard card component is used to display the card with icon, title and count.
+ * @param icon - Icon for the card
+ * @param title - Title for the card
+ * @param count - Count for the card
+ * @returns Dashboard card component
+ *
+ */
+const dashBoardCard = (props: {
+ icon: React.ReactNode;
+ title: string;
+ count?: number;
+}): JSX.Element => {
+ const { icon, count, title } = props;
+ return (
+
+
+
+
+
+
+
+ {count ?? 0}
+ {title}
+
+
+
+
+ );
+};
+
+export default dashBoardCard;
diff --git a/src/components/OrganizationDashCards/DashboardCardLoading.tsx b/src/components/OrganizationDashCards/DashboardCardLoading.tsx
new file mode 100644
index 0000000000..b37c1e2065
--- /dev/null
+++ b/src/components/OrganizationDashCards/DashboardCardLoading.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Card, Row } from 'react-bootstrap';
+import Col from 'react-bootstrap/Col';
+import styles from './Dashboardcard.module.css';
+
+/**
+ * Dashboard card loading component is a loading state for the dashboard card. It is used when the data is being fetched.
+ * @returns JSX.Element
+ */
+const dashBoardCardLoading = (): JSX.Element => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default dashBoardCardLoading;
diff --git a/src/components/OrganizationDashCards/Dashboardcard.module.css b/src/components/OrganizationDashCards/Dashboardcard.module.css
new file mode 100644
index 0000000000..365657fb4f
--- /dev/null
+++ b/src/components/OrganizationDashCards/Dashboardcard.module.css
@@ -0,0 +1,60 @@
+.cardBody {
+ padding: 1.25rem 1.5rem;
+}
+
+.cardBody .iconWrapper {
+ position: relative;
+ height: 48px;
+ width: 48px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.cardBody .iconWrapper .themeOverlay {
+ background: var(--bs-primary);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ opacity: 0.12;
+ border-radius: 50%;
+}
+
+.cardBody .textWrapper .primaryText {
+ font-size: 24px;
+ font-weight: bold;
+ display: block;
+}
+
+.cardBody .textWrapper .secondaryText {
+ font-size: 14px;
+ display: block;
+ color: var(--bs-secondary);
+}
+
+@media (max-width: 600px) {
+ .cardBody {
+ min-height: 120px;
+ }
+
+ .cardBody .iconWrapper {
+ position: absolute;
+ top: 1rem;
+ left: 1rem;
+ }
+
+ .cardBody .textWrapper {
+ margin-top: calc(0.5rem + 36px);
+ text-align: right;
+ }
+
+ .cardBody .textWrapper .primaryText {
+ font-size: 1.5rem;
+ }
+
+ .cardBody .textWrapper .secondaryText {
+ font-size: 1rem;
+ }
+}
diff --git a/src/components/OrganizationScreen/OrganizationScreen.module.css b/src/components/OrganizationScreen/OrganizationScreen.module.css
new file mode 100644
index 0000000000..9b8190a3ad
--- /dev/null
+++ b/src/components/OrganizationScreen/OrganizationScreen.module.css
@@ -0,0 +1,178 @@
+.pageContainer {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ padding: 1rem 1.5rem 0 calc(300px + 2rem + 1.5rem);
+}
+
+.expand {
+ padding-left: 4rem;
+ animation: moveLeft 0.5s ease-in-out;
+}
+.avatarStyle {
+ border-radius: 100%;
+}
+.profileContainer {
+ border: none;
+ padding: 2.1rem 0.5rem;
+ height: 52px;
+ border-radius: 8px 0px 0px 8px;
+ display: flex;
+ align-items: center;
+ background-color: white !important;
+ box-shadow:
+ 0 4px 4px 0 rgba(177, 177, 177, 0.2),
+ 0 6px 20px 0 rgba(151, 151, 151, 0.19);
+}
+.profileContainer:focus {
+ outline: none;
+ background-color: var(--bs-gray-100);
+}
+.imageContainer {
+ width: 56px;
+}
+.profileContainer .profileText {
+ flex: 1;
+ text-align: start;
+ overflow: hidden;
+ margin-right: 4px;
+}
+.angleDown {
+ margin-left: 4px;
+}
+.profileContainer .profileText .primaryText {
+ font-size: 1rem;
+ font-weight: 600;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2; /* number of lines to show */
+ -webkit-box-orient: vertical;
+ word-wrap: break-word;
+ white-space: normal;
+}
+.profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ font-weight: 400;
+ color: var(--bs-secondary);
+ display: block;
+ text-transform: capitalize;
+}
+
+.contract {
+ padding-left: calc(300px + 2rem + 1.5rem);
+ animation: moveRight 0.5s ease-in-out;
+}
+
+.collapseSidebarButton {
+ position: fixed;
+ height: 40px;
+ bottom: 0;
+ z-index: 9999;
+ width: calc(300px + 2rem);
+ background-color: rgba(245, 245, 245, 0.7);
+ color: black;
+ border: none;
+ border-radius: 0px;
+}
+
+.collapseSidebarButton:hover,
+.opendrawer:hover {
+ opacity: 1;
+ color: black !important;
+}
+.opendrawer {
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 100vh;
+ z-index: 9999;
+ background-color: rgba(245, 245, 245);
+ border: none;
+ border-radius: 0px;
+ margin-right: 20px;
+ color: black;
+}
+.profileDropdown {
+ background-color: transparent !important;
+}
+.profileDropdown .dropdown-toggle .btn .btn-normal {
+ display: none !important;
+ background-color: transparent !important;
+}
+.dropdownToggle {
+ background-image: url(/public/images/svg/angleDown.svg);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-color: azure;
+}
+
+.dropdownToggle::after {
+ border-top: none !important;
+ border-bottom: none !important;
+}
+
+.opendrawer:hover {
+ transition: background-color 0.5s ease;
+ background-color: var(--bs-primary);
+}
+.collapseSidebarButton:hover {
+ transition: background-color 0.5s ease;
+ background-color: var(--bs-primary);
+}
+
+@media (max-width: 1120px) {
+ .contract {
+ padding-left: calc(276px + 2rem + 1.5rem);
+ }
+ .collapseSidebarButton {
+ width: calc(250px + 2rem);
+ }
+}
+
+@media (max-height: 900px) {
+ .pageContainer {
+ padding: 1rem 1.5rem 0 calc(300px + 2rem);
+ }
+ .collapseSidebarButton {
+ height: 30px;
+ width: calc(300px + 1rem);
+ }
+}
+@media (max-height: 650px) {
+ .pageContainer {
+ padding: 1rem 1.5rem 0 calc(270px);
+ }
+ .collapseSidebarButton {
+ width: 250px;
+ height: 20px;
+ }
+ .opendrawer {
+ width: 30px;
+ }
+}
+
+/* For tablets */
+@media (max-width: 820px) {
+ .pageContainer {
+ padding-left: 2.5rem;
+ }
+
+ .opendrawer {
+ width: 25px;
+ }
+
+ .contract,
+ .expand {
+ animation: none;
+ }
+
+ .collapseSidebarButton {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+}
diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx
new file mode 100644
index 0000000000..cd039cc3ca
--- /dev/null
+++ b/src/components/OrganizationScreen/OrganizationScreen.test.tsx
@@ -0,0 +1,103 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { I18nextProvider } from 'react-i18next';
+import 'jest-location-mock';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import OrganizationScreen from './OrganizationScreen';
+import { ORGANIZATION_EVENT_LIST } from 'GraphQl/Queries/Queries';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import styles from './OrganizationScreen.module.css';
+
+const mockID: string | undefined = '123';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: mockID }),
+ useMatch: () => ({ params: { eventId: 'event123', orgId: '123' } }),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATION_EVENT_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ eventsByOrganization: [
+ {
+ _id: 'event123',
+ title: 'Test Event Title',
+ description: 'Test Description',
+ startDate: '2024-01-01',
+ endDate: '2024-01-02',
+ location: 'Test Location',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ recurring: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ ],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+describe('Testing OrganizationScreen', () => {
+ const renderComponent = (): void => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ };
+
+ test('renders correctly with event title', async () => {
+ renderComponent();
+
+ await waitFor(() => {
+ const mainPage = screen.getByTestId('mainpageright');
+ expect(mainPage).toBeInTheDocument();
+ });
+ });
+
+ test('handles drawer toggle correctly', () => {
+ renderComponent();
+
+ const closeButton = screen.getByTestId('closeMenu');
+ fireEvent.click(closeButton);
+
+ // Check for contract class after closing
+ expect(screen.getByTestId('mainpageright')).toHaveClass('_expand_ccl5z_8');
+
+ const openButton = screen.getByTestId('openMenu');
+ fireEvent.click(openButton);
+
+ // Check for expand class after opening
+ expect(screen.getByTestId('mainpageright')).toHaveClass(
+ '_contract_ccl5z_61',
+ );
+ });
+
+ test('handles window resize', () => {
+ renderComponent();
+
+ window.innerWidth = 800;
+ fireEvent(window, new Event('resize'));
+
+ expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand);
+ });
+});
diff --git a/src/components/OrganizationScreen/OrganizationScreen.tsx b/src/components/OrganizationScreen/OrganizationScreen.tsx
new file mode 100644
index 0000000000..85fb6ee181
--- /dev/null
+++ b/src/components/OrganizationScreen/OrganizationScreen.tsx
@@ -0,0 +1,189 @@
+import LeftDrawerOrg from 'components/LeftDrawerOrg/LeftDrawerOrg';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import {
+ Navigate,
+ Outlet,
+ useLocation,
+ useParams,
+ useMatch,
+} from 'react-router-dom';
+import { updateTargets } from 'state/action-creators';
+import { useAppDispatch } from 'state/hooks';
+import type { RootState } from 'state/reducers';
+import type { TargetsType } from 'state/reducers/routesReducer';
+import styles from './OrganizationScreen.module.css';
+import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown';
+import { Button } from 'react-bootstrap';
+import type { InterfaceMapType } from 'utils/interfaces';
+import { useQuery } from '@apollo/client';
+import { ORGANIZATION_EVENT_LIST } from 'GraphQl/Queries/Queries';
+interface InterfaceEvent {
+ _id: string;
+ title: string;
+}
+
+/**
+ * Component for the organization screen
+ *
+ * This component displays the organization screen and handles the layout
+ * including a side drawer, header, and main content area. It adjusts
+ * the layout based on the screen size and shows the appropriate content
+ * based on the route.
+ *
+ * @returns JSX.Element representing the organization screen
+ */
+const OrganizationScreen = (): JSX.Element => {
+ // Get the current location to determine the translation key
+ const location = useLocation();
+ const titleKey: string | undefined = map[location.pathname.split('/')[1]];
+ const { t } = useTranslation('translation', { keyPrefix: titleKey });
+
+ // State to manage visibility of the side drawer
+ const [hideDrawer, setHideDrawer] = useState(null);
+
+ // Get the organization ID from the URL parameters
+ const { orgId } = useParams();
+ const [eventName, setEventName] = useState(null);
+
+ const isEventPath = useMatch('/event/:orgId/:eventId');
+
+ // If no organization ID is found, navigate back to the home page
+ if (!orgId) {
+ /*istanbul ignore next*/
+ return ;
+ }
+
+ // Get the application routes from the Redux store
+ const appRoutes: {
+ targets: TargetsType[];
+ } = useSelector((state: RootState) => state.appRoutes);
+ const { targets } = appRoutes;
+
+ const dispatch = useAppDispatch();
+
+ // Update targets whenever the organization ID changes
+ useEffect(() => {
+ dispatch(updateTargets(orgId));
+ }, [orgId]);
+
+ const { data: eventsData } = useQuery(ORGANIZATION_EVENT_LIST, {
+ variables: { id: orgId },
+ });
+
+ useEffect(() => {
+ if (isEventPath?.params.eventId && eventsData?.eventsByOrganization) {
+ const eventId = isEventPath.params.eventId;
+ const event = eventsData.eventsByOrganization.find(
+ (e: InterfaceEvent) => e._id === eventId,
+ );
+ /*istanbul ignore next*/
+ if (!event) {
+ console.warn(`Event with id ${eventId} not found`);
+ setEventName(null);
+ return;
+ }
+ setEventName(event.title);
+ } else {
+ setEventName(null);
+ }
+ }, [isEventPath, eventsData]);
+
+ // Handle screen resizing to show/hide the side drawer
+ const handleResize = (): void => {
+ if (window.innerWidth <= 820) {
+ setHideDrawer(!hideDrawer);
+ }
+ };
+
+ // Set up event listener for window resize
+ useEffect(() => {
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return (
+ <>
+ {hideDrawer ? (
+ {
+ setHideDrawer(!hideDrawer);
+ }}
+ data-testid="openMenu"
+ >
+
+
+ ) : (
+ {
+ setHideDrawer(!hideDrawer);
+ }}
+ data-testid="closeMenu"
+ >
+
+
+ )}
+
+
+
+
+
+
+
{t('title')}
+ {eventName && {eventName} }
+
+
+
+
+
+ >
+ );
+};
+
+export default OrganizationScreen;
+
+/**
+ * Mapping object to get translation keys based on route
+ */
+const map: InterfaceMapType = {
+ orgdash: 'dashboard',
+ orgpeople: 'organizationPeople',
+ orgtags: 'organizationTags',
+ requests: 'requests',
+ orgads: 'advertisement',
+ member: 'memberDetail',
+ orgevents: 'organizationEvents',
+ orgactionitems: 'organizationActionItems',
+ orgagendacategory: 'organizationAgendaCategory',
+ orgcontribution: 'orgContribution',
+ orgpost: 'orgPost',
+ orgfunds: 'funds',
+ orgfundcampaign: 'fundCampaign',
+ fundCampaignPledge: 'pledges',
+ orgsetting: 'orgSettings',
+ orgstore: 'addOnStore',
+ blockuser: 'blockUnblockUser',
+ orgvenues: 'organizationVenues',
+ event: 'eventManagement',
+ leaderboard: 'leaderboard',
+};
diff --git a/src/components/Pagination/Pagination.test.tsx b/src/components/Pagination/Pagination.test.tsx
new file mode 100644
index 0000000000..40ac2ed19a
--- /dev/null
+++ b/src/components/Pagination/Pagination.test.tsx
@@ -0,0 +1,64 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+import Pagination from './Pagination';
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+
+describe('Testing Pagination component', () => {
+ const props = {
+ count: 5,
+ page: 10,
+ rowsPerPage: 5,
+ onPageChange: (): number => {
+ return 10;
+ },
+ };
+
+ test('Component should be rendered properly on rtl', async () => {
+ render(
+
+
+
+
+ ,
+ );
+ await act(async () => {
+ userEvent.click(screen.getByTestId(/nextPage/i));
+ userEvent.click(screen.getByTestId(/previousPage/i));
+ });
+ });
+});
+
+const props = {
+ count: 5,
+ page: 10,
+ rowsPerPage: 5,
+ onPageChange: (): number => {
+ return 10;
+ },
+ theme: { direction: 'rtl' },
+};
+
+test('Component should be rendered properly', async () => {
+ const theme = createTheme({
+ direction: 'rtl',
+ });
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await act(async () => {
+ userEvent.click(screen.getByTestId(/nextPage/i));
+ userEvent.click(screen.getByTestId(/previousPage/i));
+ });
+});
diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx
new file mode 100644
index 0000000000..637492a289
--- /dev/null
+++ b/src/components/Pagination/Pagination.tsx
@@ -0,0 +1,132 @@
+import React from 'react';
+import { useTheme } from '@mui/material/styles';
+import Box from '@mui/material/Box';
+import IconButton from '@mui/material/IconButton';
+import FirstPageIcon from '@mui/icons-material/FirstPage';
+import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft';
+import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
+import LastPageIcon from '@mui/icons-material/LastPage';
+
+interface InterfaceTablePaginationActionsProps {
+ count: number; // Total number of items
+ page: number; // Current page index
+ rowsPerPage: number; // Number of items per page
+ onPageChange: (
+ event: React.MouseEvent,
+ newPage: number, // New page index to navigate to
+ ) => void; // Callback function for page changes
+}
+
+/**
+ * Pagination component for navigating between pages in a table.
+ *
+ * This component provides buttons to navigate to the first page, previous page,
+ * next page, and last page of a table. The visibility and functionality of the
+ * buttons are controlled based on the current page and the total number of items.
+ *
+ * @param props - Component properties.
+ * @returns The rendered component.
+ */
+function pagination(props: InterfaceTablePaginationActionsProps): JSX.Element {
+ const theme = useTheme();
+ const { count, page, rowsPerPage, onPageChange } = props;
+
+ /**
+ * Handles the event when the "First Page" button is clicked.
+ * Navigates to the first page (page 0).
+ *
+ * @param event - The click event.
+ */
+ /* istanbul ignore next */
+ const handleFirstPageButtonClick = (
+ event: React.MouseEvent,
+ ): void => {
+ onPageChange(event, 0);
+ };
+
+ /**
+ * Handles the event when the "Previous Page" button is clicked.
+ * Navigates to the previous page.
+ *
+ * @param event - The click event.
+ */
+ const handleBackButtonClick = (
+ event: React.MouseEvent,
+ ): void => {
+ onPageChange(event, page - 1);
+ };
+
+ /**
+ * Handles the event when the "Next Page" button is clicked.
+ * Navigates to the next page.
+ *
+ * @param event - The click event.
+ */
+
+ /* istanbul ignore next */
+ const handleNextButtonClick = (
+ event: React.MouseEvent,
+ ): void => {
+ onPageChange(event, page + 1);
+ };
+
+ /**
+ * Handles the event when the "Last Page" button is clicked.
+ * Navigates to the last page.
+ *
+ * @param event - The click event.
+ */
+ /* istanbul ignore next */
+ const handleLastPageButtonClick = (
+ event: React.MouseEvent,
+ ): void => {
+ onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
+ };
+
+ return (
+
+
+ {theme.direction === 'rtl' ? : }
+
+
+ {theme.direction === 'rtl' ? (
+
+ ) : (
+
+ )}
+
+ = Math.ceil(count / rowsPerPage) - 1}
+ aria-label="next page"
+ data-testid="nextPage"
+ >
+ {theme.direction === 'rtl' ? (
+
+ ) : (
+
+ )}
+
+ = Math.ceil(count / rowsPerPage) - 1}
+ aria-label="last page"
+ data-testid="lastPage"
+ >
+ {theme.direction === 'rtl' ? : }
+
+
+ );
+}
+
+export default pagination;
diff --git a/src/components/PaginationList/PaginationList.css b/src/components/PaginationList/PaginationList.css
new file mode 100644
index 0000000000..2354c9a5f9
--- /dev/null
+++ b/src/components/PaginationList/PaginationList.css
@@ -0,0 +1,7 @@
+.MuiTablePagination-selectLabel {
+ margin-top: 1rem;
+}
+
+.MuiTablePagination-displayedRows {
+ margin-top: 1rem;
+}
diff --git a/src/components/PaginationList/PaginationList.tsx b/src/components/PaginationList/PaginationList.tsx
new file mode 100644
index 0000000000..430e8ba1aa
--- /dev/null
+++ b/src/components/PaginationList/PaginationList.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import { Hidden, TablePagination } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+
+import Pagination from '../Pagination/Pagination';
+import './PaginationList.css';
+
+interface InterfacePropsInterface {
+ count: number;
+ rowsPerPage: number;
+ page: number;
+ onPageChange: (
+ event: React.MouseEvent | null,
+ newPage: number,
+ ) => void;
+ onRowsPerPageChange: (
+ event: React.ChangeEvent,
+ ) => void;
+}
+/**
+ * A component that provides pagination controls for a table.
+ * It uses different pagination styles based on screen size.
+ *
+ * @param count - The total number of rows in the table.
+ * @param rowsPerPage - The number of rows displayed per page.
+ * @param page - The current page number.
+ * @param onPageChange - Callback function to handle page changes.
+ * @param onRowsPerPageChange - Callback function to handle changes in rows per page.
+ */
+
+const PaginationList = ({
+ count,
+ rowsPerPage,
+ page,
+ onPageChange,
+ onRowsPerPageChange,
+}: InterfacePropsInterface): JSX.Element => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'paginationList',
+ });
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+};
+
+export default PaginationList;
diff --git a/src/components/ProfileDropdown/ProfileDropdown.module.css b/src/components/ProfileDropdown/ProfileDropdown.module.css
new file mode 100644
index 0000000000..46af582126
--- /dev/null
+++ b/src/components/ProfileDropdown/ProfileDropdown.module.css
@@ -0,0 +1,75 @@
+.profileContainer {
+ border: none;
+ padding: 2.1rem 0.5rem;
+ height: 52px;
+ border-radius: 8px 0px 0px 8px;
+ display: flex;
+ align-items: center;
+ background-color: white !important;
+ box-shadow:
+ 0 4px 4px 0 rgba(177, 177, 177, 0.2),
+ 0 6px 44px 0 rgba(246, 246, 246, 0.19);
+}
+.profileContainer:focus {
+ outline: none;
+ background-color: var(--bs-gray-100);
+}
+.imageContainer {
+ width: 56px;
+ height: 56px;
+ border-radius: 100%;
+ margin-right: 10px;
+}
+.imageContainer img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 100%;
+}
+.profileContainer .profileText {
+ flex: 1;
+ text-align: start;
+ overflow: hidden;
+ margin-right: 4px;
+}
+.angleDown {
+ margin-left: 4px;
+}
+.profileContainer .profileText .primaryText {
+ font-size: 1rem;
+ font-weight: 600;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2; /* number of lines to show */
+ -webkit-box-orient: vertical;
+ word-wrap: break-word;
+ white-space: normal;
+}
+.profileContainer .profileText .secondaryText {
+ font-size: 0.8rem;
+ font-weight: 400;
+ color: var(--bs-secondary);
+ display: block;
+ text-transform: capitalize;
+}
+.profileDropdown {
+ background-color: transparent !important;
+}
+.profileDropdown .dropdown-toggle .btn .btn-normal {
+ display: none !important;
+ background-color: transparent !important;
+}
+.dropdownToggle {
+ background-image: url(/public/images/svg/angleDown.svg);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-color: azure;
+}
+
+.dropdownToggle::after {
+ border-top: none !important;
+ border-bottom: none !important;
+}
+.avatarStyle {
+ border-radius: 100%;
+}
diff --git a/src/components/ProfileDropdown/ProfileDropdown.test.tsx b/src/components/ProfileDropdown/ProfileDropdown.test.tsx
new file mode 100644
index 0000000000..785f33ee92
--- /dev/null
+++ b/src/components/ProfileDropdown/ProfileDropdown.test.tsx
@@ -0,0 +1,148 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { BrowserRouter } from 'react-router-dom';
+import ProfileDropdown from './ProfileDropdown';
+import 'jest-localstorage-mock';
+import { MockedProvider } from '@apollo/react-testing';
+import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+import useLocalStorage from 'utils/useLocalstorage';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
+
+const { setItem } = useLocalStorage();
+const MOCKS = [
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ result: {
+ data: {
+ revokeRefreshTokenForUser: true,
+ },
+ },
+ },
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 30,
+ },
+ },
+ },
+ delay: 1000,
+ },
+];
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+ clear: jest.fn(),
+}));
+
+beforeEach(() => {
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ setItem(
+ 'UserImage',
+ 'https://api.dicebear.com/5.x/initials/svg?seed=John%20Doe',
+ );
+ setItem('SuperAdmin', false);
+ setItem('AdminFor', []);
+ setItem('id', '123');
+});
+
+afterEach(() => {
+ jest.clearAllMocks();
+ localStorage.clear();
+});
+afterEach(() => {
+ jest.clearAllMocks();
+ localStorage.clear();
+});
+
+describe('ProfileDropdown Component', () => {
+ test('renders with user information', () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByTestId('display-name')).toBeInTheDocument();
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
+ expect(screen.getByText('User')).toBeInTheDocument();
+ expect(screen.getByTestId('display-type')).toBeInTheDocument();
+ expect(screen.getByAltText('profile picture')).toBeInTheDocument();
+ });
+
+ test('renders Super admin', () => {
+ setItem('SuperAdmin', true);
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('SuperAdmin')).toBeInTheDocument();
+ });
+ test('renders Admin', () => {
+ setItem('AdminFor', ['123']);
+ render(
+
+
+
+
+ ,
+ );
+ expect(screen.getByText('Admin')).toBeInTheDocument();
+ });
+
+ test('logout functionality clears local storage and redirects to home', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await act(async () => {
+ userEvent.click(screen.getByTestId('togDrop'));
+ });
+
+ userEvent.click(screen.getByTestId('logoutBtn'));
+
+ expect(global.window.location.pathname).toBe('/');
+ });
+
+ describe('Member screen routing testing', () => {
+ test('member screen', async () => {
+ render(
+
+
+
+
+ ,
+ );
+ await act(async () => {
+ userEvent.click(screen.getByTestId('togDrop'));
+ });
+
+ userEvent.click(screen.getByTestId('profileBtn'));
+ expect(global.window.location.pathname).toBe('/user/settings');
+ });
+ });
+});
diff --git a/src/components/ProfileDropdown/ProfileDropdown.tsx b/src/components/ProfileDropdown/ProfileDropdown.tsx
new file mode 100644
index 0000000000..059e5b910a
--- /dev/null
+++ b/src/components/ProfileDropdown/ProfileDropdown.tsx
@@ -0,0 +1,124 @@
+import Avatar from 'components/Avatar/Avatar';
+import React from 'react';
+import { ButtonGroup, Dropdown } from 'react-bootstrap';
+import { useNavigate } from 'react-router-dom';
+import useLocalStorage from 'utils/useLocalstorage';
+import styles from './ProfileDropdown.module.css';
+import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
+import { useMutation } from '@apollo/client';
+import { useTranslation } from 'react-i18next';
+import useSession from 'utils/useSession';
+
+/**
+ * Renders a profile dropdown menu for the user.
+ *
+ * This component displays the user's profile picture or an avatar, their name (truncated if necessary),
+ * and their role (SuperAdmin, Admin, or User). It provides options to view the profile or log out.
+ *
+ * - If a user image is available, it displays that; otherwise, it shows an avatar.
+ * - The displayed name is truncated if it exceeds a specified length.
+ * - The logout function revokes the refresh token and clears local storage before redirecting to the home page.
+ *
+ * @returns JSX.Element - The profile dropdown menu.
+ */
+const profileDropdown = (): JSX.Element => {
+ const { endSession } = useSession();
+ const { t: tCommon } = useTranslation('common');
+ const [revokeRefreshToken] = useMutation(REVOKE_REFRESH_TOKEN);
+ const { getItem } = useLocalStorage();
+ const superAdmin = getItem('SuperAdmin');
+ const adminFor = getItem('AdminFor');
+ const userRole = superAdmin
+ ? 'SuperAdmin'
+ : adminFor?.length > 0
+ ? 'Admin'
+ : 'User';
+ const firstName = getItem('FirstName');
+ const lastName = getItem('LastName');
+ const userImage = getItem('UserImage');
+ const userID = getItem('id');
+ const navigate = useNavigate();
+
+ const logout = async (): Promise => {
+ try {
+ await revokeRefreshToken();
+ } catch (error) {
+ /*istanbul ignore next*/
+ console.error('Error revoking refresh token:', error);
+ }
+ localStorage.clear();
+ endSession();
+ navigate('/');
+ };
+ const MAX_NAME_LENGTH = 20;
+ const fullName = `${firstName} ${lastName}`;
+ const displayedName =
+ fullName.length > MAX_NAME_LENGTH
+ ? /*istanbul ignore next*/
+ fullName.substring(0, MAX_NAME_LENGTH - 3) + '...'
+ : fullName;
+
+ return (
+
+
+
+ {userImage && userImage !== 'null' ? (
+ /*istanbul ignore next*/
+
+ ) : (
+
+ )}
+
+
+
+ {displayedName}
+
+
+ {`${userRole}`}
+
+
+
+
+
+
+ userRole === 'User'
+ ? navigate(`/user/settings`)
+ : navigate(`/member/${userID}`)
+ }
+ aria-label="View Profile"
+ >
+ {tCommon('viewProfile')}
+
+
+ {tCommon('logout')}
+
+
+
+ );
+};
+
+export default profileDropdown;
diff --git a/src/components/RecurrenceOptions/CustomRecurrence.test.tsx b/src/components/RecurrenceOptions/CustomRecurrence.test.tsx
new file mode 100644
index 0000000000..fc0cacf5c4
--- /dev/null
+++ b/src/components/RecurrenceOptions/CustomRecurrence.test.tsx
@@ -0,0 +1,721 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import OrganizationEvents from '../../screens/OrganizationEvents/OrganizationEvents';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast } from 'react-toastify';
+import { createTheme } from '@mui/material';
+import { ThemeProvider } from 'react-bootstrap';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { MOCKS } from '../../screens/OrganizationEvents/OrganizationEventsMocks';
+
+const theme = createTheme({
+ palette: {
+ primary: {
+ main: '#31bb6b',
+ },
+ },
+});
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const translations = JSON.parse(
+ JSON.stringify(
+ i18nForTest.getDataByLanguage('en')?.translation.organizationEvents,
+ ),
+);
+
+jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+ return {
+ DateTimePicker: jest.requireActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ ).DesktopDateTimePicker,
+ };
+});
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warning: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+describe('Testing the creaction of recurring events with custom recurrence patterns', () => {
+ const formData = {
+ title: 'Dummy Org',
+ description: 'This is a dummy organization',
+ startDate: '03/28/2022',
+ endDate: '03/30/2022',
+ recurrenceEndDate: '04/15/2023',
+ location: 'New Delhi',
+ startTime: '09:00 AM',
+ endTime: '05:00 PM',
+ };
+
+ test('Changing the recurrence frequency', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customDailyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customDailyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Day');
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customWeeklyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customWeeklyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Week');
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customMonthlyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customMonthlyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Month');
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customYearlyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customYearlyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Year');
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceSubmitBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Selecting and unselecting recurrence weekdays', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceSubmitBtn'),
+ ).toBeInTheDocument();
+ });
+
+ const weekDaysOptions = screen.getAllByTestId('recurrenceWeekDay');
+
+ weekDaysOptions.forEach((weekDay) => {
+ userEvent.click(weekDay);
+ });
+
+ weekDaysOptions.forEach((weekDay) => {
+ userEvent.click(weekDay);
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceSubmitBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Selecting different monthly recurrence options from the dropdown menu', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ const endDatePicker = screen.getByLabelText('End Date');
+ fireEvent.change(endDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customMonthlyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customMonthlyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Month');
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('monthlyRecurrenceOptions')).toHaveTextContent(
+ 'Monthly on Day 28',
+ );
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOptionOnThatOccurence'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(
+ screen.getByTestId('monthlyRecurrenceOptionOnThatOccurence'),
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('monthlyRecurrenceOptions')).toHaveTextContent(
+ 'Monthly on Fourth Monday',
+ );
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOptionOnLastOccurence'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(
+ screen.getByTestId('monthlyRecurrenceOptionOnLastOccurence'),
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('monthlyRecurrenceOptions')).toHaveTextContent(
+ 'Monthly on Last Monday',
+ );
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOptionOnThatDay'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOptionOnThatDay'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('monthlyRecurrenceOptions')).toHaveTextContent(
+ 'Monthly on Day 28',
+ );
+ });
+ });
+
+ test('Selecting the "Ends on" option for specifying the end of recurrence', async () => {
+ // i.e. when would the recurring event end: never, on a certain date, or after a certain number of occurences
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('never')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('never'));
+ userEvent.click(screen.getByTestId('on'));
+ userEvent.click(screen.getByTestId('after'));
+ userEvent.click(screen.getByTestId('never'));
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceSubmitBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Creating a bi monthly recurring event through custom recurrence modal', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toBeInTheDocument();
+ });
+
+ userEvent.type(screen.getByPlaceholderText(/Enter Title/i), formData.title);
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Description/i),
+ formData.description,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Location/i),
+ formData.location,
+ );
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ const eventEndDatePicker = screen.getByLabelText('End Date');
+ fireEvent.change(eventEndDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customMonthlyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customMonthlyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Month');
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOptionOnThatOccurence'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(
+ screen.getByTestId('monthlyRecurrenceOptionOnThatOccurence'),
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('on')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('on'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('on')).toBeChecked();
+ });
+
+ await waitFor(() => {
+ expect(screen.getAllByLabelText('End Date')[1]).toBeEnabled();
+ });
+
+ const recurrenceEndDatePicker = screen.getAllByLabelText('End Date')[1];
+ fireEvent.change(recurrenceEndDatePicker, {
+ target: { value: formData.recurrenceEndDate },
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceIntervalInput'),
+ ).toBeInTheDocument();
+ });
+
+ const recurrenceCount = screen.getByTestId('customRecurrenceIntervalInput');
+ fireEvent.change(recurrenceCount, {
+ target: { value: 2 },
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceSubmitBtn'),
+ ).not.toBeInTheDocument();
+ });
+
+ expect(screen.getByTestId('recurrenceOptions')).toHaveTextContent(
+ 'Every 2 months on Fourth Monday, until April...',
+ // "..." because of the overlay component that would trim the recurrence rule text at 45 characters
+ );
+
+ userEvent.click(screen.getByTestId('createEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('createEventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Creating a daily recurring event with a certain number of occurences', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toBeInTheDocument();
+ });
+
+ userEvent.type(screen.getByPlaceholderText(/Enter Title/i), formData.title);
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Description/i),
+ formData.description,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Location/i),
+ formData.location,
+ );
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ const endDatePicker = screen.getByLabelText('End Date');
+
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ fireEvent.change(endDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ userEvent.click(screen.getByTestId('customRecurrenceFrequencyDropdown'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customDailyRecurrence')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('customDailyRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceFrequencyDropdown'),
+ ).toHaveTextContent('Day');
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('after')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('after'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('after')).toBeChecked();
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceCountInput'),
+ ).toBeInTheDocument();
+ });
+
+ const recurrenceCount = screen.getByTestId('customRecurrenceCountInput');
+ fireEvent.change(recurrenceCount, {
+ target: { value: 100 },
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrenceCountInput')).toHaveValue(100);
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceSubmitBtn'));
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceSubmitBtn'),
+ ).not.toBeInTheDocument();
+ });
+
+ expect(screen.getByTestId('recurrenceOptions')).toHaveTextContent(
+ 'Daily, 100 times',
+ );
+
+ userEvent.click(screen.getByTestId('createEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('createEventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/RecurrenceOptions/CustomRecurrenceModal.module.css b/src/components/RecurrenceOptions/CustomRecurrenceModal.module.css
new file mode 100644
index 0000000000..5fe5d33c47
--- /dev/null
+++ b/src/components/RecurrenceOptions/CustomRecurrenceModal.module.css
@@ -0,0 +1,59 @@
+.titlemodal {
+ color: #707070;
+ font-weight: 600;
+ font-size: 20px;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ border-bottom: 3px solid #31bb6b;
+ width: 65%;
+}
+
+.recurrenceRuleNumberInput {
+ width: 70px;
+}
+
+.recurrenceRuleDateBox {
+ width: 70%;
+}
+
+.recurrenceDayButton {
+ width: 33px;
+ height: 33px;
+ border: 1px solid var(--bs-gray);
+ cursor: pointer;
+ transition: background-color 0.3s;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 0.5rem;
+ border-radius: 50%;
+}
+
+.recurrenceDayButton:hover {
+ background-color: var(--bs-gray);
+}
+
+.recurrenceDayButton.selected {
+ background-color: var(--bs-primary);
+ border-color: var(--bs-primary);
+ color: var(--bs-white);
+}
+
+.recurrenceDayButton span {
+ color: var(--bs-gray);
+ padding: 0.25rem;
+ text-align: center;
+}
+
+.recurrenceDayButton:hover span {
+ color: var(--bs-white);
+}
+
+.recurrenceDayButton.selected span {
+ color: var(--bs-white);
+}
+
+.recurrenceRuleSubmitBtn {
+ margin-left: 82%;
+ padding: 7px 15px;
+}
diff --git a/src/components/RecurrenceOptions/CustomRecurrenceModal.tsx b/src/components/RecurrenceOptions/CustomRecurrenceModal.tsx
new file mode 100644
index 0000000000..deb1a03dab
--- /dev/null
+++ b/src/components/RecurrenceOptions/CustomRecurrenceModal.tsx
@@ -0,0 +1,430 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Dropdown, Form, FormControl, Modal } from 'react-bootstrap';
+import styles from './CustomRecurrenceModal.module.css';
+import { DatePicker } from '@mui/x-date-pickers';
+import {
+ Days,
+ Frequency,
+ daysOptions,
+ endsAfter,
+ endsNever,
+ endsOn,
+ frequencies,
+ getRecurrenceRuleText,
+ getWeekDayOccurenceInMonth,
+ isLastOccurenceOfWeekDay,
+ recurrenceEndOptions,
+} from 'utils/recurrenceUtils';
+import type {
+ InterfaceRecurrenceRuleState,
+ RecurrenceEndOption,
+ WeekDays,
+} from 'utils/recurrenceUtils';
+import type { Dayjs } from 'dayjs';
+import dayjs from 'dayjs';
+
+/**
+ * Props for the CustomRecurrenceModal component.
+ */
+interface InterfaceCustomRecurrenceModalProps {
+ recurrenceRuleState: InterfaceRecurrenceRuleState;
+ recurrenceRuleText: string;
+ setRecurrenceRuleState: (
+ state: React.SetStateAction,
+ ) => void;
+ customRecurrenceModalIsOpen: boolean;
+ hideCustomRecurrenceModal: () => void;
+ setCustomRecurrenceModalIsOpen: (
+ state: React.SetStateAction,
+ ) => void;
+ t: (key: string) => string;
+ tCommon: (key: string) => string;
+}
+
+/**
+ * A modal for setting up custom recurrence rules.
+ *
+ * This component allows users to configure how often an event should repeat, and
+ * when it should end. It includes options for daily, weekly, monthly, and yearly
+ * recurrence, as well as specific end options.
+ *
+ * @param props - The props object containing various configurations and state management functions.
+ * @returns The JSX element representing the CustomRecurrenceModal.
+ */
+const CustomRecurrenceModal: React.FC = ({
+ recurrenceRuleState,
+ recurrenceRuleText,
+ setRecurrenceRuleState,
+ customRecurrenceModalIsOpen,
+ hideCustomRecurrenceModal,
+ setCustomRecurrenceModalIsOpen,
+ t,
+ tCommon,
+}) => {
+ const {
+ recurrenceStartDate,
+ recurrenceEndDate,
+ frequency,
+ weekDays,
+ interval,
+ count,
+ } = recurrenceRuleState;
+ const [selectedRecurrenceEndOption, setSelectedRecurrenceEndOption] =
+ useState(endsNever);
+
+ useEffect(() => {
+ if (recurrenceEndDate) {
+ setSelectedRecurrenceEndOption(endsOn);
+ } else if (count) {
+ setSelectedRecurrenceEndOption(endsAfter);
+ }
+ }, [recurrenceRuleState]);
+
+ /**
+ * Handles changes to the recurrence end option.
+ *
+ * Updates the recurrence rule state based on the selected option.
+ *
+ * @param e - The event object from the radio button change.
+ */
+ const handleRecurrenceEndOptionChange = (
+ e: React.ChangeEvent,
+ ): void => {
+ const selectedRecurrenceEndOption = e.target.value as RecurrenceEndOption;
+ setSelectedRecurrenceEndOption(selectedRecurrenceEndOption);
+ if (selectedRecurrenceEndOption === endsNever) {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ recurrenceEndDate: null,
+ count: undefined,
+ });
+ }
+ if (selectedRecurrenceEndOption === endsOn) {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ recurrenceEndDate: dayjs().add(1, 'month').toDate(),
+ count: undefined,
+ });
+ }
+ if (selectedRecurrenceEndOption === endsAfter) {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ recurrenceEndDate: null,
+ count: 10,
+ });
+ }
+ };
+
+ /**
+ * Handles clicks on day buttons for weekly recurrence.
+ *
+ * Toggles the selected state of a day button in the weekly recurrence setup.
+ *
+ * @param day - The day of the week to toggle.
+ */
+ const handleDayClick = (day: WeekDays): void => {
+ if (weekDays !== undefined && weekDays.includes(day)) {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ weekDays: weekDays.filter((d) => d !== day),
+ weekDayOccurenceInMonth: undefined,
+ });
+ } else {
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ weekDays: [...(weekDays ?? []), day],
+ weekDayOccurenceInMonth: undefined,
+ });
+ }
+ };
+
+ /**
+ * Toggles the visibility of the custom recurrence modal.
+ */
+ const handleCustomRecurrenceSubmit = (): void => {
+ setCustomRecurrenceModalIsOpen(!customRecurrenceModalIsOpen);
+ };
+
+ return (
+ <>
+
+
+ {t('customRecurrence')}
+
+
+
+
+
+
+
+ {t('repeatsEvery')}
+ {' '}
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ interval: Number(e.target.value),
+ })
+ }
+ />
+
+
+ {`${frequencies[frequency]}${interval && interval > 1 ? 's' : ''}`}
+
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.DAILY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="customDailyRecurrence"
+ >
+ {interval && interval > 1 ? 'Days' : 'Day'}
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.WEEKLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="customWeeklyRecurrence"
+ >
+ {interval && interval > 1 ? 'Weeks' : 'Week'}
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="customMonthlyRecurrence"
+ >
+ {interval && interval > 1 ? 'Months' : 'Month'}
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.YEARLY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="customYearlyRecurrence"
+ >
+ {interval && interval > 1 ? 'Years' : 'Year'}
+
+
+
+
+
+ {frequency === Frequency.WEEKLY && (
+
+
+ {t('repeatsOn')}
+
+
+
+ {daysOptions.map((day, index) => (
+
handleDayClick(Days[index])}
+ data-testid="recurrenceWeekDay"
+ >
+ {day}
+
+ ))}
+
+
+ )}
+
+ {frequency === Frequency.MONTHLY && (
+
+
+
+ {recurrenceRuleText}
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="monthlyRecurrenceOptionOnThatDay"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDayOccurenceInMonth: undefined,
+ })}
+
+
+ {getWeekDayOccurenceInMonth(recurrenceStartDate) !== 5 && (
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth:
+ getWeekDayOccurenceInMonth(recurrenceStartDate),
+ })
+ }
+ data-testid="monthlyRecurrenceOptionOnThatOccurence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth:
+ getWeekDayOccurenceInMonth(recurrenceStartDate),
+ })}
+
+
+ )}
+ {isLastOccurenceOfWeekDay(recurrenceStartDate) && (
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: -1,
+ })
+ }
+ data-testid="monthlyRecurrenceOptionOnLastOccurence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: -1,
+ })}
+
+
+ )}
+
+
+
+ )}
+
+
+
+
+
+
+
+ {tCommon('done')}
+
+
+
+
+ >
+ );
+};
+
+export default CustomRecurrenceModal;
diff --git a/src/components/RecurrenceOptions/RecurrenceOptions.test.tsx b/src/components/RecurrenceOptions/RecurrenceOptions.test.tsx
new file mode 100644
index 0000000000..2d283460da
--- /dev/null
+++ b/src/components/RecurrenceOptions/RecurrenceOptions.test.tsx
@@ -0,0 +1,587 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import OrganizationEvents from '../../screens/OrganizationEvents/OrganizationEvents';
+import { store } from 'state/store';
+import i18n from 'utils/i18nForTest';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast } from 'react-toastify';
+import { createTheme } from '@mui/material';
+import { ThemeProvider } from 'react-bootstrap';
+import { LocalizationProvider } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { MOCKS } from '../../screens/OrganizationEvents/OrganizationEventsMocks';
+
+const theme = createTheme({
+ palette: {
+ primary: {
+ main: '#31bb6b',
+ },
+ },
+});
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(
+ i18n.getDataByLanguage('en')?.translation.organizationEvents ?? {},
+ ),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+ return {
+ DateTimePicker: jest.requireActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ ).DesktopDateTimePicker,
+ };
+});
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warning: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+describe('Testing the creaction of recurring events through recurrence options', () => {
+ const formData = {
+ title: 'Dummy Org',
+ description: 'This is a dummy organization',
+ startDate: '03/28/2022',
+ endDate: '03/30/2022',
+ location: 'New Delhi',
+ startTime: '09:00 AM',
+ endTime: '05:00 PM',
+ };
+
+ test('Recurrence options Dropdown shows up after checking the Recurring switch', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+ });
+
+ test('Showing different recurrence options through the Dropdown', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+ });
+
+ test('Toggling of custom recurrence modal', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('customRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrence'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('customRecurrenceModalCloseBtn'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('customRecurrenceModalCloseBtn'));
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('customRecurrenceModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Selecting different recurrence options from the dropdown menu', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ const endDatePicker = screen.getByLabelText('End Date');
+
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ fireEvent.change(endDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ expect(screen.queryByTestId('recurrenceOptions')).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('dailyRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('dailyRecurrence'));
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('weeklyRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('weeklyRecurrence'));
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOnThatDay'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOnThatDay'));
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOnThatOccurence'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOnThatOccurence'));
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('monthlyRecurrenceOnLastOccurence'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('monthlyRecurrenceOnLastOccurence'));
+
+ // changing the startDate would change the weekDayOccurenceInMonth, if it is defined
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('yearlyRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('yearlyRecurrence'));
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('mondayToFridayRecurrence'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('mondayToFridayRecurrence'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toHaveTextContent(
+ 'Monday to Friday',
+ );
+ });
+ }, 30000);
+
+ test('Creating a recurring event with the daily recurrence option', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toBeInTheDocument();
+ });
+
+ userEvent.type(screen.getByPlaceholderText(/Enter Title/i), formData.title);
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Description/i),
+ formData.description,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Location/i),
+ formData.location,
+ );
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ const endDatePicker = screen.getByLabelText('End Date');
+
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ fireEvent.change(endDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('alldayCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(translations.startTime)).toBeInTheDocument();
+ });
+
+ const startTimePicker = screen.getByLabelText(translations.startTime);
+ const endTimePicker = screen.getByLabelText(translations.endTime);
+
+ fireEvent.change(startTimePicker, {
+ target: { value: formData.startTime },
+ });
+
+ fireEvent.change(endTimePicker, {
+ target: { value: formData.endTime },
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('dailyRecurrence')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('dailyRecurrence'));
+
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toHaveValue(
+ formData.title,
+ );
+ expect(screen.getByPlaceholderText(/Enter Location/i)).toHaveValue(
+ formData.location,
+ );
+ expect(screen.getByPlaceholderText(/Enter Description/i)).toHaveValue(
+ formData.description,
+ );
+ expect(startDatePicker).toHaveValue(formData.startDate);
+ expect(endDatePicker).toHaveValue(formData.endDate);
+ expect(startTimePicker).toHaveValue(formData.startTime);
+ expect(endTimePicker).toHaveValue(formData.endTime);
+ expect(screen.getByTestId('alldayCheck')).not.toBeChecked();
+ expect(screen.getByTestId('recurringCheck')).toBeChecked();
+
+ expect(screen.getByTestId('recurrenceOptions')).toHaveTextContent('Daily');
+
+ userEvent.click(screen.getByTestId('createEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('createEventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ test('Creating a recurring event with the monday to friday recurrence option', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('createEventModalBtn')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('createEventModalBtn'));
+
+ await waitFor(() => {
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toBeInTheDocument();
+ });
+
+ userEvent.type(screen.getByPlaceholderText(/Enter Title/i), formData.title);
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Description/i),
+ formData.description,
+ );
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Enter Location/i),
+ formData.location,
+ );
+
+ const startDatePicker = screen.getByLabelText('Start Date');
+ const endDatePicker = screen.getByLabelText('End Date');
+
+ fireEvent.change(startDatePicker, {
+ target: { value: formData.startDate },
+ });
+
+ fireEvent.change(endDatePicker, {
+ target: { value: formData.endDate },
+ });
+
+ userEvent.click(screen.getByTestId('alldayCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(translations.startTime)).toBeInTheDocument();
+ });
+
+ const startTimePicker = screen.getByLabelText(translations.startTime);
+ const endTimePicker = screen.getByLabelText(translations.endTime);
+
+ fireEvent.change(startTimePicker, {
+ target: { value: formData.startTime },
+ });
+
+ fireEvent.change(endTimePicker, {
+ target: { value: formData.endTime },
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurringCheck')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurringCheck'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrenceOptions')).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('recurrenceOptions'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('mondayToFridayRecurrence'),
+ ).toBeInTheDocument();
+ });
+
+ userEvent.click(screen.getByTestId('mondayToFridayRecurrence'));
+
+ expect(screen.getByPlaceholderText(/Enter Title/i)).toHaveValue(
+ formData.title,
+ );
+ expect(screen.getByPlaceholderText(/Enter Location/i)).toHaveValue(
+ formData.location,
+ );
+ expect(screen.getByPlaceholderText(/Enter Description/i)).toHaveValue(
+ formData.description,
+ );
+ expect(startDatePicker).toHaveValue(formData.startDate);
+ expect(endDatePicker).toHaveValue(formData.endDate);
+ expect(startTimePicker).toHaveValue(formData.startTime);
+ expect(endTimePicker).toHaveValue(formData.endTime);
+ expect(screen.getByTestId('alldayCheck')).not.toBeChecked();
+ expect(screen.getByTestId('recurringCheck')).toBeChecked();
+
+ expect(screen.getByTestId('recurrenceOptions')).toHaveTextContent(
+ 'Monday to Friday',
+ );
+
+ userEvent.click(screen.getByTestId('createEventBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.queryByTestId('createEventModalCloseBtn'),
+ ).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/RecurrenceOptions/RecurrenceOptions.tsx b/src/components/RecurrenceOptions/RecurrenceOptions.tsx
new file mode 100644
index 0000000000..f1772f1b08
--- /dev/null
+++ b/src/components/RecurrenceOptions/RecurrenceOptions.tsx
@@ -0,0 +1,255 @@
+import React, { useState } from 'react';
+import { Dropdown, OverlayTrigger } from 'react-bootstrap';
+import {
+ Days,
+ Frequency,
+ type InterfaceRecurrenceRuleState,
+ getRecurrenceRuleText,
+ getWeekDayOccurenceInMonth,
+ isLastOccurenceOfWeekDay,
+ mondayToFriday,
+} from 'utils/recurrenceUtils';
+import CustomRecurrenceModal from './CustomRecurrenceModal';
+
+interface InterfaceRecurrenceOptionsProps {
+ recurrenceRuleState: InterfaceRecurrenceRuleState;
+ recurrenceRuleText: string;
+ setRecurrenceRuleState: (
+ state: React.SetStateAction,
+ ) => void;
+ popover: JSX.Element;
+ t: (key: string) => string;
+ tCommon: (key: string) => string;
+}
+/**
+ * Renders a dropdown menu for selecting recurrence options.
+ *
+ * This component allows users to choose various recurrence rules (daily, weekly, monthly, yearly) for a given event.
+ * It displays the current recurrence rule text and provides options to modify it, including a custom recurrence modal.
+ *
+ * The dropdown menu includes options for:
+ * - Daily recurrence
+ * - Weekly recurrence (including a specific day of the week or Monday to Friday)
+ * - Monthly recurrence (on a specific day or occurrence)
+ * - Yearly recurrence
+ * - Custom recurrence (opens a modal for advanced settings)
+ *
+ * The displayed recurrence rule text is truncated if it exceeds a specified length, with an overlay showing the full text on hover.
+ *
+ * @param props - The properties to configure the recurrence options dropdown.
+ * @param recurrenceRuleState - The current state of the recurrence rule.
+ * @param recurrenceRuleText - The text describing the current recurrence rule.
+ * @param setRecurrenceRuleState - A function to update the recurrence rule state.
+ * @param popover - A JSX element used for displaying additional information on hover.
+ * @param t - A function for translating text.
+ * @param tCommon - A function for translating common text.
+ *
+ * @returns JSX.Element - The recurrence options dropdown and the custom recurrence modal.
+ */
+const RecurrenceOptions: React.FC = ({
+ recurrenceRuleState,
+ recurrenceRuleText,
+ setRecurrenceRuleState,
+ popover,
+ t,
+ tCommon,
+}) => {
+ const [customRecurrenceModalIsOpen, setCustomRecurrenceModalIsOpen] =
+ useState(false);
+
+ const { recurrenceStartDate } = recurrenceRuleState;
+
+ const hideCustomRecurrenceModal = (): void => {
+ setCustomRecurrenceModalIsOpen(false);
+ };
+
+ return (
+ <>
+
+
+ {recurrenceRuleText.length > 45 ? (
+
+
+ {`${recurrenceRuleText.substring(0, 45)}...`}
+
+
+ ) : (
+ {recurrenceRuleText}
+ )}
+
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.DAILY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="dailyRecurrence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.DAILY,
+ })}
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.WEEKLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="weeklyRecurrence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.WEEKLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ })}
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="monthlyRecurrenceOnThatDay"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDayOccurenceInMonth: undefined,
+ })}
+
+
+ {getWeekDayOccurenceInMonth(recurrenceStartDate) !== 5 && (
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth:
+ getWeekDayOccurenceInMonth(recurrenceStartDate),
+ })
+ }
+ data-testid="monthlyRecurrenceOnThatOccurence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth:
+ getWeekDayOccurenceInMonth(recurrenceStartDate),
+ })}
+
+
+ )}
+ {isLastOccurenceOfWeekDay(recurrenceStartDate) && (
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: -1,
+ })
+ }
+ data-testid="monthlyRecurrenceOnLastOccurence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.MONTHLY,
+ weekDays: [Days[recurrenceStartDate.getDay()]],
+ weekDayOccurenceInMonth: -1,
+ })}
+
+
+ )}
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.YEARLY,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="yearlyRecurrence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.YEARLY,
+ weekDayOccurenceInMonth: undefined,
+ })}
+
+
+
+ setRecurrenceRuleState({
+ ...recurrenceRuleState,
+ frequency: Frequency.WEEKLY,
+ weekDays: mondayToFriday,
+ weekDayOccurenceInMonth: undefined,
+ })
+ }
+ data-testid="mondayToFridayRecurrence"
+ >
+
+ {getRecurrenceRuleText({
+ ...recurrenceRuleState,
+ frequency: Frequency.WEEKLY,
+ weekDays: mondayToFriday,
+ })}
+
+
+ setCustomRecurrenceModalIsOpen(true)}
+ data-testid="customRecurrence"
+ >
+ Custom...
+
+
+
+
+ {/* Custom Recurrence Modal */}
+
+ >
+ );
+};
+
+export default RecurrenceOptions;
diff --git a/src/components/RequestsTableItem/RequestsTableItem.module.css b/src/components/RequestsTableItem/RequestsTableItem.module.css
new file mode 100644
index 0000000000..9f12bfe996
--- /dev/null
+++ b/src/components/RequestsTableItem/RequestsTableItem.module.css
@@ -0,0 +1,66 @@
+.tableItem {
+ width: 100%;
+}
+
+.tableItem td {
+ padding: 0.5rem;
+}
+
+.tableItem .index {
+ padding-left: 2.5rem;
+ padding-top: 1rem;
+}
+
+.tableItem .name {
+ padding-left: 1.5rem;
+ padding-top: 1rem;
+}
+
+.tableItem .email {
+ padding-left: 1.5rem;
+ padding-top: 1rem;
+}
+
+.acceptButton {
+ background: #31bb6b;
+ width: 120px;
+ height: 46px;
+ margin-left: -1rem;
+}
+
+.rejectButton {
+ background: #dc3545;
+ width: 120px;
+ height: 46px;
+ margin-left: -1rem;
+}
+
+@media (max-width: 1020px) {
+ .tableItem .index {
+ padding-left: 2rem;
+ }
+
+ .tableItem .name,
+ .tableItem .email {
+ padding-left: 1rem;
+ }
+
+ .acceptButton,
+ .rejectButton {
+ margin-left: -0.25rem;
+ }
+}
+
+@media (max-width: 520px) {
+ .tableItem .index,
+ .tableItem .name,
+ .tableItem .email {
+ padding-left: 1rem;
+ }
+
+ .acceptButton,
+ .rejectButton {
+ margin-left: 0;
+ width: 100%;
+ }
+}
diff --git a/src/components/RequestsTableItem/RequestsTableItem.test.tsx b/src/components/RequestsTableItem/RequestsTableItem.test.tsx
new file mode 100644
index 0000000000..bbd895300b
--- /dev/null
+++ b/src/components/RequestsTableItem/RequestsTableItem.test.tsx
@@ -0,0 +1,147 @@
+import React, { act } from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { render, screen } from '@testing-library/react';
+import { I18nextProvider } from 'react-i18next';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18nForTest from 'utils/i18nForTest';
+import type { InterfaceRequestsListItem } from './RequestsTableItem';
+import { MOCKS } from './RequestsTableItemMocks';
+import RequestsTableItem from './RequestsTableItem';
+import { BrowserRouter } from 'react-router-dom';
+const link = new StaticMockLink(MOCKS, true);
+import useLocalStorage from 'utils/useLocalstorage';
+import userEvent from '@testing-library/user-event';
+
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+const resetAndRefetchMock = jest.fn();
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ warning: jest.fn(),
+ },
+}));
+
+beforeEach(() => {
+ setItem('id', '123');
+});
+
+afterEach(() => {
+ localStorage.clear();
+ jest.clearAllMocks();
+});
+
+describe('Testing User Table Item', () => {
+ console.error = jest.fn((message) => {
+ if (message.includes('validateDOMNesting')) {
+ return;
+ }
+ console.warn(message);
+ });
+ test('Should render props and text elements test for the page component', async () => {
+ const props: {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+ } = {
+ request: {
+ _id: '123',
+ user: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ },
+ },
+ index: 1,
+ resetAndRefetch: resetAndRefetchMock,
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ expect(screen.getByText(/2./i)).toBeInTheDocument();
+ expect(screen.getByText(/John Doe/i)).toBeInTheDocument();
+ expect(screen.getByText(/john@example.com/i)).toBeInTheDocument();
+ });
+
+ test('Accept MembershipRequest Button works properly', async () => {
+ const props: {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+ } = {
+ request: {
+ _id: '123',
+ user: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ },
+ },
+ index: 1,
+ resetAndRefetch: resetAndRefetchMock,
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('acceptMembershipRequestBtn123'));
+ });
+
+ test('Reject MembershipRequest Button works properly', async () => {
+ const props: {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+ } = {
+ request: {
+ _id: '123',
+ user: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ },
+ },
+ index: 1,
+ resetAndRefetch: resetAndRefetchMock,
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('rejectMembershipRequestBtn123'));
+ });
+});
diff --git a/src/components/RequestsTableItem/RequestsTableItem.tsx b/src/components/RequestsTableItem/RequestsTableItem.tsx
new file mode 100644
index 0000000000..07feb5d289
--- /dev/null
+++ b/src/components/RequestsTableItem/RequestsTableItem.tsx
@@ -0,0 +1,142 @@
+import { useMutation } from '@apollo/client';
+import {
+ ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ REJECT_ORGANIZATION_REQUEST_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+import React from 'react';
+import { Button } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-toastify';
+import { errorHandler } from 'utils/errorHandler';
+import styles from './RequestsTableItem.module.css';
+
+/**
+ * Represents a membership request in the requests table.
+ */
+export interface InterfaceRequestsListItem {
+ _id: string;
+ user: {
+ firstName: string;
+ lastName: string;
+ email: string;
+ };
+}
+
+/**
+ * Props for the RequestsTableItem component.
+ *
+ */
+type Props = {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+};
+
+/**
+ * Renders a table row item for a membership request.
+ *
+ * This component displays user details and provides buttons to accept or reject
+ * the membership request. It also handles showing success or error messages using
+ * toast notifications.
+ *
+ * @param props - The props object containing request details, index, and state reset function.
+ * @returns The JSX element representing the RequestsTableItem.
+ */
+const RequestsTableItem = (props: Props): JSX.Element => {
+ const { t } = useTranslation('translation', { keyPrefix: 'requests' });
+ const { request, index, resetAndRefetch } = props;
+ const [acceptUser] = useMutation(ACCEPT_ORGANIZATION_REQUEST_MUTATION);
+ const [rejectUser] = useMutation(REJECT_ORGANIZATION_REQUEST_MUTATION);
+
+ /**
+ * Handles the acceptance of a membership request.
+ *
+ * Sends a mutation request to accept the user and shows a success message if successful.
+ * It also triggers a state reset and refetch.
+ *
+ * @param membershipRequestId - The ID of the membership request to accept.
+ */
+ const handleAcceptUser = async (
+ membershipRequestId: string,
+ ): Promise => {
+ try {
+ const { data } = await acceptUser({
+ variables: {
+ id: membershipRequestId,
+ },
+ });
+ /* istanbul ignore next */
+ if (data) {
+ toast.success(t('acceptedSuccessfully') as string);
+ resetAndRefetch();
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ /**
+ * Handles the rejection of a membership request.
+ *
+ * Sends a mutation request to reject the user and shows a success message if successful.
+ * It also triggers a state reset and refetch.
+ *
+ * @param membershipRequestId - The ID of the membership request to reject.
+ */
+ const handleRejectUser = async (
+ membershipRequestId: string,
+ ): Promise => {
+ try {
+ const { data } = await rejectUser({
+ variables: {
+ id: membershipRequestId,
+ },
+ });
+ /* istanbul ignore next */
+ if (data) {
+ toast.success(t('rejectedSuccessfully') as string);
+ resetAndRefetch();
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ return (
+
+ {index + 1}.
+ {`${request.user.firstName} ${request.user.lastName}`}
+ {request.user.email}
+
+ => {
+ await handleAcceptUser(request._id);
+ }}
+ className={styles.acceptButton}
+ >
+ {t('accept')}
+
+
+
+ => {
+ await handleRejectUser(request._id);
+ }}
+ className={styles.rejectButton}
+ >
+ {t('reject')}
+
+
+
+ );
+};
+
+export default RequestsTableItem;
diff --git a/src/components/RequestsTableItem/RequestsTableItemMocks.ts b/src/components/RequestsTableItem/RequestsTableItemMocks.ts
new file mode 100644
index 0000000000..22ea245d3a
--- /dev/null
+++ b/src/components/RequestsTableItem/RequestsTableItemMocks.ts
@@ -0,0 +1,37 @@
+import {
+ ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ REJECT_ORGANIZATION_REQUEST_MUTATION,
+} from 'GraphQl/Mutations/mutations';
+
+export const MOCKS = [
+ {
+ request: {
+ query: ACCEPT_ORGANIZATION_REQUEST_MUTATION,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ acceptMembershipRequest: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: REJECT_ORGANIZATION_REQUEST_MUTATION,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ rejectMembershipRequest: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
diff --git a/src/components/SecuredRoute/SecuredRoute.tsx b/src/components/SecuredRoute/SecuredRoute.tsx
new file mode 100644
index 0000000000..1119800215
--- /dev/null
+++ b/src/components/SecuredRoute/SecuredRoute.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import { Navigate, Outlet } from 'react-router-dom';
+import { toast } from 'react-toastify';
+import PageNotFound from 'screens/PageNotFound/PageNotFound';
+import useLocalStorage from 'utils/useLocalstorage';
+const { getItem, setItem } = useLocalStorage();
+
+/**
+ * A route guard that checks if the user is logged in and has the necessary permissions.
+ *
+ * If the user is logged in and has an admin role set, it renders the child routes.
+ * Otherwise, it redirects to the home page or shows a 404 page if admin role is not set.
+ *
+ * @returns The JSX element representing the secured route.
+ */
+const SecuredRoute = (): JSX.Element => {
+ const isLoggedIn = getItem('IsLoggedIn');
+ const adminFor = getItem('AdminFor');
+
+ return isLoggedIn === 'TRUE' ? (
+ <>{adminFor != null ? : }>
+ ) : (
+
+ );
+};
+
+// Time constants for session timeout and inactivity interval
+const timeoutMinutes = 15;
+const timeoutMilliseconds = timeoutMinutes * 60 * 1000;
+
+const inactiveIntervalMin = 1;
+const inactiveIntervalMilsec = inactiveIntervalMin * 60 * 1000;
+
+let lastActive: number = Date.now();
+
+// Update lastActive timestamp on mouse movement
+document.addEventListener('mousemove', () => {
+ lastActive = Date.now();
+});
+
+// Check for inactivity and handle session timeout
+setInterval(() => {
+ const currentTime = Date.now();
+ const timeSinceLastActive = currentTime - lastActive;
+
+ // If inactive for longer than the timeout period, show a warning and log out
+ if (timeSinceLastActive > timeoutMilliseconds) {
+ toast.warn('Kindly relogin as sessison has expired');
+
+ window.location.href = '/';
+ setItem('IsLoggedIn', 'FALSE');
+ }
+}, inactiveIntervalMilsec);
+
+export default SecuredRoute;
diff --git a/src/components/SuperAdminScreen/SuperAdminScreen.module.css b/src/components/SuperAdminScreen/SuperAdminScreen.module.css
new file mode 100644
index 0000000000..9496ef95fa
--- /dev/null
+++ b/src/components/SuperAdminScreen/SuperAdminScreen.module.css
@@ -0,0 +1,101 @@
+.pageContainer {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ padding: 1rem 1.5rem 0 calc(300px + 2rem + 1.5rem);
+}
+
+.expand {
+ padding-left: 4rem;
+ animation: moveLeft 0.5s ease-in-out;
+}
+
+.contract {
+ padding-left: calc(300px + 2rem + 1.5rem);
+ animation: moveRight 0.5s ease-in-out;
+}
+
+.collapseSidebarButton {
+ position: fixed;
+ height: 40px;
+ bottom: 0;
+ z-index: 9999;
+ width: calc(300px + 2rem);
+ background-color: rgba(245, 245, 245, 0.7);
+ color: black;
+ border: none;
+ border-radius: 0px;
+}
+
+.collapseSidebarButton:hover,
+.opendrawer:hover {
+ opacity: 1;
+ color: black !important;
+}
+.opendrawer {
+ position: fixed;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 100vh;
+ z-index: 9999;
+ background-color: rgba(245, 245, 245);
+ border: none;
+ border-radius: 0px;
+ margin-right: 20px;
+ color: black;
+}
+
+@media (max-width: 1120px) {
+ .contract {
+ padding-left: calc(250px + 2rem + 1.5rem);
+ }
+ .collapseSidebarButton {
+ width: calc(250px + 2rem);
+ }
+}
+
+/* For tablets */
+@media (max-width: 820px) {
+ .pageContainer {
+ padding-left: 2.5rem;
+ }
+
+ .opendrawer {
+ width: 25px;
+ }
+
+ .contract,
+ .expand {
+ animation: none;
+ }
+
+ .collapseSidebarButton {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+}
+
+@keyframes moveLeft {
+ from {
+ padding-left: calc(300px + 2rem + 1.5rem);
+ }
+
+ to {
+ padding-left: 1.5rem;
+ }
+}
+
+@keyframes moveRight {
+ from {
+ padding-left: 1.5rem;
+ }
+
+ to {
+ padding-left: calc(300px + 2rem + 1.5rem);
+ }
+}
diff --git a/src/components/SuperAdminScreen/SuperAdminScreen.test.tsx b/src/components/SuperAdminScreen/SuperAdminScreen.test.tsx
new file mode 100644
index 0000000000..84b740ab12
--- /dev/null
+++ b/src/components/SuperAdminScreen/SuperAdminScreen.test.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { fireEvent, render, screen } from '@testing-library/react';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import SuperAdminScreen from './SuperAdminScreen';
+
+const resizeWindow = (width: number): void => {
+ window.innerWidth = width;
+ fireEvent(window, new Event('resize'));
+};
+
+const clickToggleMenuBtn = (toggleButton: HTMLElement): void => {
+ fireEvent.click(toggleButton);
+};
+
+describe('Testing LeftDrawer in SuperAdminScreen', () => {
+ test('Testing LeftDrawer in page functionality', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ const toggleButton = screen.getByTestId('closeMenu') as HTMLElement;
+ const icon = toggleButton.querySelector('i');
+
+ // Resize window to a smaller width
+ resizeWindow(800);
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-left');
+
+ // Resize window back to a larger width
+ resizeWindow(1000);
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-right');
+
+ clickToggleMenuBtn(toggleButton);
+ expect(icon).toHaveClass('fa fa-angle-double-left');
+ });
+});
diff --git a/src/components/SuperAdminScreen/SuperAdminScreen.tsx b/src/components/SuperAdminScreen/SuperAdminScreen.tsx
new file mode 100644
index 0000000000..948dc334f7
--- /dev/null
+++ b/src/components/SuperAdminScreen/SuperAdminScreen.tsx
@@ -0,0 +1,98 @@
+import LeftDrawer from 'components/LeftDrawer/LeftDrawer';
+import React, { useEffect, useState } from 'react';
+import Button from 'react-bootstrap/Button';
+import { useTranslation } from 'react-i18next';
+import { Outlet, useLocation } from 'react-router-dom';
+import styles from './SuperAdminScreen.module.css';
+import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown';
+
+/**
+ * The SuperAdminScreen component manages the layout for the Super Admin screen,
+ * including handling the sidebar visibility and page title based on the current route.
+ *
+ * @returns The JSX element representing the Super Admin screen layout.
+ */
+const superAdminScreen = (): JSX.Element => {
+ const location = useLocation();
+ const titleKey = map[location.pathname.split('/')[1]];
+ const { t } = useTranslation('translation', { keyPrefix: titleKey });
+ const [hideDrawer, setHideDrawer] = useState(null);
+
+ /**
+ * Handles resizing of the window to show or hide the sidebar.
+ */
+ const handleResize = (): void => {
+ if (window.innerWidth <= 820) {
+ setHideDrawer(!hideDrawer);
+ }
+ };
+
+ useEffect(() => {
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return (
+ <>
+ {hideDrawer ? (
+ {
+ setHideDrawer(!hideDrawer);
+ }}
+ data-testid="openMenu"
+ >
+
+
+ ) : (
+ {
+ setHideDrawer(!hideDrawer);
+ }}
+ data-testid="closeMenu"
+ >
+
+
+ )}
+
+
+ >
+ );
+};
+
+export default superAdminScreen;
+
+/**
+ * Map of route segments to translation keys for page titles.
+ */
+const map: Record<
+ string,
+ 'orgList' | 'requests' | 'users' | 'memberDetail' | 'communityProfile'
+> = {
+ orglist: 'orgList',
+ requests: 'requests',
+ users: 'users',
+ member: 'memberDetail',
+ communityProfile: 'communityProfile',
+};
diff --git a/src/components/TableLoader/TableLoader.module.css b/src/components/TableLoader/TableLoader.module.css
new file mode 100644
index 0000000000..66349e2aa9
--- /dev/null
+++ b/src/components/TableLoader/TableLoader.module.css
@@ -0,0 +1,3 @@
+.loadingItem {
+ height: 30px;
+}
diff --git a/src/components/TableLoader/TableLoader.test.tsx b/src/components/TableLoader/TableLoader.test.tsx
new file mode 100644
index 0000000000..a7d334a1c7
--- /dev/null
+++ b/src/components/TableLoader/TableLoader.test.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+
+import type { InterfaceTableLoader } from './TableLoader';
+import TableLoader from './TableLoader';
+
+beforeAll(() => {
+ console.error = jest.fn();
+});
+
+describe('Testing Loader component', () => {
+ test('Component should be rendered properly only headerTitles is provided', () => {
+ const props: InterfaceTableLoader = {
+ noOfRows: 10,
+ headerTitles: ['header1', 'header2', 'header3'],
+ };
+ render(
+
+
+ ,
+ );
+ // Check if header titles are rendered properly
+ const data = props.headerTitles as string[];
+ data.forEach((title) => {
+ expect(screen.getByText(title)).toBeInTheDocument();
+ });
+
+ // Check if elements are rendered properly
+ for (let rowIndex = 0; rowIndex < props.noOfRows; rowIndex++) {
+ expect(
+ screen.getByTestId(`row-${rowIndex}-tableLoading`),
+ ).toBeInTheDocument();
+ for (let colIndex = 0; colIndex < data.length; colIndex++) {
+ expect(
+ screen.getByTestId(`row-${rowIndex}-col-${colIndex}-tableLoading`),
+ ).toBeInTheDocument();
+ }
+ }
+ });
+ test('Component should be rendered properly only noCols is provided', () => {
+ const props: InterfaceTableLoader = {
+ noOfRows: 10,
+ noOfCols: 3,
+ };
+ render(
+
+
+ ,
+ );
+ // Check if header titles are rendered properly
+ const data = [...Array(props.noOfCols)];
+
+ // Check if elements are rendered properly
+ for (let rowIndex = 0; rowIndex < props.noOfRows; rowIndex++) {
+ expect(
+ screen.getByTestId(`row-${rowIndex}-tableLoading`),
+ ).toBeInTheDocument();
+ for (let colIndex = 0; colIndex < data.length; colIndex++) {
+ expect(
+ screen.getByTestId(`row-${rowIndex}-col-${colIndex}-tableLoading`),
+ ).toBeInTheDocument();
+ }
+ }
+ });
+ test('Component should be throw error when noOfCols and headerTitles are undefined', () => {
+ const props = {
+ noOfRows: 10,
+ };
+ expect(() => {
+ render(
+
+
+ ,
+ );
+ }).toThrow();
+ });
+});
diff --git a/src/components/TableLoader/TableLoader.tsx b/src/components/TableLoader/TableLoader.tsx
new file mode 100644
index 0000000000..631b13d514
--- /dev/null
+++ b/src/components/TableLoader/TableLoader.tsx
@@ -0,0 +1,79 @@
+import React, { useEffect } from 'react';
+import styles from './TableLoader.module.css';
+import { Table } from 'react-bootstrap';
+
+export interface InterfaceTableLoader {
+ noOfRows: number;
+ headerTitles?: string[];
+ noOfCols?: number;
+}
+
+/**
+ * The TableLoader component displays a loading skeleton for tables.
+ * It shows a specified number of rows and columns as placeholders
+ * with a shimmering effect to indicate loading content.
+ *
+ * @param props - The properties for the TableLoader component.
+ * @param noOfRows - The number of rows to display.
+ * @param headerTitles - Optional. The titles for the table headers.
+ * @param noOfCols - Optional. The number of columns if headerTitles is not provided.
+ *
+ * @returns The JSX element representing the table loader.
+ */
+const tableLoader = (props: InterfaceTableLoader): JSX.Element => {
+ const { noOfRows, headerTitles, noOfCols } = props;
+
+ useEffect(() => {
+ if (headerTitles == undefined && noOfCols == undefined) {
+ throw new Error(
+ 'TableLoader error Either headerTitles or noOfCols is required !',
+ );
+ }
+ }, []);
+
+ return (
+ <>
+
+
+
+ {headerTitles
+ ? headerTitles.map((title, index) => {
+ return {title} ;
+ })
+ : noOfCols &&
+ [...Array(noOfCols)].map((_, index) => {
+ return ;
+ })}
+
+
+
+
+ {[...Array(noOfRows)].map((_, rowIndex) => {
+ return (
+
+ {[...Array(headerTitles ? headerTitles?.length : noOfCols)].map(
+ (_, colIndex) => {
+ return (
+
+
+
+ );
+ },
+ )}
+
+ );
+ })}
+
+
+ >
+ );
+};
+
+export default tableLoader;
diff --git a/src/components/TagActions/TagActions.module.css b/src/components/TagActions/TagActions.module.css
new file mode 100644
index 0000000000..079dffea65
--- /dev/null
+++ b/src/components/TagActions/TagActions.module.css
@@ -0,0 +1,42 @@
+.errorContainer {
+ min-height: 100vh;
+}
+
+.errorMessage {
+ margin-top: 25%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.errorIcon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.scrollContainer {
+ height: 100px;
+ overflow-y: auto;
+}
+
+.tagBadge {
+ display: flex;
+ align-items: center;
+ padding: 5px 10px;
+ border-radius: 12px;
+ box-shadow: 0 1px 3px var(--bs-gray-400);
+ max-width: calc(100% - 2rem);
+}
+
+.removeFilterIcon {
+ cursor: pointer;
+}
+
+.loadingDiv {
+ min-height: 300px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/TagActions/TagActions.test.tsx b/src/components/TagActions/TagActions.test.tsx
new file mode 100644
index 0000000000..d27f177ebe
--- /dev/null
+++ b/src/components/TagActions/TagActions.test.tsx
@@ -0,0 +1,400 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ waitFor,
+ act,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MemoryRouter, Route, Routes } from 'react-router-dom';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast } from 'react-toastify';
+import type { ApolloLink } from '@apollo/client';
+import type { InterfaceTagActionsProps } from './TagActions';
+import TagActions from './TagActions';
+import i18n from 'utils/i18nForTest';
+import {
+ MOCKS,
+ MOCKS_ERROR_ORGANIZATION_TAGS_QUERY,
+ MOCKS_ERROR_SUBTAGS_QUERY,
+} from './TagActionsMocks';
+import type { TFunction } from 'i18next';
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR_ORGANIZATION_TAGS_QUERY, true);
+const link3 = new StaticMockLink(MOCKS_ERROR_SUBTAGS_QUERY, true);
+
+async function wait(ms = 500): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(i18n.getDataByLanguage('en')?.translation.manageTag ?? {}),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const props: InterfaceTagActionsProps[] = [
+ {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: () => {},
+ tagActionType: 'assignToTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ },
+ {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: () => {},
+ tagActionType: 'removeFromTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ },
+];
+
+const renderTagActionsModal = (
+ props: InterfaceTagActionsProps,
+ link: ApolloLink,
+): RenderResult => {
+ return render(
+
+
+
+
+
+ }
+ />
+
+
+
+
+ ,
+ );
+};
+
+describe('Organisation Tags Page', () => {
+ beforeEach(() => {
+ jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: 'orgId' }),
+ }));
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('Component loads correctly and opens assignToTags modal', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+ });
+
+ test('Component loads correctly and opens removeFromTags modal', async () => {
+ const { getByText } = renderTagActionsModal(props[1], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.remove)).toBeInTheDocument();
+ });
+ });
+
+ test('Component calls hideTagActionsModal when modal is closed', async () => {
+ const hideTagActionsModalMock = jest.fn();
+
+ const props2: InterfaceTagActionsProps = {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: hideTagActionsModalMock,
+ tagActionType: 'assignToTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ };
+
+ renderTagActionsModal(props2, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('closeTagActionsModalBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('closeTagActionsModalBtn'));
+
+ await waitFor(() => {
+ expect(hideTagActionsModalMock).toHaveBeenCalled();
+ });
+ });
+
+ test('Renders error component when when query is unsuccessful', async () => {
+ const { queryByText } = renderTagActionsModal(props[0], link2);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(queryByText(translations.assign)).not.toBeInTheDocument();
+ });
+ });
+
+ test('Renders error component when when subTags query is unsuccessful', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link3);
+
+ await wait();
+
+ // expand tag 1 to list its subtags
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+
+ await waitFor(() => {
+ expect(
+ getByText(translations.errorOccurredWhileLoadingSubTags),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('searchs for tags where the name matches the provided search input', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getByPlaceholderText(translations.searchByName),
+ ).toBeInTheDocument();
+ });
+ const input = screen.getByPlaceholderText(translations.searchByName);
+ fireEvent.change(input, { target: { value: 'searchUserTag' } });
+
+ // should render the two searched tags from the mock data
+ // where name starts with "searchUserTag"
+ await waitFor(() => {
+ const tags = screen.getAllByTestId('orgUserTag');
+ expect(tags.length).toEqual(2);
+ });
+ });
+
+ test('Renders more members with infinite scroll', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+
+ // Find the infinite scroll div by test ID or another selector
+ const scrollableDiv = screen.getByTestId('scrollableDiv');
+
+ const initialTagsDataLength = screen.getAllByTestId('orgUserTag').length;
+
+ // Set scroll position to the bottom
+ fireEvent.scroll(scrollableDiv, {
+ target: { scrollY: scrollableDiv.scrollHeight },
+ });
+
+ await waitFor(() => {
+ const finalTagsDataLength = screen.getAllByTestId('orgUserTag').length;
+ expect(finalTagsDataLength).toBeGreaterThan(initialTagsDataLength);
+
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+ });
+
+ test('Selects and deselects tags', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('clearSelectedTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('clearSelectedTag2'));
+ });
+
+ test('fetches and lists the child tags and then selects and deselects them', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ // expand tag 1 to list its subtags
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('subTagsScrollableDiv1')).toBeInTheDocument();
+ });
+ // Find the infinite scroll div for subtags by test ID or another selector
+ const subTagsScrollableDiv1 = screen.getByTestId('subTagsScrollableDiv1');
+
+ const initialTagsDataLength =
+ screen.getAllByTestId('orgUserSubTags').length;
+
+ // Set scroll position to the bottom
+ fireEvent.scroll(subTagsScrollableDiv1, {
+ target: { scrollY: subTagsScrollableDiv1.scrollHeight },
+ });
+
+ await waitFor(() => {
+ const finalTagsDataLength =
+ screen.getAllByTestId('orgUserSubTags').length;
+ expect(finalTagsDataLength).toBeGreaterThan(initialTagsDataLength);
+ });
+
+ // select subtags 1 & 2
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ // deselect subtags 1 & 2
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag2'));
+
+ // hide subtags of tag 1
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+ });
+
+ test('Toasts error when no tag is selected while assigning', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('tagActionSubmitBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(translations.noTagSelected);
+ });
+ });
+
+ test('Successfully assigns to tags', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ // select userTags 2 & 3 and assign them
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag3')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag3'));
+
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfullyAssignedToTags,
+ );
+ });
+ });
+
+ test('Successfully removes from tags', async () => {
+ renderTagActionsModal(props[1], link);
+
+ await wait();
+
+ // select userTag 2 and remove people from it
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfullyRemovedFromTags,
+ );
+ });
+ });
+});
diff --git a/src/components/TagActions/TagActions.tsx b/src/components/TagActions/TagActions.tsx
new file mode 100644
index 0000000000..083341372f
--- /dev/null
+++ b/src/components/TagActions/TagActions.tsx
@@ -0,0 +1,407 @@
+import { useMutation, useQuery } from '@apollo/client';
+import type { FormEvent } from 'react';
+import React, { useEffect, useState } from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import { useParams } from 'react-router-dom';
+import type {
+ InterfaceQueryOrganizationUserTags,
+ InterfaceTagData,
+} from 'utils/interfaces';
+import styles from './TagActions.module.css';
+import { ORGANIZATION_USER_TAGS_LIST } from 'GraphQl/Queries/OrganizationQueries';
+import {
+ ASSIGN_TO_TAGS,
+ REMOVE_FROM_TAGS,
+} from 'GraphQl/Mutations/TagMutations';
+import { toast } from 'react-toastify';
+import type {
+ InterfaceOrganizationTagsQuery,
+ TagActionType,
+} from 'utils/organizationTagsUtils';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import { WarningAmberRounded } from '@mui/icons-material';
+import TagNode from './TagNode';
+import InfiniteScrollLoader from 'components/InfiniteScrollLoader/InfiniteScrollLoader';
+import type { TFunction } from 'i18next';
+
+interface InterfaceUserTagsAncestorData {
+ _id: string;
+ name: string;
+}
+
+/**
+ * Props for the `AssignToTags` component.
+ */
+export interface InterfaceTagActionsProps {
+ tagActionsModalIsOpen: boolean;
+ hideTagActionsModal: () => void;
+ tagActionType: TagActionType;
+ t: TFunction<'translation', 'manageTag'>;
+ tCommon: TFunction<'common', undefined>;
+}
+
+const TagActions: React.FC = ({
+ tagActionsModalIsOpen,
+ hideTagActionsModal,
+ tagActionType,
+ t,
+ tCommon,
+}) => {
+ const { orgId, tagId: currentTagId } = useParams();
+
+ const [tagSearchName, setTagSearchName] = useState('');
+
+ const {
+ data: orgUserTagsData,
+ loading: orgUserTagsLoading,
+ error: orgUserTagsError,
+ fetchMore: orgUserTagsFetchMore,
+ }: InterfaceOrganizationTagsQuery = useQuery(ORGANIZATION_USER_TAGS_LIST, {
+ variables: {
+ id: orgId,
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: tagSearchName } },
+ },
+ skip: !tagActionsModalIsOpen,
+ });
+
+ const loadMoreUserTags = (): void => {
+ orgUserTagsFetchMore({
+ variables: {
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: orgUserTagsData?.organizations[0].userTags.pageInfo.endCursor,
+ },
+ updateQuery: (
+ prevResult: { organizations: InterfaceQueryOrganizationUserTags[] },
+ {
+ fetchMoreResult,
+ }: {
+ fetchMoreResult?: {
+ organizations: InterfaceQueryOrganizationUserTags[];
+ };
+ },
+ ) => {
+ if (!fetchMoreResult) /* istanbul ignore next */ return prevResult;
+
+ return {
+ organizations: [
+ {
+ ...prevResult.organizations[0],
+ userTags: {
+ ...prevResult.organizations[0].userTags,
+ edges: [
+ ...prevResult.organizations[0].userTags.edges,
+ ...fetchMoreResult.organizations[0].userTags.edges,
+ ],
+ pageInfo: fetchMoreResult.organizations[0].userTags.pageInfo,
+ },
+ },
+ ],
+ };
+ },
+ });
+ };
+
+ const userTagsList =
+ orgUserTagsData?.organizations[0]?.userTags.edges.map(
+ (edge) => edge.node,
+ ) ?? /* istanbul ignore next */ [];
+
+ // tags that we have selected to assigned
+ const [selectedTags, setSelectedTags] = useState([]);
+
+ // tags that we have checked, it is there to differentiate between the selected tags and all the checked tags
+ // i.e. selected tags would only be the ones we select, but checked tags will also include the selected tag's ancestors
+ const [checkedTags, setCheckedTags] = useState>(new Set());
+
+ // next 3 states are there to keep track of the ancestor tags of the the tags that we have selected
+ // i.e. when we check a tag, all of it's ancestor tags will be checked too
+ // indicating that the users will be assigned all of the ancestor tags as well
+ const [addAncestorTagsData, setAddAncestorTagsData] = useState<
+ Set
+ >(new Set());
+ const [removeAncestorTagsData, setRemoveAncestorTagsData] = useState<
+ Set
+ >(new Set());
+ const [ancestorTagsDataMap, setAncestorTagsDataMap] = useState(new Map());
+
+ useEffect(() => {
+ const newCheckedTags = new Set(checkedTags);
+ const newAncestorTagsDataMap = new Map(ancestorTagsDataMap);
+ /* istanbul ignore next */
+ addAncestorTagsData.forEach(
+ (ancestorTag: InterfaceUserTagsAncestorData) => {
+ const prevAncestorTagValue = ancestorTagsDataMap.get(ancestorTag._id);
+ newAncestorTagsDataMap.set(
+ ancestorTag._id,
+ prevAncestorTagValue ? prevAncestorTagValue + 1 : 1,
+ );
+ newCheckedTags.add(ancestorTag._id);
+ },
+ );
+
+ setCheckedTags(newCheckedTags);
+ setAncestorTagsDataMap(newAncestorTagsDataMap);
+ }, [addAncestorTagsData]);
+
+ useEffect(() => {
+ const newCheckedTags = new Set(checkedTags);
+ const newAncestorTagsDataMap = new Map(ancestorTagsDataMap);
+ /* istanbul ignore next */
+ removeAncestorTagsData.forEach(
+ (ancestorTag: InterfaceUserTagsAncestorData) => {
+ const prevAncestorTagValue = ancestorTagsDataMap.get(ancestorTag._id);
+ if (prevAncestorTagValue === 1) {
+ newCheckedTags.delete(ancestorTag._id);
+ newAncestorTagsDataMap.delete(ancestorTag._id);
+ } else {
+ newAncestorTagsDataMap.set(ancestorTag._id, prevAncestorTagValue - 1);
+ }
+ },
+ );
+
+ setCheckedTags(newCheckedTags);
+ setAncestorTagsDataMap(newAncestorTagsDataMap);
+ }, [removeAncestorTagsData]);
+
+ const selectTag = (tag: InterfaceTagData): void => {
+ const newCheckedTags = new Set(checkedTags);
+
+ setSelectedTags((selectedTags) => [...selectedTags, tag]);
+ newCheckedTags.add(tag._id);
+
+ setAddAncestorTagsData(new Set(tag.ancestorTags));
+
+ setCheckedTags(newCheckedTags);
+ };
+
+ const deSelectTag = (tag: InterfaceTagData): void => {
+ if (!selectedTags.some((selectedTag) => selectedTag._id === tag._id)) {
+ /* istanbul ignore next */
+ return;
+ }
+
+ const newCheckedTags = new Set(checkedTags);
+
+ setSelectedTags(
+ selectedTags.filter((selectedTag) => selectedTag._id !== tag._id),
+ );
+ newCheckedTags.delete(tag._id);
+
+ setRemoveAncestorTagsData(new Set(tag.ancestorTags));
+
+ setCheckedTags(newCheckedTags);
+ };
+
+ const toggleTagSelection = (
+ tag: InterfaceTagData,
+ isSelected: boolean,
+ ): void => {
+ if (isSelected) {
+ selectTag(tag);
+ } else {
+ deSelectTag(tag);
+ }
+ };
+
+ const [assignToTags] = useMutation(ASSIGN_TO_TAGS);
+ const [removeFromTags] = useMutation(REMOVE_FROM_TAGS);
+
+ const handleTagAction = async (
+ e: FormEvent,
+ ): Promise => {
+ e.preventDefault();
+
+ if (!selectedTags.length) {
+ toast.error(t('noTagSelected'));
+ return;
+ }
+
+ const mutationObject = {
+ variables: {
+ currentTagId,
+ selectedTagIds: selectedTags.map((selectedTag) => selectedTag._id),
+ },
+ };
+
+ try {
+ const { data } =
+ tagActionType === 'assignToTags'
+ ? await assignToTags(mutationObject)
+ : await removeFromTags(mutationObject);
+
+ if (data) {
+ if (tagActionType === 'assignToTags') {
+ toast.success(t('successfullyAssignedToTags'));
+ } else {
+ toast.success(t('successfullyRemovedFromTags'));
+ }
+ hideTagActionsModal();
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+
+ if (orgUserTagsError) {
+ return (
+
+
+
+
+ {t('errorOccurredWhileLoadingOrganizationUserTags')}
+
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+ {tagActionType === 'assignToTags'
+ ? t('assignToTags')
+ : t('removeFromTags')}
+
+
+
+
+ >
+ );
+};
+
+export default TagActions;
diff --git a/src/components/TagActions/TagActionsMocks.ts b/src/components/TagActions/TagActionsMocks.ts
new file mode 100644
index 0000000000..f22458fa53
--- /dev/null
+++ b/src/components/TagActions/TagActionsMocks.ts
@@ -0,0 +1,706 @@
+import {
+ ASSIGN_TO_TAGS,
+ REMOVE_FROM_TAGS,
+} from 'GraphQl/Mutations/TagMutations';
+import { ORGANIZATION_USER_TAGS_LIST } from 'GraphQl/Queries/OrganizationQueries';
+import { USER_TAG_SUB_TAGS } from 'GraphQl/Queries/userTagQueries';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+
+export const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'userTag 1',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'userTag 2',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '2',
+ },
+ {
+ node: {
+ _id: '3',
+ name: 'userTag 3',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '3',
+ },
+ {
+ node: {
+ _id: '4',
+ name: 'userTag 4',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '4',
+ },
+ {
+ node: {
+ _id: '5',
+ name: 'userTag 5',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '5',
+ },
+ {
+ node: {
+ _id: '6',
+ name: 'userTag 6',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 6,
+ },
+ childTags: {
+ totalCount: 6,
+ },
+ ancestorTags: [],
+ },
+ cursor: '6',
+ },
+ {
+ node: {
+ _id: '7',
+ name: 'userTag 7',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 7,
+ },
+ childTags: {
+ totalCount: 7,
+ },
+ ancestorTags: [],
+ },
+ cursor: '7',
+ },
+ {
+ node: {
+ _id: '8',
+ name: 'userTag 8',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 8,
+ },
+ childTags: {
+ totalCount: 8,
+ },
+ ancestorTags: [],
+ },
+ cursor: '8',
+ },
+ {
+ node: {
+ _id: '9',
+ name: 'userTag 9',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 9,
+ },
+ childTags: {
+ totalCount: 9,
+ },
+ ancestorTags: [],
+ },
+ cursor: '9',
+ },
+ {
+ node: {
+ _id: '10',
+ name: 'userTag 10',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 10,
+ },
+ childTags: {
+ totalCount: 10,
+ },
+ ancestorTags: [],
+ },
+ cursor: '10',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '10',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 12,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: '10',
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '11',
+ name: 'userTag 11',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '11',
+ },
+ {
+ node: {
+ _id: '12',
+ name: 'userTag 12',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '12',
+ },
+ ],
+ pageInfo: {
+ startCursor: '11',
+ endCursor: '12',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 12,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: 'searchUserTag' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'searchUserTag 1',
+ parentTag: {
+ _id: '1',
+ },
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'searchUserTag 2',
+ parentTag: {
+ _id: '1',
+ },
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ result: {
+ data: {
+ getChildTags: {
+ name: 'userTag 1',
+ childTags: {
+ edges: [
+ {
+ node: {
+ _id: 'subTag1',
+ name: 'subTag 1',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag1',
+ },
+ {
+ node: {
+ _id: 'subTag2',
+ name: 'subTag 2',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag2',
+ },
+ {
+ node: {
+ _id: 'subTag3',
+ name: 'subTag 3',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag3',
+ },
+ {
+ node: {
+ _id: 'subTag4',
+ name: 'subTag 4',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag4',
+ },
+ {
+ node: {
+ _id: 'subTag5',
+ name: 'subTag 5',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag5',
+ },
+ {
+ node: {
+ _id: 'subTag6',
+ name: 'subTag 6',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag6',
+ },
+ {
+ node: {
+ _id: 'subTag7',
+ name: 'subTag 7',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag7',
+ },
+ {
+ node: {
+ _id: 'subTag8',
+ name: 'subTag 8',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag8',
+ },
+ {
+ node: {
+ _id: 'subTag9',
+ name: 'subTag 9',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag9',
+ },
+ {
+ node: {
+ _id: 'subTag10',
+ name: 'subTag 10',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag10',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'subTag1',
+ endCursor: 'subTag10',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ after: 'subTag10',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ result: {
+ data: {
+ getChildTags: {
+ name: 'userTag 1',
+ childTags: {
+ edges: [
+ {
+ node: {
+ _id: 'subTag11',
+ name: 'subTag 11',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag11',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'subTag11',
+ endCursor: 'subTag11',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: ASSIGN_TO_TAGS,
+ variables: {
+ currentTagId: '1',
+ selectedTagIds: ['2', '3'],
+ },
+ },
+ result: {
+ data: {
+ assignToUserTags: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: REMOVE_FROM_TAGS,
+ variables: {
+ currentTagId: '1',
+ selectedTagIds: ['2'],
+ },
+ },
+ result: {
+ data: {
+ removeFromUserTags: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR_ORGANIZATION_TAGS_QUERY = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ error: new Error('Mock Graphql Error for organization root tags query'),
+ },
+];
+
+export const MOCKS_ERROR_SUBTAGS_QUERY = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'userTag 1',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'userTag 2',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ error: new Error('Mock Graphql Error for subTags query'),
+ },
+];
diff --git a/src/components/TagActions/TagNode.tsx b/src/components/TagActions/TagNode.tsx
new file mode 100644
index 0000000000..4a085ecaf9
--- /dev/null
+++ b/src/components/TagActions/TagNode.tsx
@@ -0,0 +1,199 @@
+import { useQuery } from '@apollo/client';
+import { USER_TAG_SUB_TAGS } from 'GraphQl/Queries/userTagQueries';
+import React, { useState } from 'react';
+import type {
+ InterfaceQueryUserTagChildTags,
+ InterfaceTagData,
+} from 'utils/interfaces';
+import type { InterfaceOrganizationSubTagsQuery } from 'utils/organizationTagsUtils';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+import styles from './TagActions.module.css';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import InfiniteScrollLoader from 'components/InfiniteScrollLoader/InfiniteScrollLoader';
+import { WarningAmberRounded } from '@mui/icons-material';
+import type { TFunction } from 'i18next';
+
+/**
+ * Props for the `TagNode` component.
+ */
+interface InterfaceTagNodeProps {
+ tag: InterfaceTagData;
+ checkedTags: Set;
+ toggleTagSelection: (tag: InterfaceTagData, isSelected: boolean) => void;
+ t: TFunction<'translation', 'manageTag'>;
+}
+
+/**
+ * Renders the Tags which can be expanded to list subtags.
+ */
+const TagNode: React.FC = ({
+ tag,
+ checkedTags,
+ toggleTagSelection,
+ t,
+}) => {
+ const [expanded, setExpanded] = useState(false);
+
+ const {
+ data: subTagsData,
+ loading: subTagsLoading,
+ error: subTagsError,
+ fetchMore: fetchMoreSubTags,
+ }: InterfaceOrganizationSubTagsQuery = useQuery(USER_TAG_SUB_TAGS, {
+ variables: {
+ id: tag._id,
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ skip: !expanded,
+ });
+
+ const loadMoreSubTags = (): void => {
+ fetchMoreSubTags({
+ variables: {
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: subTagsData?.getChildTags.childTags.pageInfo.endCursor,
+ },
+ updateQuery: (
+ prevResult: { getChildTags: InterfaceQueryUserTagChildTags },
+ {
+ fetchMoreResult,
+ }: {
+ fetchMoreResult?: { getChildTags: InterfaceQueryUserTagChildTags };
+ },
+ ) => {
+ if (!fetchMoreResult) /* istanbul ignore next */ return prevResult;
+
+ return {
+ getChildTags: {
+ ...fetchMoreResult.getChildTags,
+ childTags: {
+ ...fetchMoreResult.getChildTags.childTags,
+ edges: [
+ ...prevResult.getChildTags.childTags.edges,
+ ...fetchMoreResult.getChildTags.childTags.edges,
+ ],
+ },
+ },
+ };
+ },
+ });
+ };
+
+ if (subTagsError) {
+ return (
+
+
+
+
+ {t('errorOccurredWhileLoadingSubTags')}
+
+
+
+ );
+ }
+
+ const subTagsList =
+ subTagsData?.getChildTags.childTags.edges.map((edge) => edge.node) ??
+ /* istanbul ignore next */ [];
+
+ const handleTagClick = (): void => {
+ setExpanded(!expanded);
+ };
+
+ const handleCheckboxChange = (
+ e: React.ChangeEvent,
+ ): void => {
+ toggleTagSelection(tag, e.target.checked);
+ };
+
+ return (
+
+ );
+};
+
+export default TagNode;
diff --git a/src/components/UpdateSession/UpdateSession.css b/src/components/UpdateSession/UpdateSession.css
new file mode 100644
index 0000000000..073bbf973d
--- /dev/null
+++ b/src/components/UpdateSession/UpdateSession.css
@@ -0,0 +1,96 @@
+/* Card styles */
+.update-timeout-card {
+ width: 700px;
+ background: #ffffff;
+ border: none;
+ border-radius: 16px;
+ filter: drop-shadow(0px 4px 15.3px rgba(0, 0, 0, 0.08));
+ padding: 20px;
+}
+
+.update-timeout-card-header {
+ background: none;
+ padding: 16px;
+ border-bottom: none;
+}
+
+.update-timeout-card-title {
+ font-family: 'Lato', sans-serif;
+ font-weight: 600;
+ font-size: 24px;
+ color: #000000;
+}
+
+.update-timeout-card-body {
+ padding: 20px;
+}
+
+.update-timeout-current {
+ font-family: 'Lato', sans-serif;
+ font-weight: 400;
+ font-size: 16px;
+ color: #000000;
+ margin-bottom: 20px; /* Increased margin to create more space */
+}
+
+.update-timeout-label {
+ font-family: 'Lato', sans-serif;
+ font-weight: 400;
+ font-size: 16px;
+ color: #000000;
+ margin-bottom: 10px; /* Keep the same margin to maintain spacing with the slider */
+}
+
+.update-timeout-labels-container {
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+}
+
+.update-timeout-value {
+ color: #14ae5c;
+ font-weight: bold;
+}
+
+.update-timeout-slider-labels {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.9rem;
+ color: #757575;
+}
+
+.update-timeout-button-container {
+ display: flex;
+ justify-content: right;
+ margin-top: 20px;
+}
+
+.update-timeout-button {
+ width: 112px;
+ height: 36px;
+ background: #31bb6b;
+ border-radius: 6px;
+ font-family: 'Lato', sans-serif;
+ font-weight: 500;
+ font-size: 16px;
+ color: #ffffff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ box-shadow: none;
+}
+
+.update-timeout-button:hover {
+ background-color: #28a745;
+ border-color: #28a745;
+ box-shadow: none;
+}
+
+.update-timeout-button:active {
+ transform: scale(0.98);
+}
+
+.update-timeout-slider-container {
+ position: relative;
+}
diff --git a/src/components/UpdateSession/UpdateSession.test.tsx b/src/components/UpdateSession/UpdateSession.test.tsx
new file mode 100644
index 0000000000..ce0a868820
--- /dev/null
+++ b/src/components/UpdateSession/UpdateSession.test.tsx
@@ -0,0 +1,346 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/client/testing';
+import {
+ render,
+ screen,
+ act,
+ within,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import 'jest-localstorage-mock';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter } from 'react-router-dom';
+import { toast } from 'react-toastify';
+import UpdateTimeout from './UpdateSession';
+import i18n from 'utils/i18nForTest';
+import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
+import { UPDATE_SESSION_TIMEOUT } from 'GraphQl/Mutations/mutations';
+import { errorHandler } from 'utils/errorHandler';
+
+const MOCKS = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 30,
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: null,
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_SESSION_TIMEOUT,
+ variables: {
+ timeout: 30,
+ },
+ },
+ result: {
+ data: {
+ updateSessionTimeout: true,
+ },
+ },
+ },
+];
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ warn: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+jest.mock('utils/errorHandler', () => ({
+ errorHandler: jest.fn(),
+}));
+
+describe('Testing UpdateTimeout Component', () => {
+ let consoleWarnSpy: jest.SpyInstance;
+
+ beforeEach(() => {
+ consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('Should handle minimum slider value correctly', async () => {
+ const mockOnValueChange = jest.fn();
+
+ render(
+
+
+ ,
+ );
+
+ const slider = await screen.findByTestId('slider-thumb');
+
+ // Simulate dragging to minimum value
+ userEvent.click(slider, {
+ // Simulate clicking on the slider to focus
+ clientX: -999, // Adjust the clientX to simulate different slider positions
+ });
+
+ expect(mockOnValueChange).toHaveBeenCalledWith(15); // Adjust based on slider min value
+ });
+
+ test('Should handle maximum slider value correctly', async () => {
+ const mockOnValueChange = jest.fn();
+
+ render(
+
+
+ ,
+ );
+
+ const slider = await screen.findByTestId('slider-thumb');
+
+ // Simulate dragging to maximum value
+ userEvent.click(slider, {
+ // Simulate clicking on the slider to focus
+ clientX: 999, // Adjust the clientX to simulate different slider positions
+ });
+
+ expect(mockOnValueChange).toHaveBeenCalledWith(60); // Adjust based on slider max value
+ });
+
+ test('Should not update value if an invalid value is passed', async () => {
+ const mockOnValueChange = jest.fn();
+
+ render(
+
+
+ ,
+ );
+
+ const slider = await screen.findByTestId('slider-thumb');
+
+ // Simulate invalid value handling
+ userEvent.click(slider, {
+ // Simulate clicking on the slider to focus
+ clientX: 0, // Adjust the clientX to simulate different slider positions
+ });
+
+ // Ensure onValueChange is not called with invalid values
+ expect(mockOnValueChange).not.toHaveBeenCalled();
+ });
+
+ test('Should update slider value on user interaction', async () => {
+ const mockOnValueChange = jest.fn();
+
+ render(
+
+
+ ,
+ );
+
+ // Wait for the slider to be present
+ const slider = await screen.findByTestId('slider-thumb');
+
+ // Simulate slider interaction
+ userEvent.type(slider, '45'); // Simulate typing value
+
+ // Assert that the callback was called with the expected value
+ expect(mockOnValueChange).toHaveBeenCalledWith(expect.any(Number)); // Adjust as needed
+ });
+
+ test('Components should render properly', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ // Use getAllByText to get all elements with "Update Timeout" text
+ const updateTimeoutElements = screen.getAllByText(/Update Timeout/i);
+ expect(updateTimeoutElements).toHaveLength(1); // Check if there are exactly 2 elements with this text
+
+ expect(screen.getByText(/Current Timeout/i)).toBeInTheDocument();
+ expect(screen.getByText(/15 min/i)).toBeInTheDocument();
+
+ // Locate the parent element first
+ const sliderLabelsContainer = screen.getByTestId('slider-labels');
+
+ // Use within to query inside the parent element
+ const sliderLabels = within(sliderLabelsContainer);
+
+ // Check for the specific text within the parent element
+ expect(sliderLabels.getByText('30 min')).toBeInTheDocument();
+
+ expect(screen.getByText(/45 min/i)).toBeInTheDocument();
+ expect(screen.getByText(/60 min/i)).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /Update/i })).toBeInTheDocument();
+ });
+
+ test('Should update session timeout', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const submitButton = screen.getByTestId('update-button');
+ userEvent.click(submitButton);
+
+ // Wait for the toast success call
+
+ await wait();
+
+ expect(toast.success).toHaveBeenCalledWith(
+ expect.stringContaining('Successfully updated the Profile Details.'),
+ );
+ });
+
+ test('Should handle query errors', async () => {
+ const errorMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ expect(errorHandler).toHaveBeenCalled();
+ });
+
+ test('Should handle update errors', async () => {
+ const errorMocks = [
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: {
+ timeout: 30,
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
+ },
+ result: {
+ data: {
+ getCommunityData: null,
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_SESSION_TIMEOUT,
+ variables: { timeout: 30 },
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const submitButton = screen.getByTestId('update-button');
+ userEvent.click(submitButton);
+
+ await wait();
+
+ expect(errorHandler).toHaveBeenCalled();
+ });
+
+ test('Should handle null community object gracefully', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ // Assertions to verify the component handles null community object correctly
+ // Use getAllByText to get all elements with "Update Timeout" text
+ const updateTimeoutElements = screen.getAllByText(/Update Timeout/i);
+ expect(updateTimeoutElements).toHaveLength(1); // Check if there are exactly 2 elements with this text
+
+ expect(screen.getByText(/Current Timeout/i)).toBeInTheDocument();
+
+ // Locate the parent element first
+ const sliderLabelsContainer = screen.getByTestId('slider-labels');
+
+ // Use within to query inside the parent element
+ const sliderLabels = within(sliderLabelsContainer);
+
+ // Check for the specific text within the parent element
+ expect(sliderLabels.getByText('15 min')).toBeInTheDocument();
+
+ expect(screen.getByText(/30 min/i)).toBeInTheDocument();
+ expect(screen.getByText(/45 min/i)).toBeInTheDocument();
+ expect(screen.getByText(/60 min/i)).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /Update/i })).toBeInTheDocument();
+
+ // Check if the component displays a default value or handles the null state appropriately
+ expect(screen.getByText(/No timeout set/i)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/UpdateSession/UpdateSession.tsx b/src/components/UpdateSession/UpdateSession.tsx
new file mode 100644
index 0000000000..f49970ebaa
--- /dev/null
+++ b/src/components/UpdateSession/UpdateSession.tsx
@@ -0,0 +1,202 @@
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Card, Button, Form } from 'react-bootstrap';
+import Box from '@mui/material/Box';
+import Slider from '@mui/material/Slider';
+import { useMutation, useQuery } from '@apollo/client';
+import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
+import { toast } from 'react-toastify';
+import { errorHandler } from 'utils/errorHandler';
+import { UPDATE_SESSION_TIMEOUT } from 'GraphQl/Mutations/mutations';
+import './UpdateSession.css';
+import Loader from 'components/Loader/Loader';
+
+/**
+ * Component for updating the session timeout for a community.
+ *
+ * This component fetches the current session timeout value from the server
+ * and allows the user to update it using a slider.
+ *
+ * The component also handles form submission, making a mutation request to update the session timeout.
+ *
+ * @returns JSX.Element - The rendered component.
+ */
+
+interface TestInterfaceUpdateTimeoutProps {
+ onValueChange?: (value: number) => void;
+}
+
+const UpdateTimeout: React.FC = ({
+ onValueChange,
+}): JSX.Element => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'communityProfile',
+ });
+
+ const [timeout, setTimeout] = useState(30);
+ const [communityTimeout, setCommunityTimeout] = useState(
+ 30,
+ ); // Timeout from database for the community
+
+ const {
+ data,
+ loading,
+ error: queryError,
+ } = useQuery(GET_COMMUNITY_SESSION_TIMEOUT_DATA);
+ const [uploadSessionTimeout] = useMutation(UPDATE_SESSION_TIMEOUT);
+
+ type TimeoutDataType = {
+ timeout: number;
+ };
+
+ /**
+ * Effect that fetches the current session timeout from the server and sets the initial state.
+ * If there is an error in fetching the data, it is handled using the error handler.
+ */
+ React.useEffect(() => {
+ if (queryError) {
+ errorHandler(t, queryError as Error);
+ }
+
+ const SessionTimeoutData: TimeoutDataType | undefined =
+ data?.getCommunityData;
+
+ if (SessionTimeoutData && SessionTimeoutData.timeout !== null) {
+ setCommunityTimeout(SessionTimeoutData.timeout);
+ setTimeout(SessionTimeoutData.timeout);
+ } else {
+ setCommunityTimeout(undefined); // Handle null or undefined data
+ }
+ }, [data, queryError]);
+
+ /**
+ * Handles changes to the slider value and updates the timeout state.
+ *
+ * @param e - The event triggered by slider movement.
+ */
+ const handleOnChange = (
+ e: Event | React.ChangeEvent,
+ ): void => {
+ if ('target' in e && e.target) {
+ const target = e.target as HTMLInputElement;
+ // Ensure the value is a number and not NaN
+ const value = parseInt(target.value, 10);
+ if (!Number.isNaN(value)) {
+ setTimeout(value);
+ if (onValueChange) {
+ onValueChange(value);
+ }
+ } else {
+ console.warn('Invalid timeout value:', target.value);
+ }
+ }
+ };
+
+ /**
+ * Handles form submission to update the session timeout.
+ * It makes a mutation request to update the timeout value on the server.
+ * If the update is successful, a success toast is shown, and the state is updated.
+ *
+ * @param e - The event triggered by form submission.
+ */
+ const handleOnSubmit = async (
+ e: React.FormEvent,
+ ): Promise => {
+ e.preventDefault();
+ try {
+ await uploadSessionTimeout({
+ variables: {
+ timeout: timeout,
+ },
+ });
+
+ toast.success(t('profileChangedMsg'));
+ setCommunityTimeout(timeout);
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error as Error);
+ }
+ };
+
+ // Show a loader while the data is being fetched
+ if (loading) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+ Login Session Timeout
+
+
+
+
+
+ >
+ );
+};
+
+export default UpdateTimeout;
diff --git a/src/components/UserListCard/UserListCard.module.css b/src/components/UserListCard/UserListCard.module.css
new file mode 100644
index 0000000000..187757a531
--- /dev/null
+++ b/src/components/UserListCard/UserListCard.module.css
@@ -0,0 +1,74 @@
+.memberlist {
+ margin-top: -1px;
+}
+.memberimg {
+ width: 200px;
+ height: 100px;
+ border-radius: 7px;
+ margin-left: 20px;
+}
+.singledetails {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+.singledetails p {
+ margin-bottom: -5px;
+}
+.singledetails_data_left {
+ margin-top: 10px;
+ margin-left: 10px;
+ color: #707070;
+}
+.singledetails_data_right {
+ justify-content: right;
+ margin-top: 10px;
+ text-align: right;
+ color: #707070;
+}
+.membername {
+ font-size: 16px;
+ font-weight: bold;
+}
+.memberfont {
+ margin-top: 3px;
+}
+.memberfont > span {
+ width: 80%;
+}
+.memberfontcreated {
+ margin-top: 18px;
+}
+.memberfontcreatedbtn {
+ margin-top: 33px;
+ border-radius: 7px;
+ border-color: #31bb6b;
+ background-color: #31bb6b;
+ color: white;
+ padding-right: 10px;
+ padding-left: 10px;
+ justify-content: flex-end;
+ float: right;
+ text-align: right;
+ box-shadow: none;
+}
+#grid_wrapper {
+ align-items: left;
+}
+.peoplelistdiv {
+ margin-right: 50px;
+}
+@media only screen and (max-width: 600px) {
+ .singledetails {
+ margin-left: 20px;
+ }
+ .memberimg {
+ margin: auto;
+ }
+ .singledetails_data_right {
+ margin-right: -52px;
+ }
+ .singledetails_data_left {
+ margin-left: 0px;
+ }
+}
diff --git a/src/components/UserListCard/UserListCard.test.tsx b/src/components/UserListCard/UserListCard.test.tsx
new file mode 100644
index 0000000000..e2ad552507
--- /dev/null
+++ b/src/components/UserListCard/UserListCard.test.tsx
@@ -0,0 +1,81 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+
+import UserListCard from './UserListCard';
+import { ADD_ADMIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import i18nForTest from 'utils/i18nForTest';
+import { BrowserRouter } from 'react-router-dom';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const MOCKS = [
+ {
+ request: {
+ query: ADD_ADMIN_MUTATION,
+ variables: { userid: '784', orgid: '554' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: '1',
+ },
+ ],
+ },
+ },
+ },
+];
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing User List Card', () => {
+ global.alert = jest.fn();
+
+ test('Should render props and text elements test for the page component', async () => {
+ const props = {
+ id: '456',
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByText(/Add Admin/i));
+ });
+
+ test('Should render text elements when props value is not passed', async () => {
+ const props = {
+ id: '456',
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByText(/Add Admin/i));
+ });
+});
diff --git a/src/components/UserListCard/UserListCard.tsx b/src/components/UserListCard/UserListCard.tsx
new file mode 100644
index 0000000000..76ad110ea1
--- /dev/null
+++ b/src/components/UserListCard/UserListCard.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import Button from 'react-bootstrap/Button';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import { useTranslation } from 'react-i18next';
+
+import { ADD_ADMIN_MUTATION } from 'GraphQl/Mutations/mutations';
+import styles from './UserListCard.module.css';
+import { useParams } from 'react-router-dom';
+import { errorHandler } from 'utils/errorHandler';
+
+interface InterfaceUserListCardProps {
+ key: number;
+ id: string;
+}
+
+/**
+ * The UserListCard component allows for adding a user as an admin in a specific organization.
+ * It uses a button to trigger a mutation for updating the user's role.
+ *
+ * @param props - The properties for the UserListCard component.
+ * @param key - The unique key for the component (although not used here).
+ * @param id - The ID of the user to be promoted to admin.
+ *
+ * @returns The JSX element representing the user list card.
+ */
+function userListCard(props: InterfaceUserListCardProps): JSX.Element {
+ const { orgId: currentUrl } = useParams();
+ const [adda] = useMutation(ADD_ADMIN_MUTATION);
+
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'userListCard',
+ });
+
+ /**
+ * Handles adding a user as an admin.
+ * It performs a mutation and handles success or failure accordingly.
+ */
+ const addAdmin = async (): Promise => {
+ try {
+ const { data } = await adda({
+ variables: {
+ userid: props.id,
+ orgid: currentUrl,
+ },
+ });
+
+ /* istanbul ignore next */
+ if (data) {
+ toast.success(t('addedAsAdmin') as string);
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ errorHandler(t, error);
+ }
+ };
+
+ return (
+ <>
+
+ {t('addAdmin')}
+
+ >
+ );
+}
+export {};
+export default userListCard;
diff --git a/src/components/UserPasswordUpdate/UserPasswordUpdate.module.css b/src/components/UserPasswordUpdate/UserPasswordUpdate.module.css
new file mode 100644
index 0000000000..90b5c09be3
--- /dev/null
+++ b/src/components/UserPasswordUpdate/UserPasswordUpdate.module.css
@@ -0,0 +1,97 @@
+/* .userupdatediv{
+ border: 1px solid #e8e5e5;
+ box-shadow: 2px 1px #e8e5e5;
+ padding:25px 16px;
+ border-radius: 5px;
+ background:#fdfdfd;
+} */
+.settingstitle {
+ color: #707070;
+ font-size: 20px;
+ margin-bottom: 30px;
+ text-align: center;
+ margin-top: -10px;
+}
+.dispflex {
+ display: flex;
+ justify-content: flex-start;
+ margin: 0 auto;
+}
+.dispbtnflex {
+ width: 90%;
+ margin-top: 20px;
+ display: flex;
+ margin: 0 30%;
+}
+.dispflex > div {
+ width: 50%;
+ margin-right: 50px;
+}
+
+.radio_buttons > input {
+ margin-bottom: 20px;
+ border: none;
+ box-shadow: none;
+ padding: 0 0;
+ border-radius: 5px;
+ background: none;
+ width: 50%;
+}
+
+.whitebtn {
+ margin: 1rem 0 0;
+ margin-top: 10px;
+ border: 1px solid #e8e5e5;
+ box-shadow: 0 2px 2px #e8e5e5;
+ padding: 10px 20px;
+ border-radius: 5px;
+ background: none;
+ width: 20%;
+ font-size: 16px;
+ color: #31bb6b;
+ outline: none;
+ font-weight: 600;
+ cursor: pointer;
+ float: left;
+ transition:
+ transform 0.2s,
+ box-shadow 0.2s;
+}
+.greenregbtn {
+ margin: 1rem 0 0;
+ margin-top: 10px;
+ margin-right: 30px;
+ border: 1px solid #e8e5e5;
+ box-shadow: 0 2px 2px #e8e5e5;
+ padding: 10px 10px;
+ border-radius: 5px;
+ background-color: #31bb6b;
+ width: 20%;
+ font-size: 16px;
+ color: white;
+ outline: none;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ transform 0.2s,
+ box-shadow 0.2s;
+}
+.radio_buttons {
+ width: 55%;
+ margin-top: 10px;
+ display: flex;
+ color: #707070;
+ font-weight: 600;
+ font-size: 14px;
+}
+.radio_buttons > input {
+ transform: scale(1.2);
+}
+.radio_buttons > label {
+ margin-top: -4px;
+ margin-left: 0px;
+ margin-right: 7px;
+}
+.idtitle {
+ width: 88%;
+}
diff --git a/src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx b/src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx
new file mode 100644
index 0000000000..65f5e40f76
--- /dev/null
+++ b/src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx
@@ -0,0 +1,143 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import userEvent from '@testing-library/user-event';
+import { I18nextProvider } from 'react-i18next';
+import { UPDATE_USER_PASSWORD_MUTATION } from 'GraphQl/Mutations/mutations';
+import i18nForTest from 'utils/i18nForTest';
+import UserPasswordUpdate from './UserPasswordUpdate';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast as mockToast } from 'react-toastify';
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ error: jest.fn(),
+ success: jest.fn(),
+ },
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: UPDATE_USER_PASSWORD_MUTATION,
+ variables: {
+ previousPassword: 'anshgoyal',
+ newPassword: 'anshgoyalansh',
+ confirmNewPassword: 'anshgoyalansh',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ _id: '1',
+ },
+ ],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(ms = 5): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing User Password Update', () => {
+ const formData = {
+ previousPassword: 'Palisadoes',
+ newPassword: 'ThePalisadoesFoundation',
+ wrongPassword: 'This is wrong password',
+ confirmNewPassword: 'ThePalisadoesFoundation',
+ };
+
+ global.alert = jest.fn();
+
+ test('should render props and text elements test for the page component', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Previous Password/i),
+ formData.previousPassword,
+ );
+ userEvent.type(
+ screen.getAllByPlaceholderText(/New Password/i)[0],
+ formData.newPassword,
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(/Confirm New Password/i),
+ formData.confirmNewPassword,
+ );
+
+ userEvent.click(screen.getByText(/Save Changes/i));
+
+ expect(screen.getByText(/Cancel/i)).toBeTruthy();
+ expect(
+ screen.getByPlaceholderText(/Previous Password/i),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByPlaceholderText(/Confirm New Password/i),
+ ).toBeInTheDocument();
+ });
+
+ test('displays an error when the password field is empty', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ userEvent.click(screen.getByText(/Save Changes/i));
+
+ await wait();
+ expect(mockToast.error).toHaveBeenCalledWith(`Password can't be empty`);
+ });
+
+ test('displays an error when new and confirm password field does not match', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Previous Password/i),
+ formData.previousPassword,
+ );
+ userEvent.type(
+ screen.getAllByPlaceholderText(/New Password/i)[0],
+ formData.wrongPassword,
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(/Confirm New Password/i),
+ formData.confirmNewPassword,
+ );
+
+ userEvent.click(screen.getByText(/Save Changes/i));
+
+ expect(screen.getByText(/Cancel/i)).toBeTruthy();
+ await wait();
+ expect(mockToast.error).toHaveBeenCalledWith(
+ 'New and Confirm password do not match.',
+ );
+ });
+});
diff --git a/src/components/UserPasswordUpdate/UserPasswordUpdate.tsx b/src/components/UserPasswordUpdate/UserPasswordUpdate.tsx
new file mode 100644
index 0000000000..1ea75811c1
--- /dev/null
+++ b/src/components/UserPasswordUpdate/UserPasswordUpdate.tsx
@@ -0,0 +1,176 @@
+import React from 'react';
+import { useMutation } from '@apollo/client';
+import { UPDATE_USER_PASSWORD_MUTATION } from 'GraphQl/Mutations/mutations';
+import { useTranslation } from 'react-i18next';
+import Button from 'react-bootstrap/Button';
+import styles from './UserPasswordUpdate.module.css';
+import { toast } from 'react-toastify';
+import { Form } from 'react-bootstrap';
+
+interface InterfaceUserPasswordUpdateProps {
+ id: string;
+}
+
+/**
+ * UserUpdate component allows users to update their passwords.
+ * It handles form submission and communicates with the backend to update the user's password.
+ *
+ * @param props - The properties for the UserUpdate component.
+ * @param id - The ID of the user whose password is being updated.
+ *
+ * @returns The JSX element for updating user password.
+ */
+const UserUpdate: React.FC<
+ InterfaceUserPasswordUpdateProps
+> = (): JSX.Element => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'userPasswordUpdate',
+ });
+ const { t: tCommon } = useTranslation('common');
+ const [formState, setFormState] = React.useState({
+ previousPassword: '',
+ newPassword: '',
+ confirmNewPassword: '',
+ });
+
+ const [login] = useMutation(UPDATE_USER_PASSWORD_MUTATION);
+
+ /**
+ * Handles the password update process.
+ * It validates the form inputs and performs the mutation to update the password.
+ */
+ const loginLink = async (): Promise => {
+ if (
+ !formState.previousPassword ||
+ !formState.newPassword ||
+ !formState.confirmNewPassword
+ ) {
+ toast.error(t('passCantBeEmpty') as string);
+ return;
+ }
+
+ if (formState.newPassword !== formState.confirmNewPassword) {
+ toast.error(t('passNoMatch') as string);
+ return;
+ }
+
+ try {
+ const { data } = await login({
+ variables: {
+ previousPassword: formState.previousPassword,
+ newPassword: formState.newPassword,
+ confirmNewPassword: formState.confirmNewPassword,
+ },
+ });
+ /* istanbul ignore next */
+ if (data) {
+ toast.success(
+ tCommon('updatedSuccessfully', { item: 'Password' }) as string,
+ );
+ setTimeout(() => {
+ window.location.reload();
+ }, 2000);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ if (error instanceof Error) {
+ toast.error(error.toString());
+ }
+ }
+ };
+
+ /**
+ * Handles canceling the update process.
+ * It reloads the page to reset any changes.
+ */
+ /* istanbul ignore next */
+ const cancelUpdate = (): void => {
+ window.location.reload();
+ };
+
+ return (
+ <>
+
+ >
+ );
+};
+export default UserUpdate;
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.module.css b/src/components/UserPortal/ChatRoom/ChatRoom.module.css
new file mode 100644
index 0000000000..5fc98351cd
--- /dev/null
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.module.css
@@ -0,0 +1,250 @@
+.chatAreaContainer {
+ padding: 10px;
+ flex-grow: 1;
+ /* background-color: rgba(196, 255, 211, 0.3); */
+}
+
+.backgroundWhite {
+ background-color: white;
+}
+
+.grey {
+ color: grey;
+}
+
+.header {
+ position: sticky;
+ top: 0px;
+ background: white;
+}
+
+.userInfo {
+ display: flex;
+ border-bottom: 1px solid black;
+ padding-bottom: 5px;
+ align-items: center;
+ margin-top: 5px;
+ gap: 10px;
+}
+
+.contactImage {
+ width: 45px !important;
+ height: auto !important;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.messageSentContainer {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.messageReceivedContainer {
+ display: flex;
+ align-items: flex-end;
+}
+
+.messageReceived {
+ border: 1px solid #c2c2c2;
+ border-radius: 8px 8px 8px 0px;
+ padding: 4px 6px;
+ margin: 4px 6px;
+ width: fit-content;
+ max-width: 75%;
+ min-width: 80px;
+ padding-bottom: 0;
+ display: flex;
+ justify-content: space-between;
+}
+
+.messageSent {
+ border: 1px solid #c2c2c2;
+ border-radius: 8px 8px 0px 8px;
+ padding: 4px 6px;
+ margin: 4px 6px;
+ width: fit-content;
+ max-width: 75%;
+ background-color: rgba(196, 255, 211, 0.3);
+ min-width: 80px;
+ padding-bottom: 0;
+ display: flex;
+ justify-content: space-between;
+}
+
+.userDetails {
+ display: flex;
+ align-items: center;
+ font-size: 14px;
+ gap: 6px;
+}
+
+.userDetails .userImage {
+ height: 20px;
+ width: 20px;
+}
+
+.replyTo {
+ border-left: 4px solid pink;
+ display: flex;
+ justify-content: space-between;
+ background-color: rgb(249, 249, 250);
+ padding: 6px 0px 4px 4px;
+ border-radius: 6px 6px 6px 6px;
+}
+
+.replyToMessageContainer {
+ padding-left: 4px;
+}
+
+.replyToMessageContainer p {
+ margin: 4px 0px 0px;
+}
+
+.replyToMessage {
+ border-left: 4px solid pink;
+ border-radius: 6px;
+ margin: 6px 0px;
+ padding: 6px;
+ background-color: #dbf6db;
+}
+
+.messageReceived .replyToMessage {
+ background-color: #f2f2f2;
+}
+
+.messageTime {
+ font-size: 10px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ margin-bottom: 0px;
+ margin-left: 6px;
+}
+
+.messageContent {
+ margin-bottom: 0.5px;
+ display: flex;
+ align-items: flex-start;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.createChat {
+ border: none;
+ background-color: white;
+}
+
+.chatMessages {
+ margin: 10px 0;
+}
+
+.userDetails .subtitle {
+ font-size: 12px;
+ color: #959595;
+ margin: 0;
+}
+
+.userDetails .title {
+ font-size: 18px;
+ margin: 0;
+}
+
+.contactImage {
+ height: fit-content;
+}
+
+.senderInfo {
+ margin: 2px 2px 0px 2px;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.messageAttributes {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: flex-end;
+}
+
+.customToggle {
+ display: none;
+}
+
+.customToggle,
+.closeBtn {
+ padding: 0;
+ background: none;
+ border: none;
+ --bs-btn-active-bg: none;
+}
+.customToggle svg {
+ color: black;
+ font-size: 12px;
+}
+
+.customToggle::after,
+.closeBtn::after {
+ content: none;
+}
+
+.closeBtn svg {
+ color: black;
+ font-size: 18px;
+}
+
+.closeBtn {
+ padding: 2px 10px;
+}
+
+.closeBtn:hover {
+ background-color: transparent;
+ border-color: transparent;
+}
+
+.customToggle:hover,
+.customToggle:focus,
+.customToggle:active {
+ background: none;
+ border: none;
+}
+
+.messageReceived:hover .customToggle {
+ display: block;
+}
+
+.messageSent:hover .customToggle {
+ display: block;
+}
+
+.messageSent:hover,
+.messageReceived:hover {
+ padding: 0px 6px;
+}
+
+.messageSent:target {
+ scroll-margin-top: 100px;
+ animation-name: test;
+ animation-duration: 1s;
+}
+
+.messageReceived:target {
+ scroll-margin-top: 100px;
+ animation-name: test;
+ animation-duration: 1s;
+}
+
+@keyframes test {
+ from {
+ background-color: white;
+ }
+ to {
+ background-color: rgb(82, 83, 81);
+ }
+}
+
+a {
+ color: currentColor;
+ width: 100%;
+}
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
new file mode 100644
index 0000000000..c808485132
--- /dev/null
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
@@ -0,0 +1,1586 @@
+import React from 'react';
+
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import { MockSubscriptionLink, MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
+import {
+ MESSAGE_SENT_TO_CHAT,
+ SEND_MESSAGE_TO_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import ChatRoom from './ChatRoom';
+import { useLocalStorage } from 'utils/useLocalstorage';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const MESSAGE_SENT_TO_CHAT_MOCK = [
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: null,
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '2',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1df364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '1',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f13603ac4697a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+];
+
+const CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const CHATS_LIST_MOCK = [
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844ghjefc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: 'ujhgtrdtyuiop',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dhjmkdftyd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: '65844ewsedrffc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const GROUP_CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '2',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '1',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '1',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const SEND_MESSAGE_TO_CHAT_MOCK = [
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '4',
+ messageContent: 'Test reply message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '4',
+ messageContent: 'Test reply message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '1',
+ messageContent: 'Test reply message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: undefined,
+ messageContent: 'Hello',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '345678',
+ messageContent: 'Test reply message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: undefined,
+ messageContent: 'Test message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+];
+
+describe('Testing Chatroom Component [User Portal]', () => {
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
+
+ test('Chat room should display fallback content if no chat is active', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(await screen.findByTestId('noChatSelected')).toBeInTheDocument();
+ });
+
+ test('Selected contact is direct chat', async () => {
+ const link = new MockSubscriptionLink();
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ });
+
+ test('send message direct chat', async () => {
+ setItem('userId', '2');
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ const link2 = new StaticMockLink(mocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Hello' } });
+ });
+ expect(input.value).toBe('Hello');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+ await waitFor(() => {
+ expect(input.value).toBeFalsy();
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ console.log('MESSAGES', messages);
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+ });
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(400);
+ });
+
+ test('send message direct chat when userId is different', async () => {
+ setItem('userId', '8');
+ const mocks = [
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ const link2 = new StaticMockLink(mocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Hello' } });
+ });
+ expect(input.value).toBe('Hello');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+ await waitFor(() => {
+ expect(input.value).toBeFalsy();
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ console.log('MESSAGES', messages);
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+ });
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(400);
+ });
+
+ test('Selected contact is group chat', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ });
+
+ test('send message group chat', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test message' } });
+ });
+ expect(input.value).toBe('Test message');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+ await waitFor(() => {
+ expect(input.value).toBeFalsy();
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ const closeReplyBtn = await screen.findByTestId('closeReply');
+
+ expect(closeReplyBtn).toBeInTheDocument();
+
+ fireEvent.click(closeReplyBtn);
+
+ await wait(500);
+ });
+
+ test('reply to message', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ];
+ const link2 = new StaticMockLink(mocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test message' } });
+ });
+ expect(input.value).toBe('Test message');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+ await waitFor(() => {
+ expect(input.value).toBeFalsy();
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(400);
+ });
+});
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
new file mode 100644
index 0000000000..c23244a314
--- /dev/null
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
@@ -0,0 +1,378 @@
+import React, { useEffect, useRef, useState } from 'react';
+import type { ChangeEvent } from 'react';
+import SendIcon from '@mui/icons-material/Send';
+import { Button, Dropdown, Form, InputGroup } from 'react-bootstrap';
+import styles from './ChatRoom.module.css';
+import PermContactCalendarIcon from '@mui/icons-material/PermContactCalendar';
+import { useTranslation } from 'react-i18next';
+import { CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
+import { useMutation, useQuery, useSubscription } from '@apollo/client';
+import {
+ MESSAGE_SENT_TO_CHAT,
+ SEND_MESSAGE_TO_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import useLocalStorage from 'utils/useLocalstorage';
+import Avatar from 'components/Avatar/Avatar';
+import { MoreVert, Close } from '@mui/icons-material';
+
+interface InterfaceChatRoomProps {
+ selectedContact: string;
+}
+
+/**
+ * A chat room component that displays messages and a message input field.
+ *
+ * This component shows a list of messages between the user and a selected contact.
+ * If no contact is selected, it displays a placeholder with an icon and a message asking the user to select a contact.
+ *
+ * @param props - The properties passed to the component.
+ * @param selectedContact - The ID or name of the currently selected contact. If empty, a placeholder is shown.
+ *
+ * @returns The rendered chat room component.
+ */
+
+type DirectMessage = {
+ _id: string;
+ createdAt: Date;
+ sender: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ image: string;
+ };
+ replyTo:
+ | {
+ _id: string;
+ createdAt: Date;
+ sender: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ image: string;
+ };
+ messageContent: string;
+ receiver: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ };
+ }
+ | undefined;
+ messageContent: string;
+ type: string;
+};
+
+type Chat = {
+ _id: string;
+ isGroup: boolean;
+ name?: string;
+ image?: string;
+ messages: DirectMessage[];
+ users: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ }[];
+};
+
+export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
+ // Translation hook for text in different languages
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'userChatRoom',
+ });
+ const isMountedRef = useRef(true);
+
+ useEffect(() => {
+ return () => {
+ isMountedRef.current = false;
+ };
+ }, []);
+
+ const { getItem } = useLocalStorage();
+ const userId = getItem('userId');
+ const [chatTitle, setChatTitle] = useState('');
+ const [chatSubtitle, setChatSubtitle] = useState('');
+ const [newMessage, setNewMessage] = useState('');
+ const [chat, setChat] = useState();
+ const [replyToDirectMessage, setReplyToDirectMessage] =
+ useState(null);
+
+ /**
+ * Handles changes to the new message input field.
+ *
+ * Updates the state with the current value of the input field whenever it changes.
+ *
+ * @param e - The event triggered by the input field change.
+ */
+ const handleNewMessageChange = (e: ChangeEvent): void => {
+ const newMessageValue = e.target.value;
+ setNewMessage(newMessageValue);
+ };
+
+ const [sendMessageToChat] = useMutation(SEND_MESSAGE_TO_CHAT, {
+ variables: {
+ chatId: props.selectedContact,
+ replyTo: replyToDirectMessage?._id,
+ messageContent: newMessage,
+ },
+ });
+
+ const { data: chatData, refetch: chatRefetch } = useQuery(CHAT_BY_ID, {
+ variables: {
+ id: props.selectedContact,
+ },
+ });
+
+ useEffect(() => {
+ chatRefetch();
+ }, [props.selectedContact]);
+
+ useEffect(() => {
+ if (chatData) {
+ const chat = chatData.chatById;
+ setChat(chat);
+ if (chat.isGroup) {
+ setChatTitle(chat.name);
+ setChatSubtitle(`${chat.users.length} members`);
+ } else {
+ const otherUser = chat.users.find(
+ (user: { _id: string }) => user._id !== userId,
+ );
+ if (otherUser) {
+ setChatTitle(`${otherUser.firstName} ${otherUser.lastName}`);
+ setChatSubtitle(otherUser.email);
+ }
+ }
+ }
+ }, [chatData]);
+
+ const sendMessage = async (): Promise => {
+ await sendMessageToChat();
+ await chatRefetch();
+ setReplyToDirectMessage(null);
+ setNewMessage('');
+ };
+
+ useSubscription(MESSAGE_SENT_TO_CHAT, {
+ variables: {
+ userId: userId,
+ },
+ onData: (messageSubscriptionData) => {
+ if (
+ messageSubscriptionData?.data.data.messageSentToChat &&
+ messageSubscriptionData?.data.data.messageSentToChat
+ .chatMessageBelongsTo['_id'] == props.selectedContact
+ ) {
+ chatRefetch();
+ } else {
+ chatRefetch({
+ id: messageSubscriptionData?.data.data.messageSentToChat
+ .chatMessageBelongsTo['_id'],
+ });
+ }
+ },
+ });
+
+ useEffect(() => {
+ document
+ .getElementById('chat-area')
+ ?.lastElementChild?.scrollIntoView({ block: 'end' });
+ });
+
+ return (
+
+ {!props.selectedContact ? (
+
+
+
{t('selectContact')}
+
+ ) : (
+ <>
+
+
+
+
+
{chatTitle}
+
{chatSubtitle}
+
+
+
+
+
+ {!!chat?.messages.length && (
+
+ {chat?.messages.map((message: DirectMessage) => {
+ return (
+
+ {chat.isGroup &&
+ message.sender._id !== userId &&
+ (message.sender?.image ? (
+
+ ) : (
+
+ ))}
+
+
+ );
+ })}
+
+ )}
+
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/components/UserPortal/CommentCard/CommentCard.module.css b/src/components/UserPortal/CommentCard/CommentCard.module.css
new file mode 100644
index 0000000000..b765d60bbd
--- /dev/null
+++ b/src/components/UserPortal/CommentCard/CommentCard.module.css
@@ -0,0 +1,44 @@
+.mainContainer {
+ width: auto;
+ overflow: hidden;
+ background-color: white;
+ margin-top: 1rem;
+ padding: 0.5rem;
+ border: 1px solid #dddddd;
+ border-radius: 10px;
+}
+
+.personDetails {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.personImage {
+ border-radius: 50%;
+ margin-right: 20px;
+}
+
+.cardActions {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.cardActionBtn {
+ background-color: rgba(0, 0, 0, 0);
+ border: none;
+ color: black;
+}
+
+.cardActionBtn:hover {
+ background-color: ghostwhite;
+ border: none;
+ color: green !important;
+}
+
+.likeIcon {
+ width: 20px;
+}
diff --git a/src/components/UserPortal/CommentCard/CommentCard.test.tsx b/src/components/UserPortal/CommentCard/CommentCard.test.tsx
new file mode 100644
index 0000000000..f02e5a606a
--- /dev/null
+++ b/src/components/UserPortal/CommentCard/CommentCard.test.tsx
@@ -0,0 +1,241 @@
+import React, { act } from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { render, screen } from '@testing-library/react';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+
+import CommentCard from './CommentCard';
+import userEvent from '@testing-library/user-event';
+import { LIKE_COMMENT, UNLIKE_COMMENT } from 'GraphQl/Mutations/mutations';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { getItem, setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const MOCKS = [
+ {
+ request: {
+ query: LIKE_COMMENT,
+ variables: {
+ commentId: '1',
+ },
+ result: {
+ data: {
+ likeComment: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNLIKE_COMMENT,
+ variables: {
+ commentId: '1',
+ },
+ result: {
+ data: {
+ unlikeComment: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ },
+];
+
+const handleLikeComment = jest.fn();
+const handleDislikeComment = jest.fn();
+const link = new StaticMockLink(MOCKS, true);
+
+describe('Testing CommentCard Component [User Portal]', () => {
+ afterEach(async () => {
+ await act(async () => {
+ await i18nForTest.changeLanguage('en');
+ });
+ });
+
+ test('Component should be rendered properly if comment is already liked by the user.', async () => {
+ const cardProps = {
+ id: '1',
+ creator: {
+ id: '1',
+ firstName: 'test',
+ lastName: 'user',
+ email: 'test@user.com',
+ },
+ likeCount: 1,
+ likedBy: [
+ {
+ id: '1',
+ },
+ ],
+ text: 'testComment',
+ handleLikeComment: handleLikeComment,
+ handleDislikeComment: handleDislikeComment,
+ };
+
+ const beforeUserId = getItem('userId');
+ setItem('userId', '2');
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ if (beforeUserId) {
+ setItem('userId', beforeUserId);
+ }
+ });
+
+ test('Component should be rendered properly if comment is not already liked by the user.', async () => {
+ const cardProps = {
+ id: '1',
+ creator: {
+ id: '1',
+ firstName: 'test',
+ lastName: 'user',
+ email: 'test@user.com',
+ },
+ likeCount: 1,
+ likedBy: [
+ {
+ id: '1',
+ },
+ ],
+ text: 'testComment',
+ handleLikeComment: handleLikeComment,
+ handleDislikeComment: handleDislikeComment,
+ };
+
+ const beforeUserId = getItem('userId');
+ setItem('userId', '1');
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ if (beforeUserId) {
+ setItem('userId', beforeUserId);
+ }
+ });
+
+ test('Component renders as expected if user likes the comment.', async () => {
+ const cardProps = {
+ id: '1',
+ creator: {
+ id: '1',
+ firstName: 'test',
+ lastName: 'user',
+ email: 'test@user.com',
+ },
+ likeCount: 1,
+ likedBy: [
+ {
+ id: '1',
+ },
+ ],
+ text: 'testComment',
+ handleLikeComment: handleLikeComment,
+ handleDislikeComment: handleDislikeComment,
+ };
+
+ const beforeUserId = getItem('userId');
+ setItem('userId', '2');
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('likeCommentBtn'));
+
+ await wait();
+
+ if (beforeUserId) {
+ setItem('userId', beforeUserId);
+ }
+ });
+
+ test('Component renders as expected if user unlikes the comment.', async () => {
+ const cardProps = {
+ id: '1',
+ creator: {
+ id: '1',
+ firstName: 'test',
+ lastName: 'user',
+ email: 'test@user.com',
+ },
+ likeCount: 1,
+ likedBy: [
+ {
+ id: '1',
+ },
+ ],
+ text: 'testComment',
+ handleLikeComment: handleLikeComment,
+ handleDislikeComment: handleDislikeComment,
+ };
+
+ const beforeUserId = getItem('userId');
+ setItem('userId', '1');
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('likeCommentBtn'));
+
+ if (beforeUserId) {
+ setItem('userId', beforeUserId);
+ }
+ });
+});
diff --git a/src/components/UserPortal/CommentCard/CommentCard.tsx b/src/components/UserPortal/CommentCard/CommentCard.tsx
new file mode 100644
index 0000000000..9e8c46d241
--- /dev/null
+++ b/src/components/UserPortal/CommentCard/CommentCard.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { Button } from 'react-bootstrap';
+import styles from './CommentCard.module.css';
+import ThumbUpIcon from '@mui/icons-material/ThumbUp';
+import { useMutation } from '@apollo/client';
+import { LIKE_COMMENT, UNLIKE_COMMENT } from 'GraphQl/Mutations/mutations';
+import { toast } from 'react-toastify';
+import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
+import ThumbUpOffAltIcon from '@mui/icons-material/ThumbUpOffAlt';
+import useLocalStorage from 'utils/useLocalstorage';
+import AccountCircleIcon from '@mui/icons-material/AccountCircle';
+
+interface InterfaceCommentCardProps {
+ id: string;
+ creator: {
+ id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ };
+ likeCount: number;
+ likedBy: {
+ id: string;
+ }[];
+ text: string;
+ handleLikeComment: (commentId: string) => void;
+ handleDislikeComment: (commentId: string) => void;
+}
+
+/**
+ * Displays a card for a single comment with options to like or dislike the comment.
+ *
+ * Shows the commenter's name, the comment text, and the number of likes.
+ * Allows the user to like or dislike the comment. The button icon changes based on whether the comment is liked by the user.
+ *
+ * @param props - The properties passed to the component.
+ * @param id - The unique identifier of the comment.
+ * @param creator - Information about the creator of the comment.
+ * @param id - The unique identifier of the creator.
+ * @param firstName - The first name of the creator.
+ * @param lastName - The last name of the creator.
+ * @param email - The email address of the creator.
+ * @param likeCount - The current number of likes for the comment.
+ * @param likedBy - An array of users who have liked the comment.
+ * @param text - The text content of the comment.
+ * @param handleLikeComment - Function to call when the user likes the comment.
+ * @param handleDislikeComment - Function to call when the user dislikes the comment.
+ *
+ * @returns The rendered comment card component.
+ */
+function commentCard(props: InterfaceCommentCardProps): JSX.Element {
+ // Full name of the comment creator
+ const creatorName = `${props.creator.firstName} ${props.creator.lastName}`;
+
+ // Hook to get user ID from local storage
+ const { getItem } = useLocalStorage();
+ const userId = getItem('userId');
+
+ // Check if the current user has liked the comment
+ const likedByUser = props.likedBy.some((likedBy) => likedBy.id === userId);
+
+ // State to track the number of likes and if the comment is liked by the user
+ const [likes, setLikes] = React.useState(props.likeCount);
+ const [isLikedByUser, setIsLikedByUser] = React.useState(likedByUser);
+
+ // Mutation hooks for liking and unliking comments
+ const [likeComment, { loading: likeLoading }] = useMutation(LIKE_COMMENT);
+ const [unlikeComment, { loading: unlikeLoading }] =
+ useMutation(UNLIKE_COMMENT);
+
+ /**
+ * Toggles the like status of the comment.
+ *
+ * If the comment is already liked by the user, it will be unliked. Otherwise, it will be liked.
+ * Updates the number of likes and the like status accordingly.
+ *
+ * @returns A promise that resolves when the like/unlike operation is complete.
+ */
+ const handleToggleLike = async (): Promise => {
+ if (isLikedByUser) {
+ try {
+ const { data } = await unlikeComment({
+ variables: {
+ commentId: props.id,
+ },
+ });
+ /* istanbul ignore next */
+ if (data) {
+ setLikes((likes) => likes - 1);
+ setIsLikedByUser(false);
+ props.handleDislikeComment(props.id);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ toast.error(error as string);
+ }
+ } else {
+ try {
+ const { data } = await likeComment({
+ variables: {
+ commentId: props.id,
+ },
+ });
+ /* istanbul ignore next */
+ if (data) {
+ setLikes((likes) => likes + 1);
+ setIsLikedByUser(true);
+ props.handleLikeComment(props.id);
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ toast.error(error as string);
+ }
+ }
+ };
+
+ return (
+
+
+
+
{props.text}
+
+
+ {likeLoading || unlikeLoading ? (
+
+ ) : isLikedByUser ? (
+
+ ) : (
+
+ )}
+
+ {`${likes} Likes`}
+
+
+
+ );
+}
+
+export default commentCard;
diff --git a/src/components/UserPortal/ContactCard/ContactCard.module.css b/src/components/UserPortal/ContactCard/ContactCard.module.css
new file mode 100644
index 0000000000..19d66596c9
--- /dev/null
+++ b/src/components/UserPortal/ContactCard/ContactCard.module.css
@@ -0,0 +1,37 @@
+.contact {
+ display: flex;
+ flex-direction: row;
+ padding: 5px 10px;
+ cursor: pointer;
+ border-radius: 10px;
+ /* margin-bottom: 10px; */
+ /* border: 2px solid #f5f5f5; */
+}
+
+.contactImage {
+ width: 45px !important;
+ height: auto !important;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.contactNameContainer {
+ display: flex;
+ flex-direction: column;
+ padding: 0px 10px;
+ justify-content: center;
+}
+
+.grey {
+ color: grey;
+}
+
+.bgGreen {
+ background-color: rgba(196, 255, 211, 0.3);
+}
+
+.bgWhite {
+ background-color: white;
+}
diff --git a/src/components/UserPortal/ContactCard/ContactCard.test.tsx b/src/components/UserPortal/ContactCard/ContactCard.test.tsx
new file mode 100644
index 0000000000..05a6a0a530
--- /dev/null
+++ b/src/components/UserPortal/ContactCard/ContactCard.test.tsx
@@ -0,0 +1,119 @@
+import React, { act } from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import ContactCard from './ContactCard';
+import userEvent from '@testing-library/user-event';
+
+const link = new StaticMockLink([], true);
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+let props = {
+ id: '1',
+ title: 'Disha Talreja',
+ subtitle: 'disha@example.com',
+ email: 'noble@mittal.com',
+ isGroup: false,
+ image: '',
+ selectedContact: '',
+ type: '',
+ setSelectedContact: jest.fn(),
+ setSelectedChatType: jest.fn(),
+};
+
+describe('Testing ContactCard Component [User Portal]', () => {
+ test('Component should be rendered properly if person image is undefined', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ });
+
+ test('Component should be rendered properly if person image is not undefined', async () => {
+ props = {
+ ...props,
+ image: 'personImage',
+ };
+
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ });
+
+ test('Contact gets selectected when component is clicked', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('contactContainer'));
+
+ await wait();
+ });
+
+ test('Component is rendered with background color grey if the contact is selected', async () => {
+ props = {
+ ...props,
+ selectedContact: '1',
+ isGroup: true,
+ };
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.click(screen.getByTestId('contactContainer'));
+
+ await wait();
+ });
+});
diff --git a/src/components/UserPortal/ContactCard/ContactCard.tsx b/src/components/UserPortal/ContactCard/ContactCard.tsx
new file mode 100644
index 0000000000..ef6bf2c9d5
--- /dev/null
+++ b/src/components/UserPortal/ContactCard/ContactCard.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import styles from './ContactCard.module.css';
+import Avatar from 'components/Avatar/Avatar';
+
+interface InterfaceContactCardProps {
+ id: string;
+ title: string;
+ image: string;
+ selectedContact: string;
+ setSelectedContact: React.Dispatch>;
+ isGroup: boolean;
+}
+
+/**
+ * Displays a card for a contact in a contact list.
+ *
+ * Shows the contact's name, email, and an image or avatar.
+ * The card changes background color based on whether it is selected.
+ * Clicking on the card sets it as the selected contact and updates the contact name.
+ *
+ * @param props - The properties passed to the component.
+ * @param id - The unique identifier of the contact.
+ * @param firstName - The first name of the contact.
+ * @param lastName - The last name of the contact.
+ * @param email - The email address of the contact.
+ * @param image - The URL of the contact's image.
+ * @param selectedContact - The ID of the currently selected contact.
+ * @param setSelectedContact - Function to set the ID of the selected contact.
+ * @param setSelectedContactName - Function to set the name of the selected contact.
+ *
+ * @returns The rendered contact card component.
+ */
+function contactCard(props: InterfaceContactCardProps): JSX.Element {
+ const handleSelectedContactChange = (): void => {
+ props.setSelectedContact(props.id);
+ };
+ const [isSelected, setIsSelected] = React.useState(
+ props.selectedContact === props.id,
+ );
+
+ // Update selection state when the selected contact changes
+ React.useEffect(() => {
+ setIsSelected(props.selectedContact === props.id);
+ }, [props.selectedContact]);
+
+ return (
+ <>
+
+ {props.image ? (
+
+ ) : (
+
+ )}
+
+ {props.title}
+
+
+ >
+ );
+}
+
+export default contactCard;
diff --git a/src/components/UserPortal/CreateDirectChat/CreateDirectChat.module.css b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.module.css
new file mode 100644
index 0000000000..3795e402fa
--- /dev/null
+++ b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.module.css
@@ -0,0 +1,9 @@
+.userData {
+ height: 400px;
+ overflow-y: scroll;
+ overflow-x: hidden !important;
+}
+
+.modalContent {
+ width: 530px;
+}
diff --git a/src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx
new file mode 100644
index 0000000000..d8b3466207
--- /dev/null
+++ b/src/components/UserPortal/CreateDirectChat/CreateDirectChat.test.tsx
@@ -0,0 +1,1496 @@
+import React from 'react';
+import {
+ act,
+ fireEvent,
+ render,
+ screen,
+ waitFor,
+} from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { USERS_CONNECTION_LIST } from 'GraphQl/Queries/Queries';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import Chat from '../../../screens/UserPortal/Chat/Chat';
+import {
+ CREATE_CHAT,
+ MESSAGE_SENT_TO_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
+import useLocalStorage from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+const UserConnectionListMock = [
+ {
+ request: {
+ query: USERS_CONNECTION_LIST,
+ variables: {
+ firstName_contains: '',
+ lastName_contains: '',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ user: {
+ firstName: 'Deanne',
+ lastName: 'Marks',
+ image: null,
+ _id: '6589389d2caa9d8d69087487',
+ email: 'testuser8@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ organizationsBlockedBy: [],
+ joinedOrganizations: [
+ {
+ _id: '6537904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Queens',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Coffee Street',
+ line2: 'Apartment 501',
+ postalCode: '11427',
+ sortingCode: 'ABC-133',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6637904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Staten Island',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 church Street',
+ line2: 'Apartment 499',
+ postalCode: '10301',
+ sortingCode: 'ABC-122',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6737904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Brooklyn',
+ countryCode: 'US',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Main Street',
+ line2: 'Apt 456',
+ postalCode: '10004',
+ sortingCode: 'ABC-789',
+ state: 'NY',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6437904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Bronx',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '10451',
+ sortingCode: 'ABC-123',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ ],
+ __typename: 'User',
+ },
+ appUserProfile: {
+ _id: '64378abd85308f171cf2993d',
+ adminFor: [],
+ isSuperAdmin: false,
+ createdOrganizations: [],
+ createdEvents: [],
+ eventAdmin: [],
+ __typename: 'AppUserProfile',
+ },
+ __typename: 'UserData',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USERS_CONNECTION_LIST,
+ variables: {
+ firstName_contains: 'Disha',
+ lastName_contains: '',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ user: {
+ firstName: 'Deanne',
+ lastName: 'Marks',
+ image: null,
+ _id: '6589389d2caa9d8d69087487',
+ email: 'testuser8@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ organizationsBlockedBy: [],
+ joinedOrganizations: [
+ {
+ _id: '6537904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Queens',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Coffee Street',
+ line2: 'Apartment 501',
+ postalCode: '11427',
+ sortingCode: 'ABC-133',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6637904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Staten Island',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 church Street',
+ line2: 'Apartment 499',
+ postalCode: '10301',
+ sortingCode: 'ABC-122',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6737904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Brooklyn',
+ countryCode: 'US',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Main Street',
+ line2: 'Apt 456',
+ postalCode: '10004',
+ sortingCode: 'ABC-789',
+ state: 'NY',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6437904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Bronx',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '10451',
+ sortingCode: 'ABC-123',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ ],
+ __typename: 'User',
+ },
+ appUserProfile: {
+ _id: '64378abd85308f171cf2993d',
+ adminFor: [],
+ isSuperAdmin: false,
+ createdOrganizations: [],
+ createdEvents: [],
+ eventAdmin: [],
+ __typename: 'AppUserProfile',
+ },
+ __typename: 'UserData',
+ },
+ ],
+ },
+ },
+ },
+];
+
+const MESSAGE_SENT_TO_CHAT_MOCK = [
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: null,
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ type: 'STRING',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '2',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1df364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '1',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f13603ac4697a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+];
+
+const CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const CHATS_LIST_MOCK = [
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844ghjefc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: 'ujhgtrdtyuiop',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dhjmkdftyd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: '65844ewsedrffc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dhjmkdftyd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ {
+ _id: '65844ewsedrffc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const GROUP_CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const CREATE_CHAT_MUTATION_MOCK = [
+ {
+ request: {
+ query: CREATE_CHAT,
+ variables: {
+ organizationId: undefined,
+ userIds: ['1', '6589389d2caa9d8d69087487'],
+ isGroup: false,
+ },
+ },
+ result: {
+ data: {
+ createChat: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+describe('Testing Create Direct Chat Modal [User Portal]', () => {
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
+
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // Deprecated
+ removeListener: jest.fn(), // Deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+ });
+
+ test('Open and close create new direct chat modal', async () => {
+ const mock = [
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...UserConnectionListMock,
+ ...CHATS_LIST_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CREATE_CHAT_MUTATION_MOCK,
+ ];
+ render(
+
+
+
+