diff --git a/.github/workflows/README.md b/.github/workflows/README.md deleted file mode 100644 index abdfbabb..00000000 --- a/.github/workflows/README.md +++ /dev/null @@ -1 +0,0 @@ -GitHub actions and workflows live here... \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 00000000..49d71ddb --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,33 @@ +name: SonarCloud Code Quality Analysis + +on: + push: + branches: + - dev + - stage + - main + pull_request: + branches: + - dev + - stage + - main + +jobs: + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Needed to authenticate with SonarCloud + with: + + args: + + -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }} + -Dsonar.organization=${{ secrets.SONAR_ORGANIZATION }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8dd4607a..265817a8 100644 --- a/.gitignore +++ b/.gitignore @@ -395,4 +395,22 @@ FodyWeavers.xsd *.msp # JetBrains Rider -*.sln.iml \ No newline at end of file +*.sln.iml + +/tim-db +/data + +.env +.sonarlint +.vscode +.DS_Store +DSL/Liquibase/data/update_refresh_token.sql +model_trainer/results +protected_configs/ + +./config.env +./constants.ini + +config.env +constants.ini +jira_config.env \ No newline at end of file diff --git a/DSL/CronManager/DSL/data_model.yml b/DSL/CronManager/DSL/data_model.yml new file mode 100644 index 00000000..69108b37 --- /dev/null +++ b/DSL/CronManager/DSL/data_model.yml @@ -0,0 +1,5 @@ +model_trainer: + trigger: off + type: exec + command: "../app/scripts/python_train_script_starter.sh" + allowedEnvs: ['cookie', 'modelId', 'newModelId','updateType','previousDeploymentEnv'] \ No newline at end of file diff --git a/DSL/CronManager/DSL/data_model_progress_session.yml b/DSL/CronManager/DSL/data_model_progress_session.yml new file mode 100644 index 00000000..4ad1adbf --- /dev/null +++ b/DSL/CronManager/DSL/data_model_progress_session.yml @@ -0,0 +1,4 @@ +delete_completed_sessions: + trigger: "0 0 0 * * ?" + type: exec + command: "../app/scripts/delete_completed_data_model_progress_sessions.sh" \ No newline at end of file diff --git a/DSL/CronManager/DSL/data_validation.yml b/DSL/CronManager/DSL/data_validation.yml new file mode 100644 index 00000000..037f45ba --- /dev/null +++ b/DSL/CronManager/DSL/data_validation.yml @@ -0,0 +1,5 @@ +data_validation: + trigger: off + type: exec + command: "../app/scripts/data_validator_exec.sh" + allowedEnvs: ["cookie","dgId", "newDgId","updateType","savedFilePath","patchPayload"] \ No newline at end of file diff --git a/DSL/CronManager/DSL/datamodel_processing.yml b/DSL/CronManager/DSL/datamodel_processing.yml new file mode 100644 index 00000000..a27f6f7a --- /dev/null +++ b/DSL/CronManager/DSL/datamodel_processing.yml @@ -0,0 +1,5 @@ +datamodel_deletion: + trigger: off + type: exec + command: "../app/scripts/datamodel_deletion_exec.sh" + allowedEnvs: ["cookie","modelId"] \ No newline at end of file diff --git a/DSL/CronManager/DSL/dataset_deletion.yml b/DSL/CronManager/DSL/dataset_deletion.yml new file mode 100644 index 00000000..fdb7ac3e --- /dev/null +++ b/DSL/CronManager/DSL/dataset_deletion.yml @@ -0,0 +1,5 @@ +dataset_deletion: + trigger: off + type: exec + command: "../app/scripts/dataset_deletion_exec.sh" + allowedEnvs: ["cookie","dgId"] \ No newline at end of file diff --git a/DSL/CronManager/DSL/dataset_processor.yml b/DSL/CronManager/DSL/dataset_processor.yml new file mode 100644 index 00000000..91dc6d5a --- /dev/null +++ b/DSL/CronManager/DSL/dataset_processor.yml @@ -0,0 +1,5 @@ +dataset_processor: + trigger: off + type: exec + command: "../app/scripts/data_processor_exec.sh" + allowedEnvs: ["cookie","dgId", "newDgId","updateType","savedFilePath","patchPayload", "sessionId"] \ No newline at end of file diff --git a/DSL/CronManager/DSL/dataset_progress_session.yml b/DSL/CronManager/DSL/dataset_progress_session.yml new file mode 100644 index 00000000..e60b8ba9 --- /dev/null +++ b/DSL/CronManager/DSL/dataset_progress_session.yml @@ -0,0 +1,4 @@ +delete_completed_sessions: + trigger: "0 0 0 * * ?" + type: exec + command: "../app/scripts/delete_completed_dataset_progress_sessions.sh" \ No newline at end of file diff --git a/DSL/CronManager/DSL/outlook.yml b/DSL/CronManager/DSL/outlook.yml new file mode 100644 index 00000000..3cb1572b --- /dev/null +++ b/DSL/CronManager/DSL/outlook.yml @@ -0,0 +1,9 @@ +token_refresh: + trigger: "0 0 0 ? * 7#1" + type: exec + command: "../app/scripts/outlook_refresh_token.sh" + +subscription_refresh: + trigger: "0 0 0 * * ?" + type: exec + command: "../app/scripts/outlook_subscription_refresh.sh" \ No newline at end of file diff --git a/DSL/CronManager/config/config.ini b/DSL/CronManager/config/config.ini new file mode 100644 index 00000000..79cb7622 --- /dev/null +++ b/DSL/CronManager/config/config.ini @@ -0,0 +1,7 @@ +[DSL] + +CLASSIFIER_RESQL=http://resql:8082 +OUTLOOK_CLIENT_ID=value +OUTLOOK_SECRET_KEY=value +OUTLOOK_SCOPE=User.Read Mail.ReadWrite MailboxSettings.ReadWrite offline_access +INIT_DATESET_PROCESSOR_API=http://dataset-processor:8001/init-dataset-process \ No newline at end of file diff --git a/DSL/CronManager/script/data_processor_exec.sh b/DSL/CronManager/script/data_processor_exec.sh new file mode 100755 index 00000000..d6b87644 --- /dev/null +++ b/DSL/CronManager/script/data_processor_exec.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +echo "Started Shell Script to process" +# Ensure required environment variables are set +if [ -z "$dgId" ] || [ -z "$newDgId" ] || [ -z "$cookie" ] || [ -z "$updateType" ] || [ -z "$savedFilePath" ] || [ -z "$patchPayload" ] || [ -z "$sessionId" ]; then + echo "One or more environment variables are missing." + echo "Please set dgId, newDgId, cookie, updateType, savedFilePath, patchPayload, and sessionId." + exit 1 +fi + +# Construct the payload using here document +payload=$(cat < /dev/null 2>&1 +} + +echo "cookie - $cookie" +echo "old model id - $modelId" +echo "new model id - $newModelId" +echo "update type - $updateType" +echo "previous deployment env - $previousDeploymentEnv" +echo "Deployment environment - $deploymentEnv" +echo "Model details - $modelDetails" + +echo "Activating Python Environment" +source "/app/python_virtual_env/bin/activate" + +echo "Python executable in use: $(which python3)" + +if [ -f "$REQUIREMENTS_FILE" ]; then + echo "Checking if required packages are installed..." + + while IFS= read -r requirement || [ -n "$requirement" ]; do + package_name=$(echo "$requirement" | cut -d '=' -f 1 | tr -d '[:space:]') + + if is_package_installed "$package_name"; then + echo "Package '$package_name' is already installed." + else + echo "Package '$package_name' is not installed. Installing..." + pip install "$requirement" + fi + done < "$REQUIREMENTS_FILE" +else + echo "Requirements file not found: $REQUIREMENTS_FILE" +fi + +# Check if the Python script exists +if [ -f "$PYTHON_SCRIPT" ]; then + echo "Running the Python script: $PYTHON_SCRIPT" + python3 "$PYTHON_SCRIPT" +else + echo "Python script not found: $PYTHON_SCRIPT" +fi diff --git a/DSL/DMapper/hbs/get_auth_header.handlebars b/DSL/DMapper/hbs/get_auth_header.handlebars new file mode 100644 index 00000000..e94d1d22 --- /dev/null +++ b/DSL/DMapper/hbs/get_auth_header.handlebars @@ -0,0 +1,4 @@ +{ + "val": "{{{getAuthHeader username token}}}" +} + diff --git a/DSL/DMapper/hbs/return_connected_models_ids.handlebars b/DSL/DMapper/hbs/return_connected_models_ids.handlebars new file mode 100644 index 00000000..6f680e58 --- /dev/null +++ b/DSL/DMapper/hbs/return_connected_models_ids.handlebars @@ -0,0 +1,5 @@ +{{#each data}} + {{#if @first}}[{{/if}} + {{~modelId}}{{#unless @last}}, {{/unless~}} + {{#if @last}}]{{/if}} +{{/each}} \ No newline at end of file diff --git a/DSL/DMapper/hbs/return_decrypted_outlook_token.handlebars b/DSL/DMapper/hbs/return_decrypted_outlook_token.handlebars new file mode 100644 index 00000000..b345957f --- /dev/null +++ b/DSL/DMapper/hbs/return_decrypted_outlook_token.handlebars @@ -0,0 +1,3 @@ +{ + "token": {{{base64Decrypt token false}}} +} diff --git a/DSL/DMapper/hbs/return_encrypted_outlook_token.handlebars b/DSL/DMapper/hbs/return_encrypted_outlook_token.handlebars new file mode 100644 index 00000000..20249c22 --- /dev/null +++ b/DSL/DMapper/hbs/return_encrypted_outlook_token.handlebars @@ -0,0 +1,3 @@ +{ + "token": {{{base64Encrypt token}}} +} diff --git a/DSL/DMapper/hbs/return_jira_issue_info.handlebars b/DSL/DMapper/hbs/return_jira_issue_info.handlebars new file mode 100644 index 00000000..0bb1c705 --- /dev/null +++ b/DSL/DMapper/hbs/return_jira_issue_info.handlebars @@ -0,0 +1,9 @@ +{ + "title": "{{data.fields.summary}}", + "description": "{{data.fields.description}}", + "attachments": [ + {{#each data.fields.attachment}} + "{{this.filename}}"{{#unless @last}},{{/unless}} + {{/each}} + ] +} diff --git a/DSL/DMapper/hbs/return_label_field_array.handlebars b/DSL/DMapper/hbs/return_label_field_array.handlebars new file mode 100644 index 00000000..9f6b7701 --- /dev/null +++ b/DSL/DMapper/hbs/return_label_field_array.handlebars @@ -0,0 +1,9 @@ +{{#with (mergeLabelData labels existing_labels) as |merged|}} +{ + "labels": [ + {{#each merged.labels}} + "{{this}}"{{#unless @last}},{{/unless}} + {{/each}} + ] +} +{{/with}} diff --git a/DSL/DMapper/hbs/return_label_mismatch.handlebars b/DSL/DMapper/hbs/return_label_mismatch.handlebars new file mode 100644 index 00000000..b7b3437f --- /dev/null +++ b/DSL/DMapper/hbs/return_label_mismatch.handlebars @@ -0,0 +1,3 @@ +{ + "isMismatch": "{{isLabelsMismatch newLabels correctedLabels predictedLabels}}" +} diff --git a/DSL/DMapper/hbs/return_outlook_expiration_date_time.handlebars b/DSL/DMapper/hbs/return_outlook_expiration_date_time.handlebars new file mode 100644 index 00000000..b41ad8ee --- /dev/null +++ b/DSL/DMapper/hbs/return_outlook_expiration_date_time.handlebars @@ -0,0 +1,3 @@ +{ + "expirationDateTime": "{{{getOutlookExpirationDateTime}}}" +} diff --git a/DSL/DMapper/hbs/return_outlook_payload_info.handlebars b/DSL/DMapper/hbs/return_outlook_payload_info.handlebars new file mode 100644 index 00000000..e26ccefb --- /dev/null +++ b/DSL/DMapper/hbs/return_outlook_payload_info.handlebars @@ -0,0 +1,18 @@ +{ + "subject": "{{{data.subject}}}", + "categories": "{{{data.categories}}}", + "body": "{{{jsEscape data.body.content}}}", + "fromEmailAddress": "{{{data.from.emailAddress.address}}}", + "attachments": [ + {{#each data.attachments}} + "{{{this.name}}}" + {{#unless @last}},{{/unless}} + {{/each}} + ], + "toEmailAddress": [ + {{#each data.toRecipients}} + "{{{this.emailAddress.address}}}" + {{#unless @last}},{{/unless}} + {{/each}} + ] +} \ No newline at end of file diff --git a/DSL/DMapper/hbs/return_platform_response_format.handlebars b/DSL/DMapper/hbs/return_platform_response_format.handlebars new file mode 100644 index 00000000..a46c3f56 --- /dev/null +++ b/DSL/DMapper/hbs/return_platform_response_format.handlebars @@ -0,0 +1,5 @@ +{ + "operation_type": "{{data.key}}", + "operation_status_code": "{{data.fields.summary}}", + "operation_status": "{{data.fields.project.key}}" +} diff --git a/DSL/DMapper/hbs/return_platform_status.handlebars b/DSL/DMapper/hbs/return_platform_status.handlebars new file mode 100644 index 00000000..539bb5eb --- /dev/null +++ b/DSL/DMapper/hbs/return_platform_status.handlebars @@ -0,0 +1,7 @@ +{{#*inline "platformStatus"}} +{ + "jira_connection_status": {{platformStatus "JIRA" data}}, + "outlook_connection_status": {{platformStatus "OUTLOOK" data}} +} +{{/inline}} +{{> platformStatus}} \ No newline at end of file diff --git a/DSL/DMapper/hbs/return_random_string.handlebars b/DSL/DMapper/hbs/return_random_string.handlebars new file mode 100644 index 00000000..ecefc239 --- /dev/null +++ b/DSL/DMapper/hbs/return_random_string.handlebars @@ -0,0 +1,3 @@ +{ + "randomHexString": "{{{getRandomString}}}" +} diff --git a/DSL/DMapper/hbs/return_stop_words_duplicates.handlebars b/DSL/DMapper/hbs/return_stop_words_duplicates.handlebars new file mode 100644 index 00000000..c5b86935 --- /dev/null +++ b/DSL/DMapper/hbs/return_stop_words_duplicates.handlebars @@ -0,0 +1,3 @@ +{ + "duplicates": {{{findDuplicateStopWords inputArray existingArray}}} +} diff --git a/DSL/DMapper/hbs/return_stop_words_not_existing.handlebars b/DSL/DMapper/hbs/return_stop_words_not_existing.handlebars new file mode 100644 index 00000000..0d5f1453 --- /dev/null +++ b/DSL/DMapper/hbs/return_stop_words_not_existing.handlebars @@ -0,0 +1,3 @@ +{ + "notExisting": {{{findNotExistingStopWords inputArray existingArray}}} +} diff --git a/DSL/DMapper/hbs/verify_signature.handlebars b/DSL/DMapper/hbs/verify_signature.handlebars new file mode 100644 index 00000000..a39e32d2 --- /dev/null +++ b/DSL/DMapper/hbs/verify_signature.handlebars @@ -0,0 +1,3 @@ +{ + "valid": "{{verifySignature payload headers secret}}" +} diff --git a/DSL/DMapper/lib/helpers.js b/DSL/DMapper/lib/helpers.js new file mode 100644 index 00000000..1baca2f2 --- /dev/null +++ b/DSL/DMapper/lib/helpers.js @@ -0,0 +1,131 @@ +import { createHmac, timingSafeEqual, randomBytes } from "crypto"; + +export function verifySignature(payload, headers, secret) { + const signature = headers["x-hub-signature"]; + const SHARED_SECRET = secret; + const hmac = createHmac("sha256", Buffer.from(SHARED_SECRET, "utf8")); + const payloadString = JSON.stringify(payload); + hmac.update(Buffer.from(payloadString, "utf8")); + const computedSignature = hmac.digest("hex"); + const computedSignaturePrefixed = "sha256=" + computedSignature; + const isValid = timingSafeEqual( + Buffer.from(computedSignaturePrefixed, "utf8"), + Buffer.from(signature, "utf8") + ); + return isValid; +} + +export function getAuthHeader(username, token) { + const auth = `${username}:${token}`; + const encodedAuth = Buffer.from(auth).toString("base64"); + return `Basic ${encodedAuth}`; +} + +export function mergeLabelData(labels, existing_labels) { + let mergedArray = [...labels, ...existing_labels]; + let uniqueArray = [...new Set(mergedArray)]; + return { labels: uniqueArray }; +} + +export function platformStatus(platform, data) { + const platformData = data.find((item) => item.platform === platform); + return platformData ? platformData.isConnect : false; +} + +export function isLabelsMismatch(newLabels, correctedLabels, predictedLabels) { + function check(arr, newLabels) { + if ( + Array.isArray(newLabels) && + Array.isArray(arr) && + newLabels.length === arr.length + ) { + for (let label of newLabels) { + if (!arr.includes(label)) { + return true; + } + } + return false; + } else { + return true; + } + } + + const val1 = check(correctedLabels, newLabels); + const val2 = check(predictedLabels, newLabels); + return val1 && val2; +} + +export function getOutlookExpirationDateTime() { + const currentDate = new Date(); + currentDate.setDate(currentDate.getDate() + 3); + const updatedDateISOString = currentDate.toISOString(); + return updatedDateISOString; +} + +export function findDuplicateStopWords(inputArray, existingArray) { + const set1 = new Set(existingArray); + const duplicates = inputArray.filter((item) => set1.has(item)); + const value = JSON.stringify(duplicates); + return value; +} + +export function findNotExistingStopWords(inputArray, existingArray) { + const set1 = new Set(existingArray); + const notExisting = inputArray.filter((item) => !set1.has(item)); + const value = JSON.stringify(notExisting); + return value; +} + +export function getRandomString() { + const randomHexString = randomBytes(32).toString("hex"); + return randomHexString; +} + +export function base64Decrypt(cipher, isObject) { + if (!cipher) { + return JSON.stringify({ + error: true, + message: 'Cipher is missing', + }); + } + + try { + const decodedContent = !isObject ? atob(cipher) : JSON.parse(atob(cipher)); + const cleanedContent = decodedContent.replace(/\r/g, ''); + return JSON.stringify({ + error: false, + content: cleanedContent + }); + } catch (err) { + return JSON.stringify({ + error: true, + message: 'Base64 Decryption Failed', + }); + } +} + +export function base64Encrypt(content) { + if (!content) { + return { + error: true, + message: 'Content is missing', + } + } + + try { + return JSON.stringify({ + error: false, + cipher: btoa(typeof content === 'string' ? content : JSON.stringify(content)) + }); + } catch (err) { + return JSON.stringify({ + error: true, + message: 'Base64 Encryption Failed', + }); + } +} + +export function jsEscape(str) { + return JSON.stringify(str).slice(1, -1) +} + diff --git a/DSL/DMapper/lib/requestLoggerMiddleware.js b/DSL/DMapper/lib/requestLoggerMiddleware.js new file mode 100644 index 00000000..727a36fa --- /dev/null +++ b/DSL/DMapper/lib/requestLoggerMiddleware.js @@ -0,0 +1,30 @@ +/** + * @param res Original Response Object + * @param send Original UNMODIFIED res.send function + * @return A patched res.send which takes the send content, binds it to contentBody on + * the res and then calls the original res.send after restoring it + */ +const resDotSendInterceptor = (res, send) => (content) => { + res.contentBody = content; + res.send = send; + res.send(content); +}; + +export const requestLoggerMiddleware = + ({ logger }) => + (req, res, next) => { + logger( + `Request: {method: ${req.method}, url: ${ + req.url + }, params: ${JSON.stringify(req.params)}, query: ${JSON.stringify( + req.query + )}, body: ${JSON.stringify(req.body)}` + ); + res.send = resDotSendInterceptor(res, res.send); + res.on("finish", () => { + logger( + `Response: {statusCode: ${res.statusCode}, responseData: ${res.contentBody}}` + ); + }); + next(); + }; diff --git a/DSL/Liquibase/changelog/classifier-script-v1-user-management.sql b/DSL/Liquibase/changelog/classifier-script-v1-user-management.sql new file mode 100644 index 00000000..91d4cfac --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v1-user-management.sql @@ -0,0 +1,41 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v1-changeset1 +CREATE TYPE user_status AS ENUM ('active','deleted'); + +-- changeset kalsara Magamage:classifier-script-v1-changeset2 +CREATE TABLE public."user" ( + id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY, + login VARCHAR(50) NOT NULL, + password_hash VARCHAR(60), + first_name VARCHAR(50), + last_name VARCHAR(50), + id_code VARCHAR(50) NOT NULL, + display_name VARCHAR(50), + status user_status, + csa_title VARCHAR, + csa_email VARCHAR, + created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT user_pkey PRIMARY KEY (id) +); + +CREATE TABLE public."authority" ( + name VARCHAR(50) PRIMARY KEY +); + +CREATE TABLE public."user_authority" ( + id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY, + user_id VARCHAR(50) NOT NULL, + authority_name VARCHAR[] NOT NULL, + created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT user_authority_pkey PRIMARY KEY (id) +); + +-- changeset kalsara Magamage:classifier-script-v1-changeset3 + +INSERT INTO public."user" (login,password_hash,first_name,last_name,id_code,display_name,status,csa_title,csa_email) +VALUES ('EE30303039914','ok','classifier','test','EE30303039914','classifier','active','Title','classifier.doe@example.com'); + +INSERT INTO public."user_authority" ( user_id, authority_name) +VALUES ('EE30303039914', ARRAY['ROLE_ADMINISTRATOR', 'ROLE_MODEL_TRAINER'] ); + diff --git a/DSL/Liquibase/changelog/classifier-script-v10-data-model-progress-sessions.sql b/DSL/Liquibase/changelog/classifier-script-v10-data-model-progress-sessions.sql new file mode 100644 index 00000000..35d663be --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v10-data-model-progress-sessions.sql @@ -0,0 +1,19 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v10-changeset1 +CREATE TYPE Training_Progress_Status AS ENUM ('Initiating Training', 'Training In-Progress', 'Deploying Model', 'Model Trained And Deployed'); + +-- changeset kalsara Magamage:classifier-script-v10-changeset2 +CREATE TABLE model_progress_sessions ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + model_id BIGINT NOT NULL, + model_name TEXT NOT NULL, + major_version INT NOT NULL DEFAULT 0, + minor_version INT NOT NULL DEFAULT 0, + latest BOOLEAN DEFAULT false, + process_complete BOOLEAN DEFAULT false, + progress_percentage INT, + training_progress_status Training_Progress_Status, + training_message TEXT , + CONSTRAINT model_progress_sessions_pkey PRIMARY KEY (id) +); \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v2-authority-data.xml b/DSL/Liquibase/changelog/classifier-script-v2-authority-data.xml new file mode 100644 index 00000000..b54c2f3c --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v2-authority-data.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v3-integrations.sql b/DSL/Liquibase/changelog/classifier-script-v3-integrations.sql new file mode 100644 index 00000000..9585a05d --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v3-integrations.sql @@ -0,0 +1,37 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v3-changeset1 +CREATE TYPE platform AS ENUM ('JIRA', 'OUTLOOK'); + +-- changeset kalsara Magamage:classifier-script-v3-changeset2 +CREATE TABLE public."integration_status" ( + id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY, + platform platform, + is_connect BOOLEAN NOT NULL DEFAULT FALSE, + subscription_id VARCHAR(50) DEFAULT NULL, + token TEXT DEFAULT NULL, + CONSTRAINT integration_status_pkey PRIMARY KEY (id) +); + +-- changeset kalsara Magamage:classifier-script-v3-changeset3 +INSERT INTO public."integration_status" (platform, is_connect, subscription_id, token) +VALUES + ('JIRA', FALSE, NULL, NULL), + ('OUTLOOK', FALSE, NULL, NULL); + +-- changeset kalsara Magamage:classifier-script-v3-changeset4 +CREATE TABLE public."input" ( + id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY, + inference_time_stamp TIMESTAMP WITH TIME ZONE, + input_id TEXT NOT NULL, + inference_text TEXT DEFAULT NULL, + predicted_labels JSONB, + corrected_labels JSONB, + is_corrected BOOLEAN NOT NULL DEFAULT FALSE, + average_predicted_classes_probability INT, + average_corrected_classes_probability INT, + primary_folder_id TEXT DEFAULT NULL, + platform platform, + CONSTRAINT input_pkey PRIMARY KEY (id), + CONSTRAINT input_id_unique UNIQUE (input_id) +); \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v4-configuration.sql b/DSL/Liquibase/changelog/classifier-script-v4-configuration.sql new file mode 100644 index 00000000..8b3ab1be --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v4-configuration.sql @@ -0,0 +1,15 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v4-changeset1 +CREATE TABLE public.configuration ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + key VARCHAR(128), + value VARCHAR(128), + created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + CONSTRAINT configuration_pkey PRIMARY KEY (id) +); + +-- changeset kalsara Magamage:classifier-script-v4-changeset2 +INSERT INTO public.configuration (key, value) +VALUES ('session_length', '120'); diff --git a/DSL/Liquibase/changelog/classifier-script-v5-outlook-refresh-token.xml b/DSL/Liquibase/changelog/classifier-script-v5-outlook-refresh-token.xml new file mode 100644 index 00000000..7488f738 --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v5-outlook-refresh-token.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/DSL/Liquibase/changelog/classifier-script-v6-dataset-group-metadata.sql b/DSL/Liquibase/changelog/classifier-script-v6-dataset-group-metadata.sql new file mode 100644 index 00000000..0112fe82 --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v6-dataset-group-metadata.sql @@ -0,0 +1,33 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v6-changeset1 +CREATE TYPE Validation_Status AS ENUM ('success', 'fail', 'in-progress', 'unvalidated'); + +-- changeset kalsara Magamage:classifier-script-v6-changeset2 +CREATE TABLE dataset_group_metadata ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + group_name TEXT NOT NULL, + group_key TEXT NOT NULL, + major_version INT NOT NULL DEFAULT 0, + minor_version INT NOT NULL DEFAULT 0, + patch_version INT NOT NULL DEFAULT 0, + latest BOOLEAN DEFAULT false, + is_enabled BOOLEAN DEFAULT false, + enable_allowed BOOLEAN DEFAULT false, + last_model_trained TEXT, + created_timestamp TIMESTAMP WITH TIME ZONE, + last_updated_timestamp TIMESTAMP WITH TIME ZONE, + last_trained_timestamp TIMESTAMP WITH TIME ZONE, + validation_status Validation_Status, + validation_errors JSONB, + processed_data_available BOOLEAN DEFAULT false, + raw_data_available BOOLEAN DEFAULT false, + num_samples INT, + num_pages INT, + raw_data_location TEXT, + preprocess_data_location TEXT, + validation_criteria JSONB, + class_hierarchy JSONB, + connected_models JSONB, + CONSTRAINT dataset_group_metadata_pkey PRIMARY KEY (id) +); \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v7-stop-words.sql b/DSL/Liquibase/changelog/classifier-script-v7-stop-words.sql new file mode 100644 index 00000000..ab86dcf9 --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v7-stop-words.sql @@ -0,0 +1,9 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v7-changeset1 +CREATE TABLE stop_words ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + stop_word TEXT NOT NULL, + CONSTRAINT stop_words_pkey PRIMARY KEY (id), + CONSTRAINT stop_words_unique UNIQUE (stop_word) +); \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v8-dataset-progress-sessions.sql b/DSL/Liquibase/changelog/classifier-script-v8-dataset-progress-sessions.sql new file mode 100644 index 00000000..e3bdf3e1 --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v8-dataset-progress-sessions.sql @@ -0,0 +1,20 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v8-changeset1 +CREATE TYPE Validation_Progress_Status AS ENUM ('Initiating Validation', 'Validation In-Progress', 'Cleaning Dataset', 'Generating Data', 'Success', 'Fail'); + +-- changeset kalsara Magamage:classifier-script-v8-changeset2 +CREATE TABLE dataset_progress_sessions ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + dg_id BIGINT NOT NULL, + group_name TEXT NOT NULL, + major_version INT NOT NULL DEFAULT 0, + minor_version INT NOT NULL DEFAULT 0, + patch_version INT NOT NULL DEFAULT 0, + latest BOOLEAN DEFAULT false, + process_complete BOOLEAN DEFAULT false, + progress_percentage INT, + validation_status Validation_Progress_Status, + validation_message TEXT , + CONSTRAINT dataset_progress_sessions_pkey PRIMARY KEY (id) +); \ No newline at end of file diff --git a/DSL/Liquibase/changelog/classifier-script-v9-models-metadata.sql b/DSL/Liquibase/changelog/classifier-script-v9-models-metadata.sql new file mode 100644 index 00000000..ded04541 --- /dev/null +++ b/DSL/Liquibase/changelog/classifier-script-v9-models-metadata.sql @@ -0,0 +1,55 @@ +-- liquibase formatted sql + +-- changeset kalsara Magamage:classifier-script-v9-changeset1 +CREATE TYPE Maturity_Label AS ENUM ('development', 'staging', 'production ready'); + +-- changeset kalsara Magamage:classifier-script-v9-changeset2 +CREATE TYPE Deployment_Env AS ENUM ('jira', 'outlook', 'testing', 'undeployed'); + +-- changeset kalsara Magamage:classifier-script-v9-changeset3 +CREATE TYPE Training_Status AS ENUM ('not trained', 'training in-progress', 'trained', 'retraining needed', 'untrainable'); + +-- changeset kalsara Magamage:classifier-script-v9-changeset4 +CREATE TYPE Base_Models AS ENUM ('distil-bert', 'roberta', 'bert'); + +-- changeset kalsara Magamage:classifier-script-v9-changeset5 +CREATE TABLE models_metadata ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + model_group_key TEXT NOT NULL, + model_name TEXT NOT NULL, + major_version INT NOT NULL DEFAULT 0, + minor_version INT NOT NULL DEFAULT 0, + latest BOOLEAN DEFAULT false, + maturity_label Maturity_Label, + deployment_env Deployment_Env, + training_status Training_Status, + base_models Base_Models[], + last_trained_timestamp TIMESTAMP WITH TIME ZONE, + created_timestamp TIMESTAMP WITH TIME ZONE, + connected_dg_id INT, + connected_dg_name TEXT, + connected_dg_major_version INT NOT NULL DEFAULT 0, + connected_dg_minor_version INT NOT NULL DEFAULT 0, + connected_dg_patch_version INT NOT NULL DEFAULT 0, + model_s3_location TEXT, + inference_routes JSONB, + training_results JSONB, + CONSTRAINT models_metadata_pkey PRIMARY KEY (id) +); + +-- changeset kalsara Magamage:classifier-script-v9-changeset6 +CREATE TABLE model_configurations ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + base_models Base_Models[], + deployment_platforms Deployment_Env[], + maturity_labels Maturity_Label[], + CONSTRAINT model_configurations_pkey PRIMARY KEY (id) +); + +-- changeset kalsara Magamage:classifier-script-v9-changeset7 +INSERT INTO model_configurations (base_models, deployment_platforms, maturity_labels) VALUES +( + ARRAY['distil-bert', 'roberta', 'bert']::Base_Models[], + ARRAY['jira', 'outlook', 'testing', 'undeployed']::Deployment_Env[], + ARRAY['development', 'staging', 'production ready']::Maturity_Label[] +); \ No newline at end of file diff --git a/DSL/Liquibase/data/authority.csv b/DSL/Liquibase/data/authority.csv new file mode 100644 index 00000000..c110c607 --- /dev/null +++ b/DSL/Liquibase/data/authority.csv @@ -0,0 +1,3 @@ +name +ROLE_ADMINISTRATOR +ROLE_MODEL_TRAINER diff --git a/DSL/Liquibase/liquibase.properties b/DSL/Liquibase/liquibase.properties new file mode 100644 index 00000000..66683eb2 --- /dev/null +++ b/DSL/Liquibase/liquibase.properties @@ -0,0 +1,6 @@ +changelogFile: /changelog/master.yml +url: jdbc:postgresql://localhost:5433/classifier +username: root +password: val +secureParsing: false +liquibase.hub.mode=off diff --git a/DSL/Liquibase/master.yml b/DSL/Liquibase/master.yml new file mode 100644 index 00000000..06ef5a82 --- /dev/null +++ b/DSL/Liquibase/master.yml @@ -0,0 +1,21 @@ +databaseChangeLog: + - include: + file: changelog/classifier-script-v1-user-management.sql + - include: + file: changelog/classifier-script-v2-authority-data.xml + - include: + file: changelog/classifier-script-v3-integrations.sql + - include: + file: changelog/classifier-script-v4-configuration.sql + - include: + file: changelog/classifier-script-v5-outlook-refresh-token.xml + - include: + file: changelog/classifier-script-v6-dataset-group-metadata.sql + - include: + file: changelog/classifier-script-v7-stop-words.sql + - include: + file: changelog/classifier-script-v8-dataset-progress-sessions.sql + - include: + file: changelog/classifier-script-v9-models-metadata.sql + - include: + file: changelog/classifier-script-v10-data-model-progress-sessions.sql diff --git a/DSL/OpenSearch/deploy-opensearch.sh b/DSL/OpenSearch/deploy-opensearch.sh new file mode 100755 index 00000000..3ad5274d --- /dev/null +++ b/DSL/OpenSearch/deploy-opensearch.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +URL=http://localhost:9200 +AUTH=admin +MOCK_ALLOWED=${3:-false} + +if [[ -z $URL || -z $AUTH ]]; then + echo "Url and Auth are required" + exit 1 +fi + +# ddataset_progress_sessions +curl -XDELETE "$URL/dataset_progress_sessions?ignore_unavailable=true" -u "$AUTH" --insecure +curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/dataset_progress_sessions" -ku "$AUTH" --data-binary "@fieldMappings/dataset_progress_sessions.json" +if $MOCK_ALLOWED; then curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/dataset_progress_sessions/_bulk" -ku "$AUTH" --data-binary "@mock/dataset_progress_sessions.json"; fi + + +# data_model_progress_sessions +curl -XDELETE "$URL/data_model_progress_sessions?ignore_unavailable=true" -u "$AUTH" --insecure +curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/data_model_progress_sessions" -ku "$AUTH" --data-binary "@fieldMappings/data_model_progress_sessions.json" +if $MOCK_ALLOWED; then curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/data_model_progress_sessions/_bulk" -ku "$AUTH" --data-binary "@mock/data_model_progress_sessions.json"; fi \ No newline at end of file diff --git a/DSL/OpenSearch/fieldMappings/data_model_progress_sessions.json b/DSL/OpenSearch/fieldMappings/data_model_progress_sessions.json new file mode 100644 index 00000000..b21f7890 --- /dev/null +++ b/DSL/OpenSearch/fieldMappings/data_model_progress_sessions.json @@ -0,0 +1,24 @@ +{ + "mappings": { + "properties": { + "sessionId": { + "type": "keyword" + }, + "trainingStatus": { + "type": "keyword" + }, + "trainingMessage": { + "type": "keyword" + }, + "progressPercentage": { + "type": "integer" + }, + "timestamp": { + "type": "keyword" + }, + "sentTo": { + "type": "keyword" + } + } + } +} diff --git a/DSL/OpenSearch/fieldMappings/dataset_progress_sessions.json b/DSL/OpenSearch/fieldMappings/dataset_progress_sessions.json new file mode 100644 index 00000000..18b1c2fc --- /dev/null +++ b/DSL/OpenSearch/fieldMappings/dataset_progress_sessions.json @@ -0,0 +1,24 @@ +{ + "mappings": { + "properties": { + "sessionId": { + "type": "keyword" + }, + "validationStatus": { + "type": "keyword" + }, + "validationMessage": { + "type": "keyword" + }, + "progressPercentage": { + "type": "integer" + }, + "timestamp": { + "type": "keyword" + }, + "sentTo": { + "type": "keyword" + } + } + } +} diff --git a/DSL/OpenSearch/mock/data_model_progress_sessions.json b/DSL/OpenSearch/mock/data_model_progress_sessions.json new file mode 100644 index 00000000..c4e63318 --- /dev/null +++ b/DSL/OpenSearch/mock/data_model_progress_sessions.json @@ -0,0 +1,8 @@ +{"index":{"_id":"1"}} +{"sessionId": "10001000","trainingStatus": "Initiating Training","trainingMessage": "","progressPercentage": 1,"timestamp": "1801371325497", "sentTo": []} +{"index":{"_id":"2"}} +{"sessionId": "10002000","trainingStatus": "Training In-Progress","trainingMessage": "","progressPercentage": 26,"timestamp": "1801371325597", "sentTo": []} +{"index":{"_id":"3"}} +{"sessionId": "10003000","trainingStatus": "Deploying Model","trainingMessage": "","progressPercentage": 52,"timestamp": "1801371325697", "sentTo": []} +{"index":{"_id":"4"}} +{"sessionId": "100040000","trainingStatus": "Model Trained And Deployed","trainingMessage": "","progressPercentage": 97,"timestamp": "1801371325797", "sentTo": []} diff --git a/DSL/OpenSearch/mock/dataset_progress_sessions.json b/DSL/OpenSearch/mock/dataset_progress_sessions.json new file mode 100644 index 00000000..4e400a37 --- /dev/null +++ b/DSL/OpenSearch/mock/dataset_progress_sessions.json @@ -0,0 +1,13 @@ +{"index":{"_id":"1"}} +{"sessionId": "10001000","validationStatus": "Initiating Validation","validationMessage": "","progressPercentage": 1,"timestamp": "1801371325497", "sentTo": []} +{"index":{"_id":"2"}} +{"sessionId": "20001000","validationStatus": "Validation In-Progress","validationMessage": "","progressPercentage": 26,"timestamp": "1801371325597", "sentTo": []} +{"index":{"_id":"3"}} +{"sessionId": "30001000","validationStatus": "Cleaning Dataset","validationMessage": "","progressPercentage": 52,"timestamp": "1801371325697", "sentTo": []} +{"index":{"_id":"4"}} +{"sessionId": "40001000","validationStatus": "Generating Data","validationMessage": "","progressPercentage": 97,"timestamp": "1801371325797", "sentTo": []} +{"index":{"_id":"5"}} +{"sessionId": "50001000","validationStatus": "Success","validationMessage": "","progressPercentage": 100,"timestamp": "1801371325797", "sentTo": []} +{"index":{"_id":"6"}} +{"sessionId": "60001000","validationStatus": "Fail","validationMessage": "Validation failed because class called 'complaints' in 'police' column doesn't exist in the dataset`","progressPercentage": 100,"timestamp": "1801371325797", "sentTo": []} + diff --git a/DSL/Resql/connect-platform.sql b/DSL/Resql/connect-platform.sql new file mode 100644 index 00000000..84f86043 --- /dev/null +++ b/DSL/Resql/connect-platform.sql @@ -0,0 +1,3 @@ +UPDATE integration_status +SET subscription_id = :id , is_connect = TRUE +WHERE platform =:platform::platform; \ No newline at end of file diff --git a/DSL/Resql/delete-completed-data-model-progress-sessions.sql b/DSL/Resql/delete-completed-data-model-progress-sessions.sql new file mode 100644 index 00000000..f675ab0e --- /dev/null +++ b/DSL/Resql/delete-completed-data-model-progress-sessions.sql @@ -0,0 +1,3 @@ +DELETE FROM model_progress_sessions +WHERE process_complete = true +RETURNING id; diff --git a/DSL/Resql/delete-completed-dataset-progress-sessions.sql b/DSL/Resql/delete-completed-dataset-progress-sessions.sql new file mode 100644 index 00000000..eb0b5728 --- /dev/null +++ b/DSL/Resql/delete-completed-dataset-progress-sessions.sql @@ -0,0 +1,3 @@ +DELETE FROM dataset_progress_sessions +WHERE process_complete = true +RETURNING id; diff --git a/DSL/Resql/delete-data-model-by-id.sql b/DSL/Resql/delete-data-model-by-id.sql new file mode 100644 index 00000000..3ce4e24e --- /dev/null +++ b/DSL/Resql/delete-data-model-by-id.sql @@ -0,0 +1,2 @@ +DELETE FROM models_metadata +WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/delete-dataset-group-connected-models.sql b/DSL/Resql/delete-dataset-group-connected-models.sql new file mode 100644 index 00000000..e7d5f1d2 --- /dev/null +++ b/DSL/Resql/delete-dataset-group-connected-models.sql @@ -0,0 +1,7 @@ +UPDATE dataset_group_metadata +SET connected_models = ( + SELECT jsonb_agg(elem) + FROM jsonb_array_elements(connected_models) elem + WHERE (elem->>'modelId')::int <> :model_id +) +WHERE id = :id; \ No newline at end of file diff --git a/DSL/Resql/delete-dataset-group.sql b/DSL/Resql/delete-dataset-group.sql new file mode 100644 index 00000000..c898530a --- /dev/null +++ b/DSL/Resql/delete-dataset-group.sql @@ -0,0 +1,2 @@ +DELETE FROM dataset_group_metadata +WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/delete-stop-words.sql b/DSL/Resql/delete-stop-words.sql new file mode 100644 index 00000000..ece9beb8 --- /dev/null +++ b/DSL/Resql/delete-stop-words.sql @@ -0,0 +1,2 @@ +DELETE FROM stop_words +WHERE stop_word = ANY (ARRAY[:stop_words]); \ No newline at end of file diff --git a/DSL/Resql/delete-user.sql b/DSL/Resql/delete-user.sql new file mode 100644 index 00000000..eb8ccade --- /dev/null +++ b/DSL/Resql/delete-user.sql @@ -0,0 +1,36 @@ +WITH active_administrators AS (SELECT user_id + FROM user_authority + WHERE 'ROLE_ADMINISTRATOR' = ANY (authority_name) + AND id IN (SELECT max(id) + FROM user_authority + GROUP BY user_id)), +delete_user AS ( +INSERT +INTO "user" (login, password_hash, first_name, last_name, id_code, display_name, status, created, csa_title, csa_email) +SELECT login, + password_hash, + first_name, + last_name, + id_code, + display_name, + 'deleted', + :created::timestamp with time zone, + csa_title, + csa_email +FROM "user" +WHERE id_code = :userIdCode + AND status <> 'deleted' + AND id IN (SELECT max(id) FROM "user" WHERE id_code = :userIdCode) + AND (1 < (SELECT COUNT(user_id) FROM active_administrators) + OR (1 = (SELECT COUNT(user_id) FROM active_administrators) + AND :userIdCode NOT IN (SELECT user_id FROM active_administrators)))), +delete_authority AS ( +INSERT +INTO user_authority (user_id, authority_name, created) +SELECT :userIdCode as users, ARRAY []::varchar[], :created::timestamp with time zone +FROM user_authority +WHERE 1 < (SELECT COUNT(user_id) FROM active_administrators) + OR (1 = (SELECT COUNT(user_id) FROM active_administrators) + AND :userIdCode NOT IN (SELECT user_id FROM active_administrators)) +GROUP BY users) +SELECT max(status) FROM "user" WHERE id_code = :userIdCode; diff --git a/DSL/Resql/disable-dataset-group.sql b/DSL/Resql/disable-dataset-group.sql new file mode 100644 index 00000000..d200592f --- /dev/null +++ b/DSL/Resql/disable-dataset-group.sql @@ -0,0 +1,3 @@ +UPDATE dataset_group_metadata +SET is_enabled = false +WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/disconnect-platform.sql b/DSL/Resql/disconnect-platform.sql new file mode 100644 index 00000000..0db31995 --- /dev/null +++ b/DSL/Resql/disconnect-platform.sql @@ -0,0 +1,3 @@ +UPDATE integration_status +SET subscription_id = NULL, is_connect = FALSE +WHERE platform =:platform::platform; \ No newline at end of file diff --git a/DSL/Resql/enable-dataset-group.sql b/DSL/Resql/enable-dataset-group.sql new file mode 100644 index 00000000..fc271300 --- /dev/null +++ b/DSL/Resql/enable-dataset-group.sql @@ -0,0 +1,3 @@ +UPDATE dataset_group_metadata +SET is_enabled = true +WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/export-corrected-input-metadata.sql b/DSL/Resql/export-corrected-input-metadata.sql new file mode 100644 index 00000000..435ecb0c --- /dev/null +++ b/DSL/Resql/export-corrected-input-metadata.sql @@ -0,0 +1,16 @@ +SELECT id AS inference_id, + input_id, + inference_time_stamp, + inference_text, + jsonb_pretty(predicted_labels) AS predicted_labels, + jsonb_pretty(corrected_labels) AS corrected_labels, + average_predicted_classes_probability, + average_corrected_classes_probability, + platform +FROM "input" +WHERE + (is_corrected = true) + AND (:platform = 'all' OR platform = :platform::platform) +ORDER BY + CASE WHEN :sorting = 'asc' THEN inference_time_stamp END ASC, + CASE WHEN :sorting = 'desc' THEN inference_time_stamp END DESC; \ No newline at end of file diff --git a/DSL/Resql/get-basic-input-metadata-by-id.sql b/DSL/Resql/get-basic-input-metadata-by-id.sql new file mode 100644 index 00000000..1bea93f0 --- /dev/null +++ b/DSL/Resql/get-basic-input-metadata-by-id.sql @@ -0,0 +1,2 @@ +SELECT id, input_id, platform +FROM "input" WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-basic-input-metadata-by-input-id.sql b/DSL/Resql/get-basic-input-metadata-by-input-id.sql new file mode 100644 index 00000000..3a6136d7 --- /dev/null +++ b/DSL/Resql/get-basic-input-metadata-by-input-id.sql @@ -0,0 +1,2 @@ +SELECT id, input_id, platform +FROM "input" WHERE input_id =:input_id; \ No newline at end of file diff --git a/DSL/Resql/get-configuration.sql b/DSL/Resql/get-configuration.sql new file mode 100644 index 00000000..f03b322e --- /dev/null +++ b/DSL/Resql/get-configuration.sql @@ -0,0 +1,5 @@ +SELECT id, key, value +FROM configuration +WHERE key=:key +AND id IN (SELECT max(id) from configuration GROUP BY key) +AND NOT deleted; diff --git a/DSL/Resql/get-corrected-input-metadata-by-id.sql b/DSL/Resql/get-corrected-input-metadata-by-id.sql new file mode 100644 index 00000000..c9e460eb --- /dev/null +++ b/DSL/Resql/get-corrected-input-metadata-by-id.sql @@ -0,0 +1,11 @@ +SELECT id AS inference_id, + input_id, + inference_time_stamp, + inference_text, + jsonb_pretty(predicted_labels) AS predicted_labels, + jsonb_pretty(corrected_labels) AS corrected_labels, + average_predicted_classes_probability, + average_corrected_classes_probability, + platform +FROM "input" +WHERE is_corrected = true AND input_id=:input_id diff --git a/DSL/Resql/get-data-model-basic-metadata-by-id.sql b/DSL/Resql/get-data-model-basic-metadata-by-id.sql new file mode 100644 index 00000000..ea3673de --- /dev/null +++ b/DSL/Resql/get-data-model-basic-metadata-by-id.sql @@ -0,0 +1,7 @@ +SELECT + id AS model_id, + model_name, + major_version, + minor_version +FROM models_metadata +WHERE id = :id; diff --git a/DSL/Resql/get-data-model-by-id.sql b/DSL/Resql/get-data-model-by-id.sql new file mode 100644 index 00000000..4d5f6dbc --- /dev/null +++ b/DSL/Resql/get-data-model-by-id.sql @@ -0,0 +1,2 @@ +SELECT id +FROM models_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-dataset-group-by-id.sql b/DSL/Resql/get-data-model-dataset-group-by-id.sql new file mode 100644 index 00000000..4a77b6a3 --- /dev/null +++ b/DSL/Resql/get-data-model-dataset-group-by-id.sql @@ -0,0 +1,2 @@ +SELECT id, connected_dg_id +FROM models_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-filters.sql b/DSL/Resql/get-data-model-filters.sql new file mode 100644 index 00000000..ea341747 --- /dev/null +++ b/DSL/Resql/get-data-model-filters.sql @@ -0,0 +1,31 @@ +SELECT json_build_object( + 'modelNames', modelNames, + 'modelVersions', modelVersions, + 'datasetGroups', datasetGroups, + 'deploymentsEnvs', deploymentsEnvs, + 'trainingStatuses', trainingStatuses, + 'maturityLabels', maturityLabels +) +FROM ( + SELECT + array_agg(DISTINCT model_name) AS modelNames, + array_agg(DISTINCT + major_version::TEXT || '.x' + ) FILTER (WHERE major_version > 0) || + array_agg(DISTINCT + 'x.' || minor_version::TEXT + ) FILTER (WHERE minor_version > 0) AS modelVersions, + array_agg(DISTINCT + jsonb_build_object( + 'id', connected_dg_id, + 'name', connected_dg_name || ' ' || + connected_dg_major_version::TEXT || '.' || + connected_dg_minor_version::TEXT || '.' || + connected_dg_patch_version::TEXT + ) + ) AS datasetGroups, + array_agg(DISTINCT deployment_env) AS deploymentsEnvs, + array_agg(DISTINCT training_status) AS trainingStatuses, + array_agg(DISTINCT maturity_label) AS maturityLabels + FROM models_metadata +) AS subquery; diff --git a/DSL/Resql/get-data-model-group-key-by-id.sql b/DSL/Resql/get-data-model-group-key-by-id.sql new file mode 100644 index 00000000..bff582c8 --- /dev/null +++ b/DSL/Resql/get-data-model-group-key-by-id.sql @@ -0,0 +1,2 @@ +SELECT model_group_key, major_version +FROM models_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-major-data.sql b/DSL/Resql/get-data-model-major-data.sql new file mode 100644 index 00000000..15ed959a --- /dev/null +++ b/DSL/Resql/get-data-model-major-data.sql @@ -0,0 +1,2 @@ +SELECT model_group_key, deployment_env, base_models, maturity_label +FROM models_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-metadata-by-id.sql b/DSL/Resql/get-data-model-metadata-by-id.sql new file mode 100644 index 00000000..ece03d66 --- /dev/null +++ b/DSL/Resql/get-data-model-metadata-by-id.sql @@ -0,0 +1,17 @@ +SELECT + id AS model_id, + model_name, + major_version, + minor_version, + latest, + maturity_label, + deployment_env, + training_status, + base_models, + connected_dg_id, + connected_dg_name, + connected_dg_major_version, + connected_dg_minor_version, + connected_dg_patch_version +FROM models_metadata +WHERE id = :id; diff --git a/DSL/Resql/get-data-model-minor-data.sql b/DSL/Resql/get-data-model-minor-data.sql new file mode 100644 index 00000000..0c513ef4 --- /dev/null +++ b/DSL/Resql/get-data-model-minor-data.sql @@ -0,0 +1,2 @@ +SELECT model_group_key, major_version, deployment_env, base_models, maturity_label +FROM models_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-options.sql b/DSL/Resql/get-data-model-options.sql new file mode 100644 index 00000000..91df4715 --- /dev/null +++ b/DSL/Resql/get-data-model-options.sql @@ -0,0 +1,2 @@ +SELECT base_models, deployment_platforms, maturity_labels +FROM model_configurations; \ No newline at end of file diff --git a/DSL/Resql/get-data-model-progress-sessions.sql b/DSL/Resql/get-data-model-progress-sessions.sql new file mode 100644 index 00000000..9cca7730 --- /dev/null +++ b/DSL/Resql/get-data-model-progress-sessions.sql @@ -0,0 +1,18 @@ +SELECT + mps.id, + mps.model_id, + mps.model_name, + mps.major_version, + mps.minor_version, + mps.latest, + mps.process_complete, + mps.progress_percentage, + mps.training_progress_status as training_status, + mps.training_message, + mm.maturity_label, + mm.deployment_env +FROM model_progress_sessions mps +JOIN + models_metadata mm +ON + mps.model_id = mm.id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-allowed-status-by-id.sql b/DSL/Resql/get-dataset-group-allowed-status-by-id.sql new file mode 100644 index 00000000..78a35388 --- /dev/null +++ b/DSL/Resql/get-dataset-group-allowed-status-by-id.sql @@ -0,0 +1,2 @@ +SELECT enable_allowed +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-basic-metadata-by-id.sql b/DSL/Resql/get-dataset-group-basic-metadata-by-id.sql new file mode 100644 index 00000000..66f9b4a2 --- /dev/null +++ b/DSL/Resql/get-dataset-group-basic-metadata-by-id.sql @@ -0,0 +1,8 @@ +SELECT + id AS dg_id, + group_name, + major_version, + minor_version, + patch_version +FROM dataset_group_metadata +WHERE id = :id; diff --git a/DSL/Resql/get-dataset-group-by-id.sql b/DSL/Resql/get-dataset-group-by-id.sql new file mode 100644 index 00000000..514b9075 --- /dev/null +++ b/DSL/Resql/get-dataset-group-by-id.sql @@ -0,0 +1,2 @@ +SELECT id +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-class-hierarchy.sql b/DSL/Resql/get-dataset-group-class-hierarchy.sql new file mode 100644 index 00000000..ac395f8e --- /dev/null +++ b/DSL/Resql/get-dataset-group-class-hierarchy.sql @@ -0,0 +1,2 @@ +SELECT class_hierarchy +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-connected-models.sql b/DSL/Resql/get-dataset-group-connected-models.sql new file mode 100644 index 00000000..9ed7f83c --- /dev/null +++ b/DSL/Resql/get-dataset-group-connected-models.sql @@ -0,0 +1,2 @@ +SELECT connected_models +FROM dataset_group_metadata WHERE id = :id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-fields-by-id.sql b/DSL/Resql/get-dataset-group-fields-by-id.sql new file mode 100644 index 00000000..86ed75d3 --- /dev/null +++ b/DSL/Resql/get-dataset-group-fields-by-id.sql @@ -0,0 +1,2 @@ +SELECT validation_criteria,num_pages +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-filters.sql b/DSL/Resql/get-dataset-group-filters.sql new file mode 100644 index 00000000..41d807e6 --- /dev/null +++ b/DSL/Resql/get-dataset-group-filters.sql @@ -0,0 +1,20 @@ +SELECT json_build_object( + 'dgNames', dgNames, + 'dgVersions', dgVersions, + 'dgValidationStatuses', dgValidationStatuses +) +FROM ( + SELECT + array_agg(DISTINCT group_name) AS dgNames, + array_agg(DISTINCT + major_version::TEXT || '.x.x' + ) FILTER (WHERE major_version > 0) || + array_agg(DISTINCT + 'x.' || minor_version::TEXT || '.x' + ) FILTER (WHERE minor_version > 0) || + array_agg(DISTINCT + 'x.x.' || patch_version::TEXT + ) FILTER (WHERE patch_version > 0) AS dgVersions, + array_agg(DISTINCT validation_status) AS dgValidationStatuses + FROM dataset_group_metadata +) AS subquery; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-key-by-id.sql b/DSL/Resql/get-dataset-group-key-by-id.sql new file mode 100644 index 00000000..3b5861dd --- /dev/null +++ b/DSL/Resql/get-dataset-group-key-by-id.sql @@ -0,0 +1,2 @@ +SELECT group_key, major_version, minor_version +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-group-metadata-by-id.sql b/DSL/Resql/get-dataset-group-metadata-by-id.sql new file mode 100644 index 00000000..c6fc13af --- /dev/null +++ b/DSL/Resql/get-dataset-group-metadata-by-id.sql @@ -0,0 +1,3 @@ +SELECT id, group_name, major_version, minor_version, patch_version, latest, is_enabled, num_samples, enable_allowed, + validation_status, validation_errors, connected_models, validation_criteria, class_hierarchy +FROM dataset_group_metadata WHERE id = :id; diff --git a/DSL/Resql/get-dataset-group-schema.sql b/DSL/Resql/get-dataset-group-schema.sql new file mode 100644 index 00000000..2b0676f5 --- /dev/null +++ b/DSL/Resql/get-dataset-group-schema.sql @@ -0,0 +1,2 @@ +SELECT validation_criteria,class_hierarchy +FROM dataset_group_metadata WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-dataset-progress-sessions.sql b/DSL/Resql/get-dataset-progress-sessions.sql new file mode 100644 index 00000000..d76a32b7 --- /dev/null +++ b/DSL/Resql/get-dataset-progress-sessions.sql @@ -0,0 +1,13 @@ +SELECT + id, + dg_id, + group_name, + major_version, + minor_version, + patch_version, + latest, + process_complete, + progress_percentage, + validation_status, + validation_message +FROM dataset_progress_sessions; diff --git a/DSL/Resql/get-input-metadata-exits-by-id.sql b/DSL/Resql/get-input-metadata-exits-by-id.sql new file mode 100644 index 00000000..8de7e2e9 --- /dev/null +++ b/DSL/Resql/get-input-metadata-exits-by-id.sql @@ -0,0 +1,2 @@ +SELECT id +FROM "input" WHERE id =:id; \ No newline at end of file diff --git a/DSL/Resql/get-integration-status.sql b/DSL/Resql/get-integration-status.sql new file mode 100644 index 00000000..06d857a9 --- /dev/null +++ b/DSL/Resql/get-integration-status.sql @@ -0,0 +1 @@ +SELECT * FROM integration_status \ No newline at end of file diff --git a/DSL/Resql/get-jira-input-row-data.sql b/DSL/Resql/get-jira-input-row-data.sql new file mode 100644 index 00000000..bc32ef30 --- /dev/null +++ b/DSL/Resql/get-jira-input-row-data.sql @@ -0,0 +1,3 @@ +SELECT predicted_labels, corrected_labels +FROM "input" +WHERE input_id=:inputId AND platform='JIRA'; diff --git a/DSL/Resql/get-outlook-input-row-data.sql b/DSL/Resql/get-outlook-input-row-data.sql new file mode 100644 index 00000000..7393886b --- /dev/null +++ b/DSL/Resql/get-outlook-input-row-data.sql @@ -0,0 +1,3 @@ +SELECT primary_folder_id +FROM "input" +WHERE input_id=:inputId AND platform='OUTLOOK'; diff --git a/DSL/Resql/get-paginated-corrected-input-metadata.sql b/DSL/Resql/get-paginated-corrected-input-metadata.sql new file mode 100644 index 00000000..3b907541 --- /dev/null +++ b/DSL/Resql/get-paginated-corrected-input-metadata.sql @@ -0,0 +1,18 @@ +SELECT id AS inference_id, + input_id, + inference_time_stamp, + inference_text, + jsonb_pretty(predicted_labels) AS predicted_labels, + jsonb_pretty(corrected_labels) AS corrected_labels, + average_predicted_classes_probability, + average_corrected_classes_probability, + platform, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages +FROM "input" +WHERE + (is_corrected = true) + AND (:platform = 'all' OR platform = :platform::platform) +ORDER BY + CASE WHEN :sorting = 'asc' THEN inference_time_stamp END ASC, + CASE WHEN :sorting = 'desc' THEN inference_time_stamp END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Resql/get-paginated-data-model-metadata.sql b/DSL/Resql/get-paginated-data-model-metadata.sql new file mode 100644 index 00000000..c8c9bcc0 --- /dev/null +++ b/DSL/Resql/get-paginated-data-model-metadata.sql @@ -0,0 +1,37 @@ +SELECT + dt.id, + dt.model_group_key, + dt.model_name, + dt.major_version, + dt.minor_version, + dt.latest, + dt.maturity_label, + dt.deployment_env, + dt.training_status, + dt.base_models, + dt.last_trained_timestamp, + dt.created_timestamp, + dt.connected_dg_id, + dt.connected_dg_name, + dt.connected_dg_major_version, + dt.connected_dg_minor_version, + dt.connected_dg_patch_version, + jsonb_pretty(dt.training_results) AS training_results, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages +FROM + models_metadata dt +WHERE + (:major_version = -1 OR dt.major_version = :major_version) + AND (:minor_version = -1 OR dt.minor_version = :minor_version) + AND (:model_name = 'all' OR dt.model_name = :model_name) + AND (:deployment_maturity = 'all' OR dt.maturity_label = :deployment_maturity::Maturity_Label) + AND (:training_status = 'all' OR dt.training_status = :training_status::Training_Status) + AND (:platform = 'all' OR dt.deployment_env = :platform::Deployment_Env) + AND (:dataset_group = -1 OR dt.connected_dg_id = :dataset_group) +ORDER BY + CASE WHEN :sort_by = 'name' AND :sort_type = 'asc' THEN dt.model_name END ASC, + CASE WHEN :sort_by = 'name' AND :sort_type = 'desc' THEN dt.model_name END DESC, + CASE WHEN :sort_by = 'created_timestamp' AND :sort_type = 'asc' THEN dt.created_timestamp END ASC, + CASE WHEN :sort_by = 'created_timestamp' AND :sort_type = 'desc' THEN dt.created_timestamp END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; + diff --git a/DSL/Resql/get-paginated-dataset-group-metadata.sql b/DSL/Resql/get-paginated-dataset-group-metadata.sql new file mode 100644 index 00000000..c14cb51f --- /dev/null +++ b/DSL/Resql/get-paginated-dataset-group-metadata.sql @@ -0,0 +1,27 @@ +SELECT dt.id, + dt.group_name, + dt.major_version, + dt.minor_version, + dt.patch_version, + dt.latest, + dt.is_enabled, + dt.enable_allowed, + dt.created_timestamp, + dt.last_updated_timestamp, + dt.last_trained_timestamp, + dt.last_model_trained, + dt.validation_status, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages +FROM "dataset_group_metadata" dt +WHERE + (:major_version = -1 OR dt.major_version = :major_version) + AND (:minor_version = -1 OR dt.minor_version = :minor_version) + AND (:patch_version = -1 OR dt.patch_version = :patch_version) + AND (:validation_status = 'all' OR dt.validation_status = :validation_status::Validation_Status) + AND (:group_name = 'all' OR dt.group_name = :group_name) +ORDER BY + CASE WHEN :sort_by = 'name' AND :sorting = 'asc' THEN dt.group_name END ASC, + CASE WHEN :sort_by = 'name' AND :sorting = 'desc' THEN dt.group_name END DESC, + CASE WHEN :sort_by = 'created_timestamp' AND :sorting = 'asc' THEN dt.created_timestamp END ASC, + CASE WHEN :sort_by = 'created_timestamp' AND :sorting = 'desc' THEN dt.created_timestamp END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Resql/get-platform-integration-status.sql b/DSL/Resql/get-platform-integration-status.sql new file mode 100644 index 00000000..1ee8d18a --- /dev/null +++ b/DSL/Resql/get-platform-integration-status.sql @@ -0,0 +1,3 @@ +SELECT is_connect, subscription_id +FROM integration_status +WHERE platform=:platform::platform; \ No newline at end of file diff --git a/DSL/Resql/get-platform-subscription-id.sql b/DSL/Resql/get-platform-subscription-id.sql new file mode 100644 index 00000000..c30272f7 --- /dev/null +++ b/DSL/Resql/get-platform-subscription-id.sql @@ -0,0 +1,3 @@ +SELECT subscription_id +FROM integration_status +WHERE platform=:platform::platform; \ No newline at end of file diff --git a/DSL/Resql/get-production-data-model-metadata.sql b/DSL/Resql/get-production-data-model-metadata.sql new file mode 100644 index 00000000..6b35de59 --- /dev/null +++ b/DSL/Resql/get-production-data-model-metadata.sql @@ -0,0 +1,23 @@ +SELECT + dt.id, + dt.model_group_key, + dt.model_name, + dt.major_version, + dt.minor_version, + dt.latest, + dt.maturity_label, + dt.deployment_env, + dt.training_status, + dt.base_models, + dt.last_trained_timestamp, + dt.created_timestamp, + dt.connected_dg_id, + dt.connected_dg_name, + dt.connected_dg_major_version, + dt.connected_dg_minor_version, + dt.connected_dg_patch_version, + jsonb_pretty(dt.training_results) AS training_results +FROM + models_metadata dt +WHERE + dt.deployment_env IN ('jira', 'outlook'); \ No newline at end of file diff --git a/DSL/Resql/get-stop-words.sql b/DSL/Resql/get-stop-words.sql new file mode 100644 index 00000000..7fd17820 --- /dev/null +++ b/DSL/Resql/get-stop-words.sql @@ -0,0 +1,2 @@ +SELECT array_agg(stop_word) AS stop_words_array +FROM stop_words; \ No newline at end of file diff --git a/DSL/Resql/get-test-data-models.sql b/DSL/Resql/get-test-data-models.sql new file mode 100644 index 00000000..6905a8d3 --- /dev/null +++ b/DSL/Resql/get-test-data-models.sql @@ -0,0 +1,7 @@ +SELECT + id AS model_id, + model_name, + major_version, + minor_version +FROM models_metadata +WHERE deployment_env = 'testing'; \ No newline at end of file diff --git a/DSL/Resql/get-token.sql b/DSL/Resql/get-token.sql new file mode 100644 index 00000000..ba4c16d0 --- /dev/null +++ b/DSL/Resql/get-token.sql @@ -0,0 +1,3 @@ +SELECT token +FROM integration_status +WHERE platform=:platform::platform; diff --git a/DSL/Resql/get-user-role.sql b/DSL/Resql/get-user-role.sql new file mode 100644 index 00000000..39a51f4e --- /dev/null +++ b/DSL/Resql/get-user-role.sql @@ -0,0 +1,10 @@ +SELECT ua.authority_name AS authorities +FROM "user" u + INNER JOIN (SELECT authority_name, user_id + FROM user_authority AS ua + WHERE ua.id IN (SELECT max(id) + FROM user_authority + GROUP BY user_id)) ua ON u.id_code = ua.user_id +WHERE u.id_code = :userIdCode + AND status <> 'deleted' + AND id IN (SELECT max(id) FROM "user" WHERE id_code = :userIdCode) diff --git a/DSL/Resql/get-user-with-roles.sql b/DSL/Resql/get-user-with-roles.sql new file mode 100644 index 00000000..8ef5044c --- /dev/null +++ b/DSL/Resql/get-user-with-roles.sql @@ -0,0 +1,15 @@ +SELECT DISTINCT u.login, + u.first_name, + u.last_name, + u.id_code, + u.display_name, + u.csa_title, + u.csa_email, + ua.authority_name AS authorities +FROM "user" u + LEFT JOIN (SELECT authority_name, user_id + FROM user_authority AS ua + WHERE ua.id IN (SELECT max(id) + FROM user_authority + GROUP BY user_id)) ua ON u.id_code = ua.user_id +WHERE login = :login; diff --git a/DSL/Resql/get-user.sql b/DSL/Resql/get-user.sql new file mode 100644 index 00000000..18bef7ff --- /dev/null +++ b/DSL/Resql/get-user.sql @@ -0,0 +1,5 @@ +SELECT id_code +FROM "user" +WHERE id_code = :userIdCode + AND status <> 'deleted' + AND id IN (SELECT max(id) FROM "user" WHERE id_code = :userIdCode) \ No newline at end of file diff --git a/DSL/Resql/get-users-with-roles-by-role.sql b/DSL/Resql/get-users-with-roles-by-role.sql new file mode 100644 index 00000000..50ec5199 --- /dev/null +++ b/DSL/Resql/get-users-with-roles-by-role.sql @@ -0,0 +1,41 @@ +SELECT u.login, + u.first_name, + u.last_name, + u.id_code AS userIdCode, + u.display_name, + u.csa_title, + u.csa_email, + ua.authority_name AS authorities, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages +FROM "user" u +LEFT JOIN ( + SELECT authority_name, user_id, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id DESC) AS rn + FROM user_authority AS ua + WHERE authority_name && ARRAY [ :roles ]::character varying array + AND ua.id IN ( + SELECT max(id) + FROM user_authority + GROUP BY user_id + ) +) ua ON u.id_code = ua.user_id +WHERE u.status <> 'deleted' + AND array_length(authority_name, 1) > 0 + AND u.id IN ( + SELECT max(id) + FROM "user" + GROUP BY id_code + ) +ORDER BY + CASE WHEN :sorting = 'name asc' THEN u.first_name END ASC, + CASE WHEN :sorting = 'name desc' THEN u.first_name END DESC, + CASE WHEN :sorting = 'idCode asc' THEN u.id_code END ASC, + CASE WHEN :sorting = 'idCode desc' THEN u.id_code END DESC, + CASE WHEN :sorting = 'Role asc' THEN ua.authority_name END ASC, + CASE WHEN :sorting = 'Role desc' THEN ua.authority_name END DESC, + CASE WHEN :sorting = 'displayName asc' THEN u.display_name END ASC, + CASE WHEN :sorting = 'displayName desc' THEN u.display_name END DESC, + CASE WHEN :sorting = 'csaTitle asc' THEN u.csa_title END ASC, + CASE WHEN :sorting = 'csaTitle desc' THEN u.csa_title END DESC, + CASE WHEN :sorting = 'csaEmail asc' THEN u.csa_email END ASC, + CASE WHEN :sorting = 'csaEmail desc' THEN u.csa_email END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Resql/get-validated-all-dataset-groups.sql b/DSL/Resql/get-validated-all-dataset-groups.sql new file mode 100644 index 00000000..e3aa847d --- /dev/null +++ b/DSL/Resql/get-validated-all-dataset-groups.sql @@ -0,0 +1,3 @@ +SELECT id as dg_id, group_name, major_version, minor_version, patch_version +FROM dataset_group_metadata +WHERE is_enabled AND validation_status = 'success'; diff --git a/DSL/Resql/insert-data-model-progress-session.sql b/DSL/Resql/insert-data-model-progress-session.sql new file mode 100644 index 00000000..d038409f --- /dev/null +++ b/DSL/Resql/insert-data-model-progress-session.sql @@ -0,0 +1,17 @@ +INSERT INTO "model_progress_sessions" ( + model_id, + model_name, + major_version, + minor_version, + latest, + progress_percentage, + training_progress_status +) VALUES ( + :model_id, + :model_name, + :major_version, + :minor_version, + :latest, + :progress_percentage, + :training_progress_status::Training_Progress_Status +)RETURNING id; \ No newline at end of file diff --git a/DSL/Resql/insert-dataset-group-metadata.sql b/DSL/Resql/insert-dataset-group-metadata.sql new file mode 100644 index 00000000..d13c0144 --- /dev/null +++ b/DSL/Resql/insert-dataset-group-metadata.sql @@ -0,0 +1,39 @@ +INSERT INTO "dataset_group_metadata" ( + group_name, + group_key, + major_version, + minor_version, + patch_version, + latest, + is_enabled, + enable_allowed, + created_timestamp, + last_updated_timestamp, + validation_status, + processed_data_available, + raw_data_available, + num_samples, + num_pages, + validation_criteria, + class_hierarchy + ) + VALUES ( + :group_name, + :group_key, + :major_version, + :minor_version, + :patch_version, + :latest, + :is_enabled, + :enable_allowed, + :created_timestamp::timestamp with time zone, + :last_updated_timestamp::timestamp with time zone, + :validation_status::Validation_Status, + :processed_data_available, + :raw_data_available, + :num_samples, + :num_pages, + :validation_criteria::jsonb, + :class_hierarchy::jsonb +)RETURNING id; + diff --git a/DSL/Resql/insert-dataset-progress-session.sql b/DSL/Resql/insert-dataset-progress-session.sql new file mode 100644 index 00000000..dd65f7ee --- /dev/null +++ b/DSL/Resql/insert-dataset-progress-session.sql @@ -0,0 +1,19 @@ +INSERT INTO "dataset_progress_sessions" ( + dg_id, + group_name, + major_version, + minor_version, + patch_version, + latest, + progress_percentage, + validation_status +) VALUES ( + :dg_id, + :group_name, + :major_version, + :minor_version, + :patch_version, + :latest, + :progressPercentage, + :validation_status::Validation_Progress_Status +)RETURNING id; \ No newline at end of file diff --git a/DSL/Resql/insert-input-metadata.sql b/DSL/Resql/insert-input-metadata.sql new file mode 100644 index 00000000..d11d2dd7 --- /dev/null +++ b/DSL/Resql/insert-input-metadata.sql @@ -0,0 +1,19 @@ +INSERT INTO "input" ( + input_id, + inference_time_stamp, + inference_text, + predicted_labels, + average_predicted_classes_probability, + platform, + primary_folder_id +) +VALUES ( + :input_id, + :inference_time_stamp::timestamp with time zone, + :inference_text, + :predicted_labels::jsonb, + :average_predicted_classes_probability, + :platform::platform, + :primary_folder_id +) +RETURNING id; diff --git a/DSL/Resql/insert-model-metadata.sql b/DSL/Resql/insert-model-metadata.sql new file mode 100644 index 00000000..15f41110 --- /dev/null +++ b/DSL/Resql/insert-model-metadata.sql @@ -0,0 +1,33 @@ +INSERT INTO models_metadata ( + model_group_key, + model_name, + major_version, + minor_version, + latest, + maturity_label, + deployment_env, + training_status, + base_models, + created_timestamp, + connected_dg_id, + connected_dg_name, + connected_dg_major_version, + connected_dg_minor_version, + connected_dg_patch_version +) VALUES ( + :model_group_key, + :model_name, + :major_version, + :minor_version, + :latest, + :maturity_label::Maturity_Label, + :deployment_env::Deployment_Env, + :training_status::Training_Status, + ARRAY [:base_models]::Base_Models[], + :created_timestamp::timestamp with time zone, + :connected_dg_id, + :connected_dg_name, + :connected_dg_major_version, + :connected_dg_minor_version, + :connected_dg_patch_version +) RETURNING id; diff --git a/DSL/Resql/insert-stop-words.sql b/DSL/Resql/insert-stop-words.sql new file mode 100644 index 00000000..d7b64fe4 --- /dev/null +++ b/DSL/Resql/insert-stop-words.sql @@ -0,0 +1,3 @@ +INSERT INTO stop_words (stop_word) +SELECT unnest(ARRAY[:stop_words]) +ON CONFLICT (stop_word) DO NOTHING; \ No newline at end of file diff --git a/DSL/Resql/insert-user-role.sql b/DSL/Resql/insert-user-role.sql new file mode 100644 index 00000000..e2bfe3b4 --- /dev/null +++ b/DSL/Resql/insert-user-role.sql @@ -0,0 +1,2 @@ +INSERT INTO user_authority (user_id, authority_name, created) +VALUES (:userIdCode, ARRAY [ :roles ], :created::timestamp with time zone); \ No newline at end of file diff --git a/DSL/Resql/insert-user.sql b/DSL/Resql/insert-user.sql new file mode 100644 index 00000000..0fd7c12b --- /dev/null +++ b/DSL/Resql/insert-user.sql @@ -0,0 +1,2 @@ +INSERT INTO "user" (login, first_name, last_name, display_name, password_hash, id_code, status, created, csa_title, csa_email) +VALUES (:userIdCode, :firstName, :lastName, :displayName, :displayName, :userIdCode, (:status)::user_status, :created::timestamp with time zone, :csaTitle, :csaEmail); diff --git a/DSL/Resql/save-outlook-token.sql b/DSL/Resql/save-outlook-token.sql new file mode 100644 index 00000000..5963a749 --- /dev/null +++ b/DSL/Resql/save-outlook-token.sql @@ -0,0 +1,3 @@ +UPDATE integration_status +SET token = :token +WHERE platform = 'OUTLOOK'; \ No newline at end of file diff --git a/DSL/Resql/snapshot-major-data-model.sql b/DSL/Resql/snapshot-major-data-model.sql new file mode 100644 index 00000000..2c3818d2 --- /dev/null +++ b/DSL/Resql/snapshot-major-data-model.sql @@ -0,0 +1,48 @@ +INSERT INTO models_metadata ( + model_group_key, + model_name, + major_version, + minor_version, + latest, + maturity_label, + deployment_env, + training_status, + base_models, + last_trained_timestamp, + created_timestamp, + connected_dg_id, + connected_dg_name, + connected_dg_major_version, + connected_dg_minor_version, + connected_dg_patch_version, + model_s3_location, + inference_routes, + training_results +) +SELECT + model_group_key, + model_name, + ( + SELECT COALESCE(MAX(major_version), 0) + 1 + FROM models_metadata + WHERE model_group_key = :group_key + ) AS major_version, + 0 AS minor_version, + true AS latest, + :maturity_label::Maturity_Label, + :deployment_env::Deployment_Env, + 'not trained'::Training_Status AS training_status, + ARRAY [:base_models]::Base_Models[], + NULL AS last_trained_timestamp, + created_timestamp, + :connected_dg_id, + :connected_dg_name, + :connected_dg_major_version, + :connected_dg_minor_version, + :connected_dg_patch_version, + NULL AS model_s3_location, + NULL AS inference_routes, + NULL AS training_results +FROM models_metadata +WHERE id = :id +RETURNING id; diff --git a/DSL/Resql/snapshot-major-dataset-group.sql b/DSL/Resql/snapshot-major-dataset-group.sql new file mode 100644 index 00000000..6fd9c837 --- /dev/null +++ b/DSL/Resql/snapshot-major-dataset-group.sql @@ -0,0 +1,38 @@ +INSERT INTO dataset_group_metadata ( + group_name, group_key, major_version, minor_version, patch_version, latest, + is_enabled, enable_allowed, last_model_trained, created_timestamp, + last_updated_timestamp, last_trained_timestamp, validation_status, + validation_errors, processed_data_available, raw_data_available, + num_samples, num_pages, raw_data_location, preprocess_data_location, + validation_criteria, class_hierarchy, connected_models +) +SELECT + group_name, group_key, + ( + SELECT COALESCE(MAX(major_version), 0) + 1 + FROM dataset_group_metadata + WHERE group_key = :group_key + ) AS major_version, + 0 AS minor_version, + 0 AS patch_version, + true AS latest, + false AS is_enabled, + false AS enable_allowed, + NULL AS last_model_trained, + created_timestamp, + CURRENT_TIMESTAMP AS last_updated_timestamp, + NULL AS last_trained_timestamp, + 'unvalidated'::Validation_Status AS validation_status, + NULL::JSONB AS validation_errors, + false AS processed_data_available, + false AS raw_data_available, + 0 AS num_samples, + 0 AS num_pages, + NULL AS raw_data_location, + NULL AS preprocess_data_location, + :validation_criteria::JSONB AS validation_criteria, + :class_hierarchy::JSONB AS class_hierarchy, + NULL::JSONB AS connected_models +FROM dataset_group_metadata +WHERE id = :id +RETURNING id; \ No newline at end of file diff --git a/DSL/Resql/snapshot-minor-data-model.sql b/DSL/Resql/snapshot-minor-data-model.sql new file mode 100644 index 00000000..dc2d3b33 --- /dev/null +++ b/DSL/Resql/snapshot-minor-data-model.sql @@ -0,0 +1,48 @@ +INSERT INTO models_metadata ( + model_group_key, + model_name, + major_version, + minor_version, + latest, + maturity_label, + deployment_env, + training_status, + base_models, + last_trained_timestamp, + created_timestamp, + connected_dg_id, + connected_dg_name, + connected_dg_major_version, + connected_dg_minor_version, + connected_dg_patch_version, + model_s3_location, + inference_routes, + training_results +) +SELECT + model_group_key, + model_name, + major_version, + ( + SELECT COALESCE(MAX(minor_version), 0) + 1 + FROM models_metadata + WHERE model_group_key = :group_key AND major_version = :major_version + ) AS minor_version, + true AS latest, + :maturity_label::Maturity_Label, + :deployment_env::Deployment_Env, + 'not trained'::Training_Status AS training_status, + ARRAY [:base_models]::Base_Models[], + NULL AS last_trained_timestamp, + created_timestamp, + connected_dg_id, + connected_dg_name, + connected_dg_major_version, + connected_dg_minor_version, + connected_dg_patch_version, + NULL AS model_s3_location, + NULL AS inference_routes, + NULL AS training_results +FROM models_metadata +WHERE id = :id +RETURNING id; diff --git a/DSL/Resql/snapshot-minor-dataset-group.sql b/DSL/Resql/snapshot-minor-dataset-group.sql new file mode 100644 index 00000000..e6f8ae99 --- /dev/null +++ b/DSL/Resql/snapshot-minor-dataset-group.sql @@ -0,0 +1,24 @@ +INSERT INTO dataset_group_metadata ( + group_name, group_key, major_version, minor_version, patch_version, latest, + is_enabled, enable_allowed, last_model_trained, created_timestamp, + last_updated_timestamp, last_trained_timestamp, validation_status, + validation_errors, processed_data_available, raw_data_available, + num_samples, num_pages, raw_data_location, preprocess_data_location, + validation_criteria, class_hierarchy, connected_models +) +SELECT + group_name, group_key, major_version, + ( + SELECT COALESCE(MAX(minor_version), 0) + 1 + FROM dataset_group_metadata + WHERE group_key = :group_key AND major_version = :major_version + ) AS minor_version, + 0 AS patch_version, true AS latest, + false AS is_enabled, false AS enable_allowed, last_model_trained, created_timestamp, + CURRENT_TIMESTAMP AS last_updated_timestamp, last_trained_timestamp, + 'in-progress'::Validation_Status AS validation_status, NULL AS validation_errors, false AS processed_data_available, + false AS raw_data_available, 0 AS num_samples, 0 AS num_pages, NULL AS raw_data_location, NULL AS preprocess_data_location, + validation_criteria, class_hierarchy, connected_models +FROM dataset_group_metadata +WHERE id = :id +RETURNING id; diff --git a/DSL/Resql/update-data-model-dataset-group-patch-version.sql b/DSL/Resql/update-data-model-dataset-group-patch-version.sql new file mode 100644 index 00000000..d6d381b0 --- /dev/null +++ b/DSL/Resql/update-data-model-dataset-group-patch-version.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + connected_dg_patch_version = connected_dg_patch_version + 1 +WHERE connected_dg_id = :dg_id; \ No newline at end of file diff --git a/DSL/Resql/update-data-model-dataset-group.sql b/DSL/Resql/update-data-model-dataset-group.sql new file mode 100644 index 00000000..b60ab387 --- /dev/null +++ b/DSL/Resql/update-data-model-dataset-group.sql @@ -0,0 +1,5 @@ +UPDATE models_metadata +SET + connected_dg_name = null, + connected_dg_id = null +WHERE connected_dg_id = :id; diff --git a/DSL/Resql/update-data-model-deployment-env.sql b/DSL/Resql/update-data-model-deployment-env.sql new file mode 100644 index 00000000..ff27147f --- /dev/null +++ b/DSL/Resql/update-data-model-deployment-env.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + deployment_env = :deployment_env::Deployment_Env +WHERE id = :id; diff --git a/DSL/Resql/update-data-model-maturity-label.sql b/DSL/Resql/update-data-model-maturity-label.sql new file mode 100644 index 00000000..0e751b63 --- /dev/null +++ b/DSL/Resql/update-data-model-maturity-label.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + maturity_label = :maturity_label::Maturity_Label +WHERE id = :id; diff --git a/DSL/Resql/update-data-model-progress-session.sql b/DSL/Resql/update-data-model-progress-session.sql new file mode 100644 index 00000000..13b296f3 --- /dev/null +++ b/DSL/Resql/update-data-model-progress-session.sql @@ -0,0 +1,7 @@ +UPDATE model_progress_sessions +SET + training_progress_status = :training_progress_status::Training_Progress_Status, + training_message = :training_message, + progress_percentage = :progress_percentage, + process_complete = :process_complete +WHERE id = :id; \ No newline at end of file diff --git a/DSL/Resql/update-data-model-training-data.sql b/DSL/Resql/update-data-model-training-data.sql new file mode 100644 index 00000000..1dace3b4 --- /dev/null +++ b/DSL/Resql/update-data-model-training-data.sql @@ -0,0 +1,8 @@ +UPDATE models_metadata +SET + training_status = :training_status::Training_Status, + model_s3_location = :model_s3_location, + last_trained_timestamp = :last_trained_timestamp::timestamp with time zone, + training_results = :training_results::jsonb, + inference_routes = :inference_routes::jsonb +WHERE id = :id; diff --git a/DSL/Resql/update-data-models-re-training-needed.sql b/DSL/Resql/update-data-models-re-training-needed.sql new file mode 100644 index 00000000..a0d38043 --- /dev/null +++ b/DSL/Resql/update-data-models-re-training-needed.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + training_status = 'retraining needed' +WHERE id = ANY (ARRAY[:ids]); \ No newline at end of file diff --git a/DSL/Resql/update-dataset-group-connected-models.sql b/DSL/Resql/update-dataset-group-connected-models.sql new file mode 100644 index 00000000..3ef66eac --- /dev/null +++ b/DSL/Resql/update-dataset-group-connected-models.sql @@ -0,0 +1,7 @@ +UPDATE dataset_group_metadata +SET connected_models = + CASE + WHEN connected_models IS NULL THEN jsonb_build_array(:connected_model::jsonb) + ELSE connected_models || :connected_model::jsonb + END +WHERE id = :id; diff --git a/DSL/Resql/update-dataset-group-preprocess-status.sql b/DSL/Resql/update-dataset-group-preprocess-status.sql new file mode 100644 index 00000000..02e17b96 --- /dev/null +++ b/DSL/Resql/update-dataset-group-preprocess-status.sql @@ -0,0 +1,11 @@ +UPDATE dataset_group_metadata +SET + processed_data_available = :processed_data_available, + raw_data_available = :raw_data_available, + preprocess_data_location = :preprocess_data_location, + raw_data_location = :raw_data_location, + enable_allowed = :enable_allowed, + last_updated_timestamp = :last_updated_timestamp::timestamp with time zone, + num_samples = :num_samples, + num_pages = :num_pages +WHERE id = :id; diff --git a/DSL/Resql/update-dataset-progress-session.sql b/DSL/Resql/update-dataset-progress-session.sql new file mode 100644 index 00000000..1514321b --- /dev/null +++ b/DSL/Resql/update-dataset-progress-session.sql @@ -0,0 +1,7 @@ +UPDATE dataset_progress_sessions +SET + validation_status = :validation_status::Validation_Progress_Status, + validation_message = :validation_message, + progress_percentage = :progress_percentage, + process_complete = :process_complete +WHERE id = :id; \ No newline at end of file diff --git a/DSL/Resql/update-input-metadata.sql b/DSL/Resql/update-input-metadata.sql new file mode 100644 index 00000000..d1d1f695 --- /dev/null +++ b/DSL/Resql/update-input-metadata.sql @@ -0,0 +1,7 @@ +UPDATE input +SET + is_corrected = :is_corrected, + corrected_labels = :corrected_labels::JSONB, + average_corrected_classes_probability = :average_corrected_classes_probability, + primary_folder_id = :primary_folder_id +WHERE id = :id; diff --git a/DSL/Resql/update-last-train-model-in-dataset-group.sql b/DSL/Resql/update-last-train-model-in-dataset-group.sql new file mode 100644 index 00000000..ed3ec836 --- /dev/null +++ b/DSL/Resql/update-last-train-model-in-dataset-group.sql @@ -0,0 +1,4 @@ +UPDATE dataset_group_metadata +SET + last_model_trained =:last_model_trained +WHERE id = :id; \ No newline at end of file diff --git a/DSL/Resql/update-latest-version-data-model.sql b/DSL/Resql/update-latest-version-data-model.sql new file mode 100644 index 00000000..031a22a1 --- /dev/null +++ b/DSL/Resql/update-latest-version-data-model.sql @@ -0,0 +1,3 @@ +UPDATE models_metadata +SET latest = false +WHERE model_group_key = :group_key \ No newline at end of file diff --git a/DSL/Resql/update-latest-version-dataset-group.sql b/DSL/Resql/update-latest-version-dataset-group.sql new file mode 100644 index 00000000..ce2ed3fb --- /dev/null +++ b/DSL/Resql/update-latest-version-dataset-group.sql @@ -0,0 +1,3 @@ +UPDATE dataset_group_metadata +SET latest = false +WHERE group_key = :group_key \ No newline at end of file diff --git a/DSL/Resql/update-major-version-dataset-group.sql b/DSL/Resql/update-major-version-dataset-group.sql new file mode 100644 index 00000000..f382816a --- /dev/null +++ b/DSL/Resql/update-major-version-dataset-group.sql @@ -0,0 +1,33 @@ +WITH update_latest AS ( + UPDATE dataset_group_metadata + SET latest = false + WHERE group_key = :group_key +), +update_specific AS ( + UPDATE dataset_group_metadata + SET + major_version = ( + SELECT COALESCE(MAX(major_version), 0) + 1 + FROM dataset_group_metadata + WHERE group_key = :group_key + ), + connected_models = NULL::JSONB, + preprocess_data_location = NULL, + raw_data_location = NULL, + num_samples = 0, + num_pages = 0, + last_trained_timestamp = NULL, + validation_errors = NULL::JSONB, + last_model_trained = NULL, + enable_allowed = false, + validation_status = 'unvalidated'::Validation_Status, + is_enabled = false, + minor_version = 0, + patch_version = 0, + latest = true, + last_updated_timestamp = :last_updated_timestamp::timestamp with time zone, + validation_criteria = :validation_criteria::JSONB, + class_hierarchy = :class_hierarchy::JSONB + WHERE id = :id +) +SELECT 1; \ No newline at end of file diff --git a/DSL/Resql/update-minor-dataset-group-validation-data.sql b/DSL/Resql/update-minor-dataset-group-validation-data.sql new file mode 100644 index 00000000..8c2f6a60 --- /dev/null +++ b/DSL/Resql/update-minor-dataset-group-validation-data.sql @@ -0,0 +1,6 @@ +UPDATE dataset_group_metadata +SET + validation_status = :validation_status::Validation_Status, + validation_errors = :validation_errors::jsonb, + last_updated_timestamp = CURRENT_TIMESTAMP +WHERE id = :id; diff --git a/DSL/Resql/update-minor-version-dataset-group.sql b/DSL/Resql/update-minor-version-dataset-group.sql new file mode 100644 index 00000000..3da45357 --- /dev/null +++ b/DSL/Resql/update-minor-version-dataset-group.sql @@ -0,0 +1,24 @@ +WITH update_latest AS ( + UPDATE dataset_group_metadata + SET latest = false + WHERE group_key = :group_key + RETURNING 1 +), +update_specific AS ( + UPDATE dataset_group_metadata + SET + minor_version = ( + SELECT COALESCE(MAX(minor_version), 0) + 1 + FROM dataset_group_metadata + WHERE group_key = :group_key + ), + enable_allowed = false, + validation_status = 'in-progress'::Validation_Status, + is_enabled = false, + patch_version = 0, + latest = true, + last_updated_timestamp = :last_updated_timestamp::timestamp with time zone + WHERE id = :id + RETURNING 1 +) +SELECT 1; diff --git a/DSL/Resql/update-models-deployment-platform.sql b/DSL/Resql/update-models-deployment-platform.sql new file mode 100644 index 00000000..149100fb --- /dev/null +++ b/DSL/Resql/update-models-deployment-platform.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + deployment_env = :updating_platform::Deployment_Env +WHERE deployment_env = :existing_platform::Deployment_Env \ No newline at end of file diff --git a/DSL/Resql/update-patch-dataset-group-validation-data.sql b/DSL/Resql/update-patch-dataset-group-validation-data.sql new file mode 100644 index 00000000..192db523 --- /dev/null +++ b/DSL/Resql/update-patch-dataset-group-validation-data.sql @@ -0,0 +1,5 @@ +UPDATE dataset_group_metadata +SET + validation_errors = :validation_errors::jsonb, + last_updated_timestamp = CURRENT_TIMESTAMP +WHERE id = :id; diff --git a/DSL/Resql/update-patch-version-dataset-group.sql b/DSL/Resql/update-patch-version-dataset-group.sql new file mode 100644 index 00000000..ca39976b --- /dev/null +++ b/DSL/Resql/update-patch-version-dataset-group.sql @@ -0,0 +1,15 @@ +WITH update_latest AS ( + UPDATE dataset_group_metadata + SET latest = false + WHERE group_key = :group_key + RETURNING 1 +), +update_specific AS ( + UPDATE dataset_group_metadata + SET + latest = true, + last_updated_timestamp = :last_updated_timestamp::timestamp with time zone + WHERE id = :id + RETURNING 1 +) +SELECT 1; diff --git a/DSL/Resql/update-patch-version-only-dataset-group.sql b/DSL/Resql/update-patch-version-only-dataset-group.sql new file mode 100644 index 00000000..06871cfa --- /dev/null +++ b/DSL/Resql/update-patch-version-only-dataset-group.sql @@ -0,0 +1,5 @@ +UPDATE dataset_group_metadata + SET + patch_version = patch_version + 1, + last_updated_timestamp = CURRENT_TIMESTAMP +WHERE id = :id \ No newline at end of file diff --git a/DSL/Resql/update-undeployed-previous-model.sql b/DSL/Resql/update-undeployed-previous-model.sql new file mode 100644 index 00000000..dec08e30 --- /dev/null +++ b/DSL/Resql/update-undeployed-previous-model.sql @@ -0,0 +1,4 @@ +UPDATE models_metadata +SET + deployment_env = 'undeployed' +WHERE id = :id \ No newline at end of file diff --git a/DSL/Resql/update-user.sql b/DSL/Resql/update-user.sql new file mode 100644 index 00000000..688e8df7 --- /dev/null +++ b/DSL/Resql/update-user.sql @@ -0,0 +1,16 @@ +INSERT INTO "user" (id_code, login, password_hash, first_name, last_name, display_name, status, created, csa_title, csa_email) +SELECT + :userIdCode, + login, + password_hash, + :firstName, + :lastName, + :displayName, + :status::user_status, + :created::timestamp with time zone, + :csaTitle, + :csaEmail +FROM "user" +WHERE id = ( + SELECT MAX(id) FROM "user" WHERE id_code = :userIdCode +); diff --git a/DSL/Ruuter.private/DSL/GET/.guard b/DSL/Ruuter.private/DSL/GET/.guard new file mode 100644 index 00000000..44e4f6ec --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: check-user-authority + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 401 + next: end diff --git a/DSL/Ruuter.private/DSL/GET/accounts/logout.yml b/DSL/Ruuter.private/DSL/GET/accounts/logout.yml new file mode 100644 index 00000000..d3065c5d --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/accounts/logout.yml @@ -0,0 +1,63 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'LOGOUT'" + method: post + accepts: json + returns: json + namespace: backoffice + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_user_info: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + next: check_user_info_response + +check_user_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: blacklistCustomJwt + next: return_bad_request + +blacklistCustomJwt: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-blacklist" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: blacklist_res + next: assign_cookie + +assign_cookie: + assign: + setCookie: + customJwtCookie: null + Domain: "[#DOMAIN]" + Max-Age: 0 + Secure: true + HttpOnly: true + SameSite: "Lax" + next: return_result + +return_result: + headers: + Set-Cookie: ${setCookie} + return: "Logged Out Successfully" + next: end + +return_bad_request: + return: "error: bad request" + status: 400 + next: end diff --git a/DSL/Ruuter.private/DSL/GET/accounts/user-role.yml b/DSL/Ruuter.private/DSL/GET/accounts/user-role.yml new file mode 100644 index 00000000..c46876c7 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/accounts/user-role.yml @@ -0,0 +1,53 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-ROLE'" + method: get + accepts: json + returns: json + namespace: backoffice + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_user_info: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: + "customJwtCookie" + result: res + next: check_user_info_response + +check_user_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assignIdCode + next: returnNotFound + +assignIdCode: + assign: + idCode: ${res.response.body.idCode} + next: getUserRole + +getUserRole: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-user-role" + body: + userIdCode: ${idCode} + result: roles_res + next: returnSuccess + +returnSuccess: + return: ${roles_res.response.body?.[0]?.authorities ?? []} + next: end + +returnNotFound: + return: "error: not found" + next: end diff --git a/DSL/Ruuter.private/DSL/GET/auth/jwt/extend.yml b/DSL/Ruuter.private/DSL/GET/auth/jwt/extend.yml new file mode 100644 index 00000000..5b3de56f --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/auth/jwt/extend.yml @@ -0,0 +1,41 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EXTEND'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +extend_cookie: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-extend" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: + "customJwtCookie" + result: cookie_result + next: assign_cookie + +assign_cookie: + assign: + setCookie: + customJwtCookie: ${cookie_result.response.body.token} + Domain: "[#DOMAIN]" + Secure: true + HttpOnly: true + SameSite: "Lax" + next: return_value + +return_value: + headers: + Set-Cookie: ${setCookie} + return: ${cookie_result.response.body.token} + next: end diff --git a/DSL/Ruuter.private/DSL/GET/auth/jwt/userinfo.yml b/DSL/Ruuter.private/DSL/GET/auth/jwt/userinfo.yml new file mode 100644 index 00000000..0e785ffa --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/auth/jwt/userinfo.yml @@ -0,0 +1,27 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USERINFO'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_user_info: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: + "customJwtCookie" + result: res + +return_result: + return: ${res.response.body} diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datamodel/create/options.yml b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/create/options.yml new file mode 100644 index 00000000..60d23b8e --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/create/options.yml @@ -0,0 +1,71 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'OPTIONS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_data_model_options: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-options" + result: res_options + next: check_status + +check_status: + switch: + - condition: ${200 <= res_options.response.statusCodeValue && res_options.response.statusCodeValue < 300} + next: get_dataset_group_data + next: return_bad_request + +get_dataset_group_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-validated-all-dataset-groups" + result: res_dataset + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_data_exist + next: return_bad_request + +check_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_dataset + next: assign_empty + +assign_dataset: + assign: + dataset_group: ${res_dataset.response.body} + next: assign_success_response + +assign_empty: + assign: + dataset_group: [] + next: assign_success_response + +assign_success_response: + assign: + format_res: { + baseModels: '${res_options.response.body[0].baseModels}', + deploymentPlatforms: '${res_options.response.body[0].deploymentPlatforms}', + maturityLabels: '${res_options.response.body[0].maturityLabels}', + datasetGroups: '${dataset_group}' + } + next: return_ok + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datamodel/metadata.yml b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/metadata.yml new file mode 100644 index 00000000..30780d05 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/metadata.yml @@ -0,0 +1,65 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'METADATA'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: modelId + type: number + description: "Parameter 'modelId'" + +extract_data: + assign: + model_id: ${Number(incoming.params.modelId)} + next: get_data_model_meta_data_by_id + +get_data_model_meta_data_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-metadata-by-id" + body: + id: ${model_id} + result: res_model + next: check_status + +check_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_model.response.body}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview.yml b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview.yml new file mode 100644 index 00000000..f807ebee --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview.yml @@ -0,0 +1,138 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'OVERVIEW'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: page + type: number + description: "Parameter 'page'" + - field: pageSize + type: number + description: "Parameter 'pageSize'" + - field: sortType + type: string + description: "Parameter 'sortType'" + - field: modelName + type: string + description: "Parameter 'modelName'" + - field: majorVersion + type: string + description: "Parameter 'majorVersion'" + - field: minorVersion + type: string + description: "Parameter 'minorVersion'" + - field: platform + type: string + description: "Parameter 'platform'" + - field: datasetGroup + type: number + description: "Parameter 'datasetGroup'" + - field: trainingStatus + type: string + description: "Parameter 'trainingStatus'" + - field: deploymentMaturity + type: string + description: "Parameter 'deploymentMaturity'" + - field: isProductionModel + type: boolean + description: "Parameter 'isProductionModel'" + - field: sortBy + type: string + description: "Parameter 'sortBy'" + +extract_data: + assign: + page: ${Number(incoming.params.page)} + page_size: ${Number(incoming.params.pageSize)} + sort_type: ${incoming.params.sortType} + model_name: ${incoming.params.modelName} + major_version: ${Number(incoming.params.majorVersion)} + minor_version: ${Number(incoming.params.minorVersion)} + platform: ${incoming.params.platform} + dataset_group: ${Number(incoming.params.datasetGroup)} + training_status: ${incoming.params.trainingStatus} + deployment_maturity: ${incoming.params.deploymentMaturity} + is_production_model: ${JSON.parse(incoming.params.isProductionModel)} + sort_by: ${incoming.params.sortBy} + next: check_sort_by + +check_sort_by: + switch: + - condition: ${sort_by === 'name' || sort_by === 'created_timestamp'} + next: check_production_model_status + next: set_sort_by + +set_sort_by: + assign: + sort_by: 'name' + next: check_production_model_status + +check_production_model_status: + switch: + - condition: ${is_production_model === true} + next: get_production_data_model_meta_data_overview + next: get_data_model_meta_data_overview + +get_production_data_model_meta_data_overview: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-production-data-model-metadata" + result: res_model + next: check_status + +get_data_model_meta_data_overview: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-paginated-data-model-metadata" + body: + page: ${page} + page_size: ${page_size} + sorting: ${sort_type} + model_name: ${model_name} + major_version: ${major_version} + minor_version: ${minor_version} + platform: ${platform} + dataset_group: ${dataset_group} + training_status: ${training_status} + deployment_maturity: ${deployment_maturity} + sort_type: ${sort_type} + sort_by: ${sort_by} + result: res_model + next: check_status + +check_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_model.response.body}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview/filters.yml b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview/filters.yml new file mode 100644 index 00000000..278e6bf2 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/overview/filters.yml @@ -0,0 +1,36 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'FILTERS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_data_model_filters: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-filters" + result: res_filters + next: check_status + +check_status: + switch: + - condition: ${200 <= res_filters.response.statusCodeValue && res_filters.response.statusCodeValue < 300} + next: assign_Json_format + next: return_bad_request + +assign_Json_format: + assign: + data: ${JSON.parse(res_filters.response.body[0].jsonBuildObject.value)} + next: return_ok + +return_ok: + status: 200 + return: ${data} + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datamodel/progress.yml b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/progress.yml new file mode 100644 index 00000000..728ed06b --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datamodel/progress.yml @@ -0,0 +1,48 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'PROGRESS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_data_mdoel_progress_sessions: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-progress-sessions" + result: res_sessions + next: check_status + +check_status: + switch: + - condition: ${200 <= res_sessions.response.statusCodeValue && res_sessions.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_sessions.response.body}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/data.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/data.yml new file mode 100644 index 00000000..f0097799 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/data.yml @@ -0,0 +1,109 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DATA'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: groupId + type: number + description: "Parameter 'groupId'" + - field: pageNum + type: number + description: "Parameter 'pageNum'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_data: + assign: + group_id: ${Number(incoming.params.groupId)} + page_num: ${Number(incoming.params.pageNum)} + cookie: ${incoming.headers.cookie} + next: get_dataset_group_fields_by_id + +get_dataset_group_fields_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-fields-by-id" + body: + id: ${group_id} + result: res_dataset + next: check_fields_status + +check_fields_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_fields_data_exist + next: assign_fail_response + +check_fields_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: get_dataset_group_data_by_id + next: assign_fail_response + +get_dataset_group_data_by_id: + call: http.get + args: + url: "[#CLASSIFIER_FILE_HANDLER]/datasetgroup/data/download/chunk" + headers: + type: json + cookie: ${cookie} + query: + dgId: ${group_id} + pageId: ${page_num} + result: res_data + next: check_data_status + +check_data_status: + switch: + - condition: ${200 <= res_data.response.statusCodeValue && res_data.response.statusCodeValue < 300} + next: assign_fields_response + next: assign_fail_response + +assign_fields_response: + assign: + val: ${res_dataset.response.body[0].validationCriteria === null ? [] :JSON.parse(res_dataset.response.body[0].validationCriteria.value)} + num_pages: ${res_dataset.response.body[0].numPages} + next: assign_formated_response + +assign_formated_response: + assign: + val: [{ + dgId: '${group_id}', + fields: '${val === [] ? [] :val.fields}', + numPages: '${num_pages}', + dataPayload: '${res_data.response.body}' + }] + next: assign_success_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${val}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/metadata.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/metadata.yml new file mode 100644 index 00000000..5f7f52b9 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/metadata.yml @@ -0,0 +1,85 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'METADATA'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: groupId + type: number + description: "Parameter 'groupId'" + +extract_data: + assign: + group_id: ${Number(incoming.params.groupId)} + next: get_dataset_meta_data_by_id + +get_dataset_meta_data_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-metadata-by-id" + body: + id: ${group_id} + result: res_dataset + next: check_status + +check_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_formated_response + next: assign_fail_response + +assign_formated_response: + assign: + val: [{ + dgId: '${res_dataset.response.body[0].id}', + name: '${res_dataset.response.body[0].groupName}', + majorVersion: '${res_dataset.response.body[0].majorVersion}', + minorVersion: '${res_dataset.response.body[0].minorVersion}', + patchVersion: '${res_dataset.response.body[0].patchVersion}', + latest: '${res_dataset.response.body[0].latest}', + isEnabled: '${res_dataset.response.body[0].isEnabled}', + numSamples: '${res_dataset.response.body[0].numSamples}', + enableAllowed: '${res_dataset.response.body[0].enableAllowed}', + validationStatus: '${res_dataset.response.body[0].validationStatus}', + validationErrors: '${res_dataset.response.body[0].validationErrors === null ? [] :JSON.parse(res_dataset.response.body[0].validationErrors.value)}', + linkedModels: '${res_dataset.response.body[0].connectedModels === null ? [] :JSON.parse(res_dataset.response.body[0].connectedModels.value)}', + validationCriteria: '${res_dataset.response.body[0].validationCriteria === null ? [] :JSON.parse(res_dataset.response.body[0].validationCriteria.value)}', + classHierarchy: '${res_dataset.response.body[0].classHierarchy === null ? [] :JSON.parse(res_dataset.response.body[0].classHierarchy.value)}' + }] + next: assign_success_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${val}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/page-count.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/page-count.yml new file mode 100644 index 00000000..d42266bd --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/group/page-count.yml @@ -0,0 +1,83 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'PAGE-COUNT'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: groupId + type: number + description: "Parameter 'groupId'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_data: + assign: + group_id: ${Number(incoming.params.groupId)} + cookie: ${incoming.headers.cookie} + next: get_dataset_group_fields_by_id + +get_dataset_group_fields_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-fields-by-id" + body: + id: ${group_id} + result: res_dataset + next: check_fields_status + +check_fields_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_fields_data_exist + next: assign_fail_response + +check_fields_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_fields_response + next: assign_fail_response + +assign_fields_response: + assign: + num_pages: ${res_dataset.response.body[0].numPages} + next: assign_formated_response + +assign_formated_response: + assign: + val: { + dgId: '${group_id}', + numPages: '${num_pages}' + } + next: assign_success_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${val}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview.yml new file mode 100644 index 00000000..a0324162 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview.yml @@ -0,0 +1,111 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'OVERVIEW'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: page + type: number + description: "Parameter 'page'" + - field: pageSize + type: number + description: "Parameter field 'pageSize'" + - field: sortType + type: string + description: "Parameter field 'sortType'" + - field: majorVersion + type: string + description: "Parameter field 'majorVersion'" + - field: minorVersion + type: string + description: "Parameter field 'minorVersion'" + - field: patchVersion + type: string + description: "Parameter field 'patchVersion'" + - field: groupName + type: string + description: "Parameter field 'groupName'" + - field: validationStatus + type: string + description: "Parameter field 'validationStatus'" + - field: sortBy + type: string + description: "Parameter field 'sortBy'" + +extract_data: + assign: + page: ${Number(incoming.params.page)} + page_size: ${Number(incoming.params.pageSize)} + major_version: ${Number(incoming.params.majorVersion)} + minor_version: ${Number(incoming.params.minorVersion)} + patch_version: ${Number(incoming.params.patchVersion)} + group_name: ${incoming.params.groupName} + validation_status: ${incoming.params.validationStatus} + sort_type: ${incoming.params.sortType} + sort_by: ${incoming.params.sortBy} + next: check_sort_by + +check_sort_by: + switch: + - condition: ${sort_by === 'name' || sort_by === 'created_timestamp'} + next: get_dataset_meta_data_overview + next: set_sort_by + +set_sort_by: + assign: + sort_by: 'name' + next: get_dataset_meta_data_overview + +get_dataset_meta_data_overview: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-paginated-dataset-group-metadata" + body: + page: ${page} + page_size: ${page_size} + sorting: ${sort_type} + major_version: ${major_version} + minor_version: ${minor_version} + patch_version: ${patch_version} + group_name: ${group_name} + validation_status: ${validation_status} + sort_by: ${sort_by} + result: res_dataset + next: check_status + +check_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_dataset.response.body}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview/filters.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview/filters.yml new file mode 100644 index 00000000..fec6a961 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/overview/filters.yml @@ -0,0 +1,36 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'FILTERS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_dataset_group_filters: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-filters" + result: res_filters + next: check_status + +check_status: + switch: + - condition: ${200 <= res_filters.response.statusCodeValue && res_filters.response.statusCodeValue < 300} + next: assign_Json_format + next: return_bad_request + +assign_Json_format: + assign: + data: ${JSON.parse(res_filters.response.body[0].jsonBuildObject.value)} + next: return_ok + +return_ok: + status: 200 + return: ${data} + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/progress.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/progress.yml new file mode 100644 index 00000000..8d8613c0 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/progress.yml @@ -0,0 +1,48 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'PROGRESS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_dataset_progress_sessions: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-progress-sessions" + result: res_sessions + next: check_status + +check_status: + switch: + - condition: ${200 <= res_sessions.response.statusCodeValue && res_sessions.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_sessions.response.body}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/schema.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/schema.yml new file mode 100644 index 00000000..01b6817a --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/schema.yml @@ -0,0 +1,87 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'SCHEMA'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: dgId + type: number + description: "Parameter 'dgId'" + +extract_data: + assign: + dg_id: ${Number(incoming.params.dgId)} + next: get_dataset_group_schema + +get_dataset_group_schema: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-schema" + body: + id: ${dg_id} + result: res_dataset + next: check_status + +check_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_Json_format + next: assign_not_found_response + +assign_Json_format: + assign: + validationCriteria: ${JSON.parse(res_dataset.response.body[0].validationCriteria.value)} + classHierarchy: ${JSON.parse(res_dataset.response.body[0].classHierarchy.value)} + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + validationCriteria: '${validationCriteria}', + classHierarchy: '${classHierarchy}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${ dg_id }', + operationSuccessful: false + } + next: return_bad_request + +assign_not_found_response: + assign: + format_res: { + dgId: '${ dg_id }', + operationSuccessful: false, + errorResponse: "dataset doesn't exist" + } + next: return_not_found + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_not_found: + status: 404 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/stop-words.yml b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/stop-words.yml new file mode 100644 index 00000000..ffa5fa45 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/datasetgroup/stop-words.yml @@ -0,0 +1,62 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STOP-WORDS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-stop-words" + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res.response.body.length>0 && res.response.body[0].stopWordsArray !== null} + next: assign_success_response + next: assign_empty_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + stopWords: '${res.response.body[0].stopWordsArray}' + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + operationSuccessful: true, + stopWords: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: true, + stopWords: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata-export.yml b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata-export.yml new file mode 100644 index 00000000..c5c0324d --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata-export.yml @@ -0,0 +1,78 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CORRECTED-METADATA-EXPORT'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: platform + type: string + description: "Parameter 'platform'" + - field: sortType + type: string + description: "Parameter 'sortType'" + +extract_data: + assign: + platform: ${incoming.params.platform} + sort_type: ${incoming.params.sortType} + next: get_corrected_input_metadata + +get_corrected_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/export-corrected-input-metadata" + body: + platform: ${platform} + sorting: ${sort_type} + result: res_corrected + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_corrected.response.statusCodeValue && res_corrected.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_corrected.response.body.length>0} + next: assign_success_response + next: assign_empty_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_corrected.response.body}' + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + operationSuccessful: true, + data: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata.yml b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata.yml new file mode 100644 index 00000000..5c929201 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-metadata.yml @@ -0,0 +1,88 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CORRECTED-METADATA'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: pageNum + type: number + description: "Parameter 'pageNum'" + - field: pageSize + type: number + description: "Parameter 'pageSize'" + - field: platform + type: string + description: "Parameter 'platform'" + - field: sortType + type: string + description: "Parameter 'sortType'" + +extract_data: + assign: + page_num: ${Number(incoming.params.pageNum)} + page_size: ${Number(incoming.params.pageSize)} + platform: ${incoming.params.platform} + sort_type: ${incoming.params.sortType} + next: get_corrected_input_metadata + +get_corrected_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-paginated-corrected-input-metadata" + body: + page: ${page_num} + page_size: ${page_size} + platform: ${platform} + sorting: ${sort_type} + result: res_corrected + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_corrected.response.statusCodeValue && res_corrected.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_corrected.response.body.length>0} + next: assign_success_response + next: assign_empty_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_corrected.response.body}' + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + operationSuccessful: true, + data: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-text.yml b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-text.yml new file mode 100644 index 00000000..34afd10a --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/inference/corrected-text.yml @@ -0,0 +1,73 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CORRECTED-TEXT'" + method: get + accepts: json + returns: json + namespace: classifier + allowlist: + params: + - field: inputId + type: string + description: "Parameter 'inputId'" + +extract_data: + assign: + input_id: ${incoming.params.inputId} + next: get_corrected_input_metadata_by_id + +get_corrected_input_metadata_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-corrected-input-metadata-by-id" + body: + input_id: ${input_id} + result: res_corrected + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_corrected.response.statusCodeValue && res_corrected.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_corrected.response.body.length>0} + next: assign_success_response + next: assign_empty_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_corrected.response.body}' + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + operationSuccessful: true, + data: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/integration/outlook/token.yml b/DSL/Ruuter.private/DSL/GET/classifier/integration/outlook/token.yml new file mode 100644 index 00000000..7fc61b63 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/integration/outlook/token.yml @@ -0,0 +1,64 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'TOKEN'" + method: get + accepts: json + returns: json + namespace: classifier + +get_refresh_token: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-token" + body: + platform: 'OUTLOOK' + result: res + next: set_refresh_token + +set_refresh_token: + assign: + refresh_token: ${res.response.body[0].token} + next: check_refresh_token + +check_refresh_token: + switch: + - condition: ${refresh_token !== null} + next: decrypt_token + next: return_not_found + +decrypt_token: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_decrypted_outlook_token" + headers: + type: json + body: + token: ${refresh_token} + result: token_data + next: get_access_token + +get_access_token: + call: http.post + args: + url: "https://login.microsoftonline.com/common/oauth2/v2.0/token" + contentType: formdata + headers: + type: json + body: + client_id: "[#OUTLOOK_CLIENT_ID]" + scope: "User.Read Mail.ReadWrite MailboxSettings.ReadWrite offline_access" + refresh_token: ${token_data.response.body.token.content} + grant_type: "refresh_token" + client_secret: "[#OUTLOOK_SECRET_KEY]" + result: res + next: return_result + +return_result: + return: ${res.response.body} + next: end + +return_not_found: + status: 404 + return: "refresh token not found" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/GET/classifier/integration/platform-status.yml b/DSL/Ruuter.private/DSL/GET/classifier/integration/platform-status.yml new file mode 100644 index 00000000..19a8d9a0 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/integration/platform-status.yml @@ -0,0 +1,40 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'PLATFORM-STATUS'" + method: get + accepts: json + returns: json + namespace: classifier + +get_platform_status: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-integration-status" + result: res + +check_platform_response_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: set_platform_status + next: error_fetch_data + +set_platform_status: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_platform_status" + headers: + type: json + body: + data: ${res.response.body} + result: mapped_data + next: return_result + +return_result: + return: ${mapped_data.response.body} + next: end + +error_fetch_data: + status: 400 + return: "Bad Request- Error fetching data" + next: end diff --git a/DSL/Ruuter.private/DSL/GET/classifier/testmodel/models.yml b/DSL/Ruuter.private/DSL/GET/classifier/testmodel/models.yml new file mode 100644 index 00000000..c6b0aec4 --- /dev/null +++ b/DSL/Ruuter.private/DSL/GET/classifier/testmodel/models.yml @@ -0,0 +1,61 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'models'" + method: get + accepts: json + returns: json + namespace: classifier + +get_test_data_models: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-test-data-models" + result: res_model + next: check_status + +check_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: assign_success_response + next: assign_empty_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + data: '${res_model.response.body}' + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + operationSuccessful: true, + data: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/.guard b/DSL/Ruuter.private/DSL/POST/.guard new file mode 100644 index 00000000..44e4f6ec --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: check-user-authority + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 401 + next: end diff --git a/DSL/Ruuter.private/DSL/POST/accounts/.guard b/DSL/Ruuter.private/DSL/POST/accounts/.guard new file mode 100644 index 00000000..ac8cec0d --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: check-user-authority-admin + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 401 + next: end diff --git a/DSL/Ruuter.private/DSL/POST/accounts/add.yml b/DSL/Ruuter.private/DSL/POST/accounts/add.yml new file mode 100644 index 00000000..06988af9 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/add.yml @@ -0,0 +1,89 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ADD'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: csaTitle + type: string + description: "Body field 'csaTitle'" + - field: csa_email + type: string + description: "Body field 'csa_email'" + - field: firstName + type: string + description: "Body field 'firstName'" + - field: lastName + type: string + description: "Body field 'lastName'" + - field: roles + type: array + description: "Body field 'roles'" + - field: userIdCode + type: string + description: "Body field 'userIdCode'" + +extractRequestData: + assign: + firstName: ${incoming.body.firstName} + lastName: ${incoming.body.lastName} + userIdCode: ${incoming.body.userIdCode} + displayName: ${incoming.body.firstName} + csaTitle: ${incoming.body.csaTitle} + csa_email: ${incoming.body.csa_email} + roles: ${incoming.body.roles} + +getUser: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-user" + body: + userIdCode: ${userIdCode} + result: res + next: checkIfUserExists + +checkIfUserExists: + switch: + - condition: "${res.response.body.length > 0}" + next: return_exists + next: addUser + +addUser: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-user" + body: + created: ${new Date().toISOString()} + status: "active" + firstName: ${firstName} + lastName: ${lastName} + userIdCode: ${userIdCode} + displayName: ${displayName} + csaTitle: ${csaTitle} + csaEmail: ${csa_email} + result: add_user_res + next: addRoles + +addRoles: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-user-role" + body: + userIdCode: ${userIdCode} + roles: ${roles} + created: ${new Date().toISOString()} + result: add_roles_res + next: return_result + +return_result: + return: "User added successfully" + next: end + +return_exists: + return: "error: user already exists" + status: 400 + next: end diff --git a/DSL/Ruuter.private/DSL/POST/accounts/delete.yml b/DSL/Ruuter.private/DSL/POST/accounts/delete.yml new file mode 100644 index 00000000..92885948 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/delete.yml @@ -0,0 +1,29 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: userIdCode + type: string + description: "Body field 'userIdCode'" + +extractRequestData: + assign: + userId: ${incoming.body.userIdCode} + +setConfigurationValue: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-user" + body: + userIdCode: ${userId} + created: ${new Date().toISOString()} + result: res + +return_result: + return: ${res.response.body} diff --git a/DSL/Ruuter.private/DSL/POST/accounts/edit.yml b/DSL/Ruuter.private/DSL/POST/accounts/edit.yml new file mode 100644 index 00000000..b88c2877 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/edit.yml @@ -0,0 +1,94 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EDIT'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: csaTitle + type: string + description: "Body field 'csaTitle'" + - field: csa_email + type: string + description: "Body field 'csa_email'" + - field: displayName + type: string + description: "Body field 'displayName'" + - field: firstName + type: string + description: "Body field 'firstName'" + - field: lastName + type: string + description: "Body field 'lastName'" + - field: roles + type: array + description: "Body field 'roles'" + - field: userIdCode + type: string + description: "Body field 'userIdCode'" + +extractRequestData: + assign: + firstName: ${incoming.body.firstName} + lastName: ${incoming.body.lastName} + userIdCode: ${incoming.body.userIdCode} + displayName: ${incoming.body.displayName} + csaTitle: ${incoming.body.csaTitle} + csa_email: ${incoming.body.csa_email} + roles: ${incoming.body.roles} + +getUser: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-user" + body: + userIdCode: ${userIdCode} + result: res + next: checkIfUserExists + +checkIfUserExists: + switch: + - condition: "${res.response.body.length > 0}" + next: updateUser + next: return_not_exists + +updateUser: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-user" + body: + created: ${new Date().toISOString()} + status: "active" + firstName: ${firstName} + lastName: ${lastName} + userIdCode: ${userIdCode} + displayName: ${displayName} + csaTitle: ${csaTitle} + csaEmail: ${csa_email} + result: add_user_res + next: updateRoles + +updateRoles: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-user-role" + body: + userIdCode: ${userIdCode} + roles: ${roles} + created: ${new Date().toISOString()} + result: add_roles_res + next: return_result + +return_result: + return: "User updated successfully" + status: 200 + next: end + +return_not_exists: + return: "error: user does not exist" + status: 400 + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/accounts/exists.yml b/DSL/Ruuter.private/DSL/POST/accounts/exists.yml new file mode 100644 index 00000000..931660e4 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/exists.yml @@ -0,0 +1,40 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EXISTS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: userIdCode + type: string + description: "Body field 'userIdCode'" + +extractRequestData: + assign: + userId: ${incoming.body.userIdCode} + +getUser: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-user" + body: + userIdCode: ${userId} + result: res + next: checkIfUserExists + +checkIfUserExists: + switch: + - condition: "${res.response.body.length > 0}" + next: return_exists + next: return_not_exists + +return_exists: + return: "true" + next: end + +return_not_exists: + return: "false" + next: end diff --git a/DSL/Ruuter.private/DSL/POST/accounts/users.yml b/DSL/Ruuter.private/DSL/POST/accounts/users.yml new file mode 100644 index 00000000..06278dec --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/accounts/users.yml @@ -0,0 +1,38 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USERS'" + method: post + accepts: json + returns: json + namespace: backoffice + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" + +getUsers: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-users-with-roles-by-role" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} + roles: + [ + "ROLE_ADMINISTRATOR", + "ROLE_MODEL_TRAINER" + ] + result: res + +return_result: + return: ${res.response.body} + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/create.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/create.yml new file mode 100644 index 00000000..91091b48 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/create.yml @@ -0,0 +1,228 @@ +declaration: + call: declare + version: 0.1 + description: "Insert model metadata" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelName + type: string + description: "Body field 'modelName'" + - field: dgId + type: number + description: "Body field 'dgId'" + - field: baseModels + type: array + description: "Body field 'baseModels'" + - field: deploymentPlatform + type: string + description: "Body field 'deploymentPlatform'" + - field: maturityLabel + type: string + description: "Body field 'maturityLabel'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + model_name: ${incoming.body.modelName} + dg_id: ${incoming.body.dgId} + base_models: ${incoming.body.baseModels} + deployment_platform: ${incoming.body.deploymentPlatform} + maturity_label: ${incoming.body.maturityLabel} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_name !== null && dg_id !== null && base_models !== null && deployment_platform !== null && maturity_label !== null} + next: get_dataset_group_data + next: return_incorrect_request + +get_dataset_group_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-basic-metadata-by-id" + body: + id: ${dg_id} + result: res_dataset_group + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res_dataset_group.response.statusCodeValue && res_dataset_group.response.statusCodeValue < 300} + next: check_dataset_group_exist + next: assign_fail_response + +check_dataset_group_exist: + switch: + - condition: ${res_dataset_group.response.body.length>0} + next: assign_dataset_group_data + next: return_dataset_group_not_found + +assign_dataset_group_data: + assign: + dataset_group_name: ${res_dataset_group.response.body[0].groupName} + dg_major_version: ${res_dataset_group.response.body[0].majorVersion} + dg_minor_version: ${res_dataset_group.response.body[0].minorVersion} + dg_patch_version: ${res_dataset_group.response.body[0].patchVersion} + next: get_epoch_date + +get_epoch_date: + assign: + current_epoch: ${Date.now()} + random_num: ${Math.floor(Math.random() * 100000)} + next: check_deployment_platform + +check_deployment_platform: + switch: + - condition: ${deployment_platform == 'jira' || deployment_platform == 'outlook'} + next: update_existing_models_deployment_platform + - condition: ${deployment_platform == 'testing' || deployment_platform == 'undeployed'} + next: create_model_metadata + next: assign_fail_response + +update_existing_models_deployment_platform: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-models-deployment-platform" + body: + updating_platform: 'undeployed' + existing_platform: ${deployment_platform} + result: res_update + next: check_deployment_platform__status + +check_deployment_platform__status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: create_model_metadata + next: assign_fail_response + +create_model_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-model-metadata" + body: + model_name: ${model_name} + model_group_key: "${random_num+ '_'+current_epoch}" + connected_dg_id: ${dg_id} + connected_dg_name: ${dataset_group_name} + connected_dg_major_version: ${dg_major_version} + connected_dg_minor_version: ${dg_minor_version} + connected_dg_patch_version: ${dg_patch_version} + base_models: ${base_models} + deployment_env: ${deployment_platform} + maturity_label: ${maturity_label} + training_status: not trained + major_version: 1 + minor_version: 0 + latest: true + created_timestamp: ${new Date(current_epoch).toISOString()} + result: res_model + next: check_status + +check_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: assign_model_id + next: assign_fail_response + +assign_model_id: + assign: + model_id: ${res_model.response.body[0].id} + next: get_model_data_by_id + +get_model_data_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-basic-metadata-by-id" + body: + id: ${model_id} + result: res_model_id + next: check_model_status + +check_model_status: + switch: + - condition: ${200 <= res_model_id.response.statusCodeValue && res_model_id.response.statusCodeValue < 300} + next: check_model_exist + next: assign_fail_response + +check_model_exist: + switch: + - condition: ${res_model_id.response.body.length>0} + next: update_dataset_group_connected_model + next: return_not_found + +update_dataset_group_connected_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-dataset-group-connected-models" + body: + id: ${dg_id} + connected_model: ${JSON.stringify(res_model_id.response.body[0])} + result: res_dataset + next: check_connected_model_updated_status + +check_connected_model_updated_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_model/model_trainer" + query: + cookie: ${incoming.headers.cookie.replace('customJwtCookie=','')} #Removing the customJwtCookie phrase from payload to to send cookie token only + modelId: ${model_id} + newModelId: ${model_id} + updateType: 'major' + prevDeploymentEnv: 'None' + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: true + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '', + operationSuccessful: false + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_dataset_group_not_found: + status: 404 + return: "Dataset Group Not Found" + next: end + +return_not_found: + status: 404 + return: "Model Not Found" + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/delete.yml new file mode 100644 index 00000000..f62ee792 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/delete.yml @@ -0,0 +1,86 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null} + next: get_data_model_by_id + next: return_incorrect_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/datamodel_processing/datamodel_deletion" + query: + cookie: ${incoming.headers.cookie} + modelId: ${model_id} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/update.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/update.yml new file mode 100644 index 00000000..7dc410cc --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/update.yml @@ -0,0 +1,134 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'UPDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + - field: replaceDeployment + type: boolean + description: "Body field 'replaceDeployment'" + - field: replaceDeploymentPlatform + type: string + description: "Body field 'replaceDeploymentPlatform'" + - field: bestBaseModel + type: string + description: "Body field 'bestBaseModel'" + - field: progressSessionId + type: number + description: "Body field 'progressSessionId'" + headers: + - field: cookie + type: string + description: "Cookie field" + + +extract_request: + assign: + model_id: ${incoming.body.modelId} + replace_deployment: ${incoming.body.replaceDeployment} + replace_deployment_platform: ${incoming.body.replaceDeploymentPlatform} + best_base_model: ${incoming.body.bestBaseModel} + progress_session_id: ${incoming.body.progressSessionId} + + next: get_token_info + + +# check_token_status: +# switch: +# - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} +# next: assign_access_token +# next: assign_fail_response + +# assign_access_token: +# assign: +# access_token: ${res.response.body.response.access_token} +# next: get_dataset_group_id_by_model_id + +# get_dataset_group_id_by_model_id: +# call: http.post +# args: +# url: "[#CLASSIFIER_RESQL]/get-data-model-dataset-group-by-id" +# body: +# id: ${model_id} +# result: res_model +# next: check_data_model_status + +# check_data_model_status: +# switch: +# - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} +# next: check_data_model_exist +# next: assign_fail_response + +# check_data_model_exist: +# switch: +# - condition: ${res_model.response.body.length>0} +# next: assign_dataset_group_id +# next: assign_fail_response + +# assign_dataset_group_id: +# assign: +# dg_id: ${res_model.response.body[0].connectedDgId} +# next: get_dataset_group_class_hierarchy + +# get_dataset_group_class_hierarchy: +# call: http.post +# args: +# url: "[#CLASSIFIER_RESQL]/get-dataset-group-class-hierarchy" +# body: +# id: ${dg_id} +# result: res_dataset +# next: check_dataset_group_status + +# check_dataset_group_status: +# switch: +# - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} +# next: check_dataset_group_exist +# next: assign_fail_response + +# check_dataset_group_exist: +# switch: +# - condition: ${res_dataset.response.body.length>0} +# next: assign_dataset_class_hierarchy +# next: assign_fail_response + +# assign_dataset_class_hierarchy: +# assign: +# class_hierarchy: ${JSON.parse(res_dataset.response.body[0].classHierarchy.value)} +# next: assign_success_response + +# assign_success_response: +# assign: +# format_res: { +# modelId: '${model_id}', +# outlook_access_token: '${access_token}', +# class_hierarchy: '${class_hierarchy}', +# operationSuccessful: true, +# } +# next: return_ok + +# assign_fail_response: +# assign: +# format_res: { +# modelId: '${model_id}', +# outlook_access_token: '', +# class_hierarchy: '', +# operationSuccessful: false, +# } +# next: return_bad_request + +# return_ok: +# status: 200 +# return: ${format_res} +# next: end + +# return_bad_request: +# status: 400 +# return: ${format_res} +# next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/validate.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/validate.yml new file mode 100644 index 00000000..01260256 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/deployment/outlook/validate.yml @@ -0,0 +1,120 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'VALIDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: string + description: "Body field 'modelId'" + +extract_request: + assign: + model_id: ${incoming.body.modelId} + next: get_token_info + +get_token_info: + call: http.get + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/outlook/token" + headers: + cookie: ${incoming.headers.cookie} + result: res + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_access_token + next: assign_fail_response + +assign_access_token: + assign: + access_token: ${res.response.body.response.access_token} + next: get_dataset_group_id_by_model_id + +get_dataset_group_id_by_model_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-dataset-group-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: assign_dataset_group_id + next: assign_fail_response + +assign_dataset_group_id: + assign: + dg_id: ${res_model.response.body[0].connectedDgId} + next: get_dataset_group_class_hierarchy + +get_dataset_group_class_hierarchy: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-class-hierarchy" + body: + id: ${dg_id} + result: res_dataset + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_dataset_group_exist + next: assign_fail_response + +check_dataset_group_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_dataset_class_hierarchy + next: assign_fail_response + +assign_dataset_class_hierarchy: + assign: + class_hierarchy: ${JSON.parse(res_dataset.response.body[0].classHierarchy.value)} + next: assign_success_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + outlook_access_token: '${access_token}', + class_hierarchy: '${class_hierarchy}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '${model_id}', + outlook_access_token: '', + class_hierarchy: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/metadata/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/metadata/delete.yml new file mode 100644 index 00000000..b19d3775 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/metadata/delete.yml @@ -0,0 +1,112 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null} + next: get_data_model_by_id + next: return_incorrect_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-dataset-group-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: assign_dataset_group_id + next: assign_fail_response + +assign_dataset_group_id: + assign: + dg_id: ${res_model.response.body[0].connectedDgId} + next: delete_data_model_by_id + +delete_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-data-model-by-id" + body: + id: ${model_id} + result: res_delete + next: check_dataset_delete_status + +check_dataset_delete_status: + switch: + - condition: ${200 <= res_delete.response.statusCodeValue && res_delete.response.statusCodeValue < 300} + next: delete_dataset_group_connected_models + next: assign_fail_response + +delete_dataset_group_connected_models: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-dataset-group-connected-models" + body: + id: ${dg_id} + model_id: ${model_id} + result: res_dataset_update + next: check_dataset_group_connected_models_status + +check_dataset_group_connected_models_status: + switch: + - condition: ${200 <= res_dataset_update.response.statusCodeValue && res_dataset_update.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/create.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/create.yml new file mode 100644 index 00000000..175f30ec --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/create.yml @@ -0,0 +1,188 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CREATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + - field: modelName + type: string + description: "Body field 'modelName'" + - field: majorVersion + type: number + description: "Body field 'majorVersion'" + - field: minorVersion + type: number + description: "Body field 'minorVersion'" + - field: latest + type: boolean + description: "Body field 'latest'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + model_name: ${incoming.body.modelName} + major_version: ${incoming.body.majorVersion} + minor_version: ${incoming.body.minorVersion} + latest: ${incoming.body.latest} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null || major_version !=null || minor_version !==null} + next: get_data_model_by_id + next: return_incorrect_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: get_random_string_cookie + next: return_model_not_found + +get_random_string_cookie: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_random_string" + headers: + type: json + result: res_cookie + next: check_random_string_status + +check_random_string_status: + switch: + - condition: ${200 <= res_cookie.response.statusCodeValue && res_cookie.response.statusCodeValue < 300} + next: assign_csrf_cookie + next: assign_fail_response + +assign_csrf_cookie: + assign: + csrf_cookie: ${'_csrf=' + res_cookie.response.body.randomHexString+';'} + next: create_data_model_progress_session + +create_data_model_progress_session: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-data-model-progress-session" + body: + model_id: ${model_id} + model_name: ${model_name} + major_version: ${major_version} + minor_version: ${minor_version} + latest: ${latest} + progress_percentage: 0 + training_progress_status: 'Initiating Training' + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_session_id + next: assign_fail_response + +assign_session_id: + assign: + session_id: ${res.response.body[0].id} + next: get_csrf_token + +get_csrf_token: + call: http.get + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/csrf-token" + headers: + cookie: ${csrf_cookie} + result: res_token + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res_token.response.statusCodeValue && res_token.response.statusCodeValue < 300} + next: assign_csrf_token + next: assign_fail_response + +assign_csrf_token: + assign: + token: ${res_token.response.body.csrfToken} + next: update_progress + +update_progress: + call: http.post + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/model/progress" + headers: + X-CSRF-Token: ${token} + cookie: ${csrf_cookie} + body: + sessionId: ${session_id} + progressPercentage: 0 + trainingStatus: 'Initiating Training' + trainingMessage: '' + result: res_node + next: check_node_server_status + +check_node_server_status: + switch: + - condition: ${200 <= res_node.response.statusCodeValue && res_node.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + sessionId: '${session_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + sessionId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_model_not_found: + status: 404 + return: "Model Not Found" + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/delete.yml new file mode 100644 index 00000000..36876174 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/delete.yml @@ -0,0 +1,46 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + +delete_dataset_progress_sessions: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-completed-data-model-progress-sessions" + result: res_sessions + next: check_status + +check_status: + switch: + - condition: ${200 <= res_sessions.response.statusCodeValue && res_sessions.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/update.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/update.yml new file mode 100644 index 00000000..64a9d4c3 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/progress/update.yml @@ -0,0 +1,151 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'UPDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: sessionId + type: number + description: "Body field 'sessionId'" + - field: trainingStatus + type: string + description: "Body field 'trainingStatus'" + - field: trainingMessage + type: string + description: "Body field 'trainingMessage'" + - field: progressPercentage + type: number + description: "Body field 'progressPercentage'" + - field: processComplete + type: boolean + description: "Body field 'processComplete'" + +extract_request_data: + assign: + session_id: ${incoming.body.sessionId} + training_status: ${incoming.body.trainingStatus} + training_message: ${incoming.body.trainingMessage} + progress_percentage: ${incoming.body.progressPercentage} + process_complete: ${incoming.body.processComplete} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${session_id !== null || !training_status || progress_percentage !==null} + next: get_random_string_cookie + next: return_incorrect_request + +get_random_string_cookie: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_random_string" + headers: + type: json + result: res_cookie + next: check_random_string_status + +check_random_string_status: + switch: + - condition: ${200 <= res_cookie.response.statusCodeValue && res_cookie.response.statusCodeValue < 300} + next: assign_csrf_cookie + next: assign_fail_response + +assign_csrf_cookie: + assign: + csrf_cookie: ${'_csrf=' + res_cookie.response.body.randomHexString+';'} + next: update_data_model_progress_session + +update_data_model_progress_session: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-model-progress-session" + body: + id: ${session_id} + training_progress_status: ${training_status} + progress_percentage: ${progress_percentage} + training_message: ${training_message} + process_complete: ${process_complete} + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: get_csrf_token + next: assign_fail_response + +get_csrf_token: + call: http.get + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/csrf-token" + headers: + cookie: ${csrf_cookie} + result: res_token + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res_token.response.statusCodeValue && res_token.response.statusCodeValue < 300} + next: assign_csrf_token + next: assign_fail_response + +assign_csrf_token: + assign: + token: ${res_token.response.body.csrfToken} + next: update_progress + +update_progress: + call: http.post + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/model/progress" + headers: + X-CSRF-Token: ${token} + cookie: ${csrf_cookie} + body: + sessionId: ${session_id} + progressPercentage: ${progress_percentage} + trainingStatus: ${training_status} + trainingMessage: ${training_message} + result: res_node + next: check_node_server_status + +check_node_server_status: + switch: + - condition: ${200 <= res_node.response.statusCodeValue && res_node.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + sessionId: '${session_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + sessionId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/re-train.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/re-train.yml new file mode 100644 index 00000000..484a6f35 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/re-train.yml @@ -0,0 +1,94 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'RE-TRAIN'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + cookie: ${incoming.headers.cookie} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null} + next: get_data_model_by_id + next: return_incorrect_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_model/model_trainer" + query: + cookie: ${cookie.replace('customJwtCookie=','')} #Removing the customJwtCookie phrase from payload to to send cookie token only + modelId: ${model_id} + newModelId: ${model_id} + updateType: 'retrain' + prevDeploymentEnv: 'None' + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update.yml new file mode 100644 index 00000000..ab6e2681 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update.yml @@ -0,0 +1,425 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'UPDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + - field: connectedDgId + type: number + description: "Body field 'connectedDgId'" + - field: deploymentEnv + type: string + description: "Body field 'deploymentEnv'" + - field: baseModels + type: array + description: "Body field 'baseModels'" + - field: maturityLabel + type: string + description: "Body field 'maturityLabel'" + - field: updateType + type: string + description: "Body field 'updateType'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + connected_dg_id: ${incoming.body.connectedDgId} + deployment_env: ${incoming.body.deploymentEnv} + base_models: ${incoming.body.baseModels} + maturity_label: ${incoming.body.maturityLabel} + update_type: ${incoming.body.updateType} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null && update_type !== null} + next: check_event_type + next: return_incorrect_request + +check_event_type: + switch: + - condition: ${update_type == 'major'} + next: get_dataset_group_data + - condition: ${update_type == 'minor'} + next: get_data_model_minor_data + - condition: ${update_type == 'maturityLabel'} + next: check_for_maturity_request_data + next: return_type_found + +get_dataset_group_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-basic-metadata-by-id" + body: + id: ${connected_dg_id} + result: res_dataset_group + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res_dataset_group.response.statusCodeValue && res_dataset_group.response.statusCodeValue < 300} + next: check_dataset_group_exist + next: assign_fail_response + +check_dataset_group_exist: + switch: + - condition: ${res_dataset_group.response.body.length>0} + next: assign_dataset_group_data + next: return_dataset_group_not_found + +assign_dataset_group_data: + assign: + dataset_group_name: ${res_dataset_group.response.body[0].groupName} + dg_major_version: ${res_dataset_group.response.body[0].majorVersion} + dg_minor_version: ${res_dataset_group.response.body[0].minorVersion} + dg_patch_version: ${res_dataset_group.response.body[0].patchVersion} + next: get_data_model_major_data + +get_data_model_major_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-major-data" + body: + id: ${model_id} + result: res + next: check_data_model_major_status + +check_data_model_major_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_model_major_exist + next: return_not_found + +check_data_model_major_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_major_data + next: return_not_found + +assign_major_data: + assign: + group_key: ${res.response.body[0].modelGroupKey} + deployment_env_prev: ${res.response.body[0].deploymentEnv} + base_models_prev: ${res.response.body[0].baseModels} + maturity_label_prev: ${res.response.body[0].maturityLabel} + next: update_latest_in_old_versions + +update_latest_in_old_versions: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-latest-version-data-model" + body: + group_key: ${group_key} + result: res + next: check_latest_status + +check_latest_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: update_undeployed_in_previous_model + next: assign_fail_response + +update_undeployed_in_previous_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-undeployed-previous-model" + body: + id: ${model_id} + result: res + next: check_undeployed_previous_status + +check_undeployed_previous_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: set_deployment_env + next: assign_fail_response + +set_deployment_env: + assign: + deployment_env_data_set: ${deployment_env == null ? deployment_env_prev :deployment_env } + next: check_deployment_platform + +check_deployment_platform: + switch: + - condition: ${deployment_env_data_set == 'jira' || deployment_env_data_set == 'outlook'} + next: update_existing_models_deployment_platform + - condition: ${deployment_env_data_set == 'testing' || deployment_env_data_set == 'undeployed' } + next: check_event_type_for_latest_version_again + next: return_deployment_type_found + +update_existing_models_deployment_platform: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-models-deployment-platform" + body: + updating_platform: 'undeployed' + existing_platform: ${deployment_env_data_set} + result: res_update + next: check_deployment_platform__status + +check_deployment_platform__status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: check_event_type_for_latest_version_again + next: assign_fail_response + +check_event_type_for_latest_version_again: + switch: + - condition: ${update_type == 'major'} + next: snapshot_major_data_model + - condition: ${update_type == 'minor'} + next: snapshot_minor_data_model + next: return_type_found + +snapshot_major_data_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/snapshot-major-data-model" + body: + id: ${model_id} + group_key: ${group_key} + connected_dg_id: ${connected_dg_id} + connected_dg_name: ${dataset_group_name} + connected_dg_major_version: ${dg_major_version} + connected_dg_minor_version: ${dg_minor_version} + connected_dg_patch_version: ${dg_patch_version} + deployment_env: ${deployment_env == null ? deployment_env_prev :deployment_env } + base_models: ${base_models == null ? base_models_prev :base_models} + maturity_label: ${maturity_label == null ? 'development' :maturity_label} + result: res + next: check_snapshot_status + +get_data_model_minor_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-minor-data" + body: + id: ${model_id} + result: res + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: return_not_found + +check_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_minor_day + next: return_not_found + +assign_minor_day: + assign: + group_key: ${res.response.body[0].modelGroupKey} + major_version: ${res.response.body[0].majorVersion} + deployment_env_prev: ${res.response.body[0].deploymentEnv} + base_models_prev: ${res.response.body[0].baseModels} + maturity_label_prev: ${res.response.body[0].maturityLabel} + next: check_minor_data_request_status + +check_minor_data_request_status: + switch: + - condition: ${deployment_env == null } + next: assign_previous_deployment_env + - condition: ${deployment_env !== null} + next: assign_new_deployment_env + next: assign_fail_response + +assign_new_deployment_env: + assign: + deployment_env_data: ${deployment_env} + next: update_latest_in_old_versions + +assign_previous_deployment_env: + assign: + deployment_env_data: ${deployment_env_prev} + next: update_latest_in_old_versions + +get_major_model_data_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-basic-metadata-by-id" + body: + id: ${new_model_id} + result: res_model_id + next: check_model_status + +check_model_status: + switch: + - condition: ${200 <= res_model_id.response.statusCodeValue && res_model_id.response.statusCodeValue < 300} + next: check_model_exist + next: assign_fail_response + +check_model_exist: + switch: + - condition: ${res_model_id.response.body.length>0} + next: update_dataset_group_connected_model + next: return_not_found + +update_dataset_group_connected_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-dataset-group-connected-models" + body: + id: ${connected_dg_id} + connected_model: ${JSON.stringify(res_model_id.response.body[0])} + result: res_dataset + next: check_connected_model_updated_status + +check_connected_model_updated_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: execute_cron_manager + next: assign_fail_response + +snapshot_minor_data_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/snapshot-minor-data-model" + body: + id: ${model_id} + group_key: ${group_key} + major_version: ${major_version} + deployment_env: ${deployment_env_data} + base_models: ${base_models == null ? base_models_prev :base_models} + maturity_label: ${maturity_label == null ? maturity_label_prev :maturity_label} + result: res + next: check_snapshot_status + +check_snapshot_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_updated_data_exist + next: assign_fail_response + +check_updated_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_new_model_id + next: return_not_found + +assign_new_model_id: + assign: + new_model_id: ${res.response.body[0].id} + next: check_event_type_again + +check_event_type_again: + switch: + - condition: ${update_type == 'major'} + next: get_major_model_data_by_id + - condition: ${update_type == 'minor'} + next: execute_cron_manager + next: return_type_found + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_model/model_trainer" + query: + cookie: ${incoming.headers.cookie.replace('customJwtCookie=','')} #Removing the customJwtCookie phrase from payload to to send cookie token only + modelId: ${model_id} + newModelId: ${new_model_id} + updateType: ${update_type} + previousDeploymentEnv: ${deployment_env_prev} + result: res + next: assign_success_response + +check_for_maturity_request_data: + switch: + - condition: ${model_id !== null && maturity_label !== null} + next: update_maturity_data_model + next: return_incorrect_request + +update_maturity_data_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-model-maturity-label" + body: + id: ${model_id} + maturity_label: ${maturity_label} + result: res_update + next: check_model_maturity_update_status + +check_model_maturity_update_status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: assign_maturity_success_response + next: assign_maturity_fail_response + +assign_maturity_success_response: + assign: + format_res: { + modelId: '${model_id}', + operationSuccessful: true, + } + next: return_ok + +assign_maturity_fail_response: + assign: + format_res: { + modelId: '', + operationSuccessful: false, + } + next: return_bad_request + +assign_success_response: + assign: + format_res: { + model_id: '${model_id}', + new_model_id: '${new_model_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + model_id: '${model_id}', + new_model_id: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_not_found: + status: 400 + return: "Data Group Not Found" + next: end + +return_type_found: + status: 400 + return: "Update Type Not Found" + next: end + +return_deployment_type_found: + status: 400 + return: "Deployment Platform Type Not Found" + next: end + +return_dataset_group_not_found: + status: 404 + return: "Dataset Group Not Found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/dataset-group.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/dataset-group.yml new file mode 100644 index 00000000..f151807b --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/dataset-group.yml @@ -0,0 +1,70 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DATASET-GROUP'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null} + next: update_dataset_group_models + next: return_incorrect_request + +update_dataset_group_models: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-model-dataset-group" + body: + id: ${dg_id} + result: res_update + next: check_dataset_model_update_status + +check_dataset_model_update_status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/training/status.yml b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/training/status.yml new file mode 100644 index 00000000..8ca5aba9 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datamodel/update/training/status.yml @@ -0,0 +1,114 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + - field: trainingStatus + type: string + description: "Body field 'trainingStatus'" + - field: modelS3Location + type: string + description: "Body field 'modelS3Location'" + - field: trainingResults + type: json + description: "Body field 'trainingResults'" + - field: inferenceRoutes + type: json + description: "Body field 'inferenceRoutes'" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + training_status: ${incoming.body.trainingStatus} + model_s3_location: ${incoming.body.modelS3Location} + training_results: ${incoming.body.trainingResults} + inference_routes: ${incoming.body.inferenceRoutes} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null && training_status !== null && model_s3_location !== null} + next: get_data_model_by_id + next: return_incorrect_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: update_training_data + next: assign_fail_response + +update_training_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-model-training-data" + body: + id: ${model_id} + training_status: ${training_status} + model_s3_location: ${model_s3_location} + last_trained_timestamp: ${new Date().toISOString()} + training_results: ${JSON.stringify(training_results)} + inference_routes: ${JSON.stringify(inference_routes)} + result: res_update + next: check_data_model_update_status + +check_data_model_update_status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + trainingStatus: '${training_status}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '${model_id}', + trainingStatus: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/create.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/create.yml new file mode 100644 index 00000000..953bc7f8 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/create.yml @@ -0,0 +1,104 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CREATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: groupName + type: string + description: "Body field 'groupName'" + - field: validationCriteria + type: json + description: "Body field 'validationCriteria'" + - field: classHierarchy + type: json + description: "Body field 'classHierarchy'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + group_name: ${incoming.body.groupName} + validation_criteria: ${incoming.body.validationCriteria} + class_hierarchy: ${incoming.body.classHierarchy} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${group_name !== null || validation_criteria !=null || class_hierarchy !==null } + next: get_epoch_date + next: return_incorrect_request + +get_epoch_date: + assign: + current_epoch: ${Date.now()} + random_num: ${Math.floor(Math.random() * 100000)} + next: create_dataset_group_metadata + +create_dataset_group_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-dataset-group-metadata" + body: + group_name: ${group_name} + group_key: "${random_num+ '_'+current_epoch}" + major_version: 1 + minor_version: 0 + patch_version: 0 + latest: true + is_enabled: false + enable_allowed: false + created_timestamp: ${new Date(current_epoch).toISOString()} + last_updated_timestamp: ${new Date(current_epoch).toISOString()} + validation_status: unvalidated + processed_data_available: false + raw_data_available: false + num_samples: 0 + num_pages: 0 + validation_criteria: ${JSON.stringify(validation_criteria)} + class_hierarchy: ${JSON.stringify(class_hierarchy)} + result: res_dataset + next: check_status + +check_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${res_dataset.response.body[0].id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete.yml new file mode 100644 index 00000000..0e3d4333 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete.yml @@ -0,0 +1,86 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null} + next: get_dataset_group_by_id + next: return_incorrect_request + +get_dataset_group_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-by-id" + body: + id: ${dg_id} + result: res_dataset + next: check_dataset_fields_status + +check_dataset_fields_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_dataset_data_exist + next: assign_fail_response + +check_dataset_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/dataset_deletion/dataset_deletion" + query: + cookie: ${incoming.headers.cookie} + dgId: ${dg_id} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete/stop-words.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete/stop-words.yml new file mode 100644 index 00000000..2dfb8ac8 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/delete/stop-words.yml @@ -0,0 +1,124 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STOP-WORDS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: stopWords + type: array + description: "Body field 'stopWords'" + +extract_request_data: + assign: + stop_words: ${incoming.body.stopWords} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${stop_words !== null || stop_words.length > 0 } + next: get_stop_words + next: return_incorrect_request + +get_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-stop-words" + result: res_stop_words + next: check_stop-words_status + +check_stop-words_status: + switch: + - condition: ${200 <= res_stop_words.response.statusCodeValue && res_stop_words.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_stop_words.response.body.length>0 && res_stop_words.response.body[0].stopWordsArray !== null} + next: get_not_existing_stop_words + next: assign_fail_response + +get_not_existing_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_stop_words_not_existing" + headers: + type: json + body: + inputArray: ${stop_words} + existingArray: ${res_stop_words.response.body[0].stopWordsArray} + result: res_not_existing + next: check_not_existing_status + +check_not_existing_status: + switch: + - condition: ${200 <= res_not_existing.response.statusCodeValue && res_not_existing.response.statusCodeValue < 300} + next: check_for_not_existing + next: return_not_found + +check_for_not_existing: + switch: + - condition: ${res_not_existing.response.body.notExisting.length > 0 } + next: assign_not_existing_response + next: delete_stop_words + +delete_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-stop-words" + body: + stop_words: ${stop_words} + result: res_stop_words + next: check_delete_status + +check_delete_status: + switch: + - condition: ${200 <= res_stop_words.response.statusCodeValue && res_stop_words.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + nonexistent: false, + nonexistentItems: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + nonexistent: false, + nonexistentItems: '${[]}' + } + next: return_bad_request + +assign_not_existing_response: + assign: + format_res: { + operationSuccessful: false, + nonexistent: true, + nonexistentItems: '${res_not_existing.response.body.notExisting}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/metadata/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/metadata/delete.yml new file mode 100644 index 00000000..8122dd3a --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/metadata/delete.yml @@ -0,0 +1,91 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null} + next: get_dataset_group_by_id + next: return_incorrect_request + +get_dataset_group_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-by-id" + body: + id: ${dg_id} + result: res_dataset + next: check_dataset_fields_status + +check_dataset_fields_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_dataset_data_exist + next: assign_fail_response + +check_dataset_data_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: delete_dataset_group + next: assign_fail_response + +delete_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-dataset-group" + body: + id: ${dg_id} + result: res_delete + next: check_dataset_delete_status + +check_dataset_delete_status: + switch: + - condition: ${200 <= res_delete.response.statusCodeValue && res_delete.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/create.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/create.yml new file mode 100644 index 00000000..e8f53322 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/create.yml @@ -0,0 +1,167 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CREATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: groupName + type: number + description: "Body field 'groupName'" + - field: majorVersion + type: number + description: "Body field 'majorVersion'" + - field: minorVersion + type: number + description: "Body field 'minorVersion'" + - field: patchVersion + type: number + description: "Body field 'patchVersion'" + - field: latest + type: boolean + description: "Body field 'latest'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + group_name: ${incoming.body.groupName} + major_version: ${incoming.body.majorVersion} + minor_version: ${incoming.body.minorVersion} + patch_version: ${incoming.body.patchVersion} + latest: ${incoming.body.latest} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null || major_version !=null || minor_version !==null || patch_version !==null } + next: get_random_string_cookie + next: return_incorrect_request + +get_random_string_cookie: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_random_string" + headers: + type: json + result: res_cookie + next: check_random_string_status + +check_random_string_status: + switch: + - condition: ${200 <= res_cookie.response.statusCodeValue && res_cookie.response.statusCodeValue < 300} + next: assign_csrf_cookie + next: assign_fail_response + +assign_csrf_cookie: + assign: + csrf_cookie: ${'_csrf=' + res_cookie.response.body.randomHexString+';'} + next: create_dataset_progress_session + +create_dataset_progress_session: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-dataset-progress-session" + body: + dg_id: ${dg_id} + group_name: ${group_name} + major_version: ${major_version} + minor_version: ${minor_version} + patch_version: ${patch_version} + latest: ${latest} + progressPercentage: 0 + validation_status: 'Initiating Validation' + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_session_id + next: assign_fail_response + +assign_session_id: + assign: + session_id: ${res.response.body[0].id} + next: get_csrf_token + +get_csrf_token: + call: http.get + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/csrf-token" + headers: + cookie: ${csrf_cookie} + result: res_token + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res_token.response.statusCodeValue && res_token.response.statusCodeValue < 300} + next: assign_csrf_token + next: assign_fail_response + +assign_csrf_token: + assign: + token: ${res_token.response.body.csrfToken} + next: update_progress + +update_progress: + call: http.post + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/dataset/progress" + headers: + X-CSRF-Token: ${token} + cookie: ${csrf_cookie} + body: + sessionId: ${session_id} + progressPercentage: 0 + validationStatus: 'Initiating Validation' + validationMessage: '' + result: res_node + next: check_node_server_status + +check_node_server_status: + switch: + - condition: ${200 <= res_node.response.statusCodeValue && res_node.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + sessionId: '${session_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + sessionId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/delete.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/delete.yml new file mode 100644 index 00000000..b29f34c6 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/delete.yml @@ -0,0 +1,46 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: classifier + +delete_dataset_progress_sessions: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/delete-completed-dataset-progress-sessions" + result: res_sessions + next: check_status + +check_status: + switch: + - condition: ${200 <= res_sessions.response.statusCodeValue && res_sessions.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/update.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/update.yml new file mode 100644 index 00000000..45fbc13a --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/progress/update.yml @@ -0,0 +1,151 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'UPDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: sessionId + type: number + description: "Body field 'sessionId'" + - field: validationStatus + type: string + description: "Body field 'validationStatus'" + - field: validationMessage + type: string + description: "Body field 'validationMessage'" + - field: progressPercentage + type: number + description: "Body field 'progressPercentage'" + - field: processComplete + type: boolean + description: "Body field 'processComplete'" + +extract_request_data: + assign: + session_id: ${incoming.body.sessionId} + validation_status: ${incoming.body.validationStatus} + validation_message: ${incoming.body.validationMessage} + progress_percentage: ${incoming.body.progressPercentage} + process_complete: ${incoming.body.processComplete} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${session_id !== null || !validation_status || progress_percentage !==null} + next: get_random_string_cookie + next: return_incorrect_request + +get_random_string_cookie: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_random_string" + headers: + type: json + result: res_cookie + next: check_random_string_status + +check_random_string_status: + switch: + - condition: ${200 <= res_cookie.response.statusCodeValue && res_cookie.response.statusCodeValue < 300} + next: assign_csrf_cookie + next: assign_fail_response + +assign_csrf_cookie: + assign: + csrf_cookie: ${'_csrf=' + res_cookie.response.body.randomHexString+';'} + next: update_dataset_progress_session + +update_dataset_progress_session: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-dataset-progress-session" + body: + id: ${session_id} + validation_status: ${validation_status} + progress_percentage: ${progress_percentage} + validation_message: ${validation_message} + process_complete: ${process_complete} + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: get_csrf_token + next: assign_fail_response + +get_csrf_token: + call: http.get + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/csrf-token" + headers: + cookie: ${csrf_cookie} + result: res_token + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res_token.response.statusCodeValue && res_token.response.statusCodeValue < 300} + next: assign_csrf_token + next: assign_fail_response + +assign_csrf_token: + assign: + token: ${res_token.response.body.csrfToken} + next: update_progress + +update_progress: + call: http.post + args: + url: "[#CLASSIFIER_NOTIFICATIONS]/dataset/progress" + headers: + X-CSRF-Token: ${token} + cookie: ${csrf_cookie} + body: + sessionId: ${session_id} + progressPercentage: ${progress_percentage} + validationStatus: ${validation_status} + validationMessage: ${validation_message} + result: res_node + next: check_node_server_status + +check_node_server_status: + switch: + - condition: ${200 <= res_node.response.statusCodeValue && res_node.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + sessionId: '${session_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + sessionId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/major.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/major.yml new file mode 100644 index 00000000..11e81c49 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/major.yml @@ -0,0 +1,136 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'MAJOR'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: validationCriteria + type: json + description: "Body field 'validationCriteria'" + - field: classHierarchy + type: json + description: "Body field 'classHierarchy'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + validation_criteria: ${incoming.body.validationCriteria} + class_hierarchy: ${incoming.body.classHierarchy} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${validation_criteria !== null && class_hierarchy !=null} + next: get_dataset_group + next: return_incorrect_request + +get_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-key-by-id" + body: + id: ${dg_id} + result: res + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: return_not_found + +check_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_group_key + next: return_not_found + +assign_group_key: + assign: + group_key: ${res.response.body[0].groupKey} + next: update_latest_in_old_version + +update_latest_in_old_version: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-latest-version-dataset-group" + body: + group_key: ${group_key} + result: res + next: check_latest_status + +check_latest_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: snapshot_dataset_group + next: assign_fail_response + +snapshot_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/snapshot-major-dataset-group" + body: + id: ${dg_id} + group_key: ${group_key} + validation_criteria: ${JSON.stringify(validation_criteria)} + class_hierarchy: ${JSON.stringify(class_hierarchy)} + result: res + next: check_snapshot_status + +check_snapshot_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_updated_data_exist + next: assign_fail_response + +check_updated_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_new_dg_id + next: return_not_found + +assign_new_dg_id: + assign: + new_dg_id: ${res.response.body[0].id} + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + newDgId: '${new_dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + newDgId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/minor.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/minor.yml new file mode 100644 index 00000000..879251e3 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/minor.yml @@ -0,0 +1,146 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'MINOR'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: s3FilePath + type: string + description: "Body field 's3FilePath'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + s3_file_path: ${incoming.body.s3FilePath} + next: get_dataset_group + +get_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-key-by-id" + body: + id: ${dg_id} + result: res + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: return_not_found + +check_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_group_key + next: return_not_found + +assign_group_key: + assign: + group_key: ${res.response.body[0].groupKey} + major_version: ${res.response.body[0].majorVersion} + next: update_latest_in_old_version + +update_latest_in_old_version: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-latest-version-dataset-group" + body: + group_key: ${group_key} + result: res + next: check_latest_status + +check_latest_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: snapshot_dataset_group + next: assign_fail_response + +snapshot_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/snapshot-minor-dataset-group" + body: + id: ${dg_id} + group_key: ${group_key} + major_version: ${major_version} + result: res + next: check_snapshot_status + +check_snapshot_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_updated_data_exist + next: assign_fail_response + +check_updated_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_new_dg_id + next: return_not_found + +assign_new_dg_id: + assign: + new_dg_id: ${res.response.body[0].id} + next: execute_cron_manager + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_validation/data_validation" + query: + cookie: ${incoming.headers.cookie} + dgId: ${dg_id} + newDgId: ${new_dg_id} + updateType: 'minor' + savedFilePath: ${s3_file_path} + patchPayload: ${[]} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + newDgId: '${new_dg_id}', + validationStatus: 'in-progress', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + newDgId: '', + validationStatus: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_not_found: + status: 400 + return: "Data Group Not Found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/patch.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/patch.yml new file mode 100644 index 00000000..12bc7a04 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/patch.yml @@ -0,0 +1,118 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'PATCH'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: updateDataPayload + type: json + description: "Body field 'updateDataPayload'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + group_key: ${incoming.body.groupKey} + update_data_payload: ${incoming.body.updateDataPayload} + next: get_dataset_group + +get_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-key-by-id" + body: + id: ${dg_id} + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: return_not_found + +check_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: assign_group_key + next: return_not_found + +assign_group_key: + assign: + group_key: ${res.response.body[0].groupKey} + major_version: ${res.response.body[0].majorVersion} + minor_version: ${res.response.body[0].minorVersion} + next: update_old_dataset_group + +update_old_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-patch-version-dataset-group" + body: + id: ${dg_id} + last_updated_timestamp: ${new Date().toISOString()} + group_key: ${group_key} + result: res + next: check_old_dataset_status + +check_old_dataset_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_validation/data_validation" + query: + cookie: ${incoming.headers.cookie} + dgId: ${dg_id} + newDgId: 0 + updateType: 'patch' + savedFilePath: 'None' + patchPayload: ${encodeURIComponent(JSON.stringify(update_data_payload))} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_not_found: + status: 400 + return: "Data Group Not Found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/preprocess/status.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/preprocess/status.yml new file mode 100644 index 00000000..2c59d606 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/preprocess/status.yml @@ -0,0 +1,95 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: processedDataAvailable + type: boolean + description: "Body field 'processedDataAvailable'" + - field: rawDataAvailable + type: boolean + description: "Body field 'rawDataAvailable'" + - field: preprocessDataLocation + type: string + description: "Body field 'preprocessDataLocation'" + - field: rawDataLocation + type: string + description: "Body field 'rawDataLocation'" + - field: enableAllowed + type: boolean + description: "Body field 'enableAllowed'" + - field: numSamples + type: integer + description: "Body field 'numSamples'" + - field: numPages + type: integer + description: "Body field 'numPages'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + processed_data_available: ${incoming.body.processedDataAvailable} + raw_data_available: ${incoming.body.rawDataAvailable} + preprocess_data_location: ${incoming.body.preprocessDataLocation} + raw_data_location: ${incoming.body.rawDataLocation} + enable_allowed: ${incoming.body.enableAllowed} + num_samples: ${incoming.body.numSamples} + num_pages: ${incoming.body.numPages} + next: update_dataset_group_preprocess_status + +update_dataset_group_preprocess_status: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-dataset-group-preprocess-status" + body: + id: ${dg_id} + processed_data_available: ${processed_data_available} + raw_data_available: ${raw_data_available} + preprocess_data_location: ${preprocess_data_location} + raw_data_location: ${raw_data_location} + enable_allowed: ${enable_allowed} + last_updated_timestamp: ${new Date().toISOString()} + num_samples: ${num_samples} + num_pages: ${num_pages} + result: res + next: check_preprocess_status + +check_preprocess_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/status.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/status.yml new file mode 100644 index 00000000..f487e49d --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/status.yml @@ -0,0 +1,141 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: operationType + type: string + description: "Body field 'operationType'" + +extract_request_data: + assign: + id: ${incoming.body.dgId} + operation_type: ${incoming.body.operationType} + next: get_dataset_group + +get_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-allowed-status-by-id" + body: + id: ${id} + result: res + next: check_status + +check_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_data_exist + next: assign_not_found_response + +check_data_exist: + switch: + - condition: ${res.response.body.length>0} + next: check_operation_type + next: assign_not_found_response + +check_operation_type: + switch: + - condition: ${operation_type === 'enable'} + next: validate_dataset_group + - condition: ${operation_type === 'disable'} + next: disable_dataset_group + next: operation_not_support + +validate_dataset_group: + switch: + - condition: ${res.response.body[0].enableAllowed === true} + next: enable_dataset_group + next: assign_not_allowed_response + +enable_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/enable-dataset-group" + body: + id: ${id} + result: res + next: check_enable_disable_status + +disable_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/disable-dataset-group" + body: + id: ${id} + result: res + next: check_enable_disable_status + +check_enable_disable_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_success_response + next: assign_status_update_error_response + +assign_success_response: + assign: + format_res: { + dgId: '${id}', + operationType: '${operation_type}', + operationSuccessful: true, + errorResponse: "" + } + next: return_ok + +assign_not_allowed_response: + assign: + format_res: { + dgId: '${id}', + operationType: '${operation_type}', + operationSuccessful: false, + errorResponse: "This dataset is not ready to be enabled" + } + next: return_not_allowed + +assign_not_found_response: + assign: + format_res: { + dgId: '${id}', + operationType: '${operation_type}', + operationSuccessful: false, + errorResponse: "dataset doesn't exist" + } + next: return_not_found + +assign_status_update_error_response: + assign: + format_res: { + dgId: '${id}', + operationType: '${operation_type}', + operationSuccessful: false, + errorResponse: "Dataset group status not updated" + } + next: return_not_found + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_not_found: + status: 400 + return: ${format_res} + next: end + +return_not_allowed: + status: 400 + return: ${format_res} + next: end + +operation_not_support: + status: 400 + return: "Bad Request-Operation not support" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/stop-words.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/stop-words.yml new file mode 100644 index 00000000..ded16e0b --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/stop-words.yml @@ -0,0 +1,124 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STOP-WORDS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: stopWords + type: array + description: "Body field 'stopWords'" + +extract_request_data: + assign: + stop_words: ${incoming.body.stopWords} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${stop_words !== null || stop_words.length > 0 } + next: get_stop_words + next: return_incorrect_request + +get_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-stop-words" + result: res_stop_words + next: check_stop-words_status + +check_stop-words_status: + switch: + - condition: ${200 <= res_stop_words.response.statusCodeValue && res_stop_words.response.statusCodeValue < 300} + next: check_data_exist + next: assign_fail_response + +check_data_exist: + switch: + - condition: ${res_stop_words.response.body.length>0 && res_stop_words.response.body[0].stopWordsArray !== null} + next: get_duplicate_stop_words + next: insert_stop_words + +get_duplicate_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_stop_words_duplicates" + headers: + type: json + body: + inputArray: ${stop_words} + existingArray: ${res_stop_words.response.body[0].stopWordsArray} + result: res_duplicates + next: check_duplicates_status + +check_duplicates_status: + switch: + - condition: ${200 <= res_duplicates.response.statusCodeValue && res_duplicates.response.statusCodeValue < 300} + next: check_for_duplicates + next: return_not_found + +check_for_duplicates: + switch: + - condition: ${res_duplicates.response.body.duplicates.length > 0 } + next: assign_duplicate_response + next: insert_stop_words + +insert_stop_words: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-stop-words" + body: + stop_words: ${stop_words} + result: res_stop_words + next: check_insert_status + +check_insert_status: + switch: + - condition: ${200 <= res_stop_words.response.statusCodeValue && res_stop_words.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + duplicate: false, + duplicateItems: '${[]}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + duplicate: false, + duplicateItems: '${[]}' + } + next: return_bad_request + +assign_duplicate_response: + assign: + format_res: { + operationSuccessful: false, + duplicate: true, + duplicateItems: '${res_duplicates.response.body.duplicates}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/last-train-model.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/last-train-model.yml new file mode 100644 index 00000000..5f9362c2 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/last-train-model.yml @@ -0,0 +1,112 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: lastTrainModel + type: string + description: "Body field 'lastTrainModel'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + last_train_model: ${incoming.body.lastTrainModel} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null && last_train_model !== null} + next: get_dataset_group_by_id + next: return_incorrect_request + +get_dataset_group_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-by-id" + body: + id: ${dg_id} + result: res_dataset + next: check_dataset_status + +check_dataset_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_dataset_exist + next: assign_fail_response + +check_dataset_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: update_last_train_model + next: assign_fail_response + +update_last_train_model: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-last-train-model-in-dataset-group" + body: + id: ${dg_id} + last_model_trained: ${last_train_model} + result: res_update + next: check_data_model_update_status + +check_data_model_update_status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + lastTrainModel: '${last_train_model}', + operationSuccessful: true, + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + dgId: '${dg_id}', + lastTrainModel: '${last_train_model}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + lastTrainModel: '${last_train_model}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_not_found: + status: 404 + return: "Data Group Not Found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/status.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/status.yml new file mode 100644 index 00000000..fb68cc32 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/training/status.yml @@ -0,0 +1,138 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${dg_id !== null} + next: get_dataset_group + next: return_incorrect_request + +get_dataset_group: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-connected-models" + body: + id: ${dg_id} + result: res + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_dataset_group_exist + next: assign_fail_response + +check_dataset_group_exist: + switch: + - condition: ${res.response.body.length>0} + next: check_connected_model_exist + next: return_not_found + +check_connected_model_exist: + switch: + - condition: ${res.response.body[0].connectedModels !== null} + next: get_connected_dg_group_model_ids + next: assign_empty_response + +get_connected_dg_group_model_ids: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_connected_models_ids" + headers: + type: json + body: + data: ${JSON.parse(res.response.body[0].connectedModels.value)} + result: res_models + next: check_connected_dg_group_model_ids_status + +check_connected_dg_group_model_ids_status: + switch: + - condition: ${200 <= res_models.response.statusCodeValue && res_models.response.statusCodeValue < 300} + next: assign_model_ids + next: assign_fail_response + +assign_model_ids: + assign: + model_ids: ${res_models.response.body} + next: update_training_status + +update_training_status: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-models-re-training-needed" + body: + ids: ${model_ids} + result: res_update + next: check_data_model_update_status + +check_data_model_update_status: + switch: + - condition: ${200 <= res_update.response.statusCodeValue && res_update.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + modelIds: '${model_ids}', + trainingStatus: 'retraining needed', + operationSuccessful: true, + } + next: return_ok + +assign_empty_response: + assign: + format_res: { + dgId: '${dg_id}', + modelIds: '', + trainingStatus: 'retraining needed', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + modelIds: '', + trainingStatus: 'retraining needed', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_not_found: + status: 404 + return: "Data Group Not Found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/validation/status.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/validation/status.yml new file mode 100644 index 00000000..ecf7af86 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetgroup/update/validation/status.yml @@ -0,0 +1,189 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + - field: newDgId + type: number + description: "Body field 'newDgId'" + - field: updateType + type: string + description: "Body field 'updateType'" + - field: patchPayload + type: json + description: "Body field 'patchPayload'" + - field: savedFilePath + type: string + description: "Body field 'savedFilePath'" + - field: validationStatus + type: string + description: "Body field 'validationStatus'" + - field: validationErrors + type: array + description: "Body field 'validationErrors'" + - field: sessionId + type: number + description: "Body field 'sessionId'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + new_dg_id: ${incoming.body.newDgId} + update_type: ${incoming.body.updateType} + patch_payload: ${incoming.body.patchPayload} + save_file_path: ${incoming.body.savedFilePath} + validation_status: ${incoming.body.validationStatus} + validation_errors: ${incoming.body.validationErrors} + session_id: ${incoming.body.sessionId} + next: check_request_type + +check_request_type: + switch: + - condition: ${update_type == 'minor'} + next: assign_minor_type + - condition: ${update_type == 'patch'} + next: assign_patch_type + next: update_type_not_found + +assign_patch_type: + assign: + active_dg_id: ${incoming.body.dgId} + next: update_patch_dataset_group_validation + +assign_minor_type: + assign: + active_dg_id: ${incoming.body.newDgId} + next: update_minor_dataset_group_validation + +update_minor_dataset_group_validation: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-minor-dataset-group-validation-data" + body: + id: ${active_dg_id} + validation_status: ${validation_status} + validation_errors: ${JSON.stringify(validation_errors)} + result: res + next: check_minor_status + +check_minor_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_minor_validation_status_type + next: assign_fail_response + +check_minor_validation_status_type: + switch: + - condition: ${validation_status === 'success'} + next: execute_cron_manager + next: assign_success_response + +update_patch_dataset_group_validation: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-patch-dataset-group-validation-data" + body: + id: ${active_dg_id} + validation_errors: ${JSON.stringify(validation_errors)} + result: res + next: check_patch_status + +check_patch_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_patch_validation_status_type + next: assign_fail_response + +check_patch_validation_status_type: + switch: + - condition: ${validation_status === 'success'} + next: update_patch_version_only + next: assign_success_response + +update_patch_version_only: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-patch-version-only-dataset-group" + body: + id: ${dg_id} + result: res + next: check_patch_version_only_status + +check_patch_version_only_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: update_patch_version_in_model_metadata + next: assign_fail_response + +update_patch_version_in_model_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-data-model-dataset-group-patch-version" + body: + dg_id: ${dg_id} + result: res + next: check_patch_version_in_model_metadata_status + +check_patch_version_in_model_metadata_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: execute_cron_manager + next: assign_fail_response + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/dataset_processor/dataset_processor" + query: + cookie: ${incoming.headers.cookie} + dgId: ${dg_id} + newDgId: ${new_dg_id} + updateType: ${update_type} + savedFilePath: ${save_file_path} + patchPayload: ${patch_payload} + sessionId: ${session_id} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${active_dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${active_dg_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +update_type_not_found: + status: 400 + return: "Update type not found" + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/datasetprocessor/initiate.yml b/DSL/Ruuter.private/DSL/POST/classifier/datasetprocessor/initiate.yml new file mode 100644 index 00000000..28ea8893 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/datasetprocessor/initiate.yml @@ -0,0 +1,56 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'INITIATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: dgId + type: number + description: "Body field 'dgId'" + +extract_request_data: + assign: + dg_id: ${incoming.body.dgId} + # cookie: ${incoming.headers.cookie} + next: execute_cron_manager + +execute_cron_manager: + call: http.post + args: + url: "[#CLASSIFIER_CRON_MANAGER]/execute/data_processor/init_data_processor" + query: + # cookie: ${incoming.header.cookie} + dgId: ${dg_id} + result: res + next: assign_success_response + +assign_success_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + dgId: '${dg_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + diff --git a/DSL/Ruuter.private/DSL/POST/classifier/inference/create.yml b/DSL/Ruuter.private/DSL/POST/classifier/inference/create.yml new file mode 100644 index 00000000..86272416 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/inference/create.yml @@ -0,0 +1,160 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CREATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inputId + type: string + description: "Body field 'inputId'" + - field: inferenceText + type: string + description: "Body field 'inferenceText'" + - field: predictedLabels + type: json + description: "Body field 'predictedLabels'" + - field: averagePredictedClassesProbability + type: int + description: "Body field 'averagePredictedClassesProbability'" + - field: platform + type: string + description: "Body field 'platform'" + - field: primaryFolderId + type: string + description: "Body field 'primaryFolderId'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + input_id: ${incoming.body.inputId} + inference_text: ${incoming.body.inferenceText} + predicted_labels_org: ${incoming.body.predictedLabels} + average_predicted_classes_probability: ${incoming.body.averagePredictedClassesProbability} + platform: ${incoming.body.platform} + primary_folder_id: ${incoming.body.primaryFolderId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${input_id !== null || inference_text !== null || predicted_labels_org !== null || average_predicted_classes_probability !== null || platform !== null} + next: check_platform_data + next: return_incorrect_request + +check_platform_data: + switch: + - condition: ${platform == 'OUTLOOK'} + next: check_primary_folder_exist + next: get_epoch_date + +check_primary_folder_exist: + switch: + - condition: ${primary_folder_id !== null} + next: get_epoch_date + next: return_primary_folder_not_found + +get_epoch_date: + assign: + current_epoch: ${Date.now()} + next: create_input_metadata + +create_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-input-metadata" + body: + input_id: ${input_id} + inference_time_stamp: ${new Date(current_epoch).toISOString()} + inference_text: ${inference_text} + predicted_labels: ${JSON.stringify(predicted_labels_org)} + average_predicted_classes_probability: ${average_predicted_classes_probability} + platform: ${platform} + primary_folder_id: ${platform !== 'OUTLOOK' ? '' :primary_folder_id} + result: res_input + next: check_status + +check_status: + switch: + - condition: ${200 <= res_input.response.statusCodeValue && res_input.response.statusCodeValue < 300} + next: check_input_type + next: assign_fail_response + +check_input_type: + switch: + - condition: ${platform == 'OUTLOOK'} + next: outlook_flow + - condition: ${platform == 'JIRA'} + next: jira_flow + next: assign_success_response + +outlook_flow: + call: http.post + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/outlook/label" + headers: + cookie: ${incoming.headers.cookie} + body: + mailId: ${input_id} + folderId: ${primary_folder_id} + result: res_label + next: check_label_status + +jira_flow: + call: http.post + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/jira/cloud/label" + headers: + cookie: ${incoming.headers.cookie} + body: + issueKey: ${input_id} + labels: ${predicted_labels_org} + result: res_label + next: check_label_status + +check_label_status: + switch: + - condition: ${200 <= res_label.response.statusCodeValue && res_label.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + inferenceId: '${res_input.response.body[0].id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + inferenceId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_primary_folder_not_found: + status: 400 + return: 'Missing Primary Folder Id' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/inference/exist.yml b/DSL/Ruuter.private/DSL/POST/classifier/inference/exist.yml new file mode 100644 index 00000000..0e7d40b2 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/inference/exist.yml @@ -0,0 +1,78 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'EXIST'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inputId + type: string + description: "Body field 'inputId'" + +extract_data: + assign: + input_id: ${incoming.body.inputId} + exist: false + next: get_input_metadata_by_id + +get_input_metadata_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-basic-input-metadata-by-input-id" + body: + input_id: ${input_id} + result: res_input_id + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_input_id.response.statusCodeValue && res_input_id.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_input_id.response.body.length>0} + next: assign_exist_data + next: assign_fail_response + +assign_exist_data: + assign: + exist : true + value: [{ + inferenceId: '${res_input_id.response.body[0].id}', + inputId: '${res_input_id.response.body[0].inputId}', + platform: '${res_input_id.response.body[0].platform}' + }] + next: assign_success_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + exist: '${exist}', + data: '${value}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + exist: '${exist}', + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/inference/update/corrected.yml b/DSL/Ruuter.private/DSL/POST/classifier/inference/update/corrected.yml new file mode 100644 index 00000000..45e7deb4 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/inference/update/corrected.yml @@ -0,0 +1,141 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CORRECTED'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inferenceId + type: number + description: "Body field 'inferenceId'" + - field: isCorrected + type: boolean + description: "Body field 'isCorrected'" + - field: correctedLabels + type: json + description: "Body field 'correctedLabels'" + - field: averageCorrectedClassesProbability + type: int + description: "Body field 'averageCorrectedClassesProbability'" + - field: primaryFolderId + type: string + description: "Body field 'primaryFolderId'" + - field: platform + type: string + description: "Body field 'platform'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + inference_id: ${incoming.body.inferenceId} + is_corrected: ${incoming.body.isCorrected} + corrected_labels: ${incoming.body.correctedLabels} + average_corrected_classes_probability: ${incoming.body.averageCorrectedClassesProbability} + primary_folder_id: ${incoming.body.primaryFolderId} + platform: ${incoming.body.platform} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${inference_id !== null || corrected_labels !== null || average_corrected_classes_probability !== null || platform !== null} + next: check_platform_data + next: return_incorrect_request + +check_platform_data: + switch: + - condition: ${platform == 'OUTLOOK'} + next: check_primary_folder_exist + next: get_input_metadata_by_id + +check_primary_folder_exist: + switch: + - condition: ${primary_folder_id !== null} + next: get_input_metadata_by_id + next: return_primary_folder_not_found + +get_input_metadata_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-input-metadata-exits-by-id" + body: + id: ${inference_id} + result: res_input_id + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_input_id.response.statusCodeValue && res_input_id.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_input_id.response.body.length>0} + next: update_input_metadata + next: return_inference_data_not_found + +update_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-input-metadata" + body: + id: ${inference_id} + is_corrected: ${is_corrected} + corrected_labels: ${JSON.stringify(corrected_labels)} + average_corrected_classes_probability: ${average_corrected_classes_probability} + primary_folder_id: ${platform !== 'OUTLOOK' ? '' :primary_folder_id} + result: res_input + next: check_status + +check_status: + switch: + - condition: ${200 <= res_input.response.statusCodeValue && res_input.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + inferenceId: '${inference_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + inferenceId: '${inference_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_primary_folder_not_found: + status: 400 + return: 'Missing Primary Folder Id' + next: end + +return_inference_data_not_found: + status: 400 + return: 'Inference data not found' + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/.guard b/DSL/Ruuter.private/DSL/POST/classifier/integration/.guard new file mode 100644 index 00000000..fae02a67 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: check-user-authority-admin + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 400 + next: end diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/label.yml b/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/label.yml new file mode 100644 index 00000000..fb91746b --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/label.yml @@ -0,0 +1,79 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'LABEL'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: issueKey + type: string + description: "Body field 'issueId'" + - field: labels + type: array + description: "Body field 'labels'" + +extract_request_data: + assign: + issue_key: ${incoming.body.issueKey} + label_list: ${incoming.body.labels} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${issue_key == null || label_list == null} + next: return_incorrect_request + next: get_auth_header + +get_auth_header: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/get_auth_header" + headers: + type: json + body: + username: "[#JIRA_USERNAME]" + token: "[#JIRA_API_TOKEN]" + result: auth_header + next: set_data + +set_data: + assign: + all_labels: { + labels: '${label_list}' + } + next: update_jira_issue + +update_jira_issue: + call: http.put + args: + url: "[#JIRA_CLOUD_DOMAIN]/rest/api/3/issue/${issue_key}" + headers: + Authorization: ${auth_header.response.body.val} + body: + fields: ${all_labels} + result: res_jira + next: check_status + +check_status: + switch: + - condition: ${200 <= res_jira.response.statusCodeValue && res_jira.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Jira Ticket Updated" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + +return_incorrect_request: + status: 400 + return: 'missing labels' + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/subscribe.yml b/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/subscribe.yml new file mode 100644 index 00000000..99b42524 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/jira/cloud/subscribe.yml @@ -0,0 +1,118 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'SUBSCRIBE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: isConnect + type: boolean + description: "Body field 'isConnect'" + +extract_request_data: + assign: + is_connect: ${incoming.body.isConnect} + next: get_platform_integration_status + +get_platform_integration_status: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-platform-integration-status" + body: + platform: 'JIRA' + result: res + next: assign_db_platform_integration_data + +assign_db_platform_integration_data: + assign: + db_platform_status: ${res.response.body[0].isConnect} + next: validate_request + +validate_request: + switch: + - condition: ${db_platform_status !== is_connect} + next: get_auth_header + next: return_already_request + +get_auth_header: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/get_auth_header" + headers: + type: json + body: + username: "[#JIRA_USERNAME]" + token: "[#JIRA_API_TOKEN]" + result: auth_header + next: check_integration_type + +check_integration_type: + switch: + - condition: ${is_connect === true} + next: subscribe_jira + next: unsubscribe_jira + +subscribe_jira: + call: http.put + args: + url: "[#JIRA_CLOUD_DOMAIN]/rest/webhooks/1.0/webhook/[#JIRA_WEBHOOK_ID]" + headers: + Authorization: ${auth_header.response.body.val} + body: + enabled: true + result: res + next: check_subscribe_response + +check_subscribe_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: set_subscription_data + next: return_result + +set_subscription_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/connect-platform" + body: + id: '[#JIRA_WEBHOOK_ID]' + platform: 'JIRA' + result: res + next: return_result + +unsubscribe_jira: + call: http.put + args: + url: "[#JIRA_CLOUD_DOMAIN]/rest/webhooks/1.0/webhook/[#JIRA_WEBHOOK_ID]" + headers: + Authorization: ${auth_header.response.body.val} + body: + enabled: false + result: res + next: check_unsubscribe_response + +check_unsubscribe_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: remove_subscription_data + next: return_result + +remove_subscription_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/disconnect-platform" + body: + platform: 'JIRA' + result: res + next: return_result + +return_result: + return: res.response.body + next: end + +return_already_request: + status: 400 + return: "Already Requested-Bad Request" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/label.yml b/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/label.yml new file mode 100644 index 00000000..f4e8be21 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/label.yml @@ -0,0 +1,118 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'Label'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: mailId + type: string + description: "Body field 'mailId'" + - field: folderId + type: string + description: "Body field 'folderId'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + mail_id: ${incoming.body.mailId} + folder_id: ${incoming.body.folderId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${mail_id !== null || folder_id !== null} + next: get_token_info + next: return_incorrect_request + +get_token_info: + call: http.get + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/outlook/token" + headers: + cookie: ${incoming.headers.cookie} + result: res + next: assign_access_token + +assign_access_token: + assign: + access_token: ${res.response.body.response.access_token} + next: get_email_exist + +get_email_exist: + call: http.get + args: + url: "https://graph.microsoft.com/v1.0/me/messages/${mail_id}" + headers: + Authorization: ${'Bearer ' + access_token} + result: res + next: check_email_status + +check_email_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: get_folder_exist + next: return_email_not_found + +get_folder_exist: + call: http.get + args: + url: "https://graph.microsoft.com/v1.0/me/mailFolders/${folder_id}" + headers: + Authorization: ${'Bearer ' + access_token} + result: res + next: check_folder_status + +check_folder_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: update_mail_folder + next: return_folder_not_found + +update_mail_folder: + call: http.post + args: + url: "https://graph.microsoft.com/v1.0/me/messages/${mail_id}/move" + headers: + Authorization: ${'Bearer ' + access_token} + body: + destinationId: ${folder_id} + result: res_mail + next: check_status + +check_status: + switch: + - condition: ${200 <= res_mail.response.statusCodeValue && res_mail.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Outlook Email Destination Updated" + next: end + +return_folder_not_found: + status: 404 + return: 'Folder Not Found' + next: end + +return_email_not_found: + status: 404 + return: 'Email Not Found' + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + +return_incorrect_request: + status: 400 + return: 'missing labels' + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/subscribe.yml b/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/subscribe.yml new file mode 100644 index 00000000..0e57c2c8 --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/outlook/subscribe.yml @@ -0,0 +1,160 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'SUBSCRIBE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: isConnect + type: boolean + description: "Body field 'isConnect'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + is_connect: ${incoming.body.isConnect} + next: get_platform_integration_status + +get_platform_integration_status: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-platform-integration-status" + body: + platform: 'OUTLOOK' + result: res + next: assign_db_platform_integration_data + +assign_db_platform_integration_data: + assign: + db_platform_status: ${res.response.body[0].isConnect} + subscription_id: ${res.response.body[0].subscriptionId} + next: validate_request + +validate_request: + switch: + - condition: ${db_platform_status !== is_connect} + next: get_token_info + next: return_already_request + +get_token_info: + call: http.get + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/outlook/token" + headers: + cookie: ${incoming.headers.cookie} + result: res + next: assign_access_token + +assign_access_token: + assign: + access_token: ${res.response.body.response.access_token} + next: check_integration_type + +check_integration_type: + switch: + - condition: ${is_connect === true && subscription_id === null} + next: set_expiration_date_time + - condition: ${is_connect === false && subscription_id !== null} + next: unsubscribe_outlook + next: return_bad_request + +set_expiration_date_time: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_outlook_expiration_date_time" + headers: + type: json + result: expiration_date_time_res + next: assign_expiration_date_time + +assign_expiration_date_time: + assign: + expiration_date_time: ${expiration_date_time_res.response.body.expirationDateTime} + next: subscribe_outlook + +subscribe_outlook: + call: http.post + args: + url: "https://graph.microsoft.com/v1.0/subscriptions" + headers: + Authorization: ${'Bearer ' + access_token} + body: + changeType: "created,updated" + notificationUrl: "[#CLASSIFIER_RUUTER_PUBLIC_FRONTEND_URL]/classifier/integration/outlook/accept" + resource: "me/messages" + expirationDateTime: ${expiration_date_time} + clientState: "secretClientValue" + result: res_subscribe + next: check_subscribe_response + +check_subscribe_response: + switch: + - condition: ${200 <= res_subscribe.response.statusCodeValue && res_subscribe.response.statusCodeValue < 300} + next: set_subscription_data + next: return_bad_request + +set_subscription_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/connect-platform" + body: + id: ${res_subscribe.response.body.id} + platform: 'OUTLOOK' + result: set_status_res + next: check_db_status + +unsubscribe_outlook: + call: http.delete + args: + url: "https://graph.microsoft.com/v1.0/subscriptions/${subscription_id}" + headers: + Authorization: ${'Bearer ' + access_token} + result: res_unsubscribe + next: check_unsubscribe_response + +check_unsubscribe_response: + switch: + - condition: ${200 <= res_unsubscribe.response.statusCodeValue && res_unsubscribe.response.statusCodeValue < 300} + next: remove_subscription_data + next: return_bad_request + +remove_subscription_data: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/disconnect-platform" + body: + platform: 'OUTLOOK' + result: set_status_res + next: check_db_status + +check_db_status: + switch: + - condition: ${200 <= set_status_res.response.statusCodeValue && set_status_res.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "service successful" + next: end + +return_not_found: + status: 404 + return: "Subscription not found" + next: end + +return_already_request: + status: 400 + return: "Already Requested-Bad Request" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/integration/toggle-platform.yml b/DSL/Ruuter.private/DSL/POST/classifier/integration/toggle-platform.yml new file mode 100644 index 00000000..ea41974e --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/integration/toggle-platform.yml @@ -0,0 +1,114 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'TOGGLE-PLATFORM'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: operation + type: string + description: "Body field 'operation'" + - field: platform + type: string + description: "Body field 'platform'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + operation: ${incoming.body.operation} + platform: ${incoming.body.platform} + cookie: ${incoming.headers.cookie} + next: check_operation + +check_operation: + switch: + - condition: ${operation === 'enable'} + next: assign_true + - condition: ${operation === 'disable'} + next: assign_false + next: operation_not_support + +assign_true: + assign: + is_connect: true + next: check_platform + +assign_false: + assign: + is_connect: false + next: check_platform + +check_platform: + switch: + - condition: ${platform === 'jira'} + next: assign_jira_url + - condition: ${platform === 'outlook'} + next: assign_outlook_url + next: platform_not_support + +assign_jira_url: + assign: + url: "jira/cloud/subscribe" + next: route_to_platform + +assign_outlook_url: + assign: + url: "outlook/subscribe" + next: route_to_platform + +route_to_platform: + call: http.post + args: + url: "[#CLASSIFIER_RUUTER_PRIVATE]/classifier/integration/${url}" + headers: + type: json + cookie: ${cookie} + body: + isConnect: ${is_connect} + result: res + next: check_platform_response_status + +check_platform_response_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_success + next: assign_fail + +assign_success: + assign: + operation_status: 'success' + next: assign_response + +assign_fail: + assign: + operation_status: 'failed' + next: assign_response + +assign_response: + assign: + format_res: { + operation_type: '${operation}', + operation_status_code: '${res.response.statusCodeValue}', + operation_status: '${operation_status}' + } + next: return_result + +return_result: + return: '${format_res}' + next: end + +operation_not_support: + status: 400 + return: "Bad Request-Operation not support" + next: end + +platform_not_support: + status: 400 + return: "Bad Request- Platform not support" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/POST/classifier/testmodel/test-data.yml b/DSL/Ruuter.private/DSL/POST/classifier/testmodel/test-data.yml new file mode 100644 index 00000000..171c13ef --- /dev/null +++ b/DSL/Ruuter.private/DSL/POST/classifier/testmodel/test-data.yml @@ -0,0 +1,121 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'TEST-DATA'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + - field: text + type: string + description: "Body field 'text'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + model_id: ${incoming.body.modelId} + text: ${incoming.body.text} + cookie: ${incoming.headers.cookie} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${model_id !== null && text !== null} + next: check_for_text_data + next: return_incorrect_request + +check_for_text_data: + switch: + - condition: ${text !== ''} + next: get_data_model_by_id + next: return_empty_request + +get_data_model_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: send_data_to_predict + next: return_model_not_found + +send_data_to_predict: + call: http.post + args: + url: "[#CLASSIFIER_MODEL_INFERENCE]/classifier/deployment/testing/inference" + headers: + cookie: ${cookie} + body: + modelId: ${model_id} + text: ${text} + result: res_predict + next: check_data_predict_status + +check_data_predict_status: + switch: + - condition: ${200 <= res_predict.response.statusCodeValue && res_predict.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + data: '${res_predict.response.body}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '${model_id}', + data: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_model_not_found: + status: 404 + return: 'Model Not Found' + next: end + +return_empty_request: + status: 400 + return: 'Text Data Empty' + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority-admin.yml b/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority-admin.yml new file mode 100644 index 00000000..0e8f64f8 --- /dev/null +++ b/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority-admin.yml @@ -0,0 +1,52 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CHECK-USER-AUTHORITY'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_cookie_info: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + next: check_cookie_info_response + +check_cookie_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_user_authority + next: return_bad_request + +check_user_authority: + switch: + - condition: ${res.response.body === null} + next: return_unauthorized + - condition: ${res.response.body.authorities.includes("ROLE_ADMINISTRATOR")} + next: return_authorized + next: return_unauthorized + +return_authorized: + return: ${res.response.body} + next: end + +return_unauthorized: + status: 401 + return: false + next: end + +return_bad_request: + status: 400 + return: false + next: end diff --git a/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority.yml b/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority.yml new file mode 100644 index 00000000..d1d06588 --- /dev/null +++ b/DSL/Ruuter.private/DSL/TEMPLATES/check-user-authority.yml @@ -0,0 +1,52 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CHECK-USER-AUTHORITY'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_cookie_info: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + next: check_cookie_info_response + +check_cookie_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_user_authority + next: return_bad_request + +check_user_authority: + switch: + - condition: ${res.response.body === null} + next: return_unauthorized + - condition: ${res.response.body.authorities.includes("ROLE_ADMINISTRATOR") || res.response.body.authorities.includes("ROLE_MODEL_TRAINER")} + next: return_authorized + next: return_unauthorized + +return_authorized: + return: ${res.response.body} + next: end + +return_unauthorized: + status: 401 + return: false + next: end + +return_bad_request: + status: 400 + return: false + next: end diff --git a/DSL/Ruuter.public/DSL/GET/internal/outlook/token.yml b/DSL/Ruuter.public/DSL/GET/internal/outlook/token.yml new file mode 100644 index 00000000..7fc61b63 --- /dev/null +++ b/DSL/Ruuter.public/DSL/GET/internal/outlook/token.yml @@ -0,0 +1,64 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'TOKEN'" + method: get + accepts: json + returns: json + namespace: classifier + +get_refresh_token: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-token" + body: + platform: 'OUTLOOK' + result: res + next: set_refresh_token + +set_refresh_token: + assign: + refresh_token: ${res.response.body[0].token} + next: check_refresh_token + +check_refresh_token: + switch: + - condition: ${refresh_token !== null} + next: decrypt_token + next: return_not_found + +decrypt_token: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_decrypted_outlook_token" + headers: + type: json + body: + token: ${refresh_token} + result: token_data + next: get_access_token + +get_access_token: + call: http.post + args: + url: "https://login.microsoftonline.com/common/oauth2/v2.0/token" + contentType: formdata + headers: + type: json + body: + client_id: "[#OUTLOOK_CLIENT_ID]" + scope: "User.Read Mail.ReadWrite MailboxSettings.ReadWrite offline_access" + refresh_token: ${token_data.response.body.token.content} + grant_type: "refresh_token" + client_secret: "[#OUTLOOK_SECRET_KEY]" + result: res + next: return_result + +return_result: + return: ${res.response.body} + next: end + +return_not_found: + status: 404 + return: "refresh token not found" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/DSL/POST/auth/login.yml b/DSL/Ruuter.public/DSL/POST/auth/login.yml new file mode 100644 index 00000000..d63682d2 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/auth/login.yml @@ -0,0 +1,79 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'LOGIN'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: login + type: string + description: "Body field 'login'" + - field: password + type: string + description: "Body field 'password'" + +extractRequestData: + assign: + login: ${incoming.body.login} + password: ${incoming.body.password} + next: getUserWithRole + +getUserWithRole: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-user-with-roles" + body: + login: ${login} + password: ${password} + result: user_result + next: check_user_result + +check_user_result: + switch: + - condition: "${user_result.response.body.length > 0}" + next: get_session_length + next: return_user_not_found + +get_session_length: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-configuration" + body: + key: "session_length" + result: session_result + next: generate_cookie + +generate_cookie: + call: http.post + args: + url: "[#CLASSIFIER_TIM]/jwt/custom-jwt-generate" + body: + JWTName: "customJwtCookie" + expirationInMinutes: ${session_result.response.body[0]?.value ?? '120'} + content: ${user_result.response.body[0]} + result: cookie_result + next: assign_cookie + +assign_cookie: + assign: + setCookie: + customJwtCookie: ${cookie_result.response.body.token} + Domain: "[#DOMAIN]" + Secure: true + HttpOnly: true + SameSite: "Lax" + next: return_value + +return_value: + headers: + Set-Cookie: ${setCookie} + return: ${cookie_result.response.body.token} + next: end + +return_user_not_found: + status: 400 + return: "User Not Found" + next: end diff --git a/DSL/Ruuter.public/DSL/POST/classifier/integration/jira/cloud/accept.yml b/DSL/Ruuter.public/DSL/POST/classifier/integration/jira/cloud/accept.yml new file mode 100644 index 00000000..e030e451 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/classifier/integration/jira/cloud/accept.yml @@ -0,0 +1,169 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'ACCEPT'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: headers + type: object + description: "Body field 'headers'" + - field: payload + type: json + description: "Body field 'payload'" + - field: issue_info + type: string + description: "Body field 'issue_info'" + - field: event_type + type: string + description: "Body field 'event_type'" + +get_webhook_data: + assign: + headers: ${incoming.headers} + payload: ${incoming.body} + issue_info: ${incoming.body.issue} + event_type: ${incoming.body.webhookEvent} + next: verify_jira_signature + +verify_jira_signature: + call: http.post + args: + url: "[#CLASSIFIER_ANONYMIZER]/verify_signature" + headers: + Content-Type: "application/json" + x-hub-signature: ${headers['x-hub-signature']} + body: + payload: ${payload} + secret: "[#JIRA_WEBHOOK_SECRET]" + result: valid_data + next: assign_verification + +assign_verification: + assign: + is_valid: ${valid_data.response.body.status} + next: validate_url_signature + +validate_url_signature: + switch: + - condition: ${is_valid === true} + next: check_event_type + next: return_error_found + +check_event_type: + switch: + - condition: ${event_type === 'jira:issue_updated'} + next: get_existing_labels + next: get_jira_issue_info + +get_existing_labels: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-jira-input-row-data" + body: + inputId: ${issue_info.key} + result: res + next: check_input_response + +check_input_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_previous_labels + next: return_db_request_fail + +check_previous_labels: + switch: + - condition: ${res.response.body.length > 0} + next: assign_previous_labels + next: get_jira_issue_info + +assign_previous_labels: + assign: + previous_corrected_labels: ${res.response.body[0].correctedLabels !==null ? JSON.parse(res.response.body[0].correctedLabels.value) :[]} + previous_predicted_labels: ${res.response.body[0].predictedLabels !==null ? JSON.parse(res.response.body[0].predictedLabels.value) :[]} + next: validate_issue_labels + +validate_issue_labels: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_label_mismatch" + headers: + type: json + body: + newLabels: ${issue_info.fields.labels} + correctedLabels: ${previous_corrected_labels} + predictedLabels: ${previous_predicted_labels} + result: label_response + next: check_label_mismatch + +check_label_mismatch: + switch: + - condition: ${label_response.response.body.isMismatch === 'true'} + next: get_jira_issue_info + next: return_data + +get_jira_issue_info: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_jira_issue_info" + headers: + type: json + body: + data: ${issue_info} + result: extract_info + next: send_issue_data + +send_issue_data: + call: http.post + args: + url: "[#CLASSIFIER_ANONYMIZER]/anonymize" + headers: + type: json + body: + platform: 'JIRA' + key: ${issue_info.key} + data: ${extract_info} + parentFolderId: null + mailId: null + labels: ${issue_info.fields.labels} + response: + statusCodeValue: 200 + result: res + +check_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Jira data send successfully" + next: end + +return_data: + status: 200 + return: "Not Sent" + next: end + +return_error_found: + status: 400 + return: "Error Found" + next: end + +return_db_request_fail: + status: 400 + return: "Fetch data for labels failed" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + + + + diff --git a/DSL/Ruuter.public/DSL/POST/classifier/integration/outlook/accept.yml b/DSL/Ruuter.public/DSL/POST/classifier/integration/outlook/accept.yml new file mode 100644 index 00000000..b62b6e8d --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/classifier/integration/outlook/accept.yml @@ -0,0 +1,214 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'ACCEPT'" + method: post + accepts: json + returns: text/* + namespace: classifier + allowlist: + params: + - field: validationToken + type: string + description: "parameter 'validationToken'" + body: + - field: payload + type: json + description: "body field is 'payload'" + +extract_request_data: + assign: + validation_token: ${incoming.params.validationToken} + payload: ${incoming.body} + next: check_process_flow + +check_process_flow: + switch: + - condition: ${validation_token !==null} + next: return_validation_token_response + - condition: ${payload !==null} + next: assign_outlook_mail_info + next: return_error_found + +return_validation_token_response: + wrapper: false + headers: + Content-Type: text/* + return: ${validation_token} + status: 200 + next: end + +assign_outlook_mail_info: + assign: + resource: ${payload.value[0].resource} + event_type: ${payload.value[0].changeType} + next: get_refresh_token + +get_refresh_token: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-token" + body: + platform: 'OUTLOOK' + result: res + next: set_refresh_token + +set_refresh_token: + assign: + refresh_token: ${res.response.body[0].token} + next: check_refresh_token + +check_refresh_token: + switch: + - condition: ${refresh_token !== null} + next: decrypt_token + next: return_not_found + +decrypt_token: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_decrypted_outlook_token" + headers: + type: json + body: + token: ${refresh_token} + result: token_data + next: get_access_token + +get_access_token: + call: http.post + args: + url: "https://login.microsoftonline.com/common/oauth2/v2.0/token" + contentType: formdata + headers: + type: json + body: + client_id: "[#OUTLOOK_CLIENT_ID]" + scope: "User.Read Mail.ReadWrite MailboxSettings.ReadWrite offline_access" + refresh_token: ${token_data.response.body.token.content} + grant_type: "refresh_token" + client_secret: "[#OUTLOOK_SECRET_KEY]" + result: res + next: assign_access_token + +check_access_token: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_access_token + next: return_access_token_not_found + +assign_access_token: + assign: + access_token: ${res.response.body.access_token} + next: get_extracted_mail_info + +get_extracted_mail_info: + call: http.get + args: + url: "https://graph.microsoft.com/v1.0/${resource}?$expand=attachments" + headers: + Authorization: ${'Bearer ' + access_token} + result: mail_info_data + next: check_extracted_mail_info + +check_extracted_mail_info: + switch: + - condition: ${200 <= mail_info_data.response.statusCodeValue && mail_info_data.response.statusCodeValue < 300} + next: get_existing_folder_id + next: return_mail_info_not_found + +get_existing_folder_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-outlook-input-row-data" + body: + inputId: ${mail_info_data.response.body.internetMessageId} + result: existing_outlook_info + next: check_input_response + +check_input_response: + switch: + - condition: ${200 <= existing_outlook_info.response.statusCodeValue && existing_outlook_info.response.statusCodeValue < 300} + next: check_folders_exist + next: return_db_request_fail + +check_folders_exist: + switch: + - condition: ${existing_outlook_info.response.body.length>0} + next: check_folder_id + next: check_event_type + +check_folder_id: + switch: + - condition: ${existing_outlook_info.response.body[0].primaryFolderId !== mail_info_data.response.body.parentFolderId} + next: rearrange_mail_payload + next: return_Folder_Match + +check_event_type: + switch: + - condition: ${event_type === 'updated'} + next: return_folder_not_found + next: rearrange_mail_payload + +rearrange_mail_payload: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_outlook_payload_info" + headers: + type: json + body: + data: ${mail_info_data.response.body} + result: outlook_body + next: send_outlook_data + +send_outlook_data: + call: http.post + args: + url: "[#CLASSIFIER_ANONYMIZER]/anonymize" + headers: + type: json + body: + platform: 'OUTLOOK' + key: ${mail_info_data.response.body.internetMessageId} + data: ${outlook_body.response.body} + parentFolderId: ${mail_info_data.response.body.parentFolderId} + mailId: ${mail_info_data.response.body.id} + labels: None + result: res + next: check_response + +check_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Outlook data send successfully" + next: end + +return_Folder_Match: + status: 200 + return: "Folder Match,No Update" + next: end + +return_mail_info_not_found: + status: 404 + return: "Mail Info Not Found" + next: end + +return_access_token_not_found: + status: 404 + return: "Access Token Not Found" + next: end + +return_folder_not_found: + status: 404 + return: "Folder Data Not Found" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/DSL/POST/internal/corrected.yml b/DSL/Ruuter.public/DSL/POST/internal/corrected.yml new file mode 100644 index 00000000..f5c89903 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/corrected.yml @@ -0,0 +1,138 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CORRECTED'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inferenceId + type: number + description: "Body field 'inferenceId'" + - field: isCorrected + type: boolean + description: "Body field 'isCorrected'" + - field: correctedLabels + type: json + description: "Body field 'correctedLabels'" + - field: averageCorrectedClassesProbability + type: int + description: "Body field 'averageCorrectedClassesProbability'" + - field: primaryFolderId + type: string + description: "Body field 'primaryFolderId'" + - field: platform + type: string + description: "Body field 'platform'" + + +extract_request_data: + assign: + inference_id: ${incoming.body.inferenceId} + is_corrected: ${incoming.body.isCorrected} + corrected_labels: ${incoming.body.correctedLabels} + average_corrected_classes_probability: ${incoming.body.averageCorrectedClassesProbability} + primary_folder_id: ${incoming.body.primaryFolderId} + platform: ${incoming.body.platform} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${inference_id !== null || corrected_labels !== null || average_corrected_classes_probability !== null || platform !== null} + next: check_platform_data + next: return_incorrect_request + +check_platform_data: + switch: + - condition: ${platform == 'OUTLOOK'} + next: check_primary_folder_exist + next: get_input_metadata_by_id + +check_primary_folder_exist: + switch: + - condition: ${primary_folder_id !== null} + next: get_input_metadata_by_id + next: return_primary_folder_not_found + +get_input_metadata_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-input-metadata-exits-by-id" + body: + id: ${inference_id} + result: res_input_id + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_input_id.response.statusCodeValue && res_input_id.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_input_id.response.body.length>0} + next: update_input_metadata + next: return_inference_data_not_found + +update_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/update-input-metadata" + body: + id: ${inference_id} + is_corrected: ${is_corrected} + corrected_labels: ${JSON.stringify(corrected_labels)} + average_corrected_classes_probability: ${average_corrected_classes_probability} + primary_folder_id: ${platform !== 'OUTLOOK' ? '' :primary_folder_id} + result: res_input + next: check_status + +check_status: + switch: + - condition: ${200 <= res_input.response.statusCodeValue && res_input.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + inferenceId: '${inference_id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + inferenceId: '${inference_id}', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_primary_folder_not_found: + status: 400 + return: 'Missing Primary Folder Id' + next: end + +return_inference_data_not_found: + status: 400 + return: 'Inference data not found' + next: end diff --git a/DSL/Ruuter.public/DSL/POST/internal/create.yml b/DSL/Ruuter.public/DSL/POST/internal/create.yml new file mode 100644 index 00000000..6262723d --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/create.yml @@ -0,0 +1,156 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'CREATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inputId + type: string + description: "Body field 'inputId'" + - field: mailId + type: string + description: "Body field 'mailId'" + - field: inferenceText + type: string + description: "Body field 'inferenceText'" + - field: predictedLabels + type: json + description: "Body field 'predictedLabels'" + - field: averagePredictedClassesProbability + type: int + description: "Body field 'averagePredictedClassesProbability'" + - field: platform + type: string + description: "Body field 'platform'" + - field: primaryFolderId + type: string + description: "Body field 'primaryFolderId'" + +extract_request_data: + assign: + input_id: ${incoming.body.inputId} + mail_id: ${incoming.body.mailId} + inference_text: ${incoming.body.inferenceText} + predicted_labels_org: ${incoming.body.predictedLabels} + average_predicted_classes_probability: ${incoming.body.averagePredictedClassesProbability} + platform: ${incoming.body.platform} + primary_folder_id: ${incoming.body.primaryFolderId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${input_id !== null || inference_text !== null || predicted_labels_org !== null || average_predicted_classes_probability !== null || platform !== null} + next: check_platform_data + next: return_incorrect_request + +check_platform_data: + switch: + - condition: ${platform == 'OUTLOOK'} + next: check_primary_folder_exist + next: get_epoch_date + +check_primary_folder_exist: + switch: + - condition: ${primary_folder_id !== null} + next: get_epoch_date + next: return_primary_folder_not_found + +get_epoch_date: + assign: + current_epoch: ${Date.now()} + next: create_input_metadata + +create_input_metadata: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/insert-input-metadata" + body: + input_id: ${input_id} + inference_time_stamp: ${new Date(current_epoch).toISOString()} + inference_text: ${inference_text} + predicted_labels: ${JSON.stringify(predicted_labels_org)} + average_predicted_classes_probability: ${average_predicted_classes_probability} + platform: ${platform} + primary_folder_id: ${platform !== 'OUTLOOK' ? '' :primary_folder_id} + result: res_input + next: check_status + +check_status: + switch: + - condition: ${200 <= res_input.response.statusCodeValue && res_input.response.statusCodeValue < 300} + next: check_input_type + next: assign_fail_response + +check_input_type: + switch: + - condition: ${platform == 'OUTLOOK'} + next: outlook_flow + - condition: ${platform == 'JIRA'} + next: jira_flow + next: assign_success_response + +outlook_flow: + call: http.post + args: + url: "[#CLASSIFIER_RUUTER_PUBLIC_INTERNAL]/internal/outlook/label" + body: + mailId: ${mail_id} + folderId: ${primary_folder_id} + result: res_label + next: check_label_status + +jira_flow: + call: http.post + args: + url: "[#CLASSIFIER_RUUTER_PUBLIC_INTERNAL]/internal/jira/label" + body: + issueKey: ${input_id} + labels: ${predicted_labels_org} + result: res_label + next: check_label_status + +check_label_status: + switch: + - condition: ${200 <= res_label.response.statusCodeValue && res_label.response.statusCodeValue < 300} + next: assign_success_response + next: assign_fail_response + +assign_success_response: + assign: + format_res: { + inferenceId: '${res_input.response.body[0].id}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + inferenceId: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end + +return_incorrect_request: + status: 400 + return: 'Missing Required Fields' + next: end + +return_primary_folder_not_found: + status: 400 + return: 'Missing Primary Folder Id' + next: end diff --git a/DSL/Ruuter.public/DSL/POST/internal/exist.yml b/DSL/Ruuter.public/DSL/POST/internal/exist.yml new file mode 100644 index 00000000..0e7d40b2 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/exist.yml @@ -0,0 +1,78 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'EXIST'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: inputId + type: string + description: "Body field 'inputId'" + +extract_data: + assign: + input_id: ${incoming.body.inputId} + exist: false + next: get_input_metadata_by_id + +get_input_metadata_by_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-basic-input-metadata-by-input-id" + body: + input_id: ${input_id} + result: res_input_id + next: check_input_metadata_status + +check_input_metadata_status: + switch: + - condition: ${200 <= res_input_id.response.statusCodeValue && res_input_id.response.statusCodeValue < 300} + next: check_input_metadata_exist + next: assign_fail_response + +check_input_metadata_exist: + switch: + - condition: ${res_input_id.response.body.length>0} + next: assign_exist_data + next: assign_fail_response + +assign_exist_data: + assign: + exist : true + value: [{ + inferenceId: '${res_input_id.response.body[0].id}', + inputId: '${res_input_id.response.body[0].inputId}', + platform: '${res_input_id.response.body[0].platform}' + }] + next: assign_success_response + +assign_success_response: + assign: + format_res: { + operationSuccessful: true, + exist: '${exist}', + data: '${value}' + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + operationSuccessful: false, + exist: '${exist}', + data: '${[]}' + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/DSL/POST/internal/jira/accept.yml b/DSL/Ruuter.public/DSL/POST/internal/jira/accept.yml new file mode 100644 index 00000000..adbafe22 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/jira/accept.yml @@ -0,0 +1,126 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'ACCEPT'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: payload + type: string + description: "Body field 'payload'" + - field: extractData + type: json + description: "Body field 'extractData'" + +get_webhook_data: + assign: + payload: ${incoming.body.payload} + extract_data: ${incoming.body.extractData} + issue_info: ${incoming.body.payload.issue} + event_type: ${incoming.body.payload.webhookEvent} + next: check_event_type + +check_event_type: + switch: + - condition: ${event_type === 'jira:issue_updated'} + next: get_existing_labels + next: send_issue_data + +get_existing_labels: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-jira-input-row-data" + body: + inputId: ${issue_info.key} + result: res + next: check_input_response + +check_input_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_previous_labels + next: return_db_request_fail + +check_previous_labels: + switch: + - condition: ${res.response.body.length > 0} + next: assign_previous_labels + next: send_issue_data + +assign_previous_labels: + assign: + previous_corrected_labels: ${res.response.body[0].correctedLabels !==null ? JSON.parse(res.response.body[0].correctedLabels.value) :[]} + previous_predicted_labels: ${res.response.body[0].predictedLabels !==null ? JSON.parse(res.response.body[0].predictedLabels.value) :[]} + next: validate_issue_labels + +validate_issue_labels: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/return_label_mismatch" + headers: + type: json + body: + newLabels: ${issue_info.fields.labels} + correctedLabels: ${previous_corrected_labels} + predictedLabels: ${previous_predicted_labels} + result: label_response + next: check_label_mismatch + +check_label_mismatch: + switch: + - condition: ${label_response.response.body.isMismatch === 'true'} + next: send_issue_data + next: return_data + +send_issue_data: + call: http.post + args: + url: "[#CLASSIFIER_ANONYMIZER]/anonymize" + headers: + type: json + body: + platform: 'JIRA' + key: ${issue_info.key} + data: ${extract_data} + parentFolderId: None + mailId: None + labels: ${issue_info.fields.labels} + result: res + +check_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Jira data send successfully" + next: end + +return_data: + status: 200 + return: "Not Sent" + next: end + +return_error_found: + status: 400 + return: "Error Found" + next: end + +return_db_request_fail: + status: 400 + return: "Fetch data for labels failed" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + + + + diff --git a/DSL/Ruuter.public/DSL/POST/internal/jira/label.yml b/DSL/Ruuter.public/DSL/POST/internal/jira/label.yml new file mode 100644 index 00000000..5923114c --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/jira/label.yml @@ -0,0 +1,79 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'LABEL'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: issueKey + type: string + description: "Body field 'issueKey'" + - field: labels + type: array + description: "Body field 'labels'" + +extract_request_data: + assign: + issue_key: ${incoming.body.issueKey} + label_list: ${incoming.body.labels} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${issue_key == null || label_list == null} + next: return_incorrect_request + next: get_auth_header + +get_auth_header: + call: http.post + args: + url: "[#CLASSIFIER_DMAPPER]/hbs/classifier/get_auth_header" + headers: + type: json + body: + username: "[#JIRA_USERNAME]" + token: "[#JIRA_API_TOKEN]" + result: auth_header + next: set_data + +set_data: + assign: + all_labels: { + labels: '${label_list}' + } + next: update_jira_issue + +update_jira_issue: + call: http.put + args: + url: "[#JIRA_CLOUD_DOMAIN]/rest/api/3/issue/${issue_key}" + headers: + Authorization: ${auth_header.response.body.val} + body: + fields: ${all_labels} + result: res_jira + next: check_status + +check_status: + switch: + - condition: ${200 <= res_jira.response.statusCodeValue && res_jira.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Jira Ticket Updated" + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + +return_incorrect_request: + status: 400 + return: 'missing labels' + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/DSL/POST/internal/outlook/label.yml b/DSL/Ruuter.public/DSL/POST/internal/outlook/label.yml new file mode 100644 index 00000000..931b6ecb --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/outlook/label.yml @@ -0,0 +1,123 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'Label'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: mailId + type: string + description: "Body field 'mailId'" + - field: folderId + type: string + description: "Body field 'folderId'" + +extract_request_data: + assign: + mail_id: ${incoming.body.mailId} + folder_id: ${incoming.body.folderId} + next: check_for_request_data + +check_for_request_data: + switch: + - condition: ${mail_id !== null || folder_id !== null} + next: get_token_info + next: return_incorrect_request + +get_token_info: + call: http.get + args: + url: "[#CLASSIFIER_RUUTER_PUBLIC_INTERNAL]/internal/outlook/token" + result: res + next: check_access_token + +check_access_token: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_access_token + next: return_access_token_not_found + +assign_access_token: + assign: + access_token: ${res.response.body.response.access_token} + next: get_email_exist + +get_email_exist: + call: http.get + args: + url: "https://graph.microsoft.com/v1.0/me/messages/${mail_id}" + headers: + Authorization: ${'Bearer ' + access_token} + result: res + next: check_email_status + +check_email_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: get_folder_exist + next: return_email_not_found + +get_folder_exist: + call: http.get + args: + url: "https://graph.microsoft.com/v1.0/me/mailFolders/${folder_id}" + headers: + Authorization: ${'Bearer ' + access_token} + result: res + next: check_folder_status + +check_folder_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: update_mail_folder + next: return_folder_not_found + +update_mail_folder: + call: http.post + args: + url: "https://graph.microsoft.com/v1.0/me/messages/${mail_id}/move" + headers: + Authorization: ${'Bearer ' + access_token} + body: + destinationId: ${folder_id} + result: res_mail + next: check_status + +check_status: + switch: + - condition: ${200 <= res_mail.response.statusCodeValue && res_mail.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: "Outlook Email Destination Updated" + next: end + +return_folder_not_found: + status: 404 + return: 'Folder Not Found' + next: end + +return_email_not_found: + status: 404 + return: 'Email Not Found' + next: end + +return_bad_request: + status: 400 + return: "Bad Request" + next: end + +return_incorrect_request: + status: 400 + return: 'missing labels' + next: end + +return_access_token_not_found: + status: 404 + return: "Access Token Not Found" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/DSL/POST/internal/validate.yml b/DSL/Ruuter.public/DSL/POST/internal/validate.yml new file mode 100644 index 00000000..5374efe6 --- /dev/null +++ b/DSL/Ruuter.public/DSL/POST/internal/validate.yml @@ -0,0 +1,118 @@ +declaration: + call: declare + version: 0.1 + description: "Description placeholder for 'VALIDATE'" + method: post + accepts: json + returns: json + namespace: classifier + allowlist: + body: + - field: modelId + type: number + description: "Body field 'modelId'" + +extract_request: + assign: + model_id: ${incoming.body.modelId} + next: get_token_info + +get_token_info: + call: http.get + args: + url: "[#CLASSIFIER_RUUTER_PUBLIC_INTERNAL]/internal/outlook/token" + result: res + next: check_token_status + +check_token_status: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assign_access_token + next: assign_fail_response + +assign_access_token: + assign: + access_token: ${res.response.body.response.access_token} + next: get_dataset_group_id_by_model_id + +get_dataset_group_id_by_model_id: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-data-model-dataset-group-by-id" + body: + id: ${model_id} + result: res_model + next: check_data_model_status + +check_data_model_status: + switch: + - condition: ${200 <= res_model.response.statusCodeValue && res_model.response.statusCodeValue < 300} + next: check_data_model_exist + next: assign_fail_response + +check_data_model_exist: + switch: + - condition: ${res_model.response.body.length>0} + next: assign_dataset_group_id + next: assign_fail_response + +assign_dataset_group_id: + assign: + dg_id: ${res_model.response.body[0].connectedDgId} + next: get_dataset_group_class_hierarchy + +get_dataset_group_class_hierarchy: + call: http.post + args: + url: "[#CLASSIFIER_RESQL]/get-dataset-group-class-hierarchy" + body: + id: ${dg_id} + result: res_dataset + next: check_dataset_group_status + +check_dataset_group_status: + switch: + - condition: ${200 <= res_dataset.response.statusCodeValue && res_dataset.response.statusCodeValue < 300} + next: check_dataset_group_exist + next: assign_fail_response + +check_dataset_group_exist: + switch: + - condition: ${res_dataset.response.body.length>0} + next: assign_dataset_class_hierarchy + next: assign_fail_response + +assign_dataset_class_hierarchy: + assign: + class_hierarchy: ${JSON.parse(res_dataset.response.body[0].classHierarchy.value)} + next: assign_success_response + +assign_success_response: + assign: + format_res: { + modelId: '${model_id}', + outlook_access_token: '${access_token}', + class_hierarchy: '${class_hierarchy}', + operationSuccessful: true, + } + next: return_ok + +assign_fail_response: + assign: + format_res: { + modelId: '${model_id}', + outlook_access_token: '', + class_hierarchy: '', + operationSuccessful: false, + } + next: return_bad_request + +return_ok: + status: 200 + return: ${format_res} + next: end + +return_bad_request: + status: 400 + return: ${format_res} + next: end \ No newline at end of file diff --git a/GUI/.dockerignore b/GUI/.dockerignore new file mode 100644 index 00000000..ab4f96a1 --- /dev/null +++ b/GUI/.dockerignore @@ -0,0 +1,7 @@ +node_modules +npm-debug.log +build +.git +*.md +.gitignore +.env.development diff --git a/GUI/.env.development b/GUI/.env.development new file mode 100644 index 00000000..7ff4d8bb --- /dev/null +++ b/GUI/.env.development @@ -0,0 +1,8 @@ +REACT_APP_RUUTER_API_URL=http://localhost:8086 +REACT_APP_RUUTER_PRIVATE_API_URL=http://localhost:8088 +REACT_APP_EXTERNAL_API_URL=http://localhost:8000 +REACT_APP_CUSTOMER_SERVICE_LOGIN=http://localhost:3004/et/dev-auth +REACT_APP_SERVICE_ID=conversations,settings,monitoring +REACT_APP_NOTIFICATION_NODE_URL=http://localhost:4040 +REACT_APP_CSP=upgrade-insecure-requests; default-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; connect-src 'self' http://localhost:8086 http://localhost:8088 http://localhost:8085 http://localhost:4040; +REACT_APP_ENABLE_HIDDEN_FEATURES=TRUE \ No newline at end of file diff --git a/GUI/.env.example b/GUI/.env.example new file mode 100644 index 00000000..9ff59d7b --- /dev/null +++ b/GUI/.env.example @@ -0,0 +1 @@ +REACT_APP_RUUTER_API_URL= diff --git a/GUI/.eslintrc.json b/GUI/.eslintrc.json new file mode 100644 index 00000000..5e603ecd --- /dev/null +++ b/GUI/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "react-app" +} diff --git a/GUI/.gitignore b/GUI/.gitignore new file mode 100644 index 00000000..d79b5ca1 --- /dev/null +++ b/GUI/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# testing +/coverage + +# production +/build + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/GUI/.prettierignore b/GUI/.prettierignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/GUI/.prettierignore @@ -0,0 +1 @@ +node_modules diff --git a/GUI/.prettierrc b/GUI/.prettierrc new file mode 100644 index 00000000..0a725205 --- /dev/null +++ b/GUI/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/GUI/Dockerfile b/GUI/Dockerfile new file mode 100644 index 00000000..355f3c30 --- /dev/null +++ b/GUI/Dockerfile @@ -0,0 +1,23 @@ +ARG nginx_version=nginx:1.25.4-alpine + +FROM node:22.0.0-alpine AS image +WORKDIR /usr/buerokratt-classifier +COPY ./package*.json ./ + +FROM image AS build +RUN npm install --legacy-peer-deps --mode=development +COPY . . +RUN ./node_modules/.bin/vite build --mode=development +VOLUME /usr/buerokratt-classifier + +FROM $nginx_version AS web +COPY ./nginx/http-nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build ./usr/buerokratt-classifier/build/assets /usr/share/nginx/html/buerokratt-classifier/chat/assets +COPY --from=build ./usr/buerokratt-classifier/build/index.html /usr/share/nginx/html/buerokratt-classifier +COPY --from=build ./usr/buerokratt-classifier/build/favicon.ico /usr/share/nginx/html/buerokratt-classifier +RUN apk add --no-cache bash +COPY ./nginx/scripts/env.sh /docker-entrypoint.d/env.sh +RUN chmod +x /docker-entrypoint.d/env.sh +EXPOSE 3001 + +ENTRYPOINT [ "bash", "/docker-entrypoint.d/env.sh" ] diff --git a/GUI/Dockerfile.dev b/GUI/Dockerfile.dev new file mode 100644 index 00000000..48b7890e --- /dev/null +++ b/GUI/Dockerfile.dev @@ -0,0 +1,14 @@ +FROM node:22.0.0-alpine AS image +WORKDIR /app +COPY ./package.json . + +FROM image AS build +RUN npm install --legacy-peer-deps --mode=development +COPY . . +RUN ./node_modules/.bin/vite build --mode=development + +EXPOSE 3001 + +ENV REACT_APP_ENABLE_HIDDEN_FEATURES TRUE + +CMD ["npm", "run", "dev"] diff --git a/GUI/Dockerfile.test b/GUI/Dockerfile.test new file mode 100644 index 00000000..cb8261f2 --- /dev/null +++ b/GUI/Dockerfile.test @@ -0,0 +1,17 @@ +FROM node:lts AS build +WORKDIR /app +COPY ./package.json . +RUN npm install --legacy-peer-deps --mode=development +COPY . . +RUN ./node_modules/.bin/vite build --mode=development + +FROM nginx:1.25.4-alpine +COPY --from=build /app /usr/share/nginx/html +#COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 3001 +EXPOSE 80 + +ENV REACT_APP_ENABLE_HIDDEN_FEATURES TRUE + +CMD ["nginx", "-g", "daemon off;"] diff --git a/GUI/docker-compose.yml b/GUI/docker-compose.yml new file mode 100644 index 00000000..87d6970c --- /dev/null +++ b/GUI/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.9" +services: + buerokratt_chatbot: + container_name: buerokratt_classifier + build: + context: . + target: web + entrypoint: "/opt/buerokratt-chatbot/rebuild.sh" + ports: + - '3001:3001' diff --git a/GUI/entrypoint.sh b/GUI/entrypoint.sh new file mode 100755 index 00000000..636848f7 --- /dev/null +++ b/GUI/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Replace environment variables in the Nginx configuration template +envsubst '$BASE_URL $REACT_APP_RUUTER_API_URL $REACT_APP_RUUTER_V1_PRIVATE_API_URL $REACT_APP_RUUTER_V2_PRIVATE_API_URL $REACT_APP_CUSTOMER_SERVICE_LOGIN $CHOKIDAR_USEPOLLING $PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf + +# Start the Nginx server +nginx -g "daemon off;" diff --git a/GUI/i18n.ts b/GUI/i18n.ts new file mode 100644 index 00000000..6a4593d0 --- /dev/null +++ b/GUI/i18n.ts @@ -0,0 +1,26 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +import commonEN from './translations/en/common.json'; +import commonET from './translations/et/common.json'; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + debug: import.meta.env.NODE_ENV === 'development', + fallbackLng: 'et', + supportedLngs: ['et','en'], + resources: { + en: { + common: commonEN, + }, + et: { + common: commonET, + }, + }, + defaultNS: 'common', + }); + +export default i18n; diff --git a/GUI/index.html b/GUI/index.html new file mode 100644 index 00000000..047cff35 --- /dev/null +++ b/GUI/index.html @@ -0,0 +1,14 @@ + + + + + + + Bürokratt + + +
+
+ + + diff --git a/GUI/nginx/http-nginx.conf b/GUI/nginx/http-nginx.conf new file mode 100644 index 00000000..c246c74b --- /dev/null +++ b/GUI/nginx/http-nginx.conf @@ -0,0 +1,21 @@ +server { + server_name localhost; + listen 3001; # replace port depends on deploy environment + + server_tokens off; + add_header Access-Control-Allow-Origin *; + + location / { + root /usr/share/nginx/html/buerokratt-chatbot; + try_files $uri /index.html; + } + + location /status { + access_log off; + default_type text/plain; + add_header Content-Type text/plain; + return 200 "alive"; + } + + rewrite ^/$ http://$host:$server_port/chat permanent; # replace port depends on deploy environment +} diff --git a/GUI/nginx/https-nginx.conf b/GUI/nginx/https-nginx.conf new file mode 100644 index 00000000..efd2b2ca --- /dev/null +++ b/GUI/nginx/https-nginx.conf @@ -0,0 +1,26 @@ +server { + server_name localhost; + listen 443 ssl; + ssl_certificate /etc/tls/tls.crt; + ssl_certificate_key /etc/tls/tls.key; + + server_tokens off; + + location / { + root /usr/share/nginx/html/buerokratt-chatbot; + try_files $uri /index.html; + } + + location /status { + access_log off; + default_type text/plain; + add_header Content-Type text/plain; + return 200 "alive"; + } +} + +server { + listen 3001; + server_name localhost; + return 301 https://$host$request_uri; +} diff --git a/GUI/nginx/nginx.conf b/GUI/nginx/nginx.conf new file mode 100644 index 00000000..d28f7e8d --- /dev/null +++ b/GUI/nginx/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } + +} diff --git a/GUI/nginx/scripts/env.sh b/GUI/nginx/scripts/env.sh new file mode 100755 index 00000000..de80ffdc --- /dev/null +++ b/GUI/nginx/scripts/env.sh @@ -0,0 +1,12 @@ +#!/bin/sh +for envrow in $(printenv); +do + IFS='=' read -r key value <<< "${envrow}" + if [[ $key == "REACT_APP_"* ]]; then + for file in $(find /usr/share/nginx/html -type f \( -name '*.js' -o -name '*.css' \)) + do + sed -i "s|{}.${key}|\"${value}\"|g" $file; + done + fi +done +[ -z "$@" ] && nginx -g 'daemon off;' || $@ diff --git a/GUI/package-lock.json b/GUI/package-lock.json new file mode 100644 index 00000000..1453e606 --- /dev/null +++ b/GUI/package-lock.json @@ -0,0 +1,14964 @@ +{ + "name": "byk-training-module-gui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "byk-training-module-gui", + "version": "0.0.0", + "dependencies": { + "@buerokratt-ria/styles": "^0.0.1", + "@fontsource/roboto": "^4.5.8", + "@formkit/auto-animate": "^1.0.0-beta.5", + "@fortaine/fetch-event-source": "^3.0.6", + "@radix-ui/react-accessible-icon": "^1.0.1", + "@radix-ui/react-collapsible": "^1.0.1", + "@radix-ui/react-dialog": "^1.0.2", + "@radix-ui/react-popover": "^1.0.2", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-select": "^1.1.2", + "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.1", + "@radix-ui/react-toast": "^1.1.2", + "@radix-ui/react-tooltip": "^1.0.2", + "@tanstack/match-sorter-utils": "^8.7.2", + "@tanstack/react-query": "^4.20.4", + "@tanstack/react-table": "^8.7.4", + "axios": "^1.2.1", + "clsx": "^1.2.1", + "date-fns": "^2.29.3", + "downshift": "^7.0.5", + "esbuild": "^0.19.5", + "formik": "^2.4.6", + "framer-motion": "^8.5.5", + "howler": "^2.2.4", + "i18next": "^22.4.5", + "i18next-browser-languagedetector": "^7.0.1", + "linkify-react": "^4.1.1", + "linkifyjs": "^4.1.1", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "react": "^18.2.0", + "react-color": "^2.19.3", + "react-cookie": "^4.1.1", + "react-datepicker": "^4.8.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^18.2.0", + "react-hook-form": "^7.52.1", + "react-i18next": "^12.1.1", + "react-icons": "^4.10.1", + "react-idle-timer": "^5.5.2", + "react-modal": "^3.16.1", + "react-redux": "^8.1.1", + "react-router-dom": "^6.5.0", + "react-select": "^5.7.4", + "react-text-selection-popover": "^2.0.2", + "react-textarea-autosize": "^8.4.0", + "reactflow": "^11.4.0", + "regexify-string": "^1.0.19", + "rxjs": "^7.8.1", + "timeago.js": "^4.0.2", + "usehooks-ts": "^2.9.1", + "uuid": "^9.0.0", + "yup": "^1.4.0", + "zustand": "^4.4.4" + }, + "devDependencies": { + "@types/howler": "^2.2.11", + "@types/lodash": "^4.14.191", + "@types/lodash.debounce": "^4.0.7", + "@types/node": "^18.11.17", + "@types/react": "^18.0.26", + "@types/react-color": "^3.0.6", + "@types/react-datepicker": "^4.8.0", + "@types/react-dom": "^18.0.9", + "@types/uuid": "^9.0.2", + "@vitejs/plugin-react": "^3.0.0", + "eslint": "^8.30.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-react": "^7.31.11", + "eslint-plugin-typescript": "^0.14.0", + "mocksse": "^1.0.4", + "msw": "^0.49.2", + "prettier": "^2.8.1", + "sass": "^1.57.0", + "typescript": "^4.9.3", + "vite": "^4.0.0", + "vite-plugin-env-compatible": "^1.1.1", + "vite-plugin-svgr": "^2.4.0", + "vite-plugin-transform": "^2.0.1", + "vite-tsconfig-paths": "^4.0.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", + "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/eslint-parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.7.tgz", + "integrity": "sha512-SO5E3bVxDuxyNxM5agFv480YA2HO6ohZbGxbazZdIk3KQOPOGVNw6q78I9/lbviIf95eq6tPozeYnJLbjnC8IA==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "dependencies": { + "@babel/types": "^7.24.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", + "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", + "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "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-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-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", + "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", + "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", + "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-wrap-function": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", + "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", + "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", + "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", + "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^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-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.24.7", + "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.24.7.tgz", + "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", + "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^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-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", + "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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/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-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-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.7.tgz", + "integrity": "sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==", + "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-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-flow": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz", + "integrity": "sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw==", + "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-syntax-import-assertions": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", + "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "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-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", + "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7", + "@babel/plugin-syntax-async-generators": "^7.8.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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", + "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "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-class-properties": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", + "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", + "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "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.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", + "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "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-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-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-flow-strip-types": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz", + "integrity": "sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-flow": "^7.24.7" + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", + "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-function-name": "^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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", + "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", + "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", + "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" + }, + "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.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", + "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", + "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-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-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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", + "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", + "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-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", + "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", + "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.24.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==", + "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-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==", + "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-pure-annotations": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", + "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^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-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-runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "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.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", + "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "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-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz", + "integrity": "sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==", + "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-typescript": "^7.24.7" + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", + "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "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/preset-env": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", + "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@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.24.7", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@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.24.7", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.24.7", + "@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.7", + "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@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.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@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.7", + "@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.24.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", + "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.24.7", + "@babel/plugin-transform-react-jsx-development": "^7.24.7", + "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", + "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.24.7" + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@buerokratt-ria/styles": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@buerokratt-ria/styles/-/styles-0.0.1.tgz", + "integrity": "sha512-bSj7WsdQO4P/43mRgsa5sDEwBuOebXcl3+Peur8NwToqczqsTMbXSO5P6xyXHoTnHWt082PhT8ht7OAgtFSzfw==" + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/babel-plugin/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/@emotion/babel-plugin/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/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "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.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "dev": true, + "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, + "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/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", + "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz", + "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==", + "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.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, + "node_modules/@fontsource/roboto": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.8.tgz", + "integrity": "sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==" + }, + "node_modules/@formkit/auto-animate": { + "version": "1.0.0-pre-alpha.3", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-1.0.0-pre-alpha.3.tgz", + "integrity": "sha512-lMVZ3LFUIu0RIxCEwmV8nUUJQ46M2bv2NDU3hrhZivViuR1EheC8Mj5sx/ACqK5QLK8XB8z7GDIZBUGdU/9OZQ==", + "peerDependencies": { + "react": "^16.8.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@fortaine/fetch-event-source": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz", + "integrity": "sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw==", + "engines": { + "node": ">=16.15" + } + }, + "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, + "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, + "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 + }, + "node_modules/@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "peerDependencies": { + "react": "*" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "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/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/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/lr": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz", + "integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.8.5.tgz", + "integrity": "sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.8.5.tgz", + "integrity": "sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.8.5.tgz", + "integrity": "sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.8.5.tgz", + "integrity": "sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.8.5.tgz", + "integrity": "sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.8.5.tgz", + "integrity": "sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mischnic/json-sourcemap": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", + "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/lr": "^1.0.0", + "json5": "^2.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@motionone/animation": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.18.0.tgz", + "integrity": "sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==", + "dependencies": { + "@motionone/animation": "^10.18.0", + "@motionone/generators": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==" + }, + "node_modules/@motionone/utils": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mswjs/cookies": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", + "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "dev": true, + "dependencies": { + "@types/set-cookie-parser": "^2.4.0", + "set-cookie-parser": "^2.4.6" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.17.10", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", + "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "dev": true, + "dependencies": { + "@open-draft/until": "^1.0.3", + "@types/debug": "^4.1.7", + "@xmldom/xmldom": "^0.8.3", + "debug": "^4.3.3", + "headers-polyfill": "3.2.5", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.2.4", + "web-encoding": "^1.1.5" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@mswjs/interceptors/node_modules/headers-polyfill": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.2.5.tgz", + "integrity": "sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==", + "dev": true + }, + "node_modules/@mswjs/interceptors/node_modules/strict-event-emitter": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", + "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", + "dev": true, + "dependencies": { + "events": "^3.3.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.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/@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, + "node_modules/@parcel/bundler-default": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.12.0.tgz", + "integrity": "sha512-3ybN74oYNMKyjD6V20c9Gerdbh7teeNvVMwIoHIQMzuIFT6IGX53PyOLlOKRLbjxMc0TMimQQxIt2eQqxR5LsA==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/graph": "3.2.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/cache": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.12.0.tgz", + "integrity": "sha512-FX5ZpTEkxvq/yvWklRHDESVRz+c7sLTXgFuzz6uEnBcXV38j6dMSikflNpHA6q/L4GKkCqRywm9R6XQwhwIMyw==", + "dependencies": { + "@parcel/fs": "2.12.0", + "@parcel/logger": "2.12.0", + "@parcel/utils": "2.12.0", + "lmdb": "2.8.5" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/codeframe": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.12.0.tgz", + "integrity": "sha512-v2VmneILFiHZJTxPiR7GEF1wey1/IXPdZMcUlNXBiPZyWDfcuNgGGVQkx/xW561rULLIvDPharOMdxz5oHOKQg==", + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/codeframe/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/@parcel/codeframe/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/@parcel/codeframe/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/@parcel/codeframe/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/@parcel/codeframe/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/@parcel/codeframe/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/@parcel/compressor-raw": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.12.0.tgz", + "integrity": "sha512-h41Q3X7ZAQ9wbQ2csP8QGrwepasLZdXiuEdpUryDce6rF9ZiHoJ97MRpdLxOhOPyASTw/xDgE1xyaPQr0Q3f5A==", + "dependencies": { + "@parcel/plugin": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/config-default": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.12.0.tgz", + "integrity": "sha512-dPNe2n9eEsKRc1soWIY0yToMUPirPIa2QhxcCB3Z5RjpDGIXm0pds+BaiqY6uGLEEzsjhRO0ujd4v2Rmm0vuFg==", + "dependencies": { + "@parcel/bundler-default": "2.12.0", + "@parcel/compressor-raw": "2.12.0", + "@parcel/namer-default": "2.12.0", + "@parcel/optimizer-css": "2.12.0", + "@parcel/optimizer-htmlnano": "2.12.0", + "@parcel/optimizer-image": "2.12.0", + "@parcel/optimizer-svgo": "2.12.0", + "@parcel/optimizer-swc": "2.12.0", + "@parcel/packager-css": "2.12.0", + "@parcel/packager-html": "2.12.0", + "@parcel/packager-js": "2.12.0", + "@parcel/packager-raw": "2.12.0", + "@parcel/packager-svg": "2.12.0", + "@parcel/packager-wasm": "2.12.0", + "@parcel/reporter-dev-server": "2.12.0", + "@parcel/resolver-default": "2.12.0", + "@parcel/runtime-browser-hmr": "2.12.0", + "@parcel/runtime-js": "2.12.0", + "@parcel/runtime-react-refresh": "2.12.0", + "@parcel/runtime-service-worker": "2.12.0", + "@parcel/transformer-babel": "2.12.0", + "@parcel/transformer-css": "2.12.0", + "@parcel/transformer-html": "2.12.0", + "@parcel/transformer-image": "2.12.0", + "@parcel/transformer-js": "2.12.0", + "@parcel/transformer-json": "2.12.0", + "@parcel/transformer-postcss": "2.12.0", + "@parcel/transformer-posthtml": "2.12.0", + "@parcel/transformer-raw": "2.12.0", + "@parcel/transformer-react-refresh-wrap": "2.12.0", + "@parcel/transformer-svg": "2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/core": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.12.0.tgz", + "integrity": "sha512-s+6pwEj+GfKf7vqGUzN9iSEPueUssCCQrCBUlcAfKrJe0a22hTUCjewpB0I7lNrCIULt8dkndD+sMdOrXsRl6Q==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/cache": "2.12.0", + "@parcel/diagnostic": "2.12.0", + "@parcel/events": "2.12.0", + "@parcel/fs": "2.12.0", + "@parcel/graph": "3.2.0", + "@parcel/logger": "2.12.0", + "@parcel/package-manager": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/profiler": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "@parcel/workers": "2.12.0", + "abortcontroller-polyfill": "^1.1.9", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^7.0.0", + "dotenv-expand": "^5.1.0", + "json5": "^2.2.0", + "msgpackr": "^1.9.9", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/core/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/diagnostic": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.12.0.tgz", + "integrity": "sha512-8f1NOsSFK+F4AwFCKynyIu9Kr/uWHC+SywAv4oS6Bv3Acig0gtwUjugk0C9UaB8ztBZiW5TQZhw+uPZn9T/lJA==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/events": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.12.0.tgz", + "integrity": "sha512-nmAAEIKLjW1kB2cUbCYSmZOGbnGj8wCzhqnK727zCCWaA25ogzAtt657GPOeFyqW77KyosU728Tl63Fc8hphIA==", + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/fs": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.12.0.tgz", + "integrity": "sha512-NnFkuvou1YBtPOhTdZr44WN7I60cGyly2wpHzqRl62yhObyi1KvW0SjwOMa0QGNcBOIzp4G0CapoZ93hD0RG5Q==", + "dependencies": { + "@parcel/rust": "2.12.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "@parcel/watcher": "^2.0.7", + "@parcel/workers": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/graph": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-3.2.0.tgz", + "integrity": "sha512-xlrmCPqy58D4Fg5umV7bpwDx5Vyt7MlnQPxW68vae5+BA4GSWetfZt+Cs5dtotMG2oCHzZxhIPt7YZ7NRyQzLA==", + "dependencies": { + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/logger": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.12.0.tgz", + "integrity": "sha512-cJ7Paqa7/9VJ7C+KwgJlwMqTQBOjjn71FbKk0G07hydUEBISU2aDfmc/52o60ErL9l+vXB26zTrIBanbxS8rVg==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/events": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.12.0.tgz", + "integrity": "sha512-WZz3rzL8k0H3WR4qTHX6Ic8DlEs17keO9gtD4MNGyMNQbqQEvQ61lWJaIH0nAtgEetu0SOITiVqdZrb8zx/M7w==", + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi/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/@parcel/markdown-ansi/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/@parcel/markdown-ansi/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/@parcel/markdown-ansi/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/@parcel/markdown-ansi/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/@parcel/markdown-ansi/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/@parcel/namer-default": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.12.0.tgz", + "integrity": "sha512-9DNKPDHWgMnMtqqZIMiEj/R9PNWW16lpnlHjwK3ciRlMPgjPJ8+UNc255teZODhX0T17GOzPdGbU/O/xbxVPzA==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-3.3.0.tgz", + "integrity": "sha512-rhPW9DYPEIqQBSlYzz3S0AjXxjN6Ub2yS6tzzsW/4S3Gpsgk/uEq4ZfxPvoPf/6TgZndVxmKwpmxaKtGMmf3cA==", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/diagnostic": "2.12.0", + "@parcel/fs": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/optimizer-css": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.12.0.tgz", + "integrity": "sha512-ifbcC97fRzpruTjaa8axIFeX4MjjSIlQfem3EJug3L2AVqQUXnM1XO8L0NaXGNLTW2qnh1ZjIJ7vXT/QhsphsA==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-htmlnano": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.12.0.tgz", + "integrity": "sha512-MfPMeCrT8FYiOrpFHVR+NcZQlXAptK2r4nGJjfT+ndPBhEEZp4yyL7n1y7HfX9geg5altc4WTb4Gug7rCoW8VQ==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-image": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.12.0.tgz", + "integrity": "sha512-bo1O7raeAIbRU5nmNVtx8divLW9Xqn0c57GVNGeAK4mygnQoqHqRZ0mR9uboh64pxv6ijXZHPhKvU9HEpjPjBQ==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/utils": "2.12.0", + "@parcel/workers": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/optimizer-svgo": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.12.0.tgz", + "integrity": "sha512-Kyli+ZZXnoonnbeRQdoWwee9Bk2jm/49xvnfb+2OO8NN0d41lblBoRhOyFiScRnJrw7eVl1Xrz7NTkXCIO7XFQ==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "svgo": "^2.4.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-swc": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-swc/-/optimizer-swc-2.12.0.tgz", + "integrity": "sha512-iBi6LZB3lm6WmbXfzi8J3DCVPmn4FN2lw7DGXxUXu7MouDPVWfTsM6U/5TkSHJRNRogZ2gqy5q9g34NPxHbJcw==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "@swc/core": "^1.3.36", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/package-manager": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.12.0.tgz", + "integrity": "sha512-0nvAezcjPx9FT+hIL+LS1jb0aohwLZXct7jAh7i0MLMtehOi0z1Sau+QpgMlA9rfEZZ1LIeFdnZZwqSy7Ccspw==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/fs": "2.12.0", + "@parcel/logger": "2.12.0", + "@parcel/node-resolver-core": "3.3.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "@parcel/workers": "2.12.0", + "@swc/core": "^1.3.36", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/package-manager/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/packager-css": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.12.0.tgz", + "integrity": "sha512-j3a/ODciaNKD19IYdWJT+TP+tnhhn5koBGBWWtrKSu0UxWpnezIGZetit3eE+Y9+NTePalMkvpIlit2eDhvfJA==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-html": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.12.0.tgz", + "integrity": "sha512-PpvGB9hFFe+19NXGz2ApvPrkA9GwEqaDAninT+3pJD57OVBaxB8U+HN4a5LICKxjUppPPqmrLb6YPbD65IX4RA==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.12.0.tgz", + "integrity": "sha512-viMF+FszITRRr8+2iJyk+4ruGiL27Y6AF7hQ3xbJfzqnmbOhGFtLTQwuwhOLqN/mWR2VKdgbLpZSarWaO3yAMg==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@parcel/packager-js/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@parcel/packager-raw": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.12.0.tgz", + "integrity": "sha512-tJZqFbHqP24aq1F+OojFbQIc09P/u8HAW5xfndCrFnXpW4wTgM3p03P0xfw3gnNq+TtxHJ8c3UFE5LnXNNKhYA==", + "dependencies": { + "@parcel/plugin": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-svg": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.12.0.tgz", + "integrity": "sha512-ldaGiacGb2lLqcXas97k8JiZRbAnNREmcvoY2W2dvW4loVuDT9B9fU777mbV6zODpcgcHWsLL3lYbJ5Lt3y9cg==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "posthtml": "^0.16.4" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-wasm": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/packager-wasm/-/packager-wasm-2.12.0.tgz", + "integrity": "sha512-fYqZzIqO9fGYveeImzF8ll6KRo2LrOXfD+2Y5U3BiX/wp9wv17dz50QLDQm9hmTcKGWxK4yWqKQh+Evp/fae7A==", + "dependencies": { + "@parcel/plugin": "2.12.0" + }, + "engines": { + "node": ">=12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/plugin": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.12.0.tgz", + "integrity": "sha512-nc/uRA8DiMoe4neBbzV6kDndh/58a4wQuGKw5oEoIwBCHUvE2W8ZFSu7ollSXUGRzfacTt4NdY8TwS73ScWZ+g==", + "dependencies": { + "@parcel/types": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/profiler": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/profiler/-/profiler-2.12.0.tgz", + "integrity": "sha512-q53fvl5LDcFYzMUtSusUBZSjQrKjMlLEBgKeQHFwkimwR1mgoseaDBDuNz0XvmzDzF1UelJ02TUKCGacU8W2qA==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/events": "2.12.0", + "chrome-trace-event": "^1.0.2" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.12.0.tgz", + "integrity": "sha512-TqKsH4GVOLPSCanZ6tcTPj+rdVHERnt5y4bwTM82cajM21bCX1Ruwp8xOKU+03091oV2pv5ieB18pJyRF7IpIw==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "chalk": "^4.1.0", + "term-size": "^2.2.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli/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/@parcel/reporter-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==", + "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/@parcel/reporter-cli/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/@parcel/reporter-cli/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/@parcel/reporter-cli/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/@parcel/reporter-cli/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/@parcel/reporter-dev-server": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.12.0.tgz", + "integrity": "sha512-tIcDqRvAPAttRlTV28dHcbWT5K2r/MBFks7nM4nrEDHWtnrCwimkDmZTc1kD8QOCCjGVwRHcQybpHvxfwol6GA==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-tracer": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/reporter-tracer/-/reporter-tracer-2.12.0.tgz", + "integrity": "sha512-g8rlu9GxB8Ut/F8WGx4zidIPQ4pcYFjU9bZO+fyRIPrSUFH2bKijCnbZcr4ntqzDGx74hwD6cCG4DBoleq2UlQ==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "chrome-trace-event": "^1.0.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/resolver-default": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.12.0.tgz", + "integrity": "sha512-uuhbajTax37TwCxu7V98JtRLiT6hzE4VYSu5B7Qkauy14/WFt2dz6GOUXPgVsED569/hkxebPx3KCMtZW6cHHA==", + "dependencies": { + "@parcel/node-resolver-core": "3.3.0", + "@parcel/plugin": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-browser-hmr": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.12.0.tgz", + "integrity": "sha512-4ZLp2FWyD32r0GlTulO3+jxgsA3oO1P1b5oO2IWuWilfhcJH5LTiazpL5YdusUjtNn9PGN6QLAWfxmzRIfM+Ow==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-js": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.12.0.tgz", + "integrity": "sha512-sBerP32Z1crX5PfLNGDSXSdqzlllM++GVnVQVeM7DgMKS8JIFG3VLi28YkX+dYYGtPypm01JoIHCkvwiZEcQJg==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.12.0.tgz", + "integrity": "sha512-SCHkcczJIDFTFdLTzrHTkQ0aTrX3xH6jrA4UsCBL6ji61+w+ohy4jEEe9qCgJVXhnJfGLE43HNXek+0MStX+Mw==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "react-error-overlay": "6.0.9", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh/node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/runtime-service-worker": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.12.0.tgz", + "integrity": "sha512-BXuMBsfiwpIEnssn+jqfC3jkgbS8oxeo3C7xhSQsuSv+AF2FwY3O3AO1c1RBskEW3XrBLNINOJujroNw80VTKA==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/rust": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/rust/-/rust-2.12.0.tgz", + "integrity": "sha512-005cldMdFZFDPOjbDVEXcINQ3wT4vrxvSavRWI3Az0e3E18exO/x/mW9f648KtXugOXMAqCEqhFHcXECL9nmMw==", + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/source-map": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", + "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": "^12.18.3 || >=14" + } + }, + "node_modules/@parcel/transformer-babel": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.12.0.tgz", + "integrity": "sha512-zQaBfOnf/l8rPxYGnsk/ufh/0EuqvmnxafjBIpKZ//j6rGylw5JCqXSb1QvvAqRYruKeccxGv7+HrxpqKU6V4A==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-babel/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/transformer-css": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.12.0.tgz", + "integrity": "sha512-vXhOqoAlQGATYyQ433Z1DXKmiKmzOAUmKysbYH3FD+LKEKLMEl/pA14goqp00TW+A/EjtSKKyeMyHlMIIUqj4Q==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.12.0.tgz", + "integrity": "sha512-5jW4dFFBlYBvIQk4nrH62rfA/G/KzVzEDa6S+Nne0xXhglLjkm64Ci9b/d4tKZfuGWUbpm2ASAq8skti/nfpXw==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2", + "srcset": "4" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/transformer-image": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.12.0.tgz", + "integrity": "sha512-8hXrGm2IRII49R7lZ0RpmNk27EhcsH+uNKsvxuMpXPuEnWgC/ha/IrjaI29xCng1uGur74bJF43NUSQhR4aTdw==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "@parcel/workers": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/transformer-js": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.12.0.tgz", + "integrity": "sha512-OSZpOu+FGDbC/xivu24v092D9w6EGytB3vidwbdiJ2FaPgfV7rxS0WIUjH4I0OcvHAcitArRXL0a3+HrNTdQQw==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.12.0", + "@parcel/workers": "2.12.0", + "@swc/helpers": "^0.5.0", + "browserslist": "^4.6.6", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.13.7", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "node_modules/@parcel/transformer-js/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/@parcel/transformer-js/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/transformer-json": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.12.0.tgz", + "integrity": "sha512-Utv64GLRCQILK5r0KFs4o7I41ixMPllwOLOhkdjJKvf1hZmN6WqfOmB1YLbWS/y5Zb/iB52DU2pWZm96vLFQZQ==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "json5": "^2.2.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.12.0.tgz", + "integrity": "sha512-FZqn+oUtiLfPOn67EZxPpBkfdFiTnF4iwiXPqvst3XI8H+iC+yNgzmtJkunOOuylpYY6NOU5jT8d7saqWSDv2Q==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/utils": "2.12.0", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.12.0.tgz", + "integrity": "sha512-z6Z7rav/pcaWdeD+2sDUcd0mmNZRUvtHaUGa50Y2mr+poxrKilpsnFMSiWBT+oOqPt7j71jzDvrdnAF4XkCljg==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/transformer-raw": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.12.0.tgz", + "integrity": "sha512-Ht1fQvXxix0NncdnmnXZsa6hra20RXYh1VqhBYZLsDfkvGGFnXIgO03Jqn4Z8MkKoa0tiNbDhpKIeTjyclbBxQ==", + "dependencies": { + "@parcel/plugin": "2.12.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.12.0.tgz", + "integrity": "sha512-GE8gmP2AZtkpBIV5vSCVhewgOFRhqwdM5Q9jNPOY5PKcM3/Ff0qCqDiTzzGLhk0/VMBrdjssrfZkVx6S/lHdJw==", + "dependencies": { + "@parcel/plugin": "2.12.0", + "@parcel/utils": "2.12.0", + "react-refresh": "^0.9.0" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap/node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/transformer-svg": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.12.0.tgz", + "integrity": "sha512-cZJqGRJ4JNdYcb+vj94J7PdOuTnwyy45dM9xqbIMH+HSiiIkfrMsdEwYft0GTyFTdsnf+hdHn3tau7Qa5hhX+A==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/plugin": "2.12.0", + "@parcel/rust": "2.12.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.12.0.tgz", + "integrity": "sha512-8zAFiYNCwNTQcglIObyNwKfRYQK5ELlL13GuBOrSMxueUiI5ylgsGbTS1N7J3dAGZixHO8KhHGv5a71FILn9rQ==", + "dependencies": { + "@parcel/cache": "2.12.0", + "@parcel/diagnostic": "2.12.0", + "@parcel/fs": "2.12.0", + "@parcel/package-manager": "2.12.0", + "@parcel/source-map": "^2.1.1", + "@parcel/workers": "2.12.0", + "utility-types": "^3.10.0" + } + }, + "node_modules/@parcel/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-z1JhLuZ8QmDaYoEIuUCVZlhcFrS7LMfHrb2OCRui5SQFntRWBH2fNM6H/fXXUkT9SkxcuFP2DUA6/m4+Gkz72g==", + "dependencies": { + "@parcel/codeframe": "2.12.0", + "@parcel/diagnostic": "2.12.0", + "@parcel/logger": "2.12.0", + "@parcel/markdown-ansi": "2.12.0", + "@parcel/rust": "2.12.0", + "@parcel/source-map": "^2.1.1", + "chalk": "^4.1.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/utils/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/@parcel/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==", + "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/@parcel/utils/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/@parcel/utils/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/@parcel/utils/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/@parcel/utils/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/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "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.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "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.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/workers": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.12.0.tgz", + "integrity": "sha512-zv5We5Jmb+ZWXlU6A+AufyjY4oZckkxsZ8J4dvyWL0W8IQvGO1JB4FGeryyttzQv3RM3OxcN/BpTGPiDG6keBw==", + "dependencies": { + "@parcel/diagnostic": "2.12.0", + "@parcel/logger": "2.12.0", + "@parcel/profiler": "2.12.0", + "@parcel/types": "2.12.0", + "@parcel/utils": "2.12.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.12.0" + } + }, + "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/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-accessible-icon": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.0.3.tgz", + "integrity": "sha512-duVGKeWPSUILr/MdlPxV+GeULTc2rS1aihGdQ3N2qCUPMgxYLxvAsHJM3mCVLF8d5eK+ympmB22mb1F3a5biNw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", + "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", + "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz", + "integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==", + "dependencies": { + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", + "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", + "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz", + "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz", + "integrity": "sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz", + "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", + "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, + "node_modules/@reactflow/background": { + "version": "11.3.13", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.13.tgz", + "integrity": "sha512-hkvpVEhgvfTDyCvdlitw4ioKCYLaaiRXnuEG+1QM3Np+7N1DiWF1XOv5I8AFyNoJL07yXEkbECUTsHvkBvcG5A==", + "dependencies": { + "@reactflow/core": "11.11.3", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.13", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.13.tgz", + "integrity": "sha512-3xgEg6ALIVkAQCS4NiBjb7ad8Cb3D8CtA7Vvl4Hf5Ar2PIVs6FOaeft9s2iDZGtsWP35ECDYId1rIFVhQL8r+A==", + "dependencies": { + "@reactflow/core": "11.11.3", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.3.tgz", + "integrity": "sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA==", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.13", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.13.tgz", + "integrity": "sha512-m2MvdiGSyOu44LEcERDEl1Aj6x//UQRWo3HEAejNU4HQTlJnYrSN8tgrYF8TxC1+c/9UdyzQY5VYgrTwW4QWdg==", + "dependencies": { + "@reactflow/core": "11.11.3", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.13.tgz", + "integrity": "sha512-X7ceQ2s3jFLgbkg03n2RYr4hm3jTVrzkW2W/8ANv/SZfuVmF8XJxlERuD8Eka5voKqLda0ywIZGAbw9GoHLfUQ==", + "dependencies": { + "@reactflow/core": "11.11.3", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.13.tgz", + "integrity": "sha512-aknvNICO10uWdthFSpgD6ctY/CTBeJUMV9co8T9Ilugr08Nb89IQ4uD0dPmr031ewMQxixtYIkw+sSDDzd2aaQ==", + "dependencies": { + "@reactflow/core": "11.11.3", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@remix-run/router": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", + "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "engines": { + "node": ">=14.0.0" + } + }, + "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/@rushstack/eslint-patch": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", + "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", + "dev": true + }, + "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/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/hast-util-to-babel-ast/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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/core": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.5.28.tgz", + "integrity": "sha512-muCdNIqOTURUgYeyyOLYE3ShL8SZO6dw6bhRm6dCvxWzCZOncPc5fB0kjcPXTML+9KJoHL7ks5xg+vsQK+v6ig==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.8" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.5.28", + "@swc/core-darwin-x64": "1.5.28", + "@swc/core-linux-arm-gnueabihf": "1.5.28", + "@swc/core-linux-arm64-gnu": "1.5.28", + "@swc/core-linux-arm64-musl": "1.5.28", + "@swc/core-linux-x64-gnu": "1.5.28", + "@swc/core-linux-x64-musl": "1.5.28", + "@swc/core-win32-arm64-msvc": "1.5.28", + "@swc/core-win32-ia32-msvc": "1.5.28", + "@swc/core-win32-x64-msvc": "1.5.28" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.5.28.tgz", + "integrity": "sha512-sP6g63ybzIdOWNDbn51tyHN8EMt7Mb4RMeHQEsXB7wQfDvzhpWB+AbfK6Gs3Q8fwP/pmWIrWW9csKOc1K2Mmkg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.5.28.tgz", + "integrity": "sha512-Bd/agp/g7QocQG5AuorOzSC78t8OzeN+pCN/QvJj1CvPhvppjJw6e1vAbOR8vO2vvGi2pvtf3polrYQStJtSiA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.5.28.tgz", + "integrity": "sha512-Wr3TwPGIveS9/OBWm0r9VAL8wkCR0zQn46J8K01uYCmVhUNK3Muxjs0vQBZaOrGu94mqbj9OXY+gB3W7aDvGdA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.5.28.tgz", + "integrity": "sha512-8G1ZwVTuLgTAVTMPD+M97eU6WeiRIlGHwKZ5fiJHPBcz1xqIC7jQcEh7XBkobkYoU5OILotls3gzjRt8CMNyDQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.5.28.tgz", + "integrity": "sha512-0Ajdzb5Fzvz+XUbN5ESeHAz9aHHSYiQcm+vmsDi0TtPHmsalfnqEPZmnK0zPALPJPLQP2dDo4hELeDg3/c3xgA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.5.28.tgz", + "integrity": "sha512-ueQ9VejnQUM2Pt+vT0IAKoF4vYBWUP6n1KHGdILpoGe3LuafQrqu7RoyQ15C7/AYii7hAeNhTFdf6gLbg8cjFg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.5.28.tgz", + "integrity": "sha512-G5th8Mg0az8CbY4GQt9/m5hg2Y0kGIwvQBeVACuLQB6q2Y4txzdiTpjmFqUUhEvvl7Klyx1IHvNhfXs3zpt7PA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.5.28.tgz", + "integrity": "sha512-JezwCGavZ7CkNXx4yInI4kpb71L0zxzxA9BFlmnsGKEEjVQcKc3hFpmIzfFVs+eotlBUwDNb0+Yo9m6Cb7lllA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.5.28.tgz", + "integrity": "sha512-q8tW5J4RkOkl7vYShnWS//VAb2Ngolfm9WOMaF2GRJUr2Y/Xeb/+cNjdsNOqea2BzW049D5vdP7XPmir3/zUZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.5.28.tgz", + "integrity": "sha512-jap6EiB3wG1YE1hyhNr9KLPpH4PGm+5tVMfN0l7fgKtV0ikgpcEN/YF94tru+z5m2HovqYW009+Evq9dcVGmpg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.8.tgz", + "integrity": "sha512-RNFA3+7OJFNYY78x0FYwi1Ow+iF1eF5WvmfY1nXPOEH4R2p/D4Cr1vzje7dNAI2aLFqpv8Wyz4oKSWqIZArpQA==", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.15.1.tgz", + "integrity": "sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==", + "dependencies": { + "remove-accents": "0.5.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", + "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz", + "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==", + "dependencies": { + "@tanstack/query-core": "4.36.1", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.17.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.17.3.tgz", + "integrity": "sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==", + "dependencies": { + "@tanstack/table-core": "8.17.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.17.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.17.3.tgz", + "integrity": "sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "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-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "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-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "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-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "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-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" + }, + "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/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "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/howler": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.11.tgz", + "integrity": "sha512-7aBoUL6RbSIrqKnpEgfa1wSNUBK06mn08siP2QI0zYk7MXfEJAaORc4tohamQYqCqVESoDyRWSdQn2BOKWj2Qw==", + "dev": true + }, + "node_modules/@types/js-levenshtein": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.3.tgz", + "integrity": "sha512-jd+Q+sD20Qfu9e2aEXogiO3vpOC1PYJOUdyN9gvs4Qrvkg4wF43L5OhqrPeokdv8TL0/mXoYfpkcoGZMNN2pkQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/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/lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", + "dev": true + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", + "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz", + "integrity": "sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "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-color": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.12.tgz", + "integrity": "sha512-pr3uKE3lSvf7GFo1Rn2K3QktiZQFFrSgSGJ/3iMvSOYWt2pPAJ97rVdVfhWxYJZ8prAEXzoP2XX//3qGSQgu7Q==", + "dev": true, + "dependencies": { + "@types/react": "*", + "@types/reactcss": "*" + } + }, + "node_modules/@types/react-datepicker": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.6.tgz", + "integrity": "sha512-uH5fzxt9eXxnc+hDCy/iRSFqU2+9lR/q2lAmaG4WILMai1o3IOdpcV+VSypzBFJLTEC2jrfeDXcdol0CJVMq4g==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.9.2", + "@types/react": "*", + "date-fns": "^2.0.1", + "react-popper": "^2.2.5" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/reactcss": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.12.tgz", + "integrity": "sha512-BrXUQ86/wbbFiZv8h/Q1/Q1XOsaHneYmCb/tHe9+M8XBAAUc2EHfdY0DY22ZZjVSaXr5ix7j+zsqO2eGZub8lQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.9.tgz", + "integrity": "sha512-bCorlULvl0xTdjj4BPUHX4cqs9I+go2TfW/7Do1nnFYWS0CPP429Qr1AY42kiFhCwLpvAkWFr1XIBHd8j6/MCQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.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==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", + "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.12", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "magic-string": "^0.27.0", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.1.0-beta.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dev": true, + "optional": true + }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-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==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "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/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/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/axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "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-corejs3": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", + "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" + }, + "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-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "dev": true + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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==", + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "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==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "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.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "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": "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/caniuse-lite": { + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", + "funding": [ + { + "type": "opencollective", + "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/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "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==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cliui/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/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/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz", + "integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g==" + }, + "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/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "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/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", + "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "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-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/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/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/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "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/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-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "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-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "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": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "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-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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?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/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/downshift": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-7.6.2.tgz", + "integrity": "sha512-iOv+E1Hyt3JDdL9yYcOgW7nZ7GQ2Uz6YbggwXvKUSleetYhU2nXD482Rz6CzvM4lvI1At34BYruKAL4swRGxaA==", + "dependencies": { + "@babel/runtime": "^7.14.8", + "compute-scroll-into-view": "^2.0.4", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.12.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.799", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.799.tgz", + "integrity": "sha512-3D3DwWkRTzrdEpntY0hMLYwj7SeBk1138CkPE8sBDSj3WzrzOiG2rHm3luw8jucpf+WiyLBCZyU9lMHyQI9M9Q==" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "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.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "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/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": 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-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.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.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "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-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "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.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "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-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.34.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", + "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.hasown": "^1.1.4", + "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" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/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/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-testing-library": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.58.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-typescript": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz", + "integrity": "sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw==", + "deprecated": "Deprecated: Use @typescript-eslint/eslint-plugin instead", + "dev": true, + "dependencies": { + "requireindex": "~1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "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, + "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-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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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, + "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/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "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, + "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/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "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/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/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, + "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==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "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-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, + "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-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "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 + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/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/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/formik/node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "node_modules/framer-motion": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-8.5.5.tgz", + "integrity": "sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA==", + "dependencies": { + "@motionone/dom": "^10.15.3", + "hey-listen": "^1.0.8", + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "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==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "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-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-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "engines": { + "node": ">=6" + } + }, + "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/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true + }, + "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/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.8.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.2.tgz", + "integrity": "sha512-cvVIBILwuoSyD54U4cF/UXDh5yAobhNV/tPygI4lZhgOIJQE/WLWC4waBRb4I6bDVYb3OVx3lfHbaQOEoUD5sg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/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/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/headers-polyfill": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.3.0.tgz", + "integrity": "sha512-5e57etwBpNcDc0b6KCVWEh/Ro063OxPvzVimUdM0/tsYM/T7Hfy3kknIGj78SFTOhNd8AZY41U8mOHoO4LzmIQ==", + "dev": true + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, + "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/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==" + }, + "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/htmlnano": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz", + "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==", + "dependencies": { + "cosmiconfig": "^9.0.0", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^7.0.0", + "postcss": "^8.3.11", + "purgecss": "^6.0.0", + "relateurl": "^0.2.7", + "srcset": "5.0.1", + "svgo": "^3.0.2", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlnano/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/i18next": { + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz", + "integrity": "sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==", + "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.20.6" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz", + "integrity": "sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "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/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", + "dev": 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/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/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "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==", + "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==", + "dev": true, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "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/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.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/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-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "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-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-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-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==" + }, + "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-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, + "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==", + "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-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-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-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-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==", + "dev": true, + "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/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/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/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "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": "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 + }, + "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 + }, + "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": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.25.1.tgz", + "integrity": "sha512-V0RMVZzK1+rCHpymRv4URK2lNhIRyO8g7U7zOFwVAhJuat74HtkjIQpQRKNCwFEYkRGpafOpmXXLoaoBcyVtBg==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.25.1", + "lightningcss-darwin-x64": "1.25.1", + "lightningcss-freebsd-x64": "1.25.1", + "lightningcss-linux-arm-gnueabihf": "1.25.1", + "lightningcss-linux-arm64-gnu": "1.25.1", + "lightningcss-linux-arm64-musl": "1.25.1", + "lightningcss-linux-x64-gnu": "1.25.1", + "lightningcss-linux-x64-musl": "1.25.1", + "lightningcss-win32-x64-msvc": "1.25.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.25.1.tgz", + "integrity": "sha512-G4Dcvv85bs5NLENcu/s1f7ehzE3D5ThnlWSDwE190tWXRQCQaqwcuHe+MGSVI/slm0XrxnaayXY+cNl3cSricw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.25.1.tgz", + "integrity": "sha512-dYWuCzzfqRueDSmto6YU5SoGHvZTMU1Em9xvhcdROpmtOQLorurUZz8+xFxZ51lCO2LnYbfdjZ/gCqWEkwixNg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.25.1.tgz", + "integrity": "sha512-hXoy2s9A3KVNAIoKz+Fp6bNeY+h9c3tkcx1J3+pS48CqAt+5bI/R/YY4hxGL57fWAIquRjGKW50arltD6iRt/w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.25.1.tgz", + "integrity": "sha512-tWyMgHFlHlp1e5iW3EpqvH5MvsgoN7ZkylBbG2R2LWxnvH3FuWCJOhtGcYx9Ks0Kv0eZOBud789odkYLhyf1ng==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.25.1.tgz", + "integrity": "sha512-Xjxsx286OT9/XSnVLIsFEDyDipqe4BcLeB4pXQ/FEA5+2uWCCuAEarUNQumRucnj7k6ftkAHUEph5r821KBccQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.25.1.tgz", + "integrity": "sha512-IhxVFJoTW8wq6yLvxdPvyHv4NjzcpN1B7gjxrY3uaykQNXPHNIpChLB52+wfH+yS58zm1PL4LemUp8u9Cfp6Bw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.25.1.tgz", + "integrity": "sha512-RXIaru79KrREPEd6WLXfKfIp4QzoppZvD3x7vuTKkDA64PwTzKJ2jaC43RZHRt8BmyIkRRlmywNhTRMbmkPYpA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.25.1.tgz", + "integrity": "sha512-TdcNqFsAENEEFr8fJWg0Y4fZ/nwuqTRsIr7W7t2wmDUlA8eSXVepeeONYcb+gtTj1RaXn/WgNLB45SFkz+XBZA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.25.1.tgz", + "integrity": "sha512-9KZZkmmy9oGDSrnyHuxP6iMhbsgChUiu/NSgOx+U1I/wTngBStDf2i2aGRCHvFqj19HqqBEI4WuGVQBa2V6e0A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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-react": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.3.tgz", + "integrity": "sha512-rhI3zM/fxn5BfRPHfi4r9N7zgac4vOIxub1wHIWXLA5ENTMs+BGaIaFO1D1PhmxgwhIKmJz3H7uCP0Dg5JwSlA==", + "peerDependencies": { + "linkifyjs": "^4.0.0", + "react": ">= 15.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, + "node_modules/lmdb": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.8.5.tgz", + "integrity": "sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==", + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.9.5", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.1.1", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "2.8.5", + "@lmdb/lmdb-darwin-x64": "2.8.5", + "@lmdb/lmdb-linux-arm": "2.8.5", + "@lmdb/lmdb-linux-arm64": "2.8.5", + "@lmdb/lmdb-linux-x64": "2.8.5", + "@lmdb/lmdb-win32-x64": "2.8.5" + } + }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + }, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "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==" + }, + "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 + }, + "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==", + "dev": true, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "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/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/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/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mocksse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mocksse/-/mocksse-1.0.4.tgz", + "integrity": "sha512-W5DR/wwmx/EZUgjN1g+pvlhvFFtRJ3CqGRKqsK/B1hTxrjMb/t3JCbk6aomJD4WomrnueqMaTAhcAkIZJYd73w==", + "dev": true + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "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/msgpackr": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", + "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/msgpackr-extract/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/msw": { + "version": "0.49.3", + "resolved": "https://registry.npmjs.org/msw/-/msw-0.49.3.tgz", + "integrity": "sha512-kRCbDNbNnRq5LC1H/NUceZlrPAvSrMH6Or0mirIuH69NY84xwDruPn/hkXTovIK1KwDwbk+ZdoSyJlpiekLxEA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@mswjs/cookies": "^0.2.2", + "@mswjs/interceptors": "^0.17.5", + "@open-draft/until": "^1.0.3", + "@types/cookie": "^0.4.1", + "@types/js-levenshtein": "^1.1.1", + "chalk": "4.1.1", + "chokidar": "^3.4.2", + "cookie": "^0.4.2", + "graphql": "^15.0.0 || ^16.0.0", + "headers-polyfill": "^3.1.0", + "inquirer": "^8.2.0", + "is-node-process": "^1.0.1", + "js-levenshtein": "^1.1.6", + "node-fetch": "^2.6.7", + "outvariant": "^1.3.0", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.4.3", + "type-fest": "^2.19.0", + "yargs": "^17.3.1" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.4.x <= 4.9.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/msw/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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/msw/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/msw/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/msw/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/msw/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", + "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", + "engines": { + "node": "^16 || ^18 || >= 20" + } + }, + "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==", + "dev": true, + "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-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "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/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + }, + "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.hasown": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.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/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==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "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/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ordered-binary": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==" + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, + "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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parcel": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.12.0.tgz", + "integrity": "sha512-W+gxAq7aQ9dJIg/XLKGcRT0cvnStFAQHPaI0pvD0U2l6IVLueUAm3nwN7lkY62zZNmlvNx6jNtE4wlbS+CyqSg==", + "dependencies": { + "@parcel/config-default": "2.12.0", + "@parcel/core": "2.12.0", + "@parcel/diagnostic": "2.12.0", + "@parcel/events": "2.12.0", + "@parcel/fs": "2.12.0", + "@parcel/logger": "2.12.0", + "@parcel/package-manager": "2.12.0", + "@parcel/reporter-cli": "2.12.0", + "@parcel/reporter-dev-server": "2.12.0", + "@parcel/reporter-tracer": "2.12.0", + "@parcel/utils": "2.12.0", + "chalk": "^4.1.0", + "commander": "^7.0.0", + "get-port": "^4.2.0" + }, + "bin": { + "parcel": "lib/bin.js" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/parcel/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/parcel/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/parcel/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/parcel/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/parcel/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/parcel/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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-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-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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==" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz", + "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml/node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/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-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/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-color": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "dependencies": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-cookie": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz", + "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.0.1", + "hoist-non-react-statics": "^3.0.0", + "universal-cookie": "^4.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/react-datepicker": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz", + "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==", + "dependencies": { + "@popperjs/core": "^2.11.8", + "classnames": "^2.2.6", + "date-fns": "^2.30.0", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0", + "react-popper": "^2.3.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, + "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-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "node_modules/react-hook-form": { + "version": "7.52.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.1.tgz", + "integrity": "sha512-uNKIhaoICJ5KQALYZ4TOaOLElyM+xipord+Ha3crEFhTntdLvWZqVY49Wqd/0GiVCA/f9NjemLeiNPjG7Hpurg==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-i18next": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.3.1.tgz", + "integrity": "sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==", + "dependencies": { + "@babel/runtime": "^7.20.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-idle-timer": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-5.7.2.tgz", + "integrity": "sha512-+BaPfc7XEUU5JFkwZCx6fO1bLVK+RBlFH+iY4X34urvIzZiZINP6v2orePx3E6pAztJGE7t4DzvL7if2SL/0GQ==", + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "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-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-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } + }, + "node_modules/react-onclickoutside": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz", + "integrity": "sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, + "node_modules/react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/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-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", + "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "dependencies": { + "@remix-run/router": "1.16.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", + "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "dependencies": { + "@remix-run/router": "1.16.1", + "react-router": "6.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-text-selection-popover": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-text-selection-popover/-/react-text-selection-popover-2.0.2.tgz", + "integrity": "sha512-VbQnJMHX6GrMRS5QGQnb8YuFL45JRcosraTJjdmjib4Xt9MOcTHXmuIyI12xbG2QZv2Tsa+aOZvYgTlo8I00dA==", + "dependencies": { + "use-text-selection": "^1.1.3" + }, + "peerDependencies": { + "react": "^16.8.0,^17.x,^18.x", + "react-dom": "^16.8.0,^17.x,^18.x" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.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/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "dependencies": { + "lodash": "^4.0.1" + } + }, + "node_modules/reactflow": { + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.3.tgz", + "integrity": "sha512-wusd1Xpn1wgsSEv7UIa4NNraCwH9syBtubBy4xVNXg3b+CDKM+sFaF3hnMx0tr0et4km9urIDdNvwm34QiZong==", + "dependencies": { + "@reactflow/background": "11.3.13", + "@reactflow/controls": "11.2.13", + "@reactflow/core": "11.11.3", + "@reactflow/minimap": "11.7.13", + "@reactflow/node-resizer": "2.2.13", + "@reactflow/node-toolbar": "1.3.13" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "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==", + "dev": true, + "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/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/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.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/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/regexify-string": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/regexify-string/-/regexify-string-1.0.19.tgz", + "integrity": "sha512-EREOggl31J6v2Hk3ksPuOof0DMq5QhFfVQ7iDaGQ6BeA1QcrV4rhGvwCES5a72ITMmLBDAOb6cOWbn8/Ja82Ig==" + }, + "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/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/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, + "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/requireindex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", + "integrity": "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "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/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/sass": { + "version": "1.77.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.5.tgz", + "integrity": "sha512-oDfX1mukIlxacPdQqNb6mV2tVCrnE+P3nVYioy72V5tlk56CPNcO4TCuFcaCRKKfJ1M3lH95CleRS+dVKL2qMg==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <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" + } + }, + "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": "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-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", + "dev": true + }, + "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/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/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==", + "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/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.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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.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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/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": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/timeago.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz", + "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==" + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/tsconfck": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.0.tgz", + "integrity": "sha512-CMjc5zMnyAjcS9sPLytrbFmj89st2g+JYtY/c02ug4Q+CZaAtCgbyviI0n1YvjZE/pzoc6FbNsINS13DOL1B9w==", + "dev": true, + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "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/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "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/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/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-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/universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "dependencies": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, + "node_modules/universal-cookie/node_modules/@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "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/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/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.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/use-text-selection": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/use-text-selection/-/use-text-selection-1.1.5.tgz", + "integrity": "sha512-JOuQYG0vKHRj0dfax0dy/HxyF31MN0Q2UP1rl1LtFA0qnQ0Uw4XGh4BucHA9g8kxlnVFv+JTlJQ4B+TwXCGxOg==", + "dependencies": { + "parcel": "^2.0.0-beta.2" + }, + "peerDependencies": { + "react": "^17.0.1" + } + }, + "node_modules/usehooks-ts": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.16.0.tgz", + "integrity": "sha512-bez95WqYujxp6hFdM/CpRDiVPirZPxlMzOH2QB8yopoKQMXpscyZoxOjpEdaxvV+CAWUDSM62cWnqHE0E/MZ7w==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "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==", + "dev": true + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-env-compatible": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vite-plugin-env-compatible/-/vite-plugin-env-compatible-1.1.1.tgz", + "integrity": "sha512-4lqhBWhOzP+SaCPoCVdmpM5cXzjKQV5jgFauxea488oOeElXo/kw6bXkMIooZhrh9q7gclTl8en6N9NmnqUwRQ==", + "dev": true + }, + "node_modules/vite-plugin-svgr": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-2.4.0.tgz", + "integrity": "sha512-q+mJJol6ThvqkkJvvVFEndI4EaKIjSI0I3jNFgSoC9fXAz1M7kYTVUin8fhUsFojFDKZ9VHKtX6NXNaOLpbsHA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.2", + "@svgr/core": "^6.5.1" + }, + "peerDependencies": { + "vite": "^2.6.0 || 3 || 4" + } + }, + "node_modules/vite-plugin-transform": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vite-plugin-transform/-/vite-plugin-transform-2.0.1.tgz", + "integrity": "sha512-sI9SzcuFbCj04YHEmhw9C14kNnVq3QFLWq7eofjNnDWnw/p+i+6pnSvVZSx1GDVpW1ciZglrv794XEU/lGGvyA==", + "dev": true + }, + "node_modules/vite-tsconfig-paths": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", + "integrity": "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/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/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==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" + }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dev": true, + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "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.1", + "which-typed-array": "^1.1.9" + }, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "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/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==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yup": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", + "integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/zustand/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + } + } +} diff --git a/GUI/package.json b/GUI/package.json new file mode 100644 index 00000000..1016bbc9 --- /dev/null +++ b/GUI/package.json @@ -0,0 +1,112 @@ +{ + "name": "byk-training-module-gui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --port 3001 --host", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "tsc --noEmit && eslint \"./src/**/*.{js,ts,tsx}\"", + "prettier": "prettier --write \"{,!(node_modules)/**/}*.{ts,tsx,js,json,css,less,scss}\"" + }, + "dependencies": { + "@buerokratt-ria/styles": "^0.0.1", + "@fontsource/roboto": "^4.5.8", + "@formkit/auto-animate": "^1.0.0-beta.5", + "@fortaine/fetch-event-source": "^3.0.6", + "@radix-ui/react-accessible-icon": "^1.0.1", + "@radix-ui/react-collapsible": "^1.0.1", + "@radix-ui/react-dialog": "^1.0.2", + "@radix-ui/react-popover": "^1.0.2", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-select": "^1.1.2", + "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.1", + "@radix-ui/react-toast": "^1.1.2", + "@radix-ui/react-tooltip": "^1.0.2", + "@tanstack/match-sorter-utils": "^8.7.2", + "@tanstack/react-query": "^4.20.4", + "@tanstack/react-table": "^8.7.4", + "axios": "^1.2.1", + "clsx": "^1.2.1", + "date-fns": "^2.29.3", + "downshift": "^7.0.5", + "esbuild": "^0.19.5", + "formik": "^2.4.6", + "framer-motion": "^8.5.5", + "howler": "^2.2.4", + "i18next": "^22.4.5", + "i18next-browser-languagedetector": "^7.0.1", + "linkify-react": "^4.1.1", + "linkifyjs": "^4.1.1", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "react": "^18.2.0", + "react-color": "^2.19.3", + "react-cookie": "^4.1.1", + "react-datepicker": "^4.8.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^18.2.0", + "react-hook-form": "^7.52.1", + "react-i18next": "^12.1.1", + "react-icons": "^4.10.1", + "react-idle-timer": "^5.5.2", + "react-modal": "^3.16.1", + "react-redux": "^8.1.1", + "react-router-dom": "^6.5.0", + "react-select": "^5.7.4", + "react-text-selection-popover": "^2.0.2", + "react-textarea-autosize": "^8.4.0", + "reactflow": "^11.4.0", + "regexify-string": "^1.0.19", + "rxjs": "^7.8.1", + "timeago.js": "^4.0.2", + "usehooks-ts": "^2.9.1", + "uuid": "^9.0.0", + "yup": "^1.4.0", + "zustand": "^4.4.4" + }, + "devDependencies": { + "@types/howler": "^2.2.11", + "@types/lodash": "^4.14.191", + "@types/lodash.debounce": "^4.0.7", + "@types/node": "^18.11.17", + "@types/react": "^18.0.26", + "@types/react-color": "^3.0.6", + "@types/react-datepicker": "^4.8.0", + "@types/react-dom": "^18.0.9", + "@types/uuid": "^9.0.2", + "@vitejs/plugin-react": "^3.0.0", + "eslint": "^8.30.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-react": "^7.31.11", + "eslint-plugin-typescript": "^0.14.0", + "mocksse": "^1.0.4", + "msw": "^0.49.2", + "prettier": "^2.8.1", + "sass": "^1.57.0", + "typescript": "^4.9.3", + "vite": "^4.0.0", + "vite-plugin-env-compatible": "^1.1.1", + "vite-plugin-svgr": "^2.4.0", + "vite-plugin-transform": "^2.0.1", + "vite-tsconfig-paths": "^4.0.3" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "msw": { + "workerDirectory": "public" + } +} diff --git a/GUI/public/favicon.ico b/GUI/public/favicon.ico new file mode 100644 index 00000000..b9d127c2 Binary files /dev/null and b/GUI/public/favicon.ico differ diff --git a/GUI/public/mockServiceWorker.js b/GUI/public/mockServiceWorker.js new file mode 100644 index 00000000..671ec2cb --- /dev/null +++ b/GUI/public/mockServiceWorker.js @@ -0,0 +1,303 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (0.49.3). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'MOCK_NOT_FOUND': { + return passthrough() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } + } + + return passthrough() +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2]) + }) +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) +} + +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) +} diff --git a/GUI/rebuild.sh b/GUI/rebuild.sh new file mode 100755 index 00000000..c83c0b8b --- /dev/null +++ b/GUI/rebuild.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# Install dependencies +apk add nodejs + +# Rebuild the project +cd /opt/buerokratt-classifier +./node_modules/.bin/vite build -l warn +cp -ru build/* /usr/share/nginx/html/buerokratt-classifier + +# Start the Nginx server +nginx -g "daemon off;" diff --git a/GUI/src/App.tsx b/GUI/src/App.tsx new file mode 100644 index 00000000..bcdc6f60 --- /dev/null +++ b/GUI/src/App.tsx @@ -0,0 +1,92 @@ +import { FC, useEffect, useState } from 'react'; +import { Route, Routes, useNavigate, useLocation } from 'react-router-dom'; +import { Layout } from 'components'; +import useStore from 'store'; +import './locale/et_EE'; +import UserManagement from 'pages/UserManagement'; +import Integrations from 'pages/Integrations'; +import DatasetGroups from 'pages/DatasetGroups'; +import { useQuery } from '@tanstack/react-query'; +import { UserInfo } from 'types/userInfo'; +import CreateDatasetGroup from 'pages/DatasetGroups/CreateDatasetGroup'; +import StopWords from 'pages/StopWords'; +import ValidationSessions from 'pages/ValidationSessions'; +import { authQueryKeys } from 'utils/queryKeys'; +import { ROLES } from 'enums/roles'; +import DataModels from 'pages/DataModels'; +import CreateDataModel from 'pages/DataModels/CreateDataModel'; +import TrainingSessions from 'pages/TrainingSessions'; +import LoadingScreen from 'pages/LoadingScreen/LoadingScreen'; +import Unauthorized from 'pages/Unauthorized/unauthorized'; +import CorrectedTexts from 'pages/CorrectedTexts'; +import TestModel from 'pages/TestModel'; + +const App: FC = () => { + const navigate = useNavigate(); + const location = useLocation(); + const [hasRedirected, setHasRedirected] = useState(false); + const { isLoading, data } = useQuery({ + queryKey: authQueryKeys.USER_DETAILS(), + onSuccess: (res: { response: UserInfo }) => { + localStorage.setItem('exp', res.response.JWTExpirationTimestamp); + useStore.getState().setUserInfo(res.response); + }, + }); + + useEffect(() => { + if (!isLoading && data && !hasRedirected && location.pathname === '/') { + const isAdmin = data.response.authorities.some( + (item) => item === ROLES.ROLE_ADMINISTRATOR + ); + if (isAdmin) { + navigate('/user-management'); + } else { + navigate('/dataset-groups'); + } + setHasRedirected(true); + } + }, [isLoading, data, navigate, hasRedirected, location.pathname]); + + return ( + <> + {isLoading ? ( + + ) : ( + + }> + {data?.response.authorities.some( + (item) => item === ROLES.ROLE_ADMINISTRATOR + ) ? ( + <> + } /> + } /> + + ) : ( + <> + } /> + } /> + + )} + } /> + } + /> + } /> + } + />{' '} + } /> + } /> + } /> + } /> + } /> + + + )} + + ); +}; + +export default App; diff --git a/GUI/src/assets/BackArrowButton.tsx b/GUI/src/assets/BackArrowButton.tsx new file mode 100644 index 00000000..e8e60eb8 --- /dev/null +++ b/GUI/src/assets/BackArrowButton.tsx @@ -0,0 +1,31 @@ +const BackArrowButton = () => { + return ( + + + + + + + + + + + + ); +}; + +export default BackArrowButton; diff --git a/GUI/src/assets/DataModelsIcon.tsx b/GUI/src/assets/DataModelsIcon.tsx new file mode 100644 index 00000000..855dd73f --- /dev/null +++ b/GUI/src/assets/DataModelsIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const DataModelsIcon = () => { + return ( + + + + ); +}; + +export default DataModelsIcon; diff --git a/GUI/src/assets/DatabaseIcon.tsx b/GUI/src/assets/DatabaseIcon.tsx new file mode 100644 index 00000000..5ab9d3b5 --- /dev/null +++ b/GUI/src/assets/DatabaseIcon.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +const DatabaseIcon = () => { + return ( + + + + + + ); +}; + +export default DatabaseIcon; diff --git a/GUI/src/assets/Dataset.tsx b/GUI/src/assets/Dataset.tsx new file mode 100644 index 00000000..6b46aff4 --- /dev/null +++ b/GUI/src/assets/Dataset.tsx @@ -0,0 +1,18 @@ +const Dataset = () => { + return ( + + + + ); +}; + +export default Dataset; diff --git a/GUI/src/assets/IncomingTextsIcon.tsx b/GUI/src/assets/IncomingTextsIcon.tsx new file mode 100644 index 00000000..fb6ccb9d --- /dev/null +++ b/GUI/src/assets/IncomingTextsIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const IncomingTextsIcon = () => { + return ( + + + + ); +}; + +export default IncomingTextsIcon; diff --git a/GUI/src/assets/IntegrationIcon.tsx b/GUI/src/assets/IntegrationIcon.tsx new file mode 100644 index 00000000..5553ea54 --- /dev/null +++ b/GUI/src/assets/IntegrationIcon.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +const IntegrationIcon = () => { + return ( + + + + + + + + + + + + ); +}; + +export default IntegrationIcon; diff --git a/GUI/src/assets/Jira.tsx b/GUI/src/assets/Jira.tsx new file mode 100644 index 00000000..37088791 --- /dev/null +++ b/GUI/src/assets/Jira.tsx @@ -0,0 +1,55 @@ +const Jira = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + ); +}; +export default Jira; diff --git a/GUI/src/assets/Outlook.tsx b/GUI/src/assets/Outlook.tsx new file mode 100644 index 00000000..5eb0ebbc --- /dev/null +++ b/GUI/src/assets/Outlook.tsx @@ -0,0 +1,25 @@ +const Outlook = () => { + return ( + + + + + + ); +}; +export default Outlook; diff --git a/GUI/src/assets/TestModelIcon.tsx b/GUI/src/assets/TestModelIcon.tsx new file mode 100644 index 00000000..6b9c45f6 --- /dev/null +++ b/GUI/src/assets/TestModelIcon.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +const TestModelIcon = () => { + return ( + + + + + + + + + + + ); +}; + +export default TestModelIcon; diff --git a/GUI/src/assets/UserIcon.tsx b/GUI/src/assets/UserIcon.tsx new file mode 100644 index 00000000..83c84c0c --- /dev/null +++ b/GUI/src/assets/UserIcon.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +const UserIcon = () => { + return ( + + + + + ); +}; + +export default UserIcon; diff --git a/GUI/src/assets/ding.mp3 b/GUI/src/assets/ding.mp3 new file mode 100644 index 00000000..af75c65a Binary files /dev/null and b/GUI/src/assets/ding.mp3 differ diff --git a/GUI/src/assets/logo-white.svg b/GUI/src/assets/logo-white.svg new file mode 100644 index 00000000..20257361 --- /dev/null +++ b/GUI/src/assets/logo-white.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/GUI/src/assets/logo.svg b/GUI/src/assets/logo.svg new file mode 100644 index 00000000..6039e9b5 --- /dev/null +++ b/GUI/src/assets/logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/GUI/src/assets/newMessageSound.mp3 b/GUI/src/assets/newMessageSound.mp3 new file mode 100644 index 00000000..9400b22a Binary files /dev/null and b/GUI/src/assets/newMessageSound.mp3 differ diff --git a/GUI/src/components/Box/Box.scss b/GUI/src/components/Box/Box.scss new file mode 100644 index 00000000..8801c053 --- /dev/null +++ b/GUI/src/components/Box/Box.scss @@ -0,0 +1,56 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.box { + padding: get-spacing(paldiski); + border-radius: 4px; + border: 1px solid; + font-size: $veera-font-size-100; + line-height: $veera-line-height-500; + + &:hover { + cursor: grab; + } + + &--default { + background-color: get-color(black-coral-1); + border-color: get-color(black-coral-3); + } + + &--blue { + background-color: get-color(sapphire-blue-0); + border-color: get-color(sapphire-blue-2); + } + + &--yellow { + background-color: get-color(dark-tangerine-0); + border-color: get-color(dark-tangerine-4); + } + + &--green { + background-color: get-color(sea-green-1); + border-color: get-color(sea-green-3); + } + + &--red { + background-color: get-color(jasper-1); + border-color: get-color(jasper-3); + } + + &--gray { + background-color: get-color(black-coral-1); + border-color: get-color(black-coral-3); + } + + &--dark-blue { + background-color: get-color(sapphire-blue-3); + border-color: get-color(sapphire-blue-5); + } + + &--orange { + background-color: get-color(orange-3); + border-color: get-color(orange-5); + } +} diff --git a/GUI/src/components/Box/index.tsx b/GUI/src/components/Box/index.tsx new file mode 100644 index 00000000..df4d3992 --- /dev/null +++ b/GUI/src/components/Box/index.tsx @@ -0,0 +1,16 @@ +import { forwardRef, PropsWithChildren } from 'react'; +import clsx from 'clsx'; + +import './Box.scss'; + +type BoxProps = { + color?: 'default' | 'blue' | 'yellow' | 'green' | 'red' | 'gray' | 'dark-blue' | 'orange'; +} + +const Box = forwardRef>(({ color = 'default', children }, ref) => { + return ( +
{children}
+ ); +}); + +export default Box; diff --git a/GUI/src/components/Button/Button.scss b/GUI/src/components/Button/Button.scss new file mode 100644 index 00000000..fc21cab1 --- /dev/null +++ b/GUI/src/components/Button/Button.scss @@ -0,0 +1,151 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.btn { + $self: &; + appearance: none; + display: inline-flex; + align-items: center; + background: none; + border: 0; + color: get-color(black-coral-0); + cursor: pointer; + font: inherit; + gap: get-spacing(rapla); + overflow: visible; + padding: 8px 40px; + text-decoration: none; + font-size: $veera-font-size-100; + line-height: 24px; + border-radius: 20px; + white-space: nowrap; + height: fit-content; + + &:focus { + outline: none; + } + + &--disabled { + cursor: not-allowed; + } + + &--primary { + background-color: get-color(sapphire-blue-10); + + &:hover, + &:active { + background-color: get-color(sapphire-blue-13); + } + + &:focus { + box-shadow: inset 0 0 0 2px get-color(sapphire-blue-3) + } + + &#{$self}--disabled { + background-color: get-color(black-coral-2); + color: get-color(white); + } + } + + &--secondary { + background-color: get-color(white); + box-shadow: inset 0 0 0 2px get-color(black-coral-10); + color: get-color(black-coral-15); + + &:hover, + &:active { + box-shadow: inset 0 0 0 2px get-color(black-coral-2); + } + + &:focus { + box-shadow: inset 0 0 0 2px get-color(sapphire-blue-10); + } + + &#{$self}--disabled { + background-color: get-color(black-coral-2); + color: get-color(black-coral-6); + box-shadow: inset 0 0 0 2px get-color(black-coral-2); + } + } + + &--text { + padding: 0; + background: none; + color: get-color(sapphire-blue-10); + gap: 4px; + border-radius: 0; + + &:hover, + &:active { + text-decoration: underline; + } + + &:focus { + box-shadow: inset 0 0 0 2px get-color(sapphire-blue-10); + } + + &#{$self}--disabled { + color: get-color(black-coral-6); + } + } + + &--icon { + width: 36px; + height: 36px; + padding: 0; + justify-content: center; + color: get-color(black-coral-10); + font-size: 24px; + + &:hover, + &:active { + color: get-color(sapphire-blue-10); + } + + &:focus { + color: get-color(sapphire-blue-10); + box-shadow: inset 0 0 0 2px get-color(sapphire-blue-10); + } + } + + &--error { + background-color: get-color(jasper-10); + + &:hover, + &:active { + background-color: get-color(jasper-12); + } + + &:focus { + box-shadow: inset 0 0 0 2px get-color(jasper-13); + } + + &#{$self}--disabled { + background-color: get-color(black-coral-2); + } + } + + &--success { + background-color: get-color(sea-green-10); + + &:hover, + &:active { + background-color: get-color(sea-green-12); + } + + &:focus { + background-color: get-color(sea-green-10); + box-shadow: inset 0 0 0 2px get-color(sea-green-12); + } + + &#{$self}--disabled { + background-color: get-color(black-coral-2); + } + } + + &--s { + padding: 4.5px 24px; + } +} diff --git a/GUI/src/components/Button/index.tsx b/GUI/src/components/Button/index.tsx new file mode 100644 index 00000000..b35cd8c3 --- /dev/null +++ b/GUI/src/components/Button/index.tsx @@ -0,0 +1,56 @@ +import { ButtonHTMLAttributes, FC, PropsWithChildren, useRef } from 'react'; +import clsx from 'clsx'; + +import './Button.scss'; + +type ButtonProps = ButtonHTMLAttributes & { + appearance?: 'primary' | 'secondary' | 'text' | 'icon' | 'error' | 'success'; + size?: 'm' | 's'; + disabledWithoutStyle?: boolean; + showLoadingIcon?: boolean; +}; + +const Button: FC> = ({ + appearance = 'primary', + size = 'm', + disabled, + disabledWithoutStyle = false, + children, + showLoadingIcon = false, + ...rest +}) => { + const ref = useRef(null); + + const buttonClasses = clsx( + 'btn', + `btn--${appearance}`, + `btn--${size}`, + disabled && 'btn--disabled' + ); + + return ( + + ); +}; + +export default Button; \ No newline at end of file diff --git a/GUI/src/components/Card/Card.scss b/GUI/src/components/Card/Card.scss new file mode 100644 index 00000000..82d2665c --- /dev/null +++ b/GUI/src/components/Card/Card.scss @@ -0,0 +1,65 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.card { + $self: &; + background-color: get-color(white); + border: 1px solid get-color(black-coral-2); + border-radius: $veera-radius-s; + margin-bottom: 10px; + + &--borderless { + border: 0; + border-radius: 0; + + #{$self}__header { + border-radius: 0; + } + } + + &--fullWidth { + width: 100%; + } + + &__header, + &__body, + &__footer { + padding: get-spacing(haapsalu); + } + + &__header { + border-bottom: 1px solid get-color(black-coral-2); + background-color: #F9F9F9; + border-radius: $veera-radius-s $veera-radius-s 0 0; + + &.white { + background-color: white + } + } + + &__body { + &.divided { + display: flex; + flex-direction: column; + padding-left: 0px; + padding-right: 0px; + + > :not(:last-child) { + margin-bottom: get-spacing(haapsalu); + border-bottom: 1px solid get-color(black-coral-2); + padding-bottom: get-spacing(haapsalu); + padding-left: get-spacing(haapsalu); + } + + > :is(:last-child) { + padding-left: get-spacing(haapsalu); + } + } + } + + &__footer { + border-top: 1px solid get-color(black-coral-2); + } +} diff --git a/GUI/src/components/Card/index.tsx b/GUI/src/components/Card/index.tsx new file mode 100644 index 00000000..27eb7501 --- /dev/null +++ b/GUI/src/components/Card/index.tsx @@ -0,0 +1,39 @@ +import { FC, PropsWithChildren, ReactNode } from 'react'; +import clsx from 'clsx'; + +import './Card.scss'; + +type CardProps = { + header?: ReactNode; + footer?: ReactNode; + borderless?: boolean; + isHeaderLight?: boolean; + isBodyDivided?: boolean; + isFullWidth?: boolean; +}; + +const Card: FC> = ({ + header, + footer, + borderless, + isHeaderLight, + isBodyDivided, + children, + isFullWidth, +}) => { + return ( +
+ {header && ( +
+ {header} +
+ )} +
+ {children} +
+ {footer &&
{footer}
} +
+ ); +}; + +export default Card; diff --git a/GUI/src/components/Collapsible/Collapsible.scss b/GUI/src/components/Collapsible/Collapsible.scss new file mode 100644 index 00000000..24328e65 --- /dev/null +++ b/GUI/src/components/Collapsible/Collapsible.scss @@ -0,0 +1,35 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.collapsible { + border: 1px solid get-color(black-coral-2); + border-radius: 4px; + + &__trigger { + width: 100%; + display: flex; + align-items: center; + gap: 4px; + padding: get-spacing(haapsalu); + background-color: get-color(extra-light); + border-radius: 4px; + + &[aria-expanded=true] { + border-bottom: 1px solid get-color(black-coral-2); + border-radius: 4px 4px 0 0; + } + + .icon { + font-size: 21px; + } + } + + &__content { + padding: get-spacing(haapsalu); + background-color: get-color(white); + border-radius: 0 0 4px 4px; + overflow: hidden; + } +} diff --git a/GUI/src/components/Collapsible/index.tsx b/GUI/src/components/Collapsible/index.tsx new file mode 100644 index 00000000..02a13bda --- /dev/null +++ b/GUI/src/components/Collapsible/index.tsx @@ -0,0 +1,31 @@ +import { FC, PropsWithChildren, useState } from 'react'; +import * as RadixCollapsible from '@radix-ui/react-collapsible'; +import { MdOutlineAddBox, MdOutlineIndeterminateCheckBox } from 'react-icons/md'; + +import { Icon } from 'components'; +import './Collapsible.scss'; + +type CollapsibleProps = { + title: string; + defaultOpen?: boolean; +} + +const Collapsible: FC> = ({ defaultOpen = false, title, children }) => { + const [open, setOpen] = useState(defaultOpen); + + return ( + + + + + + {children} + + + ); +}; + +export default Collapsible; diff --git a/GUI/src/components/DataTable/CloseIcon.tsx b/GUI/src/components/DataTable/CloseIcon.tsx new file mode 100644 index 00000000..85de2dbb --- /dev/null +++ b/GUI/src/components/DataTable/CloseIcon.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import './DeboucedInput.scss'; + +const CloseIcon: React.FC = () => ( + + + + +); + +export default CloseIcon; diff --git a/GUI/src/components/DataTable/DataTable.scss b/GUI/src/components/DataTable/DataTable.scss new file mode 100644 index 00000000..fcf13b37 --- /dev/null +++ b/GUI/src/components/DataTable/DataTable.scss @@ -0,0 +1,197 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/typography'; + +.data-table { + width: 100%; + color: get-color(black-coral-20); + text-align: left; + margin-bottom: 0; + display: table; + + &__scrollWrapper { + height: 100%; + min-height: 150px !important; + padding: 10px 20px; + overflow-x: auto; + white-space: nowrap; + display: block; + background-color: white; + border-radius: 10px; + border: solid 1px get-color(black-coral-1); + } + + thead, + tbody { + width: 100%; + } + + th { + padding: 12px 14.5px; + color: get-color(black-coral-12); + border-bottom: 1px solid get-color(black-coral-10); + font-weight: $veera-font-weight-beta; + vertical-align: middle; + position: relative; + } + + td { + padding: 12px 24px 12px 16px; + border-bottom: 1px solid get-color(black-coral-2); + vertical-align: middle; + max-width: fit-content; + + p { + white-space: break-spaces; + } + + .entity { + display: inline-flex; + align-items: center; + padding-left: 4px; + background-color: get-color(sapphire-blue-2); + border-radius: 4px; + + span { + display: inline-flex; + font-size: $veera-font-size-80; + background-color: get-color(white); + padding: 0 4px; + border-radius: 4px; + margin: 2px 2px 2px 4px; + } + } + } + + tbody { + tr { + &:last-child { + td { + border-bottom: 0; + } + } + } + } + + &__filter { + position: absolute; + top: 100%; + left: 0; + right: 0; + padding: get-spacing(paldiski); + background-color: get-color(white); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.14); + border-radius: 0 0 4px 4px; + border: 1px solid get-color(black-coral-2); + + input { + width: 100%; + display: block; + appearance: none; + background-color: get-color(white); + border: 1px solid get-color(black-coral-6); + border-radius: 5px; + color: var(--color-black); + font-size: $veera-font-size-100; + height: 32px; + line-height: 24px; + padding: get-spacing(paldiski); + + &::placeholder { + color: get-color(black-coral-6); + } + + &:focus { + outline: none; + border-color: get-color(sapphire-blue-10); + } + } + } + + &__pagination-wrapper { + margin-top: 10px; + display: flex; + padding: 6px 16px; + } + + &__pagination { + display: flex; + align-items: center; + gap: 15px; + margin: 0 auto; + + + .data-table__page-size { + margin-left: 0; + } + + .next, + .previous { + display: flex; + color: get-color(sapphire-blue-10); + + &[disabled] { + color: get-color(black-coral-11); + cursor: initial; + } + } + + .links { + display: flex; + align-items: center; + gap: 5px; + font-size: $veera-font-size-80; + color: get-color(black-coral-10); + + li { + display: block; + + a, + span { + display: flex; + align-items: center; + justify-content: center; + width: 25px; + height: 25px; + border-radius: 50%; + + &:hover { + text-decoration: none; + } + } + + &.active { + a, + span { + color: get-color(white); + background-color: get-color(sapphire-blue-10); + } + } + } + } + } + + &__page-size { + display: flex; + align-items: center; + gap: 8px; + font-size: $veera-font-size-80; + line-height: 16px; + color: get-color(black-coral-11); + margin-left: auto; + + select { + appearance: none; + font-size: $veera-font-size-70; + line-height: 16px; + height: 30px; + min-width: 50px; + padding: 6px 10px; + border: 1px solid #8f91a8; + border-radius: 2px; + background-color: get-color(white); + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iNiIgdmlld0JveD0iMCAwIDEwIDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNS4zMTMwNiA1LjgwODIyQzUuMTU2ODUgNS45NjQ0MyA0LjkwMzU4IDUuOTY0NDMgNC43NDczNyA1LjgwODIyTDAuMjgyNzMgMS4zNDM1OEMwLjEyNjUyIDEuMTg3MzcgMC4xMjY1MiAwLjkzNDEwMiAwLjI4MjczIDAuNzc3ODkzTDAuNzc3NzA0IDAuMjgyOTE4QzAuOTMzOTE0IDAuMTI2NzA4IDEuMTg3MTggMC4xMjY3MDggMS4zNDMzOSAwLjI4MjkxN0w1LjAzMDIyIDMuOTY5NzRMOC43MTcwNCAwLjI4MjkxN0M4Ljg3MzI1IDAuMTI2NzA4IDkuMTI2NTIgMC4xMjY3MDggOS4yODI3MyAwLjI4MjkxN0w5Ljc3NzcgMC43Nzc4OTJDOS45MzM5MSAwLjkzNDEwMiA5LjkzMzkxIDEuMTg3MzcgOS43Nzc3IDEuMzQzNThMNS4zMTMwNiA1LjgwODIyWiIgZmlsbD0iIzU1NTg2NyIvPgo8L3N2Zz4K'); + background-repeat: no-repeat; + background-position: top 11px right 10px; + } + } +} diff --git a/GUI/src/components/DataTable/DeboucedInput.scss b/GUI/src/components/DataTable/DeboucedInput.scss new file mode 100644 index 00000000..753f1ad0 --- /dev/null +++ b/GUI/src/components/DataTable/DeboucedInput.scss @@ -0,0 +1,11 @@ +.input-container { + position: relative; +} + +.search-icon { + position: absolute; + top: 50%; + right: 10px; + margin-left: 10px; + transform: translateY(-50%); +} diff --git a/GUI/src/components/DataTable/DebouncedInput.tsx b/GUI/src/components/DataTable/DebouncedInput.tsx new file mode 100644 index 00000000..1ad1f52f --- /dev/null +++ b/GUI/src/components/DataTable/DebouncedInput.tsx @@ -0,0 +1,54 @@ +import { FC, InputHTMLAttributes, useEffect, useState } from 'react'; +import './DeboucedInput.scss'; +import CloseIcon from './CloseIcon'; + +type DebouncedInputProps = Omit< + InputHTMLAttributes, + 'onChange' +> & { + value: string | number | string[]; + onChange: (value: string | number | string[]) => void; + debounce?: number; +}; + +const DebouncedInput: FC = ({ + value: initialValue, + onChange, + debounce = 500, + ...props +}) => { + const [value, setValue] = useState(initialValue); + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + useEffect(() => { + const timeout = setTimeout(() => { + onChange(value); + }, debounce); + + return () => clearTimeout(timeout); + }, [value]); + + return ( +
+ setValue(e.target.value)} + /> + {value && ( + + )} +
+ ); +}; + +export default DebouncedInput; diff --git a/GUI/src/components/DataTable/Filter.tsx b/GUI/src/components/DataTable/Filter.tsx new file mode 100644 index 00000000..6622c7aa --- /dev/null +++ b/GUI/src/components/DataTable/Filter.tsx @@ -0,0 +1,121 @@ +import React, { FC, useState, MouseEvent } from 'react'; +import { Column, Table } from '@tanstack/react-table'; +import { useTranslation } from 'react-i18next'; +import { MdOutlineSearch } from 'react-icons/md'; + +import { Icon } from 'components'; +import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; +import DebouncedInput from './DebouncedInput'; + +type FilterProps = { + column: Column; + table: Table; +}; + +const Filter: FC = ({ column, table }) => { + const { t } = useTranslation(); + const [filterOpen, setFilterOpen] = useState(false); + const firstValue = table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id); + + const columnFilterValue = column.getFilterValue(); + + useDocumentEscapeListener(() => setFilterOpen(false)); + + const handleFilterToggle = (e: MouseEvent) => { + e.stopPropagation(); + setFilterOpen(!filterOpen); + }; + + return ( + <> + + {filterOpen && ( +
+ {typeof firstValue === 'number' ? ( + + column.setFilterValue((old: [number, number]) => [ + value, + old?.[1], + ]) + } + /> + ) : ( + column.setFilterValue(value)} + placeholder={t('global.search') + '...'} + // list={column.id + 'list'} + /> + )} +
+ )} + + ); + + // return typeof firstValue === 'number' ? ( + //
+ //
+ // + // column.setFilterValue((old: [number, number]) => [value, old?.[1]]) + // } + // placeholder={`Min ${ + // column.getFacetedMinMaxValues()?.[0] + // ? `(${column.getFacetedMinMaxValues()?.[0]})` + // : '' + // }`} + // className='w-24 border shadow rounded' + // /> + // + // column.setFilterValue((old: [number, number]) => [old?.[0], value]) + // } + // placeholder={`Max ${ + // column.getFacetedMinMaxValues()?.[1] + // ? `(${column.getFacetedMinMaxValues()?.[1]})` + // : '' + // }`} + // className='w-24 border shadow rounded' + // /> + //
+ //
+ //
+ // ) : ( + // <> + // + // {sortedUniqueValues.slice(0, 5000).map((value: any) => ( + // + // column.setFilterValue(value)} + // placeholder={`Search... (${column.getFacetedUniqueValues().size})`} + // className='w-36 border shadow rounded' + // list={column.id + 'list'} + // /> + //
+ // + // ); +}; + +export default Filter; diff --git a/GUI/src/components/DataTable/index.tsx b/GUI/src/components/DataTable/index.tsx new file mode 100644 index 00000000..8f748721 --- /dev/null +++ b/GUI/src/components/DataTable/index.tsx @@ -0,0 +1,242 @@ +import React, { CSSProperties, FC, ReactNode, useId } from 'react'; +import { + ColumnDef, + useReactTable, + getCoreRowModel, + flexRender, + getSortedRowModel, + SortingState, + FilterFn, + getFilteredRowModel, + VisibilityState, + getPaginationRowModel, + PaginationState, + TableMeta, + Row, + RowData, ColumnFiltersState, +} from '@tanstack/react-table'; +import { + RankingInfo, + rankItem, +} from '@tanstack/match-sorter-utils'; +import { + MdUnfoldMore, + MdExpandMore, + MdExpandLess, + MdOutlineEast, + MdOutlineWest, +} from 'react-icons/md'; +import clsx from 'clsx'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +import { Icon, Track } from 'components'; +import Filter from './Filter'; +import './DataTable.scss'; + +type DataTableProps = { + data: any; + columns: ColumnDef[]; + tableBodyPrefix?: ReactNode; + isClientSide?: boolean; + sortable?: boolean; + filterable?: boolean; + pagination?: PaginationState; + sorting?: SortingState; + setPagination?: (state: PaginationState) => void; + setSorting?: (state: SortingState) => void; + globalFilter?: string; + setGlobalFilter?: React.Dispatch>; + columnVisibility?: VisibilityState; + setColumnVisibility?: React.Dispatch>; + disableHead?: boolean; + pagesCount?: number; + meta?: TableMeta; +}; + +type ColumnMeta = { + meta: { + size: number | string; + } +} + +type CustomColumnDef = ColumnDef & ColumnMeta; + +declare module '@tanstack/table-core' { + interface FilterFns { + fuzzy: FilterFn; + } + + interface FilterMeta { + itemRank: RankingInfo; + } +} + +declare module '@tanstack/react-table' { + interface TableMeta { + getRowStyles: (row: Row) => CSSProperties; + } + class Column { + columnDef: CustomColumnDef; + } +} + +const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { + const itemRank = rankItem(row.getValue(columnId), value); + addMeta({ + itemRank, + }); + return itemRank.passed; +}; + +const DataTable: FC = ( + { + data, + columns, + isClientSide = true, + tableBodyPrefix, + sortable, + filterable, + pagination, + sorting, + setPagination, + setSorting, + globalFilter, + setGlobalFilter, + columnVisibility, + setColumnVisibility, + disableHead, + pagesCount, + meta, + }, +) => { + const id = useId(); + const { t } = useTranslation(); + const [columnFilters, setColumnFilters] = React.useState([]); + const table = useReactTable({ + data, + columns, + filterFns: { + fuzzy: fuzzyFilter, + }, + state: { + sorting, + columnFilters, + globalFilter, + columnVisibility, + ...{ pagination }, + }, + meta, + onColumnFiltersChange: setColumnFilters, + onGlobalFilterChange: setGlobalFilter, + onColumnVisibilityChange: setColumnVisibility, + globalFilterFn: fuzzyFilter, + onSortingChange: (updater) => { + if (typeof updater !== 'function') return; + setSorting?.(updater(table.getState().sorting)); + }, + onPaginationChange: (updater) => { + if (typeof updater !== 'function') return; + setPagination?.(updater(table.getState().pagination)); + }, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + ...(pagination && { getPaginationRowModel: getPaginationRowModel() }), + ...(sortable && { getSortedRowModel: getSortedRowModel() }), + manualPagination: isClientSide ? undefined : true, + manualSorting: isClientSide ? undefined : true, + pageCount: isClientSide ? undefined : pagesCount, + }); + + return ( +
+ + {!disableHead && ( + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + )} + + {tableBodyPrefix} + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder ? null : ( + + {sortable && header.column.getCanSort() && ( + + )} + {flexRender(header.column.columnDef.header, header.getContext())} + {filterable && header.column.getCanFilter() && ( + + )} + + )} +
{flexRender(cell.column.columnDef.cell, cell.getContext())}
+ {pagination && ( +
+ {(table.getPageCount() * table.getState().pagination.pageSize) > table.getState().pagination.pageSize && ( +
+ + + +
+ )} +
+ )} +
+ ); +}; + +export default DataTable; diff --git a/GUI/src/components/Dialog/Dialog.scss b/GUI/src/components/Dialog/Dialog.scss new file mode 100644 index 00000000..7af6a64b --- /dev/null +++ b/GUI/src/components/Dialog/Dialog.scss @@ -0,0 +1,63 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.dialog { + background-color: get-color(white); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.25); + border-radius: 4px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + max-width: 600px; + z-index: 101; + max-height: 90vh; + + &--large { + max-width: 800px; + } + + &__overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.54); + z-index: 100; + } + + &__header, + &__body, + &__footer { + padding: get-spacing(haapsalu); + } + + &__header { + display: flex; + align-items: center; + gap: get-spacing(haapsalu); + background-color: get-color(black-coral-0); + border-bottom: 1px solid get-color(black-coral-2); + } + + &__title { + flex: 1; + } + + &__close { + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + } + + &__body { + overflow: auto; + max-height: calc(90vh - 70px); + } + + &__footer { + border-top: 1px solid get-color(black-coral-2); + } +} diff --git a/GUI/src/components/Dialog/index.tsx b/GUI/src/components/Dialog/index.tsx new file mode 100644 index 00000000..7b2848c1 --- /dev/null +++ b/GUI/src/components/Dialog/index.tsx @@ -0,0 +1,45 @@ +import { FC, PropsWithChildren, ReactNode } from 'react'; +import * as RadixDialog from '@radix-ui/react-dialog'; +import { MdOutlineClose } from 'react-icons/md'; +import clsx from 'clsx'; +import './Dialog.scss'; +import Icon from 'components/Icon'; +import Track from 'components/Track'; + +type DialogProps = { + title?: string | null; + footer?: ReactNode; + onClose: () => void; + size?: 'default' | 'large'; + isOpen?: boolean; +} + +const Dialog: FC> = ({ title, footer, onClose, size = 'default', children,isOpen }) => { + return ( + + + + + { + title &&
+ {title} + + + +
+ } +
+ {children} +
+ {footer && ( + {footer} + )} +
+
+
+ ); +}; + +export default Dialog; diff --git a/GUI/src/components/Drawer/Drawer.scss b/GUI/src/components/Drawer/Drawer.scss new file mode 100644 index 00000000..df7bc711 --- /dev/null +++ b/GUI/src/components/Drawer/Drawer.scss @@ -0,0 +1,40 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.drawer { + position: fixed; + display: flex; + flex-direction: column; + top: 100px; + right: 0; + bottom: 0; + background-color: get-color(white); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.14); + width: 50%; + transition: transform .25s ease-out; + overflow: hidden; + z-index: 98; + + &__header { + display: flex; + align-items: center; + gap: get-spacing(haapsalu); + padding: get-spacing(haapsalu); + border-bottom: 1px solid get-color(black-coral-2); + + .icon { + font-size: 20px; + } + } + + &__title, + &__body { + flex: 1; + } + + &__body { + overflow: auto; + } +} diff --git a/GUI/src/components/Drawer/index.tsx b/GUI/src/components/Drawer/index.tsx new file mode 100644 index 00000000..9b6f771f --- /dev/null +++ b/GUI/src/components/Drawer/index.tsx @@ -0,0 +1,42 @@ +import { CSSProperties, FC, PropsWithChildren, useEffect, useRef } from 'react'; +import { MdOutlineClose } from 'react-icons/md'; +import autoAnimate from '@formkit/auto-animate'; + +import { Icon } from 'components'; +import './Drawer.scss'; + +type DrawerProps = { + title: string; + onClose: () => void; + style?: CSSProperties; +} + +const Drawer: FC> = ({ title, onClose, children, style }) => { + const ref = useRef(null); + + useEffect(() => { + ref.current && autoAnimate(ref.current); + const handleKeyup = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keyup', handleKeyup); + + return () => document.removeEventListener('keyup', handleKeyup); + }, [onClose]); + + return ( +
+
+

{title}

+ +
+
+ {children} +
+
+ ); +}; + +export default Drawer; diff --git a/GUI/src/components/FileUpload/index.tsx b/GUI/src/components/FileUpload/index.tsx new file mode 100644 index 00000000..2ee593bb --- /dev/null +++ b/GUI/src/components/FileUpload/index.tsx @@ -0,0 +1,78 @@ +import { FormInput } from 'components/FormElements'; +import React, { + ChangeEvent, + forwardRef, + useImperativeHandle, + Ref, + useRef, +} from 'react'; + +type FileUploadProps = { + onFileSelect: (file: File | undefined) => void; + accept?: string | string[]; + disabled?: boolean; +}; + +export type FileUploadHandle = { + clearFile: () => void; +}; + +const FileUpload = forwardRef( + (props: FileUploadProps, ref: Ref) => { + const { onFileSelect, accept, disabled } = props; + const fileInputRef = useRef(null); + + useImperativeHandle(ref, () => ({ + clearFile() { + onFileSelect(undefined); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }, + })); + + const handleFileChange = (e: ChangeEvent) => { + const file = e.target.files ? e.target.files[0] : undefined; + onFileSelect(file); + }; + + const restrictFormat = (accept: string | string[]) => { + if (typeof accept === 'string') { + if (accept === 'json') return '.json'; + else if (accept === 'xlsx') return '.xlsx'; + else if (accept === 'yaml') return '.yaml, .yml'; + return ''; + } else { + return accept.map(ext => `.${ext}`).join(', '); + } + + }; + + return ( +
+ + + + +
+ ); + } +); + +export default FileUpload; diff --git a/GUI/src/components/FormElements/DynamicForm/index.tsx b/GUI/src/components/FormElements/DynamicForm/index.tsx new file mode 100644 index 00000000..2ce032b9 --- /dev/null +++ b/GUI/src/components/FormElements/DynamicForm/index.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useForm } from 'react-hook-form'; +import FormInput from '../FormInput'; +import Button from 'components/Button'; +import Track from 'components/Track'; + +type DynamicFormProps = { + formData: { [key: string]: string | number }; // Adjust the type to include numbers + onSubmit: (data: any) => void; + setPatchUpdateModalOpen: React.Dispatch>; +}; + +const DynamicForm: React.FC = ({ + formData, + onSubmit, + setPatchUpdateModalOpen, +}) => { + const { register, handleSubmit } = useForm(); + + const renderInput = (key: string) => { + const isRowID = key.toLowerCase() === 'rowid'; + const inputType = isRowID ? 'number' : 'text'; + + return ( +
+ + +
+ ); + }; + + return ( +
+ {Object.keys(formData).map((key) => ( +
+
{renderInput(key)}
+
+ ))} + +
+ + +
+ +
+ ); +}; + +export default DynamicForm; diff --git a/GUI/src/components/FormElements/FormCheckbox/FormCheckbox.scss b/GUI/src/components/FormElements/FormCheckbox/FormCheckbox.scss new file mode 100644 index 00000000..8bdf863d --- /dev/null +++ b/GUI/src/components/FormElements/FormCheckbox/FormCheckbox.scss @@ -0,0 +1,57 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.checkbox { + width: 100%; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + + &__label { + display: block; + flex: 0 0 85px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__item { + input[type=checkbox] { + display: none; + + + label { + display: block; + padding-left: 32px; + position: relative; + font-size: $veera-font-size-100; + line-height: $veera-line-height-500; + + &::before { + content: ''; + display: block; + width: 16px; + height: 16px; + box-shadow: inset 0 0 0 1px get-color(black-coral-2); + border-radius: 2px; + position: absolute; + left: 4px; + top: 4px; + } + } + + &:checked { + + label { + &::before { + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTEiIHZpZXdCb3g9IjAgMCAxNCAxMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuNzQ5NzkgOC4xMjkwNkwxLjYyMjI5IDUuMDAxNTZMMC41NjEwMzUgNi4wNjI4MUw0Ljc0OTc5IDEwLjI1MTZMMTMuNzQ5OCAxLjI1MTU2TDEyLjY4ODUgMC4xOTAzMDhMNC43NDk3OSA4LjEyOTA2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=='); + background-color: get-color(sapphire-blue-10); + background-repeat: no-repeat; + background-position: center; + background-size: 13px 10px; + box-shadow: inset 0 0 0 1px get-color(sapphire-blue-10); + } + } + } + } + } +} diff --git a/GUI/src/components/FormElements/FormCheckbox/index.tsx b/GUI/src/components/FormElements/FormCheckbox/index.tsx new file mode 100644 index 00000000..66645255 --- /dev/null +++ b/GUI/src/components/FormElements/FormCheckbox/index.tsx @@ -0,0 +1,39 @@ +import { forwardRef, InputHTMLAttributes, useId } from 'react'; + +import './FormCheckbox.scss'; + +type FormCheckboxType = InputHTMLAttributes & { + label: string; + name: string; + hideLabel?: boolean; + item: { + label: string; + value: string; + checked?: boolean; + }; +} + +const FormCheckbox = forwardRef(( + { + label, + name, + hideLabel, + item, + ...rest + }, + ref, +) => { + const uid = useId(); + + return ( +
+ {label && !hideLabel && } +
+ + +
+
+ ); +}); + +export default FormCheckbox; diff --git a/GUI/src/components/FormElements/FormCheckboxes/FormCheckboxes.scss b/GUI/src/components/FormElements/FormCheckboxes/FormCheckboxes.scss new file mode 100644 index 00000000..02d57e7d --- /dev/null +++ b/GUI/src/components/FormElements/FormCheckboxes/FormCheckboxes.scss @@ -0,0 +1,67 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.checkboxes { + display: flex; + align-items: flex-start; + gap: get-spacing(paldiski); + + &__label { + display: block; + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__row { + display: flex; + gap: 20px; + } + + &__item { + input[type=checkbox] { + display: none; + + + label { + display: block; + padding-left: 32px; + position: relative; + font-size: $veera-font-size-100; + line-height: $veera-line-height-500; + + &::before { + content: ''; + display: block; + width: 16px; + height: 16px; + box-shadow: inset 0 0 0 1px get-color(black-coral-2); + border-radius: 2px; + position: absolute; + left: 4px; + top: 4px; + } + } + + &:checked { + + label { + &::before { + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTEiIHZpZXdCb3g9IjAgMCAxNCAxMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuNzQ5NzkgOC4xMjkwNkwxLjYyMjI5IDUuMDAxNTZMMC41NjEwMzUgNi4wNjI4MUw0Ljc0OTc5IDEwLjI1MTZMMTMuNzQ5OCAxLjI1MTU2TDEyLjY4ODUgMC4xOTAzMDhMNC43NDk3OSA4LjEyOTA2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg=='); + background-color: get-color(sapphire-blue-10); + background-repeat: no-repeat; + background-position: center; + background-size: 13px 10px; + box-shadow: inset 0 0 0 1px get-color(sapphire-blue-10); + } + } + } + } + } +} diff --git a/GUI/src/components/FormElements/FormCheckboxes/index.tsx b/GUI/src/components/FormElements/FormCheckboxes/index.tsx new file mode 100644 index 00000000..d845ed22 --- /dev/null +++ b/GUI/src/components/FormElements/FormCheckboxes/index.tsx @@ -0,0 +1,77 @@ +import { ChangeEvent, FC, useId, useState, useEffect } from 'react'; + +import './FormCheckboxes.scss'; + +type FormCheckboxesType = { + label: string; + name: string; + hideLabel?: boolean; + onValuesChange?: (values: Record) => void; + items: { + label: string; + value: string; + }[] |undefined; + isStack?: boolean; + error?: string; + selectedValues?: string[]; // New prop for selected values +}; + +const FormCheckboxes: FC = ({ + label, + name, + hideLabel, + onValuesChange, + items, + isStack = true, + error, + selectedValues = [], // Default to an empty array if not provided +}) => { + const id = useId(); + const [internalSelectedValues, setInternalSelectedValues] = useState(selectedValues); + + useEffect(() => { + setInternalSelectedValues(selectedValues); + }, [selectedValues]); + + const handleValuesChange = (e: ChangeEvent) => { + const { checked, value } = e.target; + + const newValues = checked + ? [...internalSelectedValues, value] // Add the checked value to the array + : internalSelectedValues.filter((v: string) => v !== value); // Remove the unchecked value from the array + + setInternalSelectedValues(newValues); + + if (onValuesChange) onValuesChange({ [name]: newValues }); + }; + + return ( +
+
+
+ {label && !hideLabel && ( + + )} +
+ {items?.map((item, index) => ( +
+ + +
+ ))} +
+
+
+
{error &&

{error}

}
+
+ ); +}; + +export default FormCheckboxes; diff --git a/GUI/src/components/FormElements/FormDatepicker/FormDatepicker.scss b/GUI/src/components/FormElements/FormDatepicker/FormDatepicker.scss new file mode 100644 index 00000000..55ac0785 --- /dev/null +++ b/GUI/src/components/FormElements/FormDatepicker/FormDatepicker.scss @@ -0,0 +1,154 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.datepicker { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper_column { + display: flex; + flex-direction: column; + gap: 7px; + position: relative; + width: 125px; + + .icon { + position: absolute; + right: 8px; + top: 8px; + pointer-events: none; + } + } + + &__wrapper_row { + display: flex; + flex-direction: row; + gap: 7px; + position: relative; + width: 125px; + + .icon { + position: absolute; + right: 8px; + top: 8px; + pointer-events: none; + } + } + + &__error { + width: 100%; + margin-right: 6px; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + color: get-color(black-coral-20); + border-radius: $veera-radius-s; + background-color: get-color(jasper-3); + font-size: 13px; + line-height: 20px; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + + &::before { + content: ''; + display: block; + background-color: get-color(jasper-3); + border-left: 16px solid transparent; + border-right: 16px solid transparent; + border-bottom: 25px; + } + } + + input { + width: 100%; + display: block; + appearance: none; + background-color: get-color(white); + border: 1px solid get-color(black-coral-6); + border-radius: $veera-radius-s; + color: var(--color-black); + font-size: $veera-font-size-100; + height: 40px; + line-height: 24px; + padding: get-spacing(paldiski); + + &::placeholder { + color: get-color(black-coral-6); + } + + &:focus { + outline: none; + border-color: get-color(sapphire-blue-10); + } + } + + &--error { + input { + border-color: get-color(jasper-10); + } + } + + &--disabled & { + input { + background-color: get-color(black-coral-0); + } + } +} + +.react-datepicker { + font-family: inherit; + font-size: 14px; + border: 1px solid get-color(black-coral-6); + border-radius: 4px; + + &-popper[data-placement^=bottom] { + padding: 0; + } + + &-wrapper { + display: block; + } + + &__input-container { + display: block; + } + + &__triangle { + &::before, + &::after { + content: none !important; + } + } + + &__navigation { + width: 50px; + height: 50px; + top: 0; + + &:hover { + background-color: var(--color-bg); + } + + &--previous { + border-top-left-radius: 4px; + border-right: 1px solid var(--color-gray); + left: 0; + } + + &--next { + border-top-right-radius: 4px; + border-left: 1px solid var(--color-gray); + right: 0; + } + } +} diff --git a/GUI/src/components/FormElements/FormDatepicker/index.tsx b/GUI/src/components/FormElements/FormDatepicker/index.tsx new file mode 100644 index 00000000..1de8e635 --- /dev/null +++ b/GUI/src/components/FormElements/FormDatepicker/index.tsx @@ -0,0 +1,98 @@ +import { forwardRef, useId } from 'react'; +import ReactDatePicker, { registerLocale } from 'react-datepicker'; +import clsx from 'clsx'; +import { et } from 'date-fns/locale'; +import { ControllerRenderProps } from 'react-hook-form'; +import { + MdChevronRight, + MdChevronLeft, + MdOutlineToday, + MdOutlineSchedule, +} from 'react-icons/md'; + +import { Icon } from 'components'; +import 'react-datepicker/dist/react-datepicker.css'; +import './FormDatepicker.scss'; + +registerLocale('et-EE', et); + +type FormDatepickerProps = ControllerRenderProps & { + label: string; + name: string; + hideLabel?: boolean; + disabled?: boolean; + placeholder?: string; + timePicker?: boolean; + direction?: 'row' | 'column'; +}; + +const FormDatepicker = forwardRef( + ( + { + label, + name, + hideLabel, + disabled, + placeholder, + timePicker, + direction = 'column', + ...rest + }, + ref + ) => { + const id = useId(); + const { value, onChange } = rest; + + const datepickerClasses = clsx( + 'datepicker', + disabled && 'datepicker--disabled' + ); + + return ( +
+ {label && !hideLabel && ( + + )} +
+ } + nextMonthButtonLabel={} + aria-label={hideLabel ? label : undefined} + showTimeSelect={timePicker} + showTimeSelectOnly={timePicker} + timeIntervals={15} + timeFormat="HH:mm:ss" + timeInputLabel="" + portalId="overlay-root" + {...rest} + onChange={onChange} + /> + + ) : ( + + ) + } + size="medium" + /> +
+
+ ); + } +); + +export default FormDatepicker; diff --git a/GUI/src/components/FormElements/FormInput/FormInput.scss b/GUI/src/components/FormElements/FormInput/FormInput.scss new file mode 100644 index 00000000..c010c478 --- /dev/null +++ b/GUI/src/components/FormElements/FormInput/FormInput.scss @@ -0,0 +1,97 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.input { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: 0px; + position: relative; + + .icon { + position: absolute; + top: 10px; + right: 10px; + } + } + + &__inline_error { + color: get-color(jasper-10); + font-size: 12px; + + } + + &__error { + width: 100%; + margin-right: 6px; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + color: get-color(black-coral-20); + border-radius: $veera-radius-s; + background-color: get-color(jasper-3); + font-size: 13px; + line-height: 20px; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + + &::before { + content: ''; + display: block; + background-color: get-color(jasper-3); + border-left: 16px solid transparent; + border-right: 16px solid transparent; + border-bottom: 25px; + } + } + + input { + width: 100%; + display: block; + appearance: none; + background-color: get-color(white); + border: 1px solid get-color(black-coral-6); + border-radius: $veera-radius-s; + color: var(--color-black); + font-size: $veera-font-size-100; + height: 40px; + line-height: 24px; + padding: get-spacing(paldiski); + + &::placeholder { + color: get-color(black-coral-6); + } + + &:focus { + outline: none; + border-color: get-color(sapphire-blue-10); + } + } + + &--error { + input { + border-color: get-color(jasper-10); + } + } + + &--disabled & { + input { + background-color: get-color(black-coral-0); + border: solid 1px get-color(jasper-10); + } + } +} diff --git a/GUI/src/components/FormElements/FormInput/index.tsx b/GUI/src/components/FormElements/FormInput/index.tsx new file mode 100644 index 00000000..dd8df673 --- /dev/null +++ b/GUI/src/components/FormElements/FormInput/index.tsx @@ -0,0 +1,50 @@ +import { forwardRef, InputHTMLAttributes, PropsWithChildren, useId } from 'react'; +import clsx from 'clsx'; +import './FormInput.scss'; +import { DefaultTFuncReturn } from 'i18next'; + +type InputProps = PropsWithChildren> & { + label: string; + name: string; + hideLabel?: boolean; + maxLength?: number; + error?: string; + placeholder?:string | DefaultTFuncReturn; +}; + +const FormInput = forwardRef( + ( + { label, name, disabled, hideLabel, maxLength, error, children,placeholder, ...rest }, + ref + ) => { + const id = useId(); + + const inputClasses = clsx('input', disabled && 'input--disabled', error && 'input--error'); + + return ( +
+ {label && !hideLabel && ( + + )} +
+ + {error &&

{error}

} + {children} +
+
+ ); + } +); + +export default FormInput; diff --git a/GUI/src/components/FormElements/FormRadios/FormRadios.scss b/GUI/src/components/FormElements/FormRadios/FormRadios.scss new file mode 100644 index 00000000..72862bc1 --- /dev/null +++ b/GUI/src/components/FormElements/FormRadios/FormRadios.scss @@ -0,0 +1,75 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.radios { + width: 100%; + display: flex; + align-items: flex-start; + gap: get-spacing(paldiski); + + &__label { + display: block; + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper { + display: flex; + gap: 8px; + } + + &__stack { + gap: 8px; + } + + &__item { + input[type=radio] { + display: none; + + + label { + display: block; + padding-left: 32px; + position: relative; + font-size: $veera-font-size-100; + line-height: $veera-line-height-500; + + &::before { + content: ''; + display: block; + width: 16px; + height: 16px; + box-shadow: inset 0 0 0 1px get-color(black-coral-2); + border-radius: 50%; + position: absolute; + left: 4px; + top: 4px; + } + } + + &:checked { + + label { + &::before { + width: 20px; + height: 20px; + box-shadow: inset 0 0 0 1px #8F91A8; + } + + &::after { + content: ''; + display: block; + width: 10px; + height: 10px; + border-radius: 50%; + background-color: get-color(sapphire-blue-10); + position: absolute; + top: 9px; + left: 9px; + } + } + } + } + } +} diff --git a/GUI/src/components/FormElements/FormRadios/index.tsx b/GUI/src/components/FormElements/FormRadios/index.tsx new file mode 100644 index 00000000..2b1956ac --- /dev/null +++ b/GUI/src/components/FormElements/FormRadios/index.tsx @@ -0,0 +1,65 @@ +import { FC, useId } from 'react'; +import './FormRadios.scss'; + +type FormRadiosType = { + label: string; + name: string; + hideLabel?: boolean; + items: { + label: string; + value: string; + }[] |undefined; + onChange: (selectedValue: string) => void; + selectedValue?: string; // New prop for the selected value + isStack?: boolean; + error?: string; +}; + +const FormRadios: FC = ({ + label, + name, + hideLabel, + items, + onChange, + selectedValue, // Use selectedValue to control the selected radio button + isStack = false, + error, +}) => { + const id = useId(); + + return ( +
+
+
+ {label && !hideLabel && ( + + )} +
+ {items?.map((item, index) => ( +
+ { + onChange(event.target.value); + }} + /> + +
+ ))} +
+
+
+
{error &&

{error}

}
+
+ ); +}; + +export default FormRadios; + + + + diff --git a/GUI/src/components/FormElements/FormSelect/FormMultiselect.tsx b/GUI/src/components/FormElements/FormSelect/FormMultiselect.tsx new file mode 100644 index 00000000..ef9480ae --- /dev/null +++ b/GUI/src/components/FormElements/FormSelect/FormMultiselect.tsx @@ -0,0 +1,124 @@ +import { FC, ReactNode, SelectHTMLAttributes, useId, useState } from 'react'; +import { useSelect } from 'downshift'; +import clsx from 'clsx'; +import { useTranslation } from 'react-i18next'; +import { MdArrowDropDown } from 'react-icons/md'; + +import { Icon } from 'components'; +import './FormSelect.scss'; + +type SelectOption = { label: string, value: string }; + +type FormMultiselectProps = SelectHTMLAttributes & { + label: ReactNode; + name: string; + placeholder?: string; + hideLabel?: boolean; + options: SelectOption[]; + selectedOptions?: SelectOption[]; + onSelectionChange?: (selection: SelectOption[] | null) => void; +}; + +const FormMultiselect: FC = ( + { + label, + hideLabel, + options, + disabled, + placeholder, + defaultValue, + selectedOptions, + onSelectionChange, + ...rest + }, +) => { + const id = useId(); + const { t } = useTranslation(); + const [selectedItems, setSelectedItems] = useState(selectedOptions ?? []); + const { + isOpen, + getToggleButtonProps, + getLabelProps, + getMenuProps, + highlightedIndex, + getItemProps, + } = useSelect({ + items: options, + stateReducer: (state, actionAndChanges) => { + const { changes, type } = actionAndChanges; + if (type === useSelect.stateChangeTypes.ItemClick) { + return { + ...changes, + isOpen: true, + highlightedIndex: state.highlightedIndex, + }; + } else { + return changes; + } + }, + selectedItem: null, + onSelectedItemChange: ({ selectedItem }) => { + if (!selectedItem) { + return; + } + const index = selectedItems.findIndex((item) => item.value === selectedItem.value); + const items = []; + if (index > 0) { + items.push( + ...selectedItems.slice(0, index), + ...selectedItems.slice(index + 1) + ); + } else if (index === 0) { + items.push(...selectedItems.slice(1)); + } else { + items.push(...selectedItems, selectedItem); + } + setSelectedItems(items); + if (onSelectionChange) onSelectionChange(items); + }, + }); + + const selectClasses = clsx( + 'select', + disabled && 'select--disabled', + ); + + const placeholderValue = placeholder || t('global.choose'); + + return ( +
+ {label && !hideLabel && } +
+
+ {selectedItems?.length > 0 ? `${t('global.chosen')} (${selectedItems?.length})` : placeholderValue} + } /> +
+ +
    + {isOpen && + options.map((item, index) => ( +
  • + s.value).includes(item.value)} + value={item.value} + onChange={() => null} + /> + {item.label} +
  • + ))} +
+
+
+ ); +}; + + +export default FormMultiselect; diff --git a/GUI/src/components/FormElements/FormSelect/FormSelect.scss b/GUI/src/components/FormElements/FormSelect/FormSelect.scss new file mode 100644 index 00000000..b6b4f434 --- /dev/null +++ b/GUI/src/components/FormElements/FormSelect/FormSelect.scss @@ -0,0 +1,128 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.select { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper { + width: 100%; + position: relative; + } + + &__error { + border: 1px solid get-color(jasper-10); + + } + + &__default { + border: 1px solid get-color(black-coral-6); + + } + + &__trigger { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + appearance: none; + background-color: get-color(white); + border-radius: $veera-radius-s; + color: get-color(black); + font-size: $veera-font-size-100; + height: 40px; + line-height: 24px; + padding: get-spacing(paldiski); + + .icon { + font-size: $veera-font-size-250; + } + + &[aria-expanded=true] { + border-color: get-color(sapphire-blue-10); + border-radius: 3px; + + + #{$self}__menu { + display: block; + } + + +#{$self}__menu_up { + display: block; + } + + .icon { + transform: rotate(180deg); + } + } + } + + &__menu { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: get-color(white); + border-radius: 4px; + border: 1px solid get-color(black-coral-2); + border-top: 1; + z-index: 9998; + max-height: 320px; + overflow: auto; + margin-top: 3px; + } + + &__menu_up { + display: none; + position: absolute; + top: auto; + left: 0; + right: 0; + bottom: 100%; + background-color: get-color(white); + border-radius: 4px; + border: 1px solid get-color(black-coral-2); + border-top: 1; + z-index: 9998; + max-height: 320px; + overflow: auto; + margin-bottom: 3px; + } + + &__option { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 15px; + + span { + display: block; + } + + &[aria-selected=true] { + background-color: #DDEBFF; + + &:hover, + &:focus { + background-color: get-color(sapphire-blue-10); + } + } + + &:hover, + &:focus { + background-color: get-color(black-coral-0); + } + } +} diff --git a/GUI/src/components/FormElements/FormSelect/index.tsx b/GUI/src/components/FormElements/FormSelect/index.tsx new file mode 100644 index 00000000..18983ef7 --- /dev/null +++ b/GUI/src/components/FormElements/FormSelect/index.tsx @@ -0,0 +1,148 @@ +import { + forwardRef, + ReactNode, + SelectHTMLAttributes, + useId, + useState, + useEffect, +} from 'react'; +import { useSelect } from 'downshift'; +import clsx from 'clsx'; +import { useTranslation } from 'react-i18next'; +import { MdArrowDropDown } from 'react-icons/md'; + +import { Icon } from 'components'; +import './FormSelect.scss'; +import { ControllerRenderProps } from 'react-hook-form'; + +type FormSelectOption = { + label: string; + value: string | { name: string; id: string }; +}; + +type FormSelectProps = Partial & + SelectHTMLAttributes & { + label: ReactNode; + name: string; + placeholder?: string; + hideLabel?: boolean; + direction?: 'down' | 'up'; + options: FormSelectOption[]; + onSelectionChange?: (selection: FormSelectOption | null) => void; + error?: string; + defaultValue?: string | { name: string; id: string }; + }; + +const itemToString = (item: FormSelectOption | null) => { + return item ? item.value.toString() : ''; +}; + +const FormSelect = forwardRef( + ( + { + label, + hideLabel, + direction = 'down', + options, + disabled, + placeholder, + defaultValue, + onSelectionChange, + error, + ...rest + }, + ref + ) => { + const id = useId(); + const { t } = useTranslation(); + + const [selectedItem, setSelectedItem] = useState( + options?.find((o) => o.value === defaultValue) || + options?.find( + (o) => typeof o.value === 'object' && o.value?.name === defaultValue + ) || + null + ); + + useEffect(() => { + const newSelectedItem = + options?.find((o) => o.value === defaultValue) || + options?.find( + (o) => typeof o.value === 'object' && o.value?.name === defaultValue + ) || + null; + setSelectedItem(newSelectedItem); + }, [defaultValue, options]); + + const { + isOpen, + getToggleButtonProps, + getLabelProps, + getMenuProps, + highlightedIndex, + getItemProps, + } = useSelect({ + id, + items: options, + itemToString, + selectedItem, + onSelectedItemChange: ({ selectedItem: newSelectedItem }) => { + setSelectedItem(newSelectedItem ?? null); + if (onSelectionChange) onSelectionChange(newSelectedItem ?? null); + }, + }); + + const selectClasses = clsx('select', disabled && 'select--disabled'); + + const placeholderValue = + placeholder || t('datasetGroups.createDataset.selectPlaceholder'); + + return ( +
+ {label && !hideLabel && ( + + )} +
+
+ {selectedItem?.label ?? placeholderValue} + } + /> +
+
    + {isOpen && + options.map((item, index) => ( +
  • + {item.label} +
  • + ))} +
+ {error &&

{error}

} +
+
+ ); + } +); + +export default FormSelect; diff --git a/GUI/src/components/FormElements/FormTextarea/FormTextarea.scss b/GUI/src/components/FormElements/FormTextarea/FormTextarea.scss new file mode 100644 index 00000000..ff1971ac --- /dev/null +++ b/GUI/src/components/FormElements/FormTextarea/FormTextarea.scss @@ -0,0 +1,109 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.textarea { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__wrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: 7px; + position: relative; + } + + &__error { + width: 100%; + margin-right: 6px; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + color: get-color(black-coral-20); + border-radius: $veera-radius-s; + background-color: get-color(jasper-3); + font-size: 13px; + line-height: 20px; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + + &::before { + content: ''; + display: block; + background-color: get-color(jasper-3); + border-left: 16px solid transparent; + border-right: 16px solid transparent; + border-bottom: 25px; + } + } + + &__max-length-top { + position: absolute; + top: 10px; + right: 8px; + font-size: $veera-font-size-80; + color: get-color(black-coral-12); + pointer-events: none; + } + + &__max-length-bottom { + position: absolute; + bottom: 10px; + right: 8px; + font-size: $veera-font-size-80; + color: get-color(black-coral-12); + pointer-events: none; + } + + textarea { + width: 100%; + display: block; + appearance: none; + background-color: get-color(white); + border: 1px solid get-color(black-coral-6); + border-radius: $veera-radius-s; + color: var(--color-black); + font-size: $veera-font-size-80; + line-height: $veera-line-height-500; + height: 40px; + min-height: 40px; + padding: get-spacing(paldiski); + + &::placeholder { + color: get-color(black-coral-6); + } + + &:focus { + outline: none; + border-color: get-color(sapphire-blue-10); + } + } + + &--error { + input { + border-color: get-color(jasper-10); + } + } + + &--disabled & { + input { + background-color: get-color(black-coral-0); + } + } + + &--maxlength-shown { + textarea { + padding-right: 70px; + } + } +} diff --git a/GUI/src/components/FormElements/FormTextarea/index.tsx b/GUI/src/components/FormElements/FormTextarea/index.tsx new file mode 100644 index 00000000..4190c782 --- /dev/null +++ b/GUI/src/components/FormElements/FormTextarea/index.tsx @@ -0,0 +1,72 @@ +import { ChangeEvent, forwardRef, useId, useState } from 'react'; +import TextareaAutosize, { TextareaAutosizeProps } from 'react-textarea-autosize'; +import clsx from 'clsx'; + +import './FormTextarea.scss'; + +type TextareaProps = TextareaAutosizeProps & { + label: string; + name: string; + hideLabel?: boolean; + showMaxLength?: boolean; + maxLengthBottom?: boolean; +}; + +const FormTextarea = forwardRef(( + { + label, + name, + maxLength = 2000, + minRows = 3, + maxRows = 3, + disabled, + hideLabel, + showMaxLength, + maxLengthBottom, + defaultValue, + onChange, + ...rest + }, + ref, +) => { + const id = useId(); + const [currentLength, setCurrentLength] = useState((typeof defaultValue === 'string' && defaultValue.length) || 0); + const textareaClasses = clsx( + 'textarea', + disabled && 'textarea--disabled', + showMaxLength && 'textarea--maxlength-shown', + ); + + const handleOnChange = (e: ChangeEvent) => { + if (showMaxLength) { + setCurrentLength(e.target.value.length); + } + }; + + return ( +
+ {label && !hideLabel && } +
+ { + if (onChange) onChange(e); + handleOnChange(e); + }} + {...rest} + /> + {showMaxLength && ( +
{currentLength}/{maxLength}
+ )} +
+
+ ); +}); + +export default FormTextarea; diff --git a/GUI/src/components/FormElements/Switch/Switch.scss b/GUI/src/components/FormElements/Switch/Switch.scss new file mode 100644 index 00000000..fddf67c0 --- /dev/null +++ b/GUI/src/components/FormElements/Switch/Switch.scss @@ -0,0 +1,68 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.switch { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + &__button { + display: flex; + align-items: center; + gap: 4px; + height: 40px; + isolation: isolate; + padding: 4px; + border-radius: 20px; + background-color: get-color(black-coral-1); + font-size: $veera-font-size-80; + line-height: $veera-line-height-500; + color: get-color(black-coral-12); + position: relative; + transition: background-color .25s ease-out; + + &[aria-checked=true] { + background-color: var(--active-color, get-color(sapphire-blue-10)); + color: get-color(sapphire-blue-10); + + #{$self} { + &__off { + color: get-color(white); + background: none; + } + + &__on { + color: var(--active-color, get-color(sapphire-blue-10)); + background-color: get-color(white); + } + } + } + } + + &__thumb { + display: none; + } + + &__on, + &__off { + display: flex; + border-radius: 20px; + padding: 5.5px 10px; + font-weight: $veera-font-weight-delta; + transition: all .25s ease-out; + } + + &__off { + font-weight: $veera-font-weight-delta; + background-color: get-color(white); + } +} diff --git a/GUI/src/components/FormElements/Switch/index.tsx b/GUI/src/components/FormElements/Switch/index.tsx new file mode 100644 index 00000000..ed414c7e --- /dev/null +++ b/GUI/src/components/FormElements/Switch/index.tsx @@ -0,0 +1,68 @@ +import { forwardRef, useId } from 'react'; +import * as RadixSwitch from '@radix-ui/react-switch'; +import { useTranslation } from 'react-i18next'; +import { ControllerRenderProps } from 'react-hook-form'; + +import './Switch.scss'; + +type SwitchProps = Partial & { + onLabel?: string; + offLabel?: string; + onColor?: string; + name?: string; + label: string; + checked?: boolean; + defaultChecked?: boolean; + hideLabel?: boolean; + onCheckedChange?: (checked: boolean) => void; +}; + +const Switch = forwardRef( + ( + { + onLabel, + offLabel, + onColor, + name, + label, + checked, + hideLabel, + onCheckedChange, + defaultChecked, + }, + ref + ) => { + const id = useId(); + const { t } = useTranslation(); + const onValueLabel = onLabel || t('global.on'); + const offValueLabel = offLabel || t('global.off'); + + return ( +
+ {label && !hideLabel && ( + + )} + + + {onValueLabel} + {offValueLabel} + +
+ ); + } +); + +export default Switch; diff --git a/GUI/src/components/FormElements/SwitchBox/SwitchBox.scss b/GUI/src/components/FormElements/SwitchBox/SwitchBox.scss new file mode 100644 index 00000000..2f7a0490 --- /dev/null +++ b/GUI/src/components/FormElements/SwitchBox/SwitchBox.scss @@ -0,0 +1,45 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.switchbox { + $self: &; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + + &__button { + width: 48px; + height: 8px; + border-radius: 4px; + background-color: get-color(black-coral-6); + position: relative; + + &[aria-checked=true] { + background-color: get-color(sapphire-blue-4); + + #{$self} { + &__thumb { + transform: translate(24px, -50%); + background-color: get-color(sapphire-blue-10); + } + } + } + } + + &__thumb { + position: absolute; + width: 24px; + height: 24px; + border-radius: 50%; + background-color: get-color(white); + border: 1px solid get-color(black-coral-2); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.14); + left: 0; + top: 50%; + transform: translateY(-50%); + transition: all .25s ease-out; + } +} diff --git a/GUI/src/components/FormElements/SwitchBox/index.tsx b/GUI/src/components/FormElements/SwitchBox/index.tsx new file mode 100644 index 00000000..1550576a --- /dev/null +++ b/GUI/src/components/FormElements/SwitchBox/index.tsx @@ -0,0 +1,44 @@ +import { forwardRef, useId } from 'react'; +import * as RadixSwitch from '@radix-ui/react-switch'; +import { ControllerRenderProps } from 'react-hook-form'; + +import './SwitchBox.scss'; + +type SwitchBoxProps = Partial & { + name?: string; + label: string; + checked?: boolean; + hideLabel?: boolean; + onCheckedChange?: (checked: boolean) => void; +} + +const SwitchBox = forwardRef(( + { + name, + label, + checked, + hideLabel, + onCheckedChange, + }, + ref, +) => { + const id = useId(); + + return ( +
+ {label && !hideLabel && } + + + +
+ ); +}); + +export default SwitchBox; diff --git a/GUI/src/components/FormElements/index.tsx b/GUI/src/components/FormElements/index.tsx new file mode 100644 index 00000000..ac295d55 --- /dev/null +++ b/GUI/src/components/FormElements/index.tsx @@ -0,0 +1,23 @@ +import FormInput from './FormInput'; +import FormTextarea from './FormTextarea'; +import FormSelect from './FormSelect'; +import FormMultiselect from './FormSelect/FormMultiselect'; +import Switch from './Switch'; +import FormCheckboxes from './FormCheckboxes'; +import FormRadios from './FormRadios'; +import FormCheckbox from './FormCheckbox'; +import FormDatepicker from './FormDatepicker'; +import SwitchBox from './SwitchBox'; + +export { + FormInput, + FormTextarea, + FormSelect, + FormMultiselect, + Switch, + FormCheckboxes, + FormRadios, + FormCheckbox, + FormDatepicker, + SwitchBox, +}; diff --git a/GUI/src/components/Header/Header.scss b/GUI/src/components/Header/Header.scss new file mode 100644 index 00000000..542c06f6 --- /dev/null +++ b/GUI/src/components/Header/Header.scss @@ -0,0 +1,10 @@ +@import '@buerokratt-ria/styles/styles/tools/spacing'; +@import '@buerokratt-ria/styles/styles/tools/color'; + +.header { + height: 100px; + padding: 24px 24px 24px 42px; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.14), 0 2px 2px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.2); + background-color: get-color(white); + z-index: 99; +} diff --git a/GUI/src/components/Header/index.tsx b/GUI/src/components/Header/index.tsx new file mode 100644 index 00000000..6902cd2c --- /dev/null +++ b/GUI/src/components/Header/index.tsx @@ -0,0 +1,190 @@ +import { FC, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +import { Track, Button, Dialog } from 'components'; +import useStore from 'store'; +import { useToast } from 'hooks/useToast'; +import apiDev from 'services/api-dev'; +import { useCookies } from 'react-cookie'; +import './Header.scss'; +import { useDialog } from 'hooks/useDialog'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { authEndpoints } from 'utils/endpoints'; +import { authQueryKeys } from 'utils/queryKeys'; +import { UserInfo } from 'types/userInfo'; + +const Header: FC = () => { + const { t } = useTranslation(); + const userInfo = useStore((state) => state.userInfo); + const toast = useToast(); + + const { open } = useDialog(); + + const [sessionTimeOutDuration, setSessionTimeOutDuration] = + useState(30); + const [sessionTimeOutModalOpened, setSessionTimeOutModalOpened] = + useState(false); + const [sessionExtentionInProgress, setSessionExtentionInProgress] = + useState(false); + const customJwtCookieKey = 'customJwtCookie'; + + useEffect(() => { + const interval = setInterval(() => { + const expirationTimeStamp = localStorage.getItem('exp'); + if ( + expirationTimeStamp !== 'null' && + expirationTimeStamp !== null && + expirationTimeStamp !== undefined + ) { + const expirationDate = new Date(parseInt(expirationTimeStamp) ?? ''); + const currentDate = new Date(Date.now()); + if ( + expirationDate.getTime() - currentDate.getTime() <= 240000 + ) { + if (!sessionTimeOutModalOpened) { + setSessionTimeOutModalOpened(true); + setSessionTimeOutDuration(30); + } + } + } + }, 2000); + return () => clearInterval(interval); + }, [open, sessionTimeOutDuration]); + + useEffect(() => { + let timer: NodeJS.Timeout | null = null; + if (sessionTimeOutModalOpened) { + timer = setInterval(() => { + setSessionTimeOutDuration((prev) => { + if (prev > 0) { + return prev - 1; + } else { + if (!sessionExtentionInProgress) handleLogout(); + return 0; + } + }); + }, 1000); + } else if (timer) { + clearInterval(timer); + } + + return () => { + if (timer) { + clearInterval(timer); + } + }; + }, [sessionTimeOutModalOpened]); + + const [_, setCookie] = useCookies([customJwtCookieKey]); + + const setNewCookie = (cookieValue: string) => { + const cookieOptions = { path: '/' }; + setCookie(customJwtCookieKey, cookieValue, cookieOptions); + }; + + const extendUserSessionMutation = useMutation({ + mutationFn: async () => { + return await apiDev.get(authEndpoints.GET_EXTENDED_COOKIE()); + }, + onSuccess: (data) => { + setNewCookie(data?.data?.response); + setSessionTimeOutDuration(30); + setSessionTimeOutModalOpened(false); + setSessionExtentionInProgress(false); + refetch() + }, + onError: (error: AxiosError) => { + handleLogout(); + }, + }); + + const { refetch } = useQuery({ + queryKey: authQueryKeys.USER_DETAILS(), + onSuccess: (res: { response: UserInfo }) => { + localStorage.setItem('exp', res.response.JWTExpirationTimestamp); + useStore.getState().setUserInfo(res.response); + }, + enabled: false + }); + const logoutMutation = useMutation({ + mutationFn: () => apiDev.get(authEndpoints.LOGOUT()), + onSuccess() { + localStorage.removeItem('exp'); + window.location.href = import.meta.env.REACT_APP_CUSTOMER_SERVICE_LOGIN; + }, + onError: async (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + }); + + const handleLogout = () => { + localStorage.removeItem('exp'); + logoutMutation.mutate(); + }; + return ( +
+
+ + {userInfo && ( + + + + )} + +
+ + {sessionTimeOutModalOpened && ( + setSessionTimeOutModalOpened(false)} + isOpen={sessionTimeOutModalOpened} + title={t('global.sessionTimeOutTitle') ?? ''} + footer={ +
+ + +
+ } + > +

+ {t('global.sessionTimeOutDesc', { + seconds: sessionTimeOutDuration, + }) ?? ''} +

+
+ )} +
+ ); +}; + +export default Header; diff --git a/GUI/src/components/Icon/Icon.scss b/GUI/src/components/Icon/Icon.scss new file mode 100644 index 00000000..ce570acf --- /dev/null +++ b/GUI/src/components/Icon/Icon.scss @@ -0,0 +1,17 @@ +@import 'src/styles/tools/spacing'; + +.icon { + display: inline-flex; + align-items: center; + justify-content: center; + + &--small { + width: get-spacing(haapsalu); + height: get-spacing(haapsalu); + } + + &--medium { + width: get-spacing(kuressaare); + height: get-spacing(kuressaare); + } +} diff --git a/GUI/src/components/Icon/index.tsx b/GUI/src/components/Icon/index.tsx new file mode 100644 index 00000000..d9ab3988 --- /dev/null +++ b/GUI/src/components/Icon/index.tsx @@ -0,0 +1,26 @@ +import { CSSProperties, forwardRef, ReactNode, StyleHTMLAttributes } from 'react'; +import * as AccessibleIcon from '@radix-ui/react-accessible-icon'; +import clsx from 'clsx'; + +import './Icon.scss'; + +type IconProps = StyleHTMLAttributes & { + label?: string | null; + icon: ReactNode; + size?: 'small' | 'medium'; +}; + +const Icon = forwardRef(({ label, icon, size = 'small', ...rest }, ref) => { + const iconClasses = clsx( + 'icon', + `icon--${size}`, + ); + + return ( + + {icon} + + ); +}); + +export default Icon; diff --git a/GUI/src/components/Label/Label.scss b/GUI/src/components/Label/Label.scss new file mode 100644 index 00000000..62f0bd19 --- /dev/null +++ b/GUI/src/components/Label/Label.scss @@ -0,0 +1,90 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.label { + $self: &; + display: flex; + padding: 1.5px 16px; + font-size: 12px; + font-weight: $veera-font-weight-delta; + border: 2px solid; + background-color: get-color(white); + border-radius: $veera-radius-s; + position: relative; + width: fit-content; + height: fit-content; + margin-right: 5px; + + &--info { + color: get-color(sapphire-blue-10); + border-color: get-color(sapphire-blue-10); + + #{$self} { + &__icon { + border-color: get-color(sapphire-blue-10); + } + } + } + + &--warning { + color: get-color(dark-tangerine-10); + border-color: get-color(dark-tangerine-10); + + #{$self} { + &__icon { + border-color: get-color(dark-tangerine-10); + } + } + } + + &--error { + color: get-color(jasper-10); + border-color: get-color(jasper-10); + + #{$self} { + &__icon { + border-color: get-color(jasper-10); + } + } + } + + &--default { + color: get-color(black-coral-7); + border-color: get-color(black-coral-7); + + #{$self} { + &__icon { + border-color: get-color(black-coral-7); + } + } + } + + &--success { + color: get-color(sea-green-10); + border-color: get-color(sea-green-10); + + #{$self} { + &__icon { + border-color: get-color(sea-green-10); + } + } + } + + &__icon { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + font-size: 13px; + line-height: 15px; + right: -8px; + top: 4px; + width: 16px; + height: 16px; + border-radius: 50%; + border: 2px solid; + background-color: get-color(white); + } +} diff --git a/GUI/src/components/Label/index.tsx b/GUI/src/components/Label/index.tsx new file mode 100644 index 00000000..e27d0d4f --- /dev/null +++ b/GUI/src/components/Label/index.tsx @@ -0,0 +1,40 @@ +import { forwardRef, PropsWithChildren, ReactNode } from 'react'; +import clsx from 'clsx'; +import { MdOutlineCheck } from 'react-icons/md'; + +import { Tooltip } from 'components'; +import './Label.scss'; + +type LabelProps = { + type?: 'warning' | 'error' | 'info' | 'success' | 'default'; + tooltip?: ReactNode; +} + +const Label = forwardRef>(( + { + type = 'default', + tooltip, + children, + }, ref, +) => { + const labelClasses = clsx( + 'label', + `label--${type}`, + tooltip && 'label--tooltip', + ); + + return ( + + {children} + {tooltip && ( + + + {type === 'success' ? : 'i'} + + + )} + + ); +}); + +export default Label; diff --git a/GUI/src/components/LabelChip/index.scss b/GUI/src/components/LabelChip/index.scss new file mode 100644 index 00000000..ed40b04a --- /dev/null +++ b/GUI/src/components/LabelChip/index.scss @@ -0,0 +1,23 @@ +.label-chip { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px 20px; + border-radius: 16px; + background-color: #e0e0e0; + margin: 4px; + gap: 7px; +} + +.label-chip .label { + margin-right: 8px; +} + +.label-chip .button { + background: none; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/GUI/src/components/LabelChip/index.tsx b/GUI/src/components/LabelChip/index.tsx new file mode 100644 index 00000000..146e80c1 --- /dev/null +++ b/GUI/src/components/LabelChip/index.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import './index.scss'; +import { MdClose } from 'react-icons/md'; + +type LabelChipProps = { + label: string; + onRemove: () => void; +}; + +const LabelChip: React.FC = ({ label, onRemove }) => { + return ( +
+ {label} + +
+ ); +}; + +export default LabelChip; diff --git a/GUI/src/components/Layout/Layout.scss b/GUI/src/components/Layout/Layout.scss new file mode 100644 index 00000000..116641c0 --- /dev/null +++ b/GUI/src/components/Layout/Layout.scss @@ -0,0 +1,28 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; + +.layout { + height: 100%; + display: flex; + + &__wrapper { + flex: 1; + display: flex; + flex-direction: column; + position: relative; + } + + &__main { + flex: 1; + display: flex; + flex-direction: column; + gap: get-spacing(haapsalu); + overflow: auto; + padding: get-spacing(haapsalu); + position: absolute; + top: 100px; + left: 0; + right: 0; + bottom: 0; + } +} diff --git a/GUI/src/components/Layout/index.tsx b/GUI/src/components/Layout/index.tsx new file mode 100644 index 00000000..0857601e --- /dev/null +++ b/GUI/src/components/Layout/index.tsx @@ -0,0 +1,24 @@ +import { FC } from 'react'; +import { Outlet } from 'react-router-dom'; +import useStore from 'store'; +import './Layout.scss'; +import { useToast } from '../../hooks/useToast'; +import Header from 'components/Header'; +import MainNavigation from 'components/MainNavigation'; + +const Layout: FC = () => { + return ( +
+ +
+
+
+ +
+
+
+ ); +}; + +export default Layout; diff --git a/GUI/src/components/MainNavigation/MainNavigation.scss b/GUI/src/components/MainNavigation/MainNavigation.scss new file mode 100644 index 00000000..a41c41e5 --- /dev/null +++ b/GUI/src/components/MainNavigation/MainNavigation.scss @@ -0,0 +1,130 @@ +@import '@buerokratt-ria/styles/styles/tools/spacing'; +@import '@buerokratt-ria/styles/styles/tools/color'; +@import '@buerokratt-ria/styles/styles/settings/variables/typography'; + +.nav { + $self: &; + width: 208px; + background-color: get-color(sapphire-blue-10); + overflow: auto; + scrollbar-width: none; + transition: width .1s ease-out; + z-index: 9999; + + &::-webkit-scrollbar { + display: none; + } + + li, a, .nav__toggle, .nav__menu-toggle { + font-size: 14px; + line-height: 1.5; + } + + &__menu-toggle { + display: flex; + align-items: center; + + &:hover { + background-color: get-color(sapphire-blue-8); + } + + &:active { + background-color: get-color(sapphire-blue-7); + } + } + + a, .nav__toggle { + width: 100%; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + color: get-color(black-coral-0); + padding: 14px 8px 14px 32px; + box-shadow: inset 0 -1px 0 get-color(sapphire-blue-14); + + span:not(.icon) { + flex: 1; + display: block; + } + + &:hover { + background-color: get-color(sapphire-blue-8); + } + + &:active { + background-color: #2E78B3; + } + + &.active { + background-color: #2E78B3; + font-weight: 700; + } + } + + &__toggle { + &[aria-expanded=true] { + font-weight: 700; + + .icon { + transform: rotate(180deg); + } + + + ul { + display: block; + } + } + + &.nav__toggle--icon { + + .icon:first-child { + transform: none; + } + } + } + + &__toggle-icon { + margin-left: auto; + } + + &__menu-toggle { + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + color: get-color(white); + padding: 14px 8px; + box-shadow: inset 0 -1px 0 get-color(sapphire-blue-14); + } + + &__submenu { + display: none; + + a, .nav__toggle { + background-color: get-color(sapphire-blue-14); + box-shadow: inset 0 -1px 0 get-color(sapphire-blue-17); + } + + #{$self} { + &__submenu { + a { + background-color: get-color(sapphire-blue-17); + box-shadow: inset 0 -1px 0 get-color(black); + padding: 14px 48px 14px 40px; + } + } + } + } +} + +.collapsed { + .nav__submenu { + visibility: hidden; + height: 0; + } + + button[aria-expanded=true] { + .icon { + transform: rotate(0deg); + } + } +} diff --git a/GUI/src/components/MainNavigation/index.tsx b/GUI/src/components/MainNavigation/index.tsx new file mode 100644 index 00000000..6c6b08df --- /dev/null +++ b/GUI/src/components/MainNavigation/index.tsx @@ -0,0 +1,178 @@ +import { FC, MouseEvent, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { NavLink, useLocation } from 'react-router-dom'; +import { MdKeyboardArrowDown } from 'react-icons/md'; +import { useQuery } from '@tanstack/react-query'; +import clsx from 'clsx'; +import { Icon } from 'components'; +import type { MenuItem } from 'types/mainNavigation'; +import './MainNavigation.scss'; +import apiDev from 'services/api-dev'; +import { userManagementEndpoints } from 'utils/endpoints'; +import { integrationQueryKeys } from 'utils/queryKeys'; +import { ROLES } from 'enums/roles'; +import UserIcon from 'assets/UserIcon'; +import IntegrationIcon from 'assets/IntegrationIcon'; +import DatabaseIcon from 'assets/DatabaseIcon'; +import DataModelsIcon from 'assets/DataModelsIcon'; +import IncomingTextsIcon from 'assets/IncomingTextsIcon'; +import TestModelIcon from 'assets/TestModelIcon'; + +const MainNavigation: FC = () => { + const { t } = useTranslation(); + const [menuItems, setMenuItems] = useState([]); + + const items = [ + { + id: 'userManagement', + label: t('menu.userManagement'), + path: '/user-management', + icon: , + }, + { + id: 'integration', + label: t('menu.integration'), + path: 'integration', + icon: , + }, + { + id: 'datasets', + label: t('menu.datasets'), + path: '#', + icon: , + children: [ + { + label: t('menu.datasetGroups'), + path: 'dataset-groups', + }, + { + label: t('menu.validationSessions'), + path: 'validation-sessions', + }, + { + label: t('menu.stopWords'), + path: 'stop-words', + }, + ], + }, + { + id: 'dataModels', + label: t('menu.dataModels'), + path: '#', + icon: , + children: [ + { + label: t('menu.models'), + path: '/data-models', + }, + { + label: t('menu.trainingSessions'), + path: 'training-sessions', + }, + ], + }, + { + id: 'correctedTexts', + label: t('menu.correctedTexts'), + path: '/corrected-texts', + icon: , + }, + { + id: 'testModel', + label: t('menu.testModel'), + path: '/test-model', + icon: , + }, + ]; + + const filterItemsByRole = (role: string[], items: MenuItem[]) => { + return items?.filter((item) => { + if (role.includes(ROLES.ROLE_ADMINISTRATOR)) return item?.id; + else if (role.includes(ROLES.ROLE_MODEL_TRAINER)) + return item?.id !== 'userManagement' && item?.id !== 'integration'; + else return false; + }); + }; + + useQuery(integrationQueryKeys.USER_ROLES(), { + queryFn: async () => { + const res = await apiDev.get(userManagementEndpoints.FETCH_USER_ROLES()); + return res?.data?.response; + }, + onSuccess: (res) => { + const roles = res; + const filteredItems = filterItemsByRole(roles, items); + setMenuItems(filteredItems); + }, + onError: (error) => { + console.error('Error fetching user roles:', error); + }, + }); + const location = useLocation(); + const [navCollapsed, setNavCollapsed] = useState(false); + + const handleNavToggle = (event: MouseEvent) => { + const isExpanded = + event?.currentTarget?.getAttribute('aria-expanded') === 'true'; + event?.currentTarget?.setAttribute( + 'aria-expanded', + isExpanded ? 'false' : 'true' + ); + }; + + const renderMenuTree = (menuItems: MenuItem[]) => { + return menuItems?.map((menuItem) => ( +
  • + {menuItem?.children ? ( +
    + +
      + {renderMenuTree(menuItem?.children)} +
    +
    + ) : ( + + {' '} + + {menuItem?.label} + + )} +
  • + )); + }; + + if (!menuItems) return null; + + return ( + + ); +}; + +export default MainNavigation; \ No newline at end of file diff --git a/GUI/src/components/Popover/Popover.scss b/GUI/src/components/Popover/Popover.scss new file mode 100644 index 00000000..9278c909 --- /dev/null +++ b/GUI/src/components/Popover/Popover.scss @@ -0,0 +1,15 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/typography'; + +.popover { + background-color: get-color(white); + padding: 4px; + border-radius: 4px; + filter: drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.25)); + font-size: $veera-font-size-80; + + &__arrow { + fill: get-color(white); + } +} diff --git a/GUI/src/components/Popover/index.tsx b/GUI/src/components/Popover/index.tsx new file mode 100644 index 00000000..929015bd --- /dev/null +++ b/GUI/src/components/Popover/index.tsx @@ -0,0 +1,27 @@ +import { FC, PropsWithChildren, ReactNode } from 'react'; +import * as RadixPopover from '@radix-ui/react-popover'; + +import './Popover.scss'; + +type PopoverProps = { + content: ReactNode; + defaultOpen?: boolean; +} + +const Popover: FC> = ({ children, content, defaultOpen = false }) => { + return ( + + + {children} + + + + {content} + + + + + ); +}; + +export default Popover; diff --git a/GUI/src/components/ProgressBar/index.scss b/GUI/src/components/ProgressBar/index.scss new file mode 100644 index 00000000..bc4f3a53 --- /dev/null +++ b/GUI/src/components/ProgressBar/index.scss @@ -0,0 +1,28 @@ +.progress-bar-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + .progress-bar-label { + margin-bottom: 4px; + font-size: 14px; + } + + .progress-bar-root { + position: relative; + overflow: hidden; + background-color: #e0e0e0; + border-radius: 4px; + width: 100%; + height: 10px; + } + + .progress-bar-indicator { + background-color: #07478d; + height: 100%; + transition: width 0.3s; + border-radius: 20px; + } + \ No newline at end of file diff --git a/GUI/src/components/ProgressBar/index.tsx b/GUI/src/components/ProgressBar/index.tsx new file mode 100644 index 00000000..20f3b328 --- /dev/null +++ b/GUI/src/components/ProgressBar/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import * as Progress from '@radix-ui/react-progress'; +import './index.scss'; // Import your CSS file for custom styles + +type ProgressBarProps = { + value: number; + max: number; + label?: string; +}; + +const ProgressBar: React.FC = ({ value, max, label }) => { + return ( +
    + + + + {label && } + +
    + ); +}; + +export default ProgressBar; diff --git a/GUI/src/components/Section/Section.scss b/GUI/src/components/Section/Section.scss new file mode 100644 index 00000000..cdbb136e --- /dev/null +++ b/GUI/src/components/Section/Section.scss @@ -0,0 +1,11 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/typography'; + +.section { + padding: get-spacing(haapsalu); + + &:not(:last-child) { + border-bottom: 1px solid get-color(black-coral-2); + } +} diff --git a/GUI/src/components/Section/index.tsx b/GUI/src/components/Section/index.tsx new file mode 100644 index 00000000..7ecd131d --- /dev/null +++ b/GUI/src/components/Section/index.tsx @@ -0,0 +1,13 @@ +import { forwardRef, PropsWithChildren } from 'react'; + +import './Section.scss'; + +const Section = forwardRef(({ children }, ref) => { + return ( +
    + {children} +
    + ); +}); + +export default Section; diff --git a/GUI/src/components/Toast/Toast.scss b/GUI/src/components/Toast/Toast.scss new file mode 100644 index 00000000..fd340916 --- /dev/null +++ b/GUI/src/components/Toast/Toast.scss @@ -0,0 +1,73 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.toast { + padding: 16px; + border-radius: 5px; + border: 1px solid; + display: flex; + flex-direction: column; + gap: 8px; + position: relative; + transition: opacity 0.25s ease-out; + + &__title { + display: flex; + align-items: center; + gap: 8px; + padding-right: 25px; + } + + &__list { + position: fixed; + bottom: 0; + right: 0; + display: flex; + flex-direction: column; + gap: 16px; + padding: 8px; + width: 408px; + max-width: 100vw; + z-index: 9999; + list-style: none; + } + + &__content { + font-size: $veera-font-size-80; + + a { + display: inline; + color: get-color(sapphire-blue-10); + text-decoration: underline; + } + } + + &__close { + position: absolute; + top: 16px; + right: 16px; + font-size: 20px; + } + + &--success { + border-color: get-color(sea-green-10); + background-color: get-color(sea-green-0); + } + + &--info { + border-color: get-color(sapphire-blue-10); + background-color: get-color(sapphire-blue-1); + } + + &--error { + border-color: get-color(jasper-10); + background-color: #FCEEEE; + } + + &--warning { + border-color: get-color(dark-tangerine-10); + background-color: get-color(dark-tangerine-1); + } +} diff --git a/GUI/src/components/Toast/index.tsx b/GUI/src/components/Toast/index.tsx new file mode 100644 index 00000000..ffa29f61 --- /dev/null +++ b/GUI/src/components/Toast/index.tsx @@ -0,0 +1,54 @@ +import { FC, useState } from 'react'; +import * as RadixToast from '@radix-ui/react-toast'; +import { + MdOutlineClose, + MdOutlineInfo, + MdCheckCircleOutline, + MdOutlineWarningAmber, + MdErrorOutline, +} from 'react-icons/md'; +import clsx from 'clsx'; + +import { Icon } from 'components'; +import type { ToastType } from 'context/ToastContext'; +import './Toast.scss'; + +type ToastProps = { + toast: ToastType; + close: () => void; +}; + +const toastIcons = { + info: , + success: , + warning: , + error: , +}; + +const Toast: FC = ({ toast, close }) => { + const [open, setOpen] = useState(true); + + const toastClasses = clsx('toast', `toast--${toast.type}`); + + return ( + + + + {toast.title} + + + {toast.message} + + + } size="medium" /> + + + ); +}; + +export default Toast; diff --git a/GUI/src/components/Tooltip/Tooltip.scss b/GUI/src/components/Tooltip/Tooltip.scss new file mode 100644 index 00000000..bd062f75 --- /dev/null +++ b/GUI/src/components/Tooltip/Tooltip.scss @@ -0,0 +1,16 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/typography'; + +.tooltip { + background-color: get-color(white); + padding: 4px; + border-radius: 4px; + filter: drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.25)); + font-size: $veera-font-size-80; + max-width: 50vw; + + &__arrow { + fill: get-color(white); + } +} diff --git a/GUI/src/components/Tooltip/index.tsx b/GUI/src/components/Tooltip/index.tsx new file mode 100644 index 00000000..3cd41ac2 --- /dev/null +++ b/GUI/src/components/Tooltip/index.tsx @@ -0,0 +1,28 @@ +import { FC, PropsWithChildren, ReactNode } from 'react'; +import * as RadixTooltip from '@radix-ui/react-tooltip'; + +import './Tooltip.scss'; + +type TooltipProps = { + content: ReactNode; +} + +const Tooltip: FC> = ({ content, children }) => { + return ( + + + + {children} + + + + {content} + + + + + + ); +}; + +export default Tooltip; diff --git a/GUI/src/components/Track/index.tsx b/GUI/src/components/Track/index.tsx new file mode 100644 index 00000000..2b66b6e7 --- /dev/null +++ b/GUI/src/components/Track/index.tsx @@ -0,0 +1,57 @@ +import { FC, HTMLAttributes, PropsWithChildren } from 'react'; + +type TrackProps = HTMLAttributes & { + gap?: number; + align?: 'left' | 'center' | 'right' | 'stretch'; + justify?: 'start' | 'between' | 'center' | 'around' | 'end'; + direction?: 'horizontal' | 'vertical'; + isMultiline?: boolean; +} + +const alignMap = { + left: 'flex-start', + center: 'center', + right: 'flex-end', + stretch: 'stretch', +}; + +const justifyMap = { + start: 'flex-start', + between: 'space-between', + center: 'center', + around: 'space-around', + end: 'flex-end', +}; + +const Track: FC> = ( + { + gap = 0, + align = 'center', + justify = 'start', + direction = 'horizontal', + isMultiline = false, + children, + style, + ...rest + }, +) => { + return ( +
    + {children} +
    + ); +}; + +export default Track; diff --git a/GUI/src/components/index.tsx b/GUI/src/components/index.tsx new file mode 100644 index 00000000..5bb3b36f --- /dev/null +++ b/GUI/src/components/index.tsx @@ -0,0 +1,55 @@ +import Layout from './Layout'; +import Button from './Button'; +import Icon from './Icon'; +import Track from './Track'; +import { + FormInput, + FormTextarea, + FormSelect, + FormMultiselect, + Switch, + FormCheckboxes, + FormRadios, + FormCheckbox, + FormDatepicker, + SwitchBox, +} from './FormElements'; +import DataTable from './DataTable'; +import Tooltip from './Tooltip'; +import Card from './Card'; +import Label from './Label'; +import Toast from './Toast'; +import Popover from './Popover'; +import Collapsible from './Collapsible'; +import Box from './Box'; +import Drawer from './Drawer'; +import Dialog from './Dialog'; +import Section from './Section'; + +export { + Layout, + Button, + Icon, + Track, + Tooltip, + DataTable, + FormInput, + FormTextarea, + FormSelect, + FormMultiselect, + FormDatepicker, + Switch, + SwitchBox, + Card, + Label, + Toast, + FormCheckboxes, + FormRadios, + FormCheckbox, + Popover, + Collapsible, + Box, + Drawer, + Dialog, + Section, +}; diff --git a/GUI/src/components/molecules/CircularSpinner/CircularSpinner.tsx b/GUI/src/components/molecules/CircularSpinner/CircularSpinner.tsx new file mode 100644 index 00000000..60eaa8ad --- /dev/null +++ b/GUI/src/components/molecules/CircularSpinner/CircularSpinner.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import './Spinner.scss'; + +interface SpinnerProps { + size?: number; +} + +const CircularSpinner: React.FC = ({ size = 80 }) => { + return ( +
    +
    +
    + ); +}; + +export default CircularSpinner; \ No newline at end of file diff --git a/GUI/src/components/molecules/CircularSpinner/Spinner.scss b/GUI/src/components/molecules/CircularSpinner/Spinner.scss new file mode 100644 index 00000000..d2297dea --- /dev/null +++ b/GUI/src/components/molecules/CircularSpinner/Spinner.scss @@ -0,0 +1,23 @@ +.spinner-container { + display: flex; + justify-content: center; + align-items: center; + height: 80vh; + } + + .spinner { + border: 4px solid rgba(0, 0, 0, 0.1); + border-top: 4px solid #3498db; + border-radius: 50%; + animation: spin 1s linear infinite; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + \ No newline at end of file diff --git a/GUI/src/components/molecules/ClassHeirarchy/TreeNode/ClassHeirarchyTreeNode.tsx b/GUI/src/components/molecules/ClassHeirarchy/TreeNode/ClassHeirarchyTreeNode.tsx new file mode 100644 index 00000000..20b359b5 --- /dev/null +++ b/GUI/src/components/molecules/ClassHeirarchy/TreeNode/ClassHeirarchyTreeNode.tsx @@ -0,0 +1,87 @@ +import { FormInput } from 'components/FormElements'; +import React, { ChangeEvent, useState } from 'react'; +import { TreeNode } from 'types/datasetGroups'; +import { isClassHierarchyDuplicatedAtSameLevel } from 'utils/datasetGroupsUtils'; +import { MdDeleteOutline } from 'react-icons/md'; +import { useTranslation } from 'react-i18next'; +import './index.css'; + +const ClassHeirarchyTreeNode = ({ + node, + onAddSubClass, + onDelete, + setNodesError, + nodesError, + nodes, +}: { + onAddSubClass: (parentId: number | string) => void; + onDelete: (nodeId: number | string) => void; + setNodesError: React.Dispatch>; + nodes?: TreeNode[]; + node: TreeNode; + nodesError?: boolean; +}) => { + const { t } = useTranslation(); + + const [fieldName, setFieldName] = useState(node.fieldName); + + const handleChange = (e: ChangeEvent) => { + setFieldName(e.target.value); + node.fieldName = e.target.value; + if (isClassHierarchyDuplicatedAtSameLevel(nodes, e.target.value)) setNodesError(true); + else setNodesError(false); + }; + + return ( +
    +
    +
    + +
    +
    onAddSubClass(node?.id)} + className="link" + > + {t('datasetGroups.classHierarchy.addSubClass')} +
    +
    onDelete(node?.id)} + className="link" + > + {t('global.delete')} +
    +
    + {node?.children && + node?.children?.map((child) => ( + + ))} +
    + ); +}; + +export default ClassHeirarchyTreeNode; \ No newline at end of file diff --git a/GUI/src/components/molecules/ClassHeirarchy/TreeNode/index.css b/GUI/src/components/molecules/ClassHeirarchy/TreeNode/index.css new file mode 100644 index 00000000..3cf9a08b --- /dev/null +++ b/GUI/src/components/molecules/ClassHeirarchy/TreeNode/index.css @@ -0,0 +1,9 @@ +.container-wrapper { + width: 450px; + margin-top: 10px; +} + +.link { + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/ClassHeirarchy/index.css b/GUI/src/components/molecules/ClassHeirarchy/index.css new file mode 100644 index 00000000..ccec348d --- /dev/null +++ b/GUI/src/components/molecules/ClassHeirarchy/index.css @@ -0,0 +1,28 @@ +/* GridLayout.css */ +.class-grid { + display: grid; + grid-template-columns: 7fr 3fr 2fr; + gap: 10px; /* Adjust gap as needed */ + padding: 10px; /* Adjust padding as needed */ + } + + .item { + padding: 20px; + background-color: #f0f0f0; + border: 1px solid #ddd; + text-align: center; + } + + /* Optional styling for specific items */ + .item-1 { + background-color: #cce5ff; + } + + .item-2 { + background-color: #d4edda; + } + + .item-3 { + background-color: #f8d7da; + } + \ No newline at end of file diff --git a/GUI/src/components/molecules/ClassHeirarchy/index.tsx b/GUI/src/components/molecules/ClassHeirarchy/index.tsx new file mode 100644 index 00000000..eba5df78 --- /dev/null +++ b/GUI/src/components/molecules/ClassHeirarchy/index.tsx @@ -0,0 +1,150 @@ +import React, { FC, PropsWithChildren, useState } from 'react'; +import Button from 'components/Button'; +import { v4 as uuidv4 } from 'uuid'; +import './index.css'; +import Dialog from 'components/Dialog'; +import { Class, TreeNode } from 'types/datasetGroups'; +import { useTranslation } from 'react-i18next'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import ClassHeirarchyTreeNode from './TreeNode/ClassHeirarchyTreeNode'; + +type ClassHierarchyProps = { + nodes: TreeNode[]; + setNodes: React.Dispatch>; + nodesError?: boolean; + setNodesError: React.Dispatch>; +}; + +const ClassHierarchy: FC> = ({ + nodes, + setNodes, + nodesError, + setNodesError, +}) => { + const { t } = useTranslation(); + const [currentNode, setCurrentNode] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + + const addMainClass = () => { + setNodes([ + ...nodes, + { id: uuidv4(), fieldName: '', level: 0, children: [] }, + ]); + }; + + const addSubClass = (parentId: number | string) => { + const addSubClassRecursive = (nodes: TreeNode[]): TreeNode[] => { + return nodes?.map((node: TreeNode) => { + if (node.id === parentId) { + const newNode = { + id: uuidv4(), + fieldName: '', + level: node.level + 1, + children: [], + }; + return { ...node, children: [...node.children, newNode] }; + } + if (node.children.length > 0) { + return { ...node, children: addSubClassRecursive(node.children) }; + } + return node; + }); + }; + if (nodes) setNodes(addSubClassRecursive(nodes)); + setNodesError(false); + }; + + const deleteNode = (nodeId: number | string) => { + const deleteNodeRecursive = (nodes: TreeNode[]): TreeNode[] => { + return nodes + ?.map((node: TreeNode) => { + if (node?.children?.length > 0) { + return { ...node, children: deleteNodeRecursive(node?.children) }; + } + return node; + }) + ?.filter((node: TreeNode) => { + if (node?.id === nodeId) { + if (node?.children?.length > 0 || node?.fieldName) { + setCurrentNode(node); + setIsModalOpen(true); + return true; + } + } + return !( + node?.id === nodeId && + node?.children?.length === 0 && + !node?.fieldName + ); + }); + }; + + if (nodes) setNodes(deleteNodeRecursive(nodes)); + }; + + const confirmDeleteNode = () => { + const deleteNodeRecursive = (nodes: TreeNode[]) => { + return nodes?.filter((node: TreeNode) => { + if (currentNode && node.id === currentNode.id) { + return false; + } + if (node.children.length > 0) { + node.children = deleteNodeRecursive(node.children); + } + return true; + }); + }; + + setNodes(deleteNodeRecursive(nodes)); + setIsModalOpen(false); + setCurrentNode(null); + }; + + return ( +
    +
    + +
    + {nodes?.map((node) => ( + + ))} +
    +
    + + + +
    + } + onClose={() => setIsModalOpen(false)} + > + {t('datasetGroups.modals.deleteClaassDesc') ?? ''} + +
    + ); +}; + +export default ClassHierarchy; diff --git a/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextTable.scss b/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextTable.scss new file mode 100644 index 00000000..de20622e --- /dev/null +++ b/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextTable.scss @@ -0,0 +1,40 @@ +.correctedHierarchy { + text-align: center; + display: flex; + align-content: center; + align-items: center; + justify-content: center; + text-wrap: wrap; + width: 100%; +} + +.hierarchyLabels { + display: flex; + align-items: center; + justify-content: center; + width: 500px; + text-wrap: wrap; +} + +.probabilityLabels { + text-align: center; + display: flex; + align-content: center; + width: 200px; + text-wrap: wrap; + justify-content: center; +} + +.inferenceTimeLabel { + text-align: right; + display: flex; + align-content: center; + align-items: center; +} + +.inferenceTimeCell { + display: flex; + align-items: center; + font-size: 14px; + flex-direction: column; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextsTables.tsx b/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextsTables.tsx new file mode 100644 index 00000000..06ffa9ee --- /dev/null +++ b/GUI/src/components/molecules/CorrectedTextTables/CorrectedTextsTables.tsx @@ -0,0 +1,201 @@ +import React, { useMemo } from 'react'; +import SkeletonTable from '../TableSkeleton/TableSkeleton'; +import DataTable from 'components/DataTable'; +import { + ColumnDef, + createColumnHelper, + PaginationState, +} from '@tanstack/react-table'; +import { useTranslation } from 'react-i18next'; +import { formatClassHierarchyArray, formatDateTime } from 'utils/commonUtilts'; +import Card from 'components/Card'; +import { InferencePayload } from 'types/correctedTextTypes'; +import './CorrectedTextTable.scss'; +import NoDataView from '../NoDataView'; + +const CorrectedTextsTable = ({ + correctedTextData, + totalPages, + isLoading, + setPagination, + pagination, + setEnableFetch +}: { + correctedTextData: InferencePayload[]; + totalPages: number; + isLoading: boolean; + setPagination: React.Dispatch>; + pagination: PaginationState; + setEnableFetch: React.Dispatch>; +}) => { + const columnHelper = createColumnHelper(); + const { t } = useTranslation(); + + const dataColumns = useMemo( + () => [ + columnHelper.accessor('inferenceTimeStamp', { + header: () => ( +
    + {t('correctedTexts.inferenceTime') ?? ''} +
    + ), + cell: (props) => ( +
    + + { + formatDateTime(props?.row?.original?.inferenceTimeStamp) + .formattedDate + } + + + { + formatDateTime(props?.row?.original?.inferenceTimeStamp) + .formattedTime + } + +
    + ), + }), + columnHelper.accessor('platform', { + header: t('correctedTexts.platform') ?? '', + }), + columnHelper.accessor('inferenceText', { + header: () => ( +
    + + {t('correctedTexts.text') ?? ''} + +
    + ), + cell: (props) => ( +
    + {props?.row?.original?.inferenceText} +
    + ), + }), + columnHelper.accessor('predictedLabels', { + header: () => ( +
    + + {t('correctedTexts.predictedHierarchy') ?? ''} + +
    + ), + cell: (props) => ( +
    + {props?.row?.original?.predictedLabels && + formatClassHierarchyArray(props?.row?.original?.predictedLabels)} +
    + ), + }), + columnHelper.accessor('averagePredictedClassesProbability', { + header: () => ( +
    + {t('correctedTexts.predictedConfidenceProbability') ?? ''} +
    + ), + cell: (props) => ( +
    + {props?.row?.original?.averagePredictedClassesProbability}% +
    + ), + meta: { + size: '90%', + }, + }), + columnHelper.accessor('correctedLabels', { + header: () => ( +
    + + {t('correctedTexts.correctedHierarchy') ?? ''} + +
    + ), + cell: (props) => ( +
    + {props?.row?.original?.correctedLabels && + formatClassHierarchyArray(props?.row?.original?.correctedLabels)} +
    + ), + }), + columnHelper.accessor('averageCorrectedClassesProbability', { + header: () => ( +
    + {t('correctedTexts.correctedConfidenceProbability') ?? ''} +
    + ), + cell: (props) => ( +
    + {props?.row?.original?.averageCorrectedClassesProbability === -1 ? + (<>{t('correctedTexts.labelNotFoundText') ?? ''}) : + (props?.row?.original?.averageCorrectedClassesProbability && ( + <>{props?.row?.original?.averageCorrectedClassesProbability}% + ))} +
    ), + }), + ], + [t] + ); + + return ( +
    +
    + {isLoading && } + {!isLoading && correctedTextData && correctedTextData.length === 0 && ( + +
    + + +
    +
    + )} + {!isLoading && correctedTextData && correctedTextData.length > 0 && ( + []} + pagination={pagination} + setPagination={(state: PaginationState) => { + if ( + state.pageIndex === pagination.pageIndex && + state.pageSize === pagination.pageSize + ) + return; + setPagination({ + pageIndex: state.pageIndex, + pageSize: state.pageSize, + }); + setEnableFetch(true) + }} + pagesCount={totalPages} + isClientSide={false} + /> + )} +
    +
    + ); +}; + +export default CorrectedTextsTable; \ No newline at end of file diff --git a/GUI/src/components/molecules/CreateDatasetGroupModals/CreateDatasetGroupModal.tsx b/GUI/src/components/molecules/CreateDatasetGroupModals/CreateDatasetGroupModal.tsx new file mode 100644 index 00000000..8f01dffa --- /dev/null +++ b/GUI/src/components/molecules/CreateDatasetGroupModals/CreateDatasetGroupModal.tsx @@ -0,0 +1,80 @@ +import { + CreateDatasetGroupModals, + ValidationErrorTypes, +} from 'enums/datasetEnums'; +import { Button, Dialog } from 'components'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; + +const CreateDatasetGroupModalController = ({ + modalType, + isModalOpen, + setIsModalOpen, + validationErrorType, +}: { + modalType: CreateDatasetGroupModals; + isModalOpen: boolean; + setIsModalOpen: React.Dispatch>; + validationErrorType?: ValidationErrorTypes; +}) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + return ( + <> + {modalType === CreateDatasetGroupModals.VALIDATION_ERROR && ( + + + +
    + } + onClose={() => setIsModalOpen(false)} + > + {validationErrorType === ValidationErrorTypes.VALIDATION_CRITERIA + ? t('datasetGroups.modals.columnInsufficientDescription') + : t('datasetGroups.modals.classsesInsufficientDescription')} + + )} + {modalType === CreateDatasetGroupModals.SUCCESS && ( + + + + + } + onClose={() => setIsModalOpen(false)} + > + {t('datasetGroups.modals.createDatasetSucceessDesc')} + + )} + + ); +}; + +export default CreateDatasetGroupModalController; diff --git a/GUI/src/components/molecules/DataModelCard/DataModel.scss b/GUI/src/components/molecules/DataModelCard/DataModel.scss new file mode 100644 index 00000000..5eed0e77 --- /dev/null +++ b/GUI/src/components/molecules/DataModelCard/DataModel.scss @@ -0,0 +1,5 @@ +.training-results-grid-container { + display: grid; + grid-template-columns: 3fr 1fr 1fr; + gap: 10px; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/DataModelCard/index.tsx b/GUI/src/components/molecules/DataModelCard/index.tsx new file mode 100644 index 00000000..e0f13cef --- /dev/null +++ b/GUI/src/components/molecules/DataModelCard/index.tsx @@ -0,0 +1,229 @@ +import { FC, PropsWithChildren } from 'react'; +import Button from 'components/Button'; +import Label from 'components/Label'; +import { useDialog } from 'hooks/useDialog'; +import './DataModel.scss'; +import { Maturity, TrainingStatus } from 'enums/dataModelsEnums'; +import Card from 'components/Card'; +import { useTranslation } from 'react-i18next'; +import { TrainingResults } from 'types/dataModels'; +import { formatDate } from 'utils/commonUtilts'; + +type DataModelCardProps = { + modelId: number; + dataModelName?: string; + datasetGroupName?: string; + version?: string; + isLatest?: boolean; + dgVersion?: string; + lastTrained?: string; + trainingStatus?: string; + platform?: string; + maturity?: string; + setId: React.Dispatch>; + setView: React.Dispatch>; + results: string | null; +}; + +const DataModelCard: FC> = ({ + modelId, + dataModelName, + datasetGroupName, + version, + isLatest, + dgVersion, + lastTrained, + trainingStatus, + platform, + maturity, + results, + setId, + setView, +}) => { + const { open, close } = useDialog(); + const { t } = useTranslation(); + const resultsJsonData: TrainingResults = JSON.parse(results ?? '{}'); + + const renderTrainingStatus = (status: string | undefined) => { + if (status === TrainingStatus.RETRAINING_NEEDED) { + return ( + + ); + } else if (status === TrainingStatus.TRAINED) { + return ( + + ); + } else if (status === TrainingStatus.TRAINING_INPROGRESS) { + return ( + + ); + } else if (status === TrainingStatus.UNTRAINABLE) { + return ( + + ); + } else if (status === TrainingStatus.NOT_TRAINED) { + return ; + } + }; + + const renderMaturityLabel = (status: string | undefined) => { + if (status === Maturity.DEVELOPMENT) { + return ( + + ); + } else if (status === Maturity.PRODUCTION) { + return ( + + ); + } else if (status === Maturity.STAGING) { + return ( + + ); + } else if (status === Maturity.TESTING) { + return ( + + ); + } + }; + + return ( +
    +
    +

    {dataModelName}

    +
    + + {isLatest ? ( + + ) : null} +
    + +
    +

    + {t('dataModels.dataModelCard.datasetGroup') ?? ''}: + {datasetGroupName} +

    +

    + {t('dataModels.dataModelCard.dgVersion') ?? ''}:{dgVersion} +

    +

    + {t('dataModels.dataModelCard.lastTrained') ?? ''}:{' '} + {lastTrained && formatDate(new Date(lastTrained), 'D.M.yy-H:m')} +

    +
    +
    + {renderTrainingStatus(trainingStatus)} + + {renderMaturityLabel(maturity)} +
    + +
    + + ), + size: 'large', + content: ( +
    +
    + {t('dataModels.trainingResults.bestPerformingModel') ?? + ''} + - +
    + +
    + {' '} + {t('dataModels.trainingResults.classes') ?? ''} +
    +
    + {t('dataModels.trainingResults.accuracy') ?? ''} +
    +
    + {t('dataModels.trainingResults.f1Score') ?? ''} +
    +
    + } + > + {results ? ( +
    +
    + {resultsJsonData?.trainingResults?.classes?.map( + (c: string, index: number) => { + return
    {c}
    ; + } + )} +
    +
    + {resultsJsonData?.trainingResults?.accuracy?.map( + (c: string, index: number) => { + return ( +
    + {parseFloat(c)?.toFixed(2)} +
    + ); + } + )} +
    +
    + {resultsJsonData?.trainingResults?.f1_score?.map( + (c: string, index: number) => { + return ( +
    + {parseFloat(c)?.toFixed(2)} +
    + ); + } + )} +
    +
    + ) : ( +
    + {t('dataModels.trainingResults.noResults') ?? ''} +
    + )} + +
    + ), + }); + }} + > + {t('dataModels.trainingResults.viewResults') ?? ''} + + +
    +
    + + ); +}; + +export default DataModelCard; diff --git a/GUI/src/components/molecules/DataModelForm/index.tsx b/GUI/src/components/molecules/DataModelForm/index.tsx new file mode 100644 index 00000000..e9377dad --- /dev/null +++ b/GUI/src/components/molecules/DataModelForm/index.tsx @@ -0,0 +1,137 @@ +import { FC } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + FormCheckboxes, + FormInput, + FormRadios, + FormSelect, + Label, +} from 'components'; +import { formattedArray } from 'utils/commonUtilts'; +import { useQuery } from '@tanstack/react-query'; +import { getCreateOptions } from 'services/data-models'; +import { dgArrayWithVersions } from 'utils/dataModelsUtils'; +import CircularSpinner from '../CircularSpinner/CircularSpinner'; +import { DataModel } from 'types/dataModels'; + +type DataModelFormType = { + dataModel: any; + handleChange: (name: keyof DataModel, value: any) => void; + errors?: Record; + type: string; +}; + +const DataModelForm: FC = ({ + dataModel, + handleChange, + errors, + type, +}) => { + const { t } = useTranslation(); + + const { data: createOptions, isLoading } = useQuery( + ['datamodels/create-options'], + () => getCreateOptions() + ); + + return ( +
    + {type === 'create' ? ( +
    +
    + handleChange('modelName', e.target.value)} + error={errors?.modelName} + /> +
    +
    + {t('dataModels.dataModelForm.modelVersion')}{' '} + +
    +
    + ) : ( +
    +
    {dataModel.modelName}
    + +
    + )} + + {((type === 'configure' && dataModel?.dgId > 0) || type === 'create') && + createOptions && + !isLoading ? ( +
    +
    + {t('dataModels.dataModelForm.datasetGroup')}{' '} +
    +
    + { + handleChange('dgId', selection?.value); + }} + defaultValue={dataModel?.dgId} + error={errors?.dgId} + /> +
    + +
    + {t('dataModels.dataModelForm.baseModels')}{' '} +
    +
    + + handleChange('baseModels', values.baseModels) + } + error={errors?.baseModels} + selectedValues={dataModel?.baseModels} + /> +
    + +
    + {t('dataModels.dataModelForm.deploymentPlatform')}{' '} +
    +
    + handleChange('platform', value)} + error={errors?.platform} + selectedValue={dataModel?.platform} + /> +
    + +
    + {t('dataModels.dataModelForm.maturityLabel')}{' '} +
    +
    + handleChange('maturity', value)} + error={errors?.maturity} + selectedValue={dataModel?.maturity} + /> +
    +
    + ) : ( + + )} +
    + ); +}; + +export default DataModelForm; diff --git a/GUI/src/components/molecules/DatasetDetailedViewTable/DatasetDetailedViewTable.tsx b/GUI/src/components/molecules/DatasetDetailedViewTable/DatasetDetailedViewTable.tsx new file mode 100644 index 00000000..c639f945 --- /dev/null +++ b/GUI/src/components/molecules/DatasetDetailedViewTable/DatasetDetailedViewTable.tsx @@ -0,0 +1,234 @@ +import BackArrowButton from 'assets/BackArrowButton'; +import { Button, Card, DataTable, Icon, Label, Switch } from 'components'; +import { ButtonAppearanceTypes, LabelType } from 'enums/commonEnums'; +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link, useNavigate } from 'react-router-dom'; +import DatasetValidationStatus from '../ValidationStatus/ValidationStatus'; +import { ViewDatasetGroupModalContexts } from 'enums/datasetEnums'; +import { generateDynamicColumns } from 'utils/dataTableUtils'; +import { MdOutlineDeleteOutline, MdOutlineEdit } from 'react-icons/md'; +import { CellContext, ColumnDef, PaginationState } from '@tanstack/react-table'; +import { getDatasets } from 'services/datasets'; +import { + DatasetDetails, + MetaData, + SelectedRowPayload, +} from 'types/datasetGroups'; +import SkeletonTable from '../TableSkeleton/TableSkeleton'; + +const DatasetDetailedViewTable = ({ + metadata, + handleOpenModals, + bannerMessage, + datasets, + isLoading, + updatedDataset, + setSelectedRow, + pagination, + setPagination, + dgId, + isMetadataLoading, +}: { + metadata: MetaData[]; + handleOpenModals: (context: ViewDatasetGroupModalContexts) => void; + bannerMessage: string; + datasets: DatasetDetails | undefined; + isLoading: boolean; + updatedDataset: any; + setSelectedRow: React.Dispatch< + React.SetStateAction + >; + pagination: PaginationState; + setPagination: React.Dispatch>; + dgId: number; + isMetadataLoading: boolean; +}) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const editView = (props: CellContext) => { + return ( + + ); + }; + + const deleteView = (props: CellContext) => ( + + ); + + const dataColumns = useMemo( + () => generateDynamicColumns(datasets?.fields ?? [], editView, deleteView), + [datasets?.fields] + ); + + return ( + <> + {isMetadataLoading && } + {metadata && !isMetadataLoading && ( +
    + +
    + navigate(0)}> + + +
    {metadata?.[0]?.name}
    + {metadata && ( + + )} + {metadata?.[0]?.latest ? ( + + ) : null} + +
    + +
    + } + > +
    +
    +

    + {t('datasetGroups.detailedView.connectedModels') ?? ''} : + {metadata?.[0]?.linkedModels?.map((model, index: number) => { + return index === metadata?.[0]?.linkedModels?.length - 1 + ? ` ${model?.modelName}` + : ` ${model?.modelName}, `; + })} +

    +

    + {t('datasetGroups.detailedView.noOfItems') ?? ''} : + {` ${metadata?.[0]?.numSamples}`} +

    +
    +
    + + +
    +
    + + {bannerMessage &&
    {bannerMessage}
    } + {(!datasets || (datasets && datasets?.numPages < 2)) && + !isLoading && ( + +
    + {(!datasets || datasets?.numPages === 0) && ( +
    +
    + {t('datasetGroups.detailedView.noData') ?? ''} +
    +

    {t('datasetGroups.detailedView.noDataDesc') ?? ''}

    + +
    + )} + {datasets && + datasets?.numPages !== 0 && + datasets?.numPages <= 2 && ( +
    +

    + {t( + 'datasetGroups.detailedView.insufficientExamplesDesc' + ) ?? ''} +

    + +
    + )} +
    +
    + )} + + )} +
    + {isLoading && } + {!isLoading && updatedDataset && updatedDataset.length > 0 && ( + []} + pagination={pagination} + setPagination={(state: PaginationState) => { + if ( + state.pageIndex === pagination.pageIndex && + state.pageSize === pagination.pageSize + ) + return; + setPagination(state); + getDatasets(state, dgId); + }} + pagesCount={datasets?.numPages} + isClientSide={false} + /> + )} +
    + + ); +}; + +export default DatasetDetailedViewTable; \ No newline at end of file diff --git a/GUI/src/components/molecules/DatasetGroupCard/DatasetGroupCard.scss b/GUI/src/components/molecules/DatasetGroupCard/DatasetGroupCard.scss new file mode 100644 index 00000000..a81ab511 --- /dev/null +++ b/GUI/src/components/molecules/DatasetGroupCard/DatasetGroupCard.scss @@ -0,0 +1,91 @@ +.row { + margin: 10px 0; + display: flex; + align-items: center; +} + +.switch-row { + justify-content: space-between; +} + +.status-indicators { + display: flex; + align-items: center; + gap: 10px; +} + +.dot { + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 8px; +} + +.green { + background-color: green; +} + +.grey { + background-color: grey; +} + +.icon-text-row { + flex-direction: column; + justify-content: center; + text-align: center; +} + +.icon-image { + width: 50px; + height: 50px; +} + +.icon-text { + margin: 5px 0 0 0; + font-size: 16px; +} + +.label-row { + justify-content: flex-end; + display: flex; + align-items: center; + margin-top: 15px; +} + +.left-label { + font-size: 14px; + color: #333; +} + +.status { + display: flex; + align-items: center; + font-size: 12px; + color: #555; + bottom: 10px; + left: 20px; +} + +.colored-label{ + background-color: white; + border: solid 2px #D73E3E; + border-radius: 6px; + color: #D73E3E; + width: fit-content; + padding: 0px 5px; + font-size: 14px; + margin-right: 5px; +} + +.text{ + font-size: 16px; + margin-bottom: 5px; +} + +.py-3{ + padding: 8px 0px; +} + +.flex{ + display: flex; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/DatasetGroupCard/index.tsx b/GUI/src/components/molecules/DatasetGroupCard/index.tsx new file mode 100644 index 00000000..d006974d --- /dev/null +++ b/GUI/src/components/molecules/DatasetGroupCard/index.tsx @@ -0,0 +1,144 @@ +import { FC, PropsWithChildren } from 'react'; +import './DatasetGroupCard.scss'; +import { Switch } from 'components/FormElements'; +import Button from 'components/Button'; +import Label from 'components/Label'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { enableDataset } from 'services/datasets'; +import { useDialog } from 'hooks/useDialog'; +import { Operation } from 'types/datasetGroups'; +import { datasetQueryKeys } from 'utils/queryKeys'; +import { DatasetViewEnum } from 'enums/datasetEnums'; +import { ButtonAppearanceTypes, LabelType } from 'enums/commonEnums'; +import { useTranslation } from 'react-i18next'; +import { formatDate } from 'utils/commonUtilts'; +import DatasetValidationStatus from '../ValidationStatus/ValidationStatus'; + +type DatasetGroupCardProps = { + datasetGroupId: number; + datasetName?: string; + version?: string; + isLatest?: boolean; + isEnabled?: boolean; + lastUpdated?: Date | null; + lastUsed?: Date | null; + validationStatus?: string; + lastModelTrained?: Date | null; + setId?: React.Dispatch>; + setView?: React.Dispatch>; +}; + +const DatasetGroupCard: FC> = ({ + datasetGroupId, + datasetName, + version, + isLatest, + isEnabled, + lastUpdated, + lastUsed, + validationStatus, + lastModelTrained, + setId, + setView, +}) => { + const queryClient = useQueryClient(); + const { open } = useDialog(); + const { t } = useTranslation(); + + const datasetEnableMutation = useMutation({ + mutationFn: (data: Operation) => enableDataset(data), + onSuccess: async () => { + await queryClient.invalidateQueries(datasetQueryKeys.DATASET_OVERVIEW(1)); + }, + onError: () => { + open({ + title: t('datasetGroups.modals.enableDatasetTitle'), + content:

    {t('datasetGroups.modals.enableDatasetDesc')}

    , + }); + }, + }); + + const datasetDisableMutation = useMutation({ + mutationFn: (data: Operation) => enableDataset(data), + onSuccess: async (response) => { + await queryClient.invalidateQueries(datasetQueryKeys.DATASET_OVERVIEW(1)); + if (response?.operationSuccessful) + open({ + title: t('datasetGroups.modals.enableDatasetTitle'), + content:

    {t('datasetGroups.modals.enableDatasetDesc')}

    , + }); + }, + onError: () => { + open({ + title: t('datasetGroups.modals.errorTitle'), + content:

    {t('datasetGroups.modals.errorDesc')}

    , + }); + }, + }); + + const handleCheck = () => { + if (isEnabled) + datasetDisableMutation.mutate({ + dgId: datasetGroupId, + operationType: 'disable', + }); + else + datasetEnableMutation.mutate({ + dgId: datasetGroupId, + operationType: 'enable', + }); + }; + + return ( +
    +
    +
    +
    {datasetName}
    + handleCheck()} + /> +
    + +
    +

    + {t('datasetGroups.datasetCard.lastModelTrained')}:{' '} + {lastModelTrained && formatDate(lastModelTrained, 'D.M.yy-H:m')} +

    +

    + {t('datasetGroups.datasetCard.lastUsedForTraining')}:{' '} + {lastUsed && formatDate(lastUsed, 'D.M.yy-H:m')} +

    +

    + {t('datasetGroups.datasetCard.lastUpdate')}:{' '} + {lastUpdated && formatDate(lastUpdated, 'D.M.yy-H:m')} +

    +
    +
    + + {isLatest ? ( + + ) : null} +
    + +
    + +
    +
    +
    + ); +}; + +export default DatasetGroupCard; diff --git a/GUI/src/components/molecules/IntegrationCard/IntegrationCard.scss b/GUI/src/components/molecules/IntegrationCard/IntegrationCard.scss new file mode 100644 index 00000000..bccbd437 --- /dev/null +++ b/GUI/src/components/molecules/IntegrationCard/IntegrationCard.scss @@ -0,0 +1,78 @@ +.card_header { + display: flex; + align-items: center; + padding: 0px 10px; +} + +.logo { + + margin-right: 8px; + width: 120px; +} + + +.title h2 { + font-size: 18px; + margin-bottom: 4px; +} + +.title p { + font-size: 14px; + color: #666; +} + +.toggle-switch { + margin-left: auto; + justify-content: end; + float: right; +} + +.footer_container { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 0px; +} + +.status-indicators { + display: flex; + align-items: center; + gap: 10px +} + +.status { + display: flex; + align-items: center; + font-size: 14px; + color: #333; +} + +.dot { + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 8px; +} + +.green { + background-color: green; +} + +.grey { + background-color: grey; +} + +.actions { + display: flex; + gap: 10px; + align-items: center; +} + +.connect { + background-color: #007bff; +} + +.disconnect { + background-color: #dc3545; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/IntegrationCard/index.tsx b/GUI/src/components/molecules/IntegrationCard/index.tsx new file mode 100644 index 00000000..563f01a7 --- /dev/null +++ b/GUI/src/components/molecules/IntegrationCard/index.tsx @@ -0,0 +1,105 @@ +import { + FC, + PropsWithChildren, + ReactNode, + useState, +} from 'react'; +import './IntegrationCard.scss'; +import { useTranslation } from 'react-i18next'; +import { Card, Switch } from 'components'; +import { + INTEGRATION_MODALS +} from 'enums/integrationEnums'; +import IntegrationModals from '../IntegrationModals/IntegrationModals'; + +type IntegrationCardProps = { + logo?: ReactNode; + channel?: string; + channelDescription?: string; + isActive?: boolean; +}; + +const IntegrationCard: FC> = ({ + logo, + channel, + channelDescription, + isActive, +}) => { + const { t } = useTranslation(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalType, setModalType] = useState( + INTEGRATION_MODALS.NULL + ); + + const renderStatusIndicators = () => { + //kept this, in case the logic is changed for the connected status + // return connectedStatus?.map((status, index) => ( + // + // + // {connectedStatus?.length > 1 + // ? `${status.status} - ${status.platform}` + // : `${status.status}`} + // + // )); + + return ( + + +
    + {isActive + ? t('integration.connected') + : t('integration.disconnected')} +
    +
    + ); + }; + + const onSelect = () => { + if (isActive) { + setModalType(INTEGRATION_MODALS.DISCONNECT_CONFIRMATION); + } else { + setModalType(INTEGRATION_MODALS.CONNECT_CONFIRMATION); + } + setIsModalOpen(true); + }; + + return ( +
    + +
    +
    {logo}
    +
    +

    {channel}

    +

    {channelDescription}

    +
    +
    +
    + +
    +
    +
    +
    + {renderStatusIndicators()} +
    +
    +
    {' '} +
    +
    +
    + + +
    + ); +}; + +export default IntegrationCard; diff --git a/GUI/src/components/molecules/IntegrationModals/IntegrationModals.tsx b/GUI/src/components/molecules/IntegrationModals/IntegrationModals.tsx new file mode 100644 index 00000000..ce4ddaa7 --- /dev/null +++ b/GUI/src/components/molecules/IntegrationModals/IntegrationModals.tsx @@ -0,0 +1,169 @@ +import { + INTEGRATION_MODALS, + INTEGRATION_OPERATIONS, +} from 'enums/integrationEnums'; +import React from 'react'; +import { Button, Dialog } from 'components'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { togglePlatform } from 'services/integration'; +import { useTranslation } from 'react-i18next'; +import { OperationConfig } from 'types/integration'; +import { integrationQueryKeys } from 'utils/queryKeys'; + +const IntegrationModals = ({ + modalType, + isModalOpen, + setIsModalOpen, + channel, + setModalType, +}: { + modalType: INTEGRATION_MODALS; + isModalOpen: boolean; + setIsModalOpen: React.Dispatch>; + channel?: string; + setModalType: React.Dispatch>; +}) => { + const { t } = useTranslation(); + const queryClient = useQueryClient(); + + const platformEnableMutation = useMutation({ + mutationFn: (data: OperationConfig) => togglePlatform(data), + onSuccess: async (data) => { + if (data.response.operation_status === 'success') { + setModalType(INTEGRATION_MODALS.INTEGRATION_SUCCESS); + await queryClient.invalidateQueries( + integrationQueryKeys.INTEGRATION_STATUS() + ); + } else { + setModalType(INTEGRATION_MODALS.INTEGRATION_ERROR); + } + }, + onError: () => { + setModalType(INTEGRATION_MODALS.INTEGRATION_ERROR); + }, + }); + + const platformDisableMutation = useMutation({ + mutationFn: (data: OperationConfig) => togglePlatform(data), + onSuccess: async (data) => { + if (data.response.operation_status === 'success') { + await queryClient.invalidateQueries( + integrationQueryKeys.INTEGRATION_STATUS() + ); + setIsModalOpen(false); + } else { + setModalType(INTEGRATION_MODALS.DISCONNECT_ERROR); + } + }, + onError: () => { + setModalType(INTEGRATION_MODALS.DISCONNECT_ERROR); + }, + }); + return ( + <> + {modalType === INTEGRATION_MODALS.INTEGRATION_SUCCESS && ( + setIsModalOpen(false)} + isOpen={isModalOpen} + title={t('integration.integrationSuccessTitle')} + > +
    + {t('integration.integrationSuccessDesc', { channel })} +
    +
    + )} + {modalType === INTEGRATION_MODALS.INTEGRATION_ERROR && ( + setIsModalOpen(false)} + isOpen={isModalOpen} + title={t('integration.integrationErrorTitle')} + > +
    + {t('integration.integrationErrorDesc', { channel })} +
    +
    + )} + {modalType === INTEGRATION_MODALS.DISCONNECT_CONFIRMATION && ( + setIsModalOpen(false)} + isOpen={isModalOpen} + title={t('integration.confirmationModalTitle')} + footer={ +
    + + +
    + } + > +
    + {t('integration.disconnectConfirmationModalDesc', { channel })} +
    +
    + )} + {modalType === INTEGRATION_MODALS.CONNECT_CONFIRMATION && ( + setIsModalOpen(false)} + isOpen={isModalOpen} + title={t('integration.confirmationModalTitle')} + footer={ +
    + + +
    + } + > +
    + {t('integration.connectConfirmationModalDesc', { channel })} +
    +
    + )} + {modalType === INTEGRATION_MODALS.DISCONNECT_ERROR && ( + setIsModalOpen(false)} + isOpen={isModalOpen} + title={t('integration.disconnectErrorTitle')} + > +
    + {t('integration.disconnectErrorDesc', { channel })} +
    +
    + )} + + ); +}; + +export default IntegrationModals; diff --git a/GUI/src/components/molecules/NoDataView/index.tsx b/GUI/src/components/molecules/NoDataView/index.tsx new file mode 100644 index 00000000..b9932739 --- /dev/null +++ b/GUI/src/components/molecules/NoDataView/index.tsx @@ -0,0 +1,19 @@ +import React, { ReactNode } from 'react'; +import { MdDashboard } from 'react-icons/md'; +interface NoDataViewProps { + text?: string; + icon?: ReactNode; +} + +const NoDataView: React.FC = ({ text, icon }) => { + return ( +
    + +
    + {text} +
    +
    + ); +}; + +export default NoDataView; diff --git a/GUI/src/components/molecules/Pagination/Pagination.scss b/GUI/src/components/molecules/Pagination/Pagination.scss new file mode 100644 index 00000000..5c89eb88 --- /dev/null +++ b/GUI/src/components/molecules/Pagination/Pagination.scss @@ -0,0 +1,194 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/typography'; + +.data-table { + width: 100%; + color: get-color(black-coral-20); + text-align: left; + margin-bottom: 0; + display: table; + + &__scrollWrapper { + height: 100%; + overflow-x: auto; + white-space: nowrap; + display: block; + padding: 5px; + background-color: white; + border-radius: 10px; + border: solid 1px get-color(black-coral-1); + } + + thead, + tbody { + width: 100%; + } + + th { + padding: 12px 14.5px; + color: get-color(black-coral-12); + border-bottom: 1px solid get-color(black-coral-10); + font-weight: $veera-font-weight-beta; + vertical-align: middle; + position: relative; + } + + td { + padding: 12px 24px 12px 16px; + border-bottom: 1px solid get-color(black-coral-2); + vertical-align: middle; + max-width: fit-content; + + p { + white-space: break-spaces; + } + + .entity { + display: inline-flex; + align-items: center; + padding-left: 4px; + background-color: get-color(sapphire-blue-2); + border-radius: 4px; + + span { + display: inline-flex; + font-size: $veera-font-size-80; + background-color: get-color(white); + padding: 0 4px; + border-radius: 4px; + margin: 2px 2px 2px 4px; + } + } + } + + tbody { + tr { + &:last-child { + td { + border-bottom: 0; + } + } + } + } + + &__filter { + position: absolute; + top: 100%; + left: 0; + right: 0; + padding: get-spacing(paldiski); + background-color: get-color(white); + border-radius: 0 0 4px 4px; + border: 1px solid get-color(black-coral-2); + + input { + width: 100%; + display: block; + appearance: none; + background-color: get-color(white); + border: 1px solid get-color(black-coral-6); + border-radius: 5px; + color: var(--color-black); + font-size: $veera-font-size-100; + height: 32px; + line-height: 24px; + padding: get-spacing(paldiski); + + &::placeholder { + color: get-color(black-coral-6); + } + + &:focus { + outline: none; + border-color: get-color(sapphire-blue-10); + } + } + } + + &__pagination-wrapper { + display: flex; + padding: 6px 16px; + } + + &__pagination { + display: flex; + align-items: center; + gap: 15px; + margin: 0 auto; + + + .data-table__page-size { + margin-left: 0; + } + + .next, + .previous { + display: flex; + color: get-color(sapphire-blue-10); + + &[disabled] { + color: get-color(black-coral-11); + cursor: initial; + } + } + + .links { + display: flex; + align-items: center; + gap: 5px; + font-size: $veera-font-size-80; + color: get-color(black-coral-10); + + li { + display: block; + + a, + span { + display: flex; + align-items: center; + justify-content: center; + width: 25px; + height: 25px; + border-radius: 50%; + + &:hover { + text-decoration: none; + } + } + + &.active { + a, + span { + color: get-color(white); + background-color: get-color(sapphire-blue-10); + } + } + } + } + } + + &__page-size { + display: flex; + align-items: center; + gap: 8px; + font-size: $veera-font-size-80; + line-height: 16px; + color: get-color(black-coral-11); + margin-left: auto; + + select { + appearance: none; + font-size: $veera-font-size-70; + line-height: 16px; + height: 30px; + min-width: 50px; + padding: 6px 10px; + border: 1px solid #8f91a8; + border-radius: 2px; + background-color: get-color(white); + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iNiIgdmlld0JveD0iMCAwIDEwIDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNS4zMTMwNiA1LjgwODIyQzUuMTU2ODUgNS45NjQ0MyA0LjkwMzU4IDUuOTY0NDMgNC43NDczNyA1LjgwODIyTDAuMjgyNzMgMS4zNDM1OEMwLjEyNjUyIDEuMTg3MzcgMC4xMjY1MiAwLjkzNDEwMiAwLjI4MjczIDAuNzc3ODkzTDAuNzc3NzA0IDAuMjgyOTE4QzAuOTMzOTE0IDAuMTI2NzA4IDEuMTg3MTggMC4xMjY3MDggMS4zNDMzOSAwLjI4MjkxN0w1LjAzMDIyIDMuOTY5NzRMOC43MTcwNCAwLjI4MjkxN0M4Ljg3MzI1IDAuMTI2NzA4IDkuMTI2NTIgMC4xMjY3MDggOS4yODI3MyAwLjI4MjkxN0w5Ljc3NzcgMC43Nzc4OTJDOS45MzM5MSAwLjkzNDEwMiA5LjkzMzkxIDEuMTg3MzcgOS43Nzc3IDEuMzQzNThMNS4zMTMwNiA1LjgwODIyWiIgZmlsbD0iIzU1NTg2NyIvPgo8L3N2Zz4K'); + background-repeat: no-repeat; + background-position: top 11px right 10px; + } + } +} diff --git a/GUI/src/components/molecules/Pagination/index.tsx b/GUI/src/components/molecules/Pagination/index.tsx new file mode 100644 index 00000000..7c1c3b9d --- /dev/null +++ b/GUI/src/components/molecules/Pagination/index.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { MdOutlineWest, MdOutlineEast } from 'react-icons/md'; +import clsx from 'clsx'; +import { Link } from 'react-router-dom'; + +interface PaginationProps { + pageCount: number; + pageIndex: number; + canPreviousPage: boolean; + canNextPage: boolean; + onPageChange: (pageIndex: number) => void; + id?: string; +} + +const Pagination: React.FC = ({ + pageCount, + pageIndex, + canPreviousPage, + canNextPage, + onPageChange, + id, +}) => { + return ( +
    + {pageCount > 1 && ( +
    + + + +
    + )} +
    + ); +}; + +export default Pagination; diff --git a/GUI/src/components/molecules/TableSkeleton/SkeletonTable.scss b/GUI/src/components/molecules/TableSkeleton/SkeletonTable.scss new file mode 100644 index 00000000..5f433ecf --- /dev/null +++ b/GUI/src/components/molecules/TableSkeleton/SkeletonTable.scss @@ -0,0 +1,31 @@ +.skeleton { + display: inline-block; + height: 1.5rem; + width: 100%; + background-color: #e0e0e0; + border-radius: 4px; + animation: pulse 1.5s infinite ease-in-out; + } + + @keyframes pulse { + 0% { + background-color: #e0e0e0; + } + 50% { + background-color: #f0f0f0; + } + 100% { + background-color: #e0e0e0; + } + } + + .table { + width: 100%; + border-collapse: collapse; + } + + .table th, + .table td { + padding: 0.75rem; + text-align: left; + } \ No newline at end of file diff --git a/GUI/src/components/molecules/TableSkeleton/TableSkeleton.tsx b/GUI/src/components/molecules/TableSkeleton/TableSkeleton.tsx new file mode 100644 index 00000000..b8a23d3f --- /dev/null +++ b/GUI/src/components/molecules/TableSkeleton/TableSkeleton.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import './SkeletonTable.scss'; + +interface SkeletonTableProps { + rowCount: number; +} + +const SkeletonTable: React.FC = ({ rowCount }) => { + const skeletonRows = Array.from({ length: rowCount }, (_, index) => ( + + +
    + + + )); + + return ( + + {skeletonRows} +
    + ); +}; + +export default SkeletonTable; \ No newline at end of file diff --git a/GUI/src/components/molecules/TrainingSessionCard/index.tsx b/GUI/src/components/molecules/TrainingSessionCard/index.tsx new file mode 100644 index 00000000..759a8876 --- /dev/null +++ b/GUI/src/components/molecules/TrainingSessionCard/index.tsx @@ -0,0 +1,66 @@ +import { useTranslation } from 'react-i18next'; +import ProgressBar from 'components/ProgressBar'; +import { Card, Label } from 'components'; + +type TrainingSessionCardProps = { + modelName: string; + isLatest: boolean; + version: string; + status?: string; + trainingMessage?: string; + progress: number; + platform?: string; + maturity?: string; +}; + +const TrainingSessionCard: React.FC = ({ + modelName, + version, + isLatest, + status, + trainingMessage, + progress, + maturity, + platform, +}) => { + const { t } = useTranslation(); + + return ( + +
    +
    {modelName}
    + {isLatest && } + + {platform && }{' '} + {maturity && } + {status === 'failed' && } +
    + + } + > +
    + {(status==='failed' || status==='deployed') && progress===100 ? ( +
    + {trainingMessage} +
    + ) : ( +
    +
    {status}
    + +
    + {trainingMessage} +
    +
    + )} +
    +
    + ); +}; + +export default TrainingSessionCard; diff --git a/GUI/src/components/molecules/UserManagementActionButtons/UserManagementActionButtons.tsx b/GUI/src/components/molecules/UserManagementActionButtons/UserManagementActionButtons.tsx new file mode 100644 index 00000000..4af03ccf --- /dev/null +++ b/GUI/src/components/molecules/UserManagementActionButtons/UserManagementActionButtons.tsx @@ -0,0 +1,91 @@ +import { FC } from 'react'; +import Button from 'components/Button'; +import Icon from 'components/Icon'; +import { useTranslation } from 'react-i18next'; +import { MdOutlineDeleteOutline, MdOutlineEdit } from 'react-icons/md'; +import { User } from 'types/user'; +import { ButtonAppearanceTypes, ToastTypes } from 'enums/commonEnums'; +import { useDialog } from 'hooks/useDialog'; +import { deleteUser } from 'services/users'; +import { userManagementQueryKeys } from 'utils/queryKeys'; +import { useToast } from 'hooks/useToast'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +const ActionButtons: FC<{ + row: User; + setEditableRow: React.Dispatch>; +}> = ({ row, setEditableRow }) => { + const { t } = useTranslation(); + const { open, close } = useDialog(); + const toast = useToast(); + const queryClient = useQueryClient(); + + const deleteUserMutation = useMutation({ + mutationFn: ({ id }: { id: string | number }) => deleteUser(id), + onSuccess: async () => { + close(); + await queryClient.invalidateQueries( + userManagementQueryKeys.getAllEmployees() + ); + toast.open({ + type: ToastTypes.SUCCESS, + title: t('global.notification'), + message: t('toast.success.userDeleted'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: ToastTypes.ERROR, + title: t('global.notificationError'), + message: error?.message ?? '', + }); + }, + }); + + return ( +
    + + + +
    + ), + }); + }} + > + } /> + {t('global.delete')} + + + ); +}; + +export default ActionButtons; diff --git a/GUI/src/components/molecules/ValidationAndHierarchyCards/ValidationAndHierarchyCards.tsx b/GUI/src/components/molecules/ValidationAndHierarchyCards/ValidationAndHierarchyCards.tsx new file mode 100644 index 00000000..e481e1f6 --- /dev/null +++ b/GUI/src/components/molecules/ValidationAndHierarchyCards/ValidationAndHierarchyCards.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import ValidationCriteriaRowsView from '../ValidationCriteria/RowsView'; +import { useTranslation } from 'react-i18next'; +import { Card } from 'components'; +import ClassHierarchy from '../ClassHeirarchy'; +import { TreeNode, ValidationRule } from 'types/datasetGroups'; + +const ValidationAndHierarchyCards = ({ + metadata, + isMetadataLoading, + validationRules, + setValidationRules, + validationRuleError, + setValidationRuleError, + nodes, + setNodes, + nodesError, + setNodesError, +}: { + metadata: any; + isMetadataLoading: boolean; + validationRules: ValidationRule[] | undefined; + setValidationRules: React.Dispatch< + React.SetStateAction + >; + validationRuleError: boolean; + setValidationRuleError: React.Dispatch>; + nodes: TreeNode[]; + setNodes: React.Dispatch; + nodesError: boolean; + setNodesError: React.Dispatch>; +}) => { + const { t } = useTranslation(); + return ( + <> + {metadata && ( +
    + + + + + + {!isMetadataLoading && ( + + )} + +
    + )} + + ); +}; + +export default ValidationAndHierarchyCards; diff --git a/GUI/src/components/molecules/ValidationCriteria/CardsView.tsx b/GUI/src/components/molecules/ValidationCriteria/CardsView.tsx new file mode 100644 index 00000000..65e4f9ac --- /dev/null +++ b/GUI/src/components/molecules/ValidationCriteria/CardsView.tsx @@ -0,0 +1,66 @@ +import React, { FC, PropsWithChildren } from 'react'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import Button from 'components/Button'; +import { ValidationRule } from 'types/datasetGroups'; +import { v4 as uuidv4 } from 'uuid'; +import DraggableItem from './DraggableItem/DraggableItem'; +import { useTranslation } from 'react-i18next'; + +type ValidationRulesProps = { + validationRules?: ValidationRule[]; + setValidationRules: React.Dispatch>; + validationRuleError?: boolean; + setValidationRuleError: React.Dispatch>; +}; +const ValidationCriteriaCardsView: FC< + PropsWithChildren +> = ({ + validationRules, + setValidationRules, + setValidationRuleError, + validationRuleError, +}) => { + const { t } = useTranslation(); + const moveItem = (fromIndex: number, toIndex: number) => { + const updatedItems = Array.from(validationRules ?? []); + const [movedItem] = updatedItems.splice(fromIndex, 1); + updatedItems.splice(toIndex, 0, movedItem); + setValidationRules(updatedItems); + }; + + const addNewClass = () => { + setValidationRuleError(false); + const updatedItems = [ + ...(validationRules ?? []), + { id: uuidv4(), fieldName: '', dataType: '', isDataClass: false }, + ]; + setValidationRules(updatedItems); + }; + + return ( + +
    + {t('datasetGroups.createDataset.validationCriteria')} +
    + {validationRules?.map((item, index) => ( + + ))} +
    + +
    +
    + ); +}; + +export default ValidationCriteriaCardsView; diff --git a/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DragableItemStyle.scss b/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DragableItemStyle.scss new file mode 100644 index 00000000..696fe446 --- /dev/null +++ b/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DragableItemStyle.scss @@ -0,0 +1,11 @@ +.dragabbleCardContainer { + display: flex; + gap: 20px; + align-items: center; +} + +.dragabbleButtonWrapper { + display: flex; + gap: 10px; + justify-content: end; +} diff --git a/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DraggableItem.tsx b/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DraggableItem.tsx new file mode 100644 index 00000000..46e6c3de --- /dev/null +++ b/GUI/src/components/molecules/ValidationCriteria/DraggableItem/DraggableItem.tsx @@ -0,0 +1,156 @@ +import React, { useCallback } from 'react'; +import { useDrag, useDrop } from 'react-dnd'; +import dataTypes from '../../../../config/dataTypesConfig.json'; +import { MdDehaze, MdDeleteOutline } from 'react-icons/md'; +import Card from 'components/Card'; +import { FormCheckbox, FormInput, FormSelect } from 'components/FormElements'; +import { ValidationRule } from 'types/datasetGroups'; +import { Link } from 'react-router-dom'; +import { isFieldNameExisting } from 'utils/datasetGroupsUtils'; +import './DragableItemStyle.scss'; +import { useTranslation } from 'react-i18next'; +import useOptionLists from 'hooks/useOptionLists'; + +const ItemTypes = { + ITEM: 'item', +}; + +const DraggableItem = ({ + item, + index, + moveItem, + setValidationRules, + validationRuleError, + validationRules, +}: { + item: ValidationRule; + index: number; + moveItem: (fromIndex: number, toIndex: number) => void; + validationRules?: ValidationRule[]; + setValidationRules: React.Dispatch>; + validationRuleError?: boolean; +}) => { + const { t } = useTranslation(); + const [, ref] = useDrag({ + type: ItemTypes.ITEM, + item: { index }, + }); + + const { dataTypesConfigs: dataTypes } = useOptionLists(); + + const [, drop] = useDrop({ + accept: ItemTypes.ITEM, + hover: (draggedItem: { index: number }) => { + if (draggedItem.index !== index) { + moveItem(draggedItem.index, index); + draggedItem.index = index; + } + }, + }); + + const deleteItem = (idToDelete: string | number) => { + const updatedItems = validationRules?.filter( + (item) => item?.id !== idToDelete + ); + updatedItems && setValidationRules(updatedItems); + }; + + const setIsDataClass = (id: string | number, isDataClass: boolean) => { + const updatedItems = validationRules?.map((item) => + item.id === id ? { ...item, isDataClass: !isDataClass } : item + ); + updatedItems && setValidationRules(updatedItems); + }; + + const handleChange = useCallback((id: string | number, newValue: string) => { + setValidationRules((prevData) => + prevData.map((item) => + item.id === id ? { ...item, fieldName: newValue } : item + ) + ); + }, []); + + const changeDataType = (id: string | number, value: string) => { + const updatedItems = validationRules?.map((item) => + item.id === id ? { ...item, dataType: value } : item + ); + updatedItems && setValidationRules(updatedItems); + }; + + const getErrorMessage = (item: ValidationRule) => { + if (validationRuleError) { + if (!item.fieldName) { + return t('datasetGroups.detailedView.fieldName'); + } + if (item.fieldName.toLowerCase() === 'rowid') { + return t('datasetGroups.detailedView.fieldNameError', { + name: item.fieldName, + }); + } + if (isFieldNameExisting(validationRules, item.fieldName)) { + return t('datasetGroups.detailedView.fieldNameExist', { + name: item.fieldName, + }); + } + } + return ''; + }; + + return ( +
    ref(drop(node))}> + +
    + handleChange(item.id, e.target.value)} + error={getErrorMessage(item)} + /> + + changeDataType(item.id, selection?.value as string) + } + error={ + validationRuleError && !item.dataType ? 'Select a data type' : '' + } + /> +
    + deleteItem(item.id)} + className="link" + > + + {t('global.delete')} + + setIsDataClass(item.id, item.isDataClass)} + style={{ + width: '150px', + }} + /> + +
    +
    +
    +
    + ); +}; + +export default DraggableItem; diff --git a/GUI/src/components/molecules/ValidationCriteria/RowViewStyle.scss b/GUI/src/components/molecules/ValidationCriteria/RowViewStyle.scss new file mode 100644 index 00000000..e46d9e57 --- /dev/null +++ b/GUI/src/components/molecules/ValidationCriteria/RowViewStyle.scss @@ -0,0 +1,18 @@ +.rowViewContentWrapper { + display: flex; + gap: 20px; + align-items: center; + padding: 25px; +} + +.rowViewButtonWrapper { + display: flex; + justify-content: end; + gap: 10px; +} + +.rowViewButton { + display: flex; + align-items: center; + gap: 10px; +} \ No newline at end of file diff --git a/GUI/src/components/molecules/ValidationCriteria/RowsView.tsx b/GUI/src/components/molecules/ValidationCriteria/RowsView.tsx new file mode 100644 index 00000000..d7e42d34 --- /dev/null +++ b/GUI/src/components/molecules/ValidationCriteria/RowsView.tsx @@ -0,0 +1,166 @@ +import React, { FC, PropsWithChildren } from 'react'; +import dataTypes from '../../../config/dataTypesConfig.json'; +import { MdAdd, MdDelete } from 'react-icons/md'; +import { FormCheckbox, FormInput, FormSelect } from 'components/FormElements'; +import { ValidationRule } from 'types/datasetGroups'; +import { Link } from 'react-router-dom'; +import { isFieldNameExisting } from 'utils/datasetGroupsUtils'; +import { v4 as uuidv4 } from 'uuid'; +import { useTranslation } from 'react-i18next'; +import './RowViewStyle.scss'; + +type ValidationRulesProps = { + validationRules?: ValidationRule[]; + setValidationRules: React.Dispatch< + React.SetStateAction + >; + validationRuleError?: boolean; + setValidationRuleError: React.Dispatch>; +}; + +const ValidationCriteriaRowsView: FC< + PropsWithChildren +> = ({ + validationRules, + setValidationRules, + setValidationRuleError, + validationRuleError, +}) => { + const { t } = useTranslation(); + const setIsDataClass = (id: string | number, isDataClass: boolean) => { + const updatedItems = validationRules?.map((item) => + item.id === id ? { ...item, isDataClass: !isDataClass } : item + ); + updatedItems && setValidationRules(updatedItems); + }; + + const changeName = (id: number | string, newValue: string) => { + setValidationRules((prevData) => + prevData?.map((item) => + item.id === id ? { ...item, fieldName: newValue } : item + ) + ); + + if (isFieldNameExisting(validationRules, newValue)) { + setValidationRuleError(true); + } else { + setValidationRuleError(false); + } + }; + + const changeDataType = (id: number | string, value: string) => { + const updatedItems = validationRules?.map((item) => + item.id === id ? { ...item, dataType: value } : item + ); + setValidationRules(updatedItems); + }; + + const addNewClass = () => { + setValidationRuleError(false); + const updatedItems = [ + ...(validationRules ?? []), + { id: uuidv4(), fieldName: '', dataType: '', isDataClass: false }, + ]; + + setValidationRules(updatedItems); + }; + + const deleteItem = (idToDelete: number | string) => { + const updatedItems = validationRules?.filter( + (item) => item.id !== idToDelete + ); + setValidationRules(updatedItems); + }; + + const getErrorMessage = (item: ValidationRule) => { + if (validationRuleError) { + if (!item.fieldName) { + return t('datasetGroups.detailedView.fieldName'); + } + if (item.fieldName.toLowerCase() === 'rowid') { + return t('datasetGroups.detailedView.fieldNameError', { + name: item.fieldName, + }); + } + if (isFieldNameExisting(validationRules, item.fieldName)) { + return t('datasetGroups.detailedView.fieldNameExist', { + name: item.fieldName, + }); + } + } + return ''; + }; + + const getBackgroundColor = (index: number) => { + if (index % 2 === 1) return '#F9F9F9'; + else return '#FFFFFF'; + }; + + return ( +
    + {validationRules?.map((item, index) => ( +
    + changeName(item.id, e.target.value)} + error={getErrorMessage(item)} + /> + + changeDataType(item.id, (selection?.value as string) ?? '') + } + error={ + validationRuleError && !item.dataType + ? t('datasetGroups.detailedView.selectDataType') ?? '' + : '' + } + /> +
    + addNewClass()} + className="rowViewButton" + > + + {t('global.add')} + + deleteItem(item.id)} + className="rowViewButton" + > + + {t('global.delete')} + + setIsDataClass(item.id, item.isDataClass)} + style={{ width: '150px' }} + /> +
    +
    + ))} +
    + ); +}; + +export default ValidationCriteriaRowsView; diff --git a/GUI/src/components/molecules/ValidationSessionCard/index.tsx b/GUI/src/components/molecules/ValidationSessionCard/index.tsx new file mode 100644 index 00000000..c92b61e9 --- /dev/null +++ b/GUI/src/components/molecules/ValidationSessionCard/index.tsx @@ -0,0 +1,54 @@ +import { useTranslation } from 'react-i18next'; +import ProgressBar from 'components/ProgressBar'; +import { Card, Label } from 'components'; + +type ValidationSessionCardProps = { + dgName:string; + version:string; + isLatest:boolean; + status?:string; + validationMessage?: string; + progress: number; + }; + +const ValidationSessionCard: React.FC = ({dgName,version,isLatest,status,validationMessage,progress}) => { + const { t } = useTranslation(); + + return ( + + {dgName} + {isLatest &&( + + )} + {status==="Fail" &&( + + )} + + } + > +
    + {(status==="Fail" || status==="Success") && progress===100 ? ( +
    + {validationMessage} +
    + ) : ( +
    +
    {status}
    + +
    + {validationMessage} +
    +
    + )} +
    +
    + ); +}; + +export default ValidationSessionCard; diff --git a/GUI/src/components/molecules/ValidationStatus/ValidationStatus.tsx b/GUI/src/components/molecules/ValidationStatus/ValidationStatus.tsx new file mode 100644 index 00000000..06835f29 --- /dev/null +++ b/GUI/src/components/molecules/ValidationStatus/ValidationStatus.tsx @@ -0,0 +1,43 @@ +import { ValidationStatus } from 'enums/datasetEnums'; +import React from 'react'; +import Label from 'components/Label'; +import { LabelType } from 'enums/commonEnums'; +import { useTranslation } from 'react-i18next'; + +const DatasetValidationStatus = ({ + status, +}: { + status: string | undefined; +}) => { + const { t } = useTranslation(); + + if (status === ValidationStatus.SUCCESS) { + return ( + + ); + } else if (status === ValidationStatus.FAIL) { + return ( + + ); + } else if (status === ValidationStatus.UNVALIDATED) { + return ( + + ); + } else if (status === ValidationStatus.IN_PROGRESS) { + return ( + + ); + } else { + return null; + } +}; + +export default DatasetValidationStatus; diff --git a/GUI/src/components/molecules/ViewDatasetGroupModalController/ViewDatasetGroupModalController.tsx b/GUI/src/components/molecules/ViewDatasetGroupModalController/ViewDatasetGroupModalController.tsx new file mode 100644 index 00000000..87978835 --- /dev/null +++ b/GUI/src/components/molecules/ViewDatasetGroupModalController/ViewDatasetGroupModalController.tsx @@ -0,0 +1,293 @@ +import React, { RefObject } from 'react'; +import { Button, Dialog, FormRadios } from 'components'; +import DynamicForm from 'components/FormElements/DynamicForm'; +import FileUpload, { FileUploadHandle } from 'components/FileUpload'; +import formats from '../../../config/formatsConfig.json'; +import { ViewDatasetGroupModalContexts } from 'enums/datasetEnums'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { useDialog } from 'hooks/useDialog'; +import { useTranslation } from 'react-i18next'; +import { SelectedRowPayload } from 'types/datasetGroups'; +import './styles.scss'; + +const ViewDatasetGroupModalController = ({ + setImportStatus, + fileUploadRef, + handleFileSelect, + handleImport, + importStatus, + setImportFormat, + importFormat, + handleExport, + setExportFormat, + selectedRow, + patchDataUpdate, + isModalOpen, + setIsModalOpen, + setOpenedModalContext, + openedModalContext, + closeModals, + deleteRow, + file, + exportFormat, + isImportDataLoading, + confirmationModalTitle, + confirmationModalDesc, + onConfirmationConfirm, + majorUpdateLoading, + patchUpdateLoading, + minorUpdateLoading, + confirmationFLow, + deleteDatasetMutationLoading +}: { + setImportStatus: React.Dispatch>; + handleFileSelect: (file: File | undefined) => void; + fileUploadRef: RefObject; + handleImport: () => void; + importStatus: string; + setImportFormat: React.Dispatch>; + importFormat: string; + handleExport: () => void; + setExportFormat: React.Dispatch>; + selectedRow: SelectedRowPayload | undefined; + patchDataUpdate: (dataset: any) => void; + isModalOpen: boolean; + setIsModalOpen: React.Dispatch>; + openedModalContext: ViewDatasetGroupModalContexts; + setOpenedModalContext: React.Dispatch< + React.SetStateAction + >; + closeModals: () => void; + deleteRow: (dataRow: any) => void; + file: File | undefined; + exportFormat: string; + isImportDataLoading: boolean; + confirmationModalTitle: string; + confirmationModalDesc: string; + onConfirmationConfirm: () => any; + majorUpdateLoading: boolean; + patchUpdateLoading: boolean; + minorUpdateLoading: boolean; + deleteDatasetMutationLoading: boolean; + confirmationFLow: string; +}) => { + const { close } = useDialog(); + const { t } = useTranslation(); + + return ( + <> + {isModalOpen && + openedModalContext === ViewDatasetGroupModalContexts.IMPORT_MODAL && ( + + + + + } + onClose={() => { + closeModals(); + setImportStatus('ABORTED'); + setImportFormat(''); + }} + > +
    +

    + {t('datasetGroups.detailedView.modals.import.fileFormatlabel')} +

    +
    + +
    +

    {t('datasetGroups.detailedView.modals.import.attachments')}

    + + {importStatus === 'STARTED' && ( +
    +
    + {t( + 'datasetGroups.detailedView.modals.import.uploadInProgress' + )} +
    +

    + {t('datasetGroups.detailedView.modals.import.uploadDesc')} +

    +
    + )} +
    +
    + )} + {isModalOpen && + openedModalContext === ViewDatasetGroupModalContexts.EXPORT_MODAL && ( + + + + + } + onClose={() => { + closeModals(); + setImportStatus('ABORTED'); + setExportFormat(''); + }} + > +
    +

    + {t('datasetGroups.detailedView.modals.export.fileFormatlabel')} +

    +
    + +
    +
    +
    + )} + {isModalOpen && + openedModalContext === + ViewDatasetGroupModalContexts.PATCH_UPDATE_MODAL && ( + + + + )} + + {isModalOpen && + openedModalContext === + ViewDatasetGroupModalContexts.DELETE_ROW_MODAL && ( + + + + + } + > + {t('datasetGroups.detailedView.modals.delete.description')} + + )} + {isModalOpen && + openedModalContext === + ViewDatasetGroupModalContexts.CONFIRMATION_MODAL && ( + + + {confirmationFLow === 'update' ? ( + + ) : ( + + )} + + } + > + {confirmationModalDesc} + + )} + + ); +}; + +export default ViewDatasetGroupModalController; \ No newline at end of file diff --git a/GUI/src/components/molecules/ViewDatasetGroupModalController/styles.scss b/GUI/src/components/molecules/ViewDatasetGroupModalController/styles.scss new file mode 100644 index 00000000..a533536c --- /dev/null +++ b/GUI/src/components/molecules/ViewDatasetGroupModalController/styles.scss @@ -0,0 +1,11 @@ + + +.upload-progress-wrapper { + text-align: center; + padding: 20px; +} + +.upload-progress-text-wrapper { + margin-bottom: 10px; + font-size: 18px; +} \ No newline at end of file diff --git a/GUI/src/config/dataTypesConfig.json b/GUI/src/config/dataTypesConfig.json new file mode 100644 index 00000000..fafa6f48 --- /dev/null +++ b/GUI/src/config/dataTypesConfig.json @@ -0,0 +1,7 @@ +[ + { "label": "Text", "value": "text" }, + { "label": "Numbers", "value": "numbers" }, + { "label": "Date Time", "value": "datetime" }, + { "label": "Email", "value": "email" }, + { "label": "File Attachments", "value": "file_attachments" } + ] \ No newline at end of file diff --git a/GUI/src/config/formatsConfig.json b/GUI/src/config/formatsConfig.json new file mode 100644 index 00000000..1c0a3ed9 --- /dev/null +++ b/GUI/src/config/formatsConfig.json @@ -0,0 +1,6 @@ +[ + { "label": "XLSX", "value": "xlsx" }, + { "label": "JSON", "value": "json" }, + { "label": "YAML", "value": "yaml" } + + ] \ No newline at end of file diff --git a/GUI/src/config/importOptionsConfig.json b/GUI/src/config/importOptionsConfig.json new file mode 100644 index 00000000..d7111cb0 --- /dev/null +++ b/GUI/src/config/importOptionsConfig.json @@ -0,0 +1,5 @@ +[ + { "label": "Import to add", "value": "add" }, + { "label": "Import to delete", "value": "delete" } + + ] \ No newline at end of file diff --git a/GUI/src/config/rolesConfig.json b/GUI/src/config/rolesConfig.json new file mode 100644 index 00000000..02b429cd --- /dev/null +++ b/GUI/src/config/rolesConfig.json @@ -0,0 +1,4 @@ +[ + { "label": "ROLE_ADMINISTRATOR", "value": "ROLE_ADMINISTRATOR" }, + { "label": "ROLE_MODEL_TRAINER", "value": "ROLE_MODEL_TRAINER" } +] diff --git a/GUI/src/constants/config.ts b/GUI/src/constants/config.ts new file mode 100644 index 00000000..5c0855f4 --- /dev/null +++ b/GUI/src/constants/config.ts @@ -0,0 +1,5 @@ +export const EMERGENCY_NOTICE_LENGTH = 250; +export const WELCOME_MESSAGE_LENGTH = 250; +export const USER_IDLE_STATUS_TIMEOUT = 300000; // milliseconds +export const CHAT_INPUT_LENGTH = 500; +export const CHAT_HISTORY_PREFERENCES_KEY = 'chat-history-preferences'; diff --git a/GUI/src/constants/menuIcons.tsx b/GUI/src/constants/menuIcons.tsx new file mode 100644 index 00000000..a53fc7c9 --- /dev/null +++ b/GUI/src/constants/menuIcons.tsx @@ -0,0 +1,24 @@ +import { MdOutlineForum, MdOutlineAdb, MdOutlineEqualizer, MdSettings, MdOutlineMonitorWeight } from 'react-icons/md'; + +export const menuIcons = [ + { + id: 'userManagement', + icon: , + }, + { + id: 'training', + icon: , + }, + { + id: 'analytics', + icon: , + }, + { + id: 'settings', + icon: , + }, + { + id: 'monitoring', + icon: , + }, +]; diff --git a/GUI/src/context/DialogContext.tsx b/GUI/src/context/DialogContext.tsx new file mode 100644 index 00000000..3141273a --- /dev/null +++ b/GUI/src/context/DialogContext.tsx @@ -0,0 +1,83 @@ +import React, { + createContext, + FC, + PropsWithChildren, + ReactNode, + useMemo, + useState, +} from 'react'; +import * as RadixDialog from '@radix-ui/react-dialog'; +import { MdOutlineClose } from 'react-icons/md'; +import clsx from 'clsx'; +import '../components/Dialog/Dialog.scss'; +import Icon from 'components/Icon'; +import Track from 'components/Track'; + +type DialogProps = { + title?: string | null; + footer?: ReactNode; + size?: 'default' | 'large'; + content: ReactNode; +}; + +type DialogContextType = { + open: (dialog: DialogProps) => void; + close: () => void; +}; + +export const DialogContext = createContext(null!); + +export const DialogProvider: FC> = ({ children }) => { + const [isOpen, setIsOpen] = useState(false); + const [dialogProps, setDialogProps] = useState(null); + + const open = (dialog: DialogProps) => { + setDialogProps(dialog); + setIsOpen(true); + }; + + const close = () => { + setIsOpen(false); + setDialogProps(null); + }; + + const contextValue = useMemo(() => ({ open, close }), []); + + return ( + + {children} + {dialogProps && ( + + + + + {dialogProps.title && ( +
    + + {dialogProps.title} + + + + +
    + )} +
    {dialogProps.content}
    + {dialogProps.footer && ( + + {dialogProps.footer} + + )} +
    +
    +
    + )} +
    + ); +}; diff --git a/GUI/src/context/ToastContext.tsx b/GUI/src/context/ToastContext.tsx new file mode 100644 index 00000000..5c07ef4a --- /dev/null +++ b/GUI/src/context/ToastContext.tsx @@ -0,0 +1,58 @@ +import { + createContext, + FC, + PropsWithChildren, + ReactNode, + useMemo, + useState, +} from 'react'; +import { useTranslation } from 'react-i18next'; +import * as RadixToast from '@radix-ui/react-toast'; + +import { Toast } from 'components'; +import { generateUEID } from 'utils/generateUEID'; + +export type ToastType = { + type: 'info' | 'success' | 'error' | 'warning'; + title: string; + message: ReactNode; +}; + +type ToastTypeWithId = ToastType & { id: string }; + +type ToastContextType = { + open: (toast: ToastType) => void; +}; + +export const ToastContext = createContext(null!); + +export const ToastProvider: FC = ({ children }) => { + const { t } = useTranslation(); + const [toasts, setToasts] = useState([]); + const open = (content: ToastType) => { + setToasts((prevState) => [ + ...prevState, + { id: generateUEID(), ...content }, + ]); + }; + const close = (id: string) => { + setToasts((prevState) => prevState.filter((toast) => toast.id === id)); + }; + + const contextValue = useMemo(() => ({ open }), []); + + return ( + + + {children} + {toasts.map((toast) => ( + close(toast.id)} /> + ))} + + + + ); +}; diff --git a/GUI/src/data/mockData.ts b/GUI/src/data/mockData.ts new file mode 100644 index 00000000..b9cde6d8 --- /dev/null +++ b/GUI/src/data/mockData.ts @@ -0,0 +1,242 @@ +export const correctedData = [ + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'jira', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, + { + inferenceId: 1234, + itemId: 'alpha1232', + inferenceTime: '23-03-2024-00:23:33', + inferencedText: 'Hello, this is a test email', + predictedLabels: [ + 'Police', + 'Communications', + 'Internal', + 'Recruitment', + 'June Intake', + 'Research & Development', + ], + averagePredictedClassesProbability: 92, + platform: 'outlook', + correctedLabels: [ + 'Police', + 'Special Agency', + 'External', + 'Reports', + 'Annual Report', + ], + averageCorrectedClassesProbability: 85, + }, +]; diff --git a/GUI/src/enums/commonEnums.ts b/GUI/src/enums/commonEnums.ts new file mode 100644 index 00000000..79f9444f --- /dev/null +++ b/GUI/src/enums/commonEnums.ts @@ -0,0 +1,18 @@ +export enum ToastTypes { + SUCCESS = 'success', + ERROR = 'error', +} + +export enum ButtonAppearanceTypes { + PRIMARY = 'primary', + SECONDARY = 'secondary', + ERROR = 'error', + TEXT = 'text', +} + +export enum LabelType { + SUCCESS = 'success', + ERROR = 'error', + INFO = 'info', + WARNING = 'warning', +} diff --git a/GUI/src/enums/dataModelsEnums.ts b/GUI/src/enums/dataModelsEnums.ts new file mode 100644 index 00000000..38264c63 --- /dev/null +++ b/GUI/src/enums/dataModelsEnums.ts @@ -0,0 +1,26 @@ +export enum TrainingStatus { + NOT_TRAINED = 'not trained', + TRAINING_INPROGRESS = 'training in-progress', + TRAINED = 'trained', + RETRAINING_NEEDED = 'retraining needed', + UNTRAINABLE = 'untrainable', +} + +export enum Maturity { + PRODUCTION = 'production ready', + STAGING = 'staging', + DEVELOPMENT = 'development', + TESTING = 'testing', +} + +export enum Platform { + JIRA = 'jira', + OUTLOOK = 'outlook', + UNDEPLOYED = 'undeployed', +} + +export enum UpdateType { + MAJOR = 'major', + MINOR = 'minor', + MATURITY_LABEL = 'maturityLabel', +} diff --git a/GUI/src/enums/datasetEnums.ts b/GUI/src/enums/datasetEnums.ts new file mode 100644 index 00000000..89e5fd30 --- /dev/null +++ b/GUI/src/enums/datasetEnums.ts @@ -0,0 +1,51 @@ +export enum ValidationStatus { + SUCCESS = 'success', + FAIL = 'fail', + UNVALIDATED = 'unvalidated', + IN_PROGRESS = 'in-progress', +} + +export enum DatasetViewEnum { + LIST = 'list', + INDIVIDUAL = 'individual', +} + +export enum CreateDatasetGroupModals { + SUCCESS = 'SUCCESS', + VALIDATION_ERROR = 'VALIDATION_ERROR', + NULL = 'NULL', +} + +export enum ViewDatasetGroupModalContexts { + EXPORT_MODAL = 'EXPORT_MODAL', + IMPORT_MODAL = 'IMPORT_MODAL', + PATCH_UPDATE_MODAL = 'PATCH_UPDATE_MODAL', + DELETE_ROW_MODAL = 'DELETE_ROW_MODAL', + CONFIRMATION_MODAL = 'CONFIRMATION_MODAL', + NULL = 'NULL', +} + +export enum UpdatePriority { + MAJOR = 'MAJOR', + MINOR = 'MINOR', + PATCH = 'PATCH', + NULL = 'NULL', +} + +export enum ImportExportDataTypes { + XLSX = 'xlsx', + JSON = 'json', + YAML = 'yaml', +} + +export enum StopWordImportOptions { + ADD = 'add', + DELETE = 'delete', +} + +export enum ValidationErrorTypes { + NAME = 'NAME', + CLASS_HIERARCHY = 'CLASS_HIERARCHY', + VALIDATION_CRITERIA = 'VALIDATION_CRITERIA', + NULL = 'NULL', +} diff --git a/GUI/src/enums/integrationEnums.ts b/GUI/src/enums/integrationEnums.ts new file mode 100644 index 00000000..4d4a521d --- /dev/null +++ b/GUI/src/enums/integrationEnums.ts @@ -0,0 +1,13 @@ +export enum INTEGRATION_MODALS { + INTEGRATION_SUCCESS = 'INTEGRATION_SUCCESS', + INTEGRATION_ERROR = 'INTEGRATION_ERROR', + DISCONNECT_CONFIRMATION = 'DISCONNECT_CONFIRMATION', + CONNECT_CONFIRMATION = 'CONNECT_CONFIRMATION', + DISCONNECT_ERROR = 'DISCONNECT_ERROR', + NULL = 'NULL', +} + +export enum INTEGRATION_OPERATIONS { + ENABLE = 'enable', + DISABLE = 'disable', +} diff --git a/GUI/src/enums/roles.ts b/GUI/src/enums/roles.ts new file mode 100644 index 00000000..b5cfd8a6 --- /dev/null +++ b/GUI/src/enums/roles.ts @@ -0,0 +1,4 @@ +export enum ROLES { + ROLE_ADMINISTRATOR = 'ROLE_ADMINISTRATOR', + ROLE_MODEL_TRAINER = 'ROLE_MODEL_TRAINER', +} diff --git a/GUI/src/hoc/with-authorization.tsx b/GUI/src/hoc/with-authorization.tsx new file mode 100644 index 00000000..9874ffa9 --- /dev/null +++ b/GUI/src/hoc/with-authorization.tsx @@ -0,0 +1,29 @@ +import { ROLES } from 'enums/roles'; +import React from 'react'; +import useStore from 'store'; + +function withAuthorization

    ( + WrappedComponent: React.ComponentType

    , + allowedRoles: ROLES[] = [] +): React.FC

    { + const CheckRoles: React.FC

    = ({ ...props }: P) => { + const userInfo = useStore((x) => x.userInfo); + const allowed = allowedRoles?.some((x) => + userInfo?.authorities.includes(x) + ); + + if (!userInfo) { + return Loading...; + } + + if (!allowed) { + return Unauthorized Access; + } + + return ; + }; + + return CheckRoles; +} + +export default withAuthorization; diff --git a/GUI/src/hooks/useAudio.tsx b/GUI/src/hooks/useAudio.tsx new file mode 100644 index 00000000..55631e2c --- /dev/null +++ b/GUI/src/hooks/useAudio.tsx @@ -0,0 +1,35 @@ +import { useEffect, useState } from 'react'; +import { Howl } from 'howler'; +import ding from '../assets/ding.mp3'; +import newMessageSound from '../assets/newMessageSound.mp3'; + +export const useAudio = (audiosrc: string) => { + const [audio, setAudio] = useState(null); + + useEffect(() => { + const howl = new Howl({ + src: audiosrc, + onloaderror: (soundId, error) => console.error(soundId, error), + onplayerror: (soundId, error) => { + console.error(soundId, error); + howl.once('unlock', () => howl.play()); + }, + }); + + setAudio(howl); + + return () => { + howl.unload(); + } + }, []); + + return [audio] as const; +} + +export const useDing = () => { + return useAudio(ding); +} + +export const useNewMessageSound = () => { + return useAudio(newMessageSound); +} diff --git a/GUI/src/hooks/useDialog.tsx b/GUI/src/hooks/useDialog.tsx new file mode 100644 index 00000000..c38ed60a --- /dev/null +++ b/GUI/src/hooks/useDialog.tsx @@ -0,0 +1,4 @@ +import { DialogContext } from 'context/DialogContext'; +import { useContext } from 'react'; + +export const useDialog = () => useContext(DialogContext); diff --git a/GUI/src/hooks/useDocumentEscapeListener.tsx b/GUI/src/hooks/useDocumentEscapeListener.tsx new file mode 100644 index 00000000..8f7b3b6b --- /dev/null +++ b/GUI/src/hooks/useDocumentEscapeListener.tsx @@ -0,0 +1,17 @@ +import { useLayoutEffect } from 'react'; + +const useDocumentEscapeListener = (callback: () => void) => { + useLayoutEffect(() => { + const handleKeyUp = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + callback(); + } + }; + + document.addEventListener('keyup', handleKeyUp); + + return () => document.removeEventListener('keyup', handleKeyUp); + }, [callback]); +}; + +export default useDocumentEscapeListener; diff --git a/GUI/src/hooks/useOptionLists.tsx b/GUI/src/hooks/useOptionLists.tsx new file mode 100644 index 00000000..41753bfe --- /dev/null +++ b/GUI/src/hooks/useOptionLists.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from 'react-i18next'; + +const useOptionLists = () => { + const { t } = useTranslation(); + + const dataTypesConfigs = [ + { label: t('optionLists.text'), value: 'text' }, + { label: t('optionLists.numbers'), value: 'numbers' }, + { label: t('optionLists.dateTimes'), value: 'datetime' }, + { label: t('optionLists.email'), value: 'email' }, + { label: t('optionLists.fileAttachements'), value: 'file_attachments' }, + ]; + + const importOptionsConfigs = [ + { label: t('optionLists.importToAdd'), value: 'add' }, + { label: t('optionLists.importToDelete'), value: 'delete' }, + ]; + + return { + dataTypesConfigs, + importOptionsConfigs, + }; +}; + +export default useOptionLists; diff --git a/GUI/src/hooks/useToast.tsx b/GUI/src/hooks/useToast.tsx new file mode 100644 index 00000000..51715549 --- /dev/null +++ b/GUI/src/hooks/useToast.tsx @@ -0,0 +1,5 @@ +import { useContext } from 'react'; + +import { ToastContext } from 'context/ToastContext'; + +export const useToast = () => useContext(ToastContext); diff --git a/GUI/src/hooks/useTrainingSessions.tsx b/GUI/src/hooks/useTrainingSessions.tsx new file mode 100644 index 00000000..b2d2baf6 --- /dev/null +++ b/GUI/src/hooks/useTrainingSessions.tsx @@ -0,0 +1,52 @@ +import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from "react"; +import { getDataModelsProgress } from "services/data-models"; +import sse from "services/sse-service"; +import { TrainingProgressData } from "types/dataModels"; +import { SSEEventData } from "types/datasetGroups"; + +export const useTrainingSessions = () => { + const [progresses, setProgresses] = useState([]); + + const { data: progressData, refetch } = useQuery( + ['datamodels/progress'], + () => getDataModelsProgress(), + { + onSuccess: (data) => { + setProgresses(data); + }, + } + ); + + const handleUpdate = (sessionId: string, newData: SSEEventData) => { + setProgresses((prevProgresses) => + prevProgresses.map((progress) => + progress.id === sessionId ? { ...progress, ...newData } : progress + ) + ); + }; + + useEffect(() => { + if (!progressData) return; + + const eventSources = progressData + .filter( + (progress) => + !(progress.trainingStatus === 'deployed'||progress.trainingStatus === 'failed') && + progress.progressPercentage !== 100 + ) + .map((progress) => + sse(`/${progress.id}`, 'dataset', (data: SSEEventData) => { + console.log(`New data for notification ${progress.id}:`, data); + handleUpdate(data.sessionId, data); + }) + ); + + return () => { + eventSources.forEach((eventSource) => eventSource?.close()); + console.log('SSE connections closed'); + }; + }, [progressData, refetch]); + + return progresses; +}; diff --git a/GUI/src/hooks/useValidationSessions.tsx b/GUI/src/hooks/useValidationSessions.tsx new file mode 100644 index 00000000..0f1155ae --- /dev/null +++ b/GUI/src/hooks/useValidationSessions.tsx @@ -0,0 +1,50 @@ +import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from "react"; +import { getDatasetGroupsProgress } from "services/datasets"; +import sse from "services/sse-service"; +import { SSEEventData, ValidationProgressData } from "types/datasetGroups"; +import { datasetQueryKeys } from "utils/queryKeys"; + +export const useValidationSessions = () => { + const [progresses, setProgresses] = useState([]); + + const { data: progressData, refetch } = useQuery( + datasetQueryKeys.GET_DATASET_GROUP_PROGRESS(), + getDatasetGroupsProgress, + { + onSuccess: (data) => setProgresses(data), + } + ); + + const handleUpdate = (sessionId: string, newData: SSEEventData) => { + setProgresses((prevProgresses) => + prevProgresses.map((progress) => + progress.id === sessionId ? { ...progress, ...newData } : progress + ) + ); + }; + + useEffect(() => { + if (!progressData) return; + + const eventSources = progressData + .filter( + (progress) => + !(progress.validationStatus === 'Success'|| progress.validationStatus === 'Fail') && + progress.progressPercentage !== 100 + ) + .map((progress) => + sse(`/${progress.id}`, 'dataset', (data: SSEEventData) => { + console.log(`New data for notification ${progress.id}:`, data); + handleUpdate(data.sessionId, data); + }) + ); + + return () => { + eventSources.forEach((eventSource) => eventSource?.close()); + console.log('SSE connections closed'); + }; + }, [progressData, refetch]); + + return progresses; + }; \ No newline at end of file diff --git a/GUI/src/locale/et_EE.ts b/GUI/src/locale/et_EE.ts new file mode 100644 index 00000000..26f69578 --- /dev/null +++ b/GUI/src/locale/et_EE.ts @@ -0,0 +1,31 @@ +import * as timeago from 'timeago.js'; + +/* To Add Weeks Support Please add the following in index 8 & 9 + ['nädal tagasi', 'nädala pärast'], + ['%s nädalat tagasi', '%s nädala pärast'], +*/ + +function locale(number: number, index: number, totalSec: number | undefined) { + const days = Math.round(Math.round(totalSec ?? 0) / (3600 * 24)); + const monthRemainingDays = days - (number * 30); + const isDaysPlural = monthRemainingDays != 1 ? 'a' : ''; + const isDaysAvailable = monthRemainingDays != 0 ? `${monthRemainingDays} päev${isDaysPlural}` : ''; + return [ + ['just nüüd', 'praegu'], + ['%s sekundit tagasi', '%s sekundi pärast'], + ['minut tagasi', 'minuti pärast'], + ['%s minutit tagasi', '%s minuti pärast'], + ['tund tagasi', 'tunni pärast'], + ['%s tundi tagasi', '%s tunni pärast'], + ['päev tagasi', 'päeva pärast'], + ['%s päeva tagasi', '%s päeva pärast'], + [`${days} päev tagasi`, 'nädala pärast'], + [`${days} päev tagasi`, '%s nädala pärast'], + [`%s kuu ja ${isDaysAvailable}`, 'kuu pärast'], + [`%s kuud ja ${isDaysAvailable}`, '%s kuu pärast'], + ['aasta tagasi', 'aasta pärast'], + ['%s aastat tagasi', '%s aasta pärast'], + ][index] as [string, string]; +} + +timeago.register('et_EE', locale); diff --git a/GUI/src/main.tsx b/GUI/src/main.tsx new file mode 100644 index 00000000..b6274de5 --- /dev/null +++ b/GUI/src/main.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import { + QueryClient, + QueryClientProvider, + QueryFunction, +} from '@tanstack/react-query'; + +import App from './App'; +import api from 'services/api'; +import apiDev from 'services/api-dev'; +import { ToastProvider } from 'context/ToastContext'; +import 'styles/main.scss'; +import '../i18n'; +import { CookiesProvider } from 'react-cookie'; +import { DialogProvider } from 'context/DialogContext'; + +const defaultQueryFn: QueryFunction | undefined = async ({ queryKey }) => { + if (queryKey.includes('prod')) { + const { data } = await apiDev.get(queryKey[0] as string); + return data; + } + + const { data } = await api.get(queryKey[0] as string); + return data; +}; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}); + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + + + + + + + + + +); diff --git a/GUI/src/mocks/handlers.ts b/GUI/src/mocks/handlers.ts new file mode 100644 index 00000000..7fc29c5d --- /dev/null +++ b/GUI/src/mocks/handlers.ts @@ -0,0 +1,21 @@ +import { rest } from 'msw'; + +import { usersData } from './users'; +import { healthzStatusData } from './healthzStatus'; + +const BASE_URL = import.meta.env.BASE_URL; + +export const handlers = [ + rest.get(BASE_URL + 'accounts/admins', (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json(usersData), + ); + }), + rest.get(BASE_URL + 'health/components-status', (req, res, ctx) => { + return res(ctx.json(healthzStatusData)); + }), + rest.post(BASE_URL + 'attachments/add', (req, res, ctx) => { + return res(ctx.status(200)); + }) +]; diff --git a/GUI/src/mocks/healthzStatus.ts b/GUI/src/mocks/healthzStatus.ts new file mode 100644 index 00000000..b3122851 --- /dev/null +++ b/GUI/src/mocks/healthzStatus.ts @@ -0,0 +1,18 @@ +export const healthzStatusData = [ + { + name: 'TIM', + version: '{{{escape_special_chars tim.version}}}', + }, + { + name: 'RUUTER', + version: '{{{escape_special_chars ruuter.version}}}', + }, + { + name: 'DMAPPER', + version: '{{{escape_special_chars dmapper.version}}}', + }, + { + name: 'RESQL', + version: '{{{escape_special_chars resql.version}}}', + }, +]; diff --git a/GUI/src/model/ruuter-response-model.ts b/GUI/src/model/ruuter-response-model.ts new file mode 100644 index 00000000..07cafc1c --- /dev/null +++ b/GUI/src/model/ruuter-response-model.ts @@ -0,0 +1,11 @@ +export interface RuuterResponse { + data: Record | null; + error: string | null; +} + +export interface CustomJwtExtendResponse { + data: { + custom_jwt_extend: string; + }; + error: null; +} diff --git a/GUI/src/modules/attachment/api.ts b/GUI/src/modules/attachment/api.ts new file mode 100644 index 00000000..6e3792d0 --- /dev/null +++ b/GUI/src/modules/attachment/api.ts @@ -0,0 +1,21 @@ +import instance from "services/api"; +import { RUUTER_ENDPOINTS } from "utils/constants"; + +const sendAttachment = async (data:any) => { + const body = { + chatId: data.chatId, + name: data.name, + type: data.type, + size: data.size, + base64: data.base64, + }; + return instance({ + url: RUUTER_ENDPOINTS.SEND_ATTACHMENT, + method: "POST", + data: body, + }).then(({ data }) => { + return data; + }); + }; + + export default sendAttachment; diff --git a/GUI/src/modules/attachment/hooks.ts b/GUI/src/modules/attachment/hooks.ts new file mode 100644 index 00000000..04985b8b --- /dev/null +++ b/GUI/src/modules/attachment/hooks.ts @@ -0,0 +1,25 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import sendAttachment from './api'; + +const useSendAttachment = () => { + const queryClient = useQueryClient(); + queryClient.setMutationDefaults(['send-attachment'], { + mutationFn: (data) => sendAttachment(data), + onMutate: async (variables) => { + const { successCb, errorCb } = variables; + return { successCb, errorCb }; + }, + onSuccess: (result, variables, context) => { + if (context.successCb) { + context.successCb(result); + } + }, + onError: (error, variables, context) => { + if (context.errorCb) { + context.errorCb(error); + } + }, + }); + return useMutation(['send-attachment']); +}; +export default useSendAttachment; diff --git a/GUI/src/pages/CorrectedTexts/index.tsx b/GUI/src/pages/CorrectedTexts/index.tsx new file mode 100644 index 00000000..0a09c219 --- /dev/null +++ b/GUI/src/pages/CorrectedTexts/index.tsx @@ -0,0 +1,234 @@ +import { FC, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { Button, Dialog, FormRadios, FormSelect } from 'components'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { correctedTextEndpoints } from 'utils/endpoints'; +import apiDev from '../../services/api-dev'; +import { InferencePayload } from 'types/correctedTextTypes'; +import { PaginationState } from '@tanstack/react-table'; +import CorrectedTextsTable from 'components/molecules/CorrectedTextTables/CorrectedTextsTables'; +import formats from '../../config/formatsConfig.json'; +import { handleDownload } from 'utils/datasetGroupsUtils'; +import { exportCorrectedTexts } from 'services/datasets'; + +const CorrectedTexts: FC = () => { + const { t } = useTranslation(); + const [enableFetch, setEnableFetch] = useState(true); + + const [filters, setFilters] = useState({ + platform: 'all', + sort: 'asc', + }); + + const [totalPages, setTotalPages] = useState(1); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 5, + }); + const [modalTitle, setModalTitle] = useState(''); + const [modalDiscription, setModalDiscription] = useState(''); + const [modalType, setModalType] = useState(''); + const [exportFormat, setExportFormat] = useState(''); + const [isModalOpen, setIsModalOpen] = useState(false); + const { + data: correctedTextData, + isLoading, + refetch, + } = useQuery({ + queryKey: ['correctedText', pagination.pageIndex], + queryFn: async () => { + const response = await apiDev.get( + correctedTextEndpoints.GET_CORRECTED_WORDS( + pagination.pageIndex, + pagination.pageSize, + filters.platform, + filters.sort + ) + ); + + return ( + (await response?.data?.response?.data) ?? ([] as InferencePayload[]) + ); + }, + onSuccess: (data: InferencePayload[]) => { + setTotalPages(data[0]?.totalPages ?? 1); + if (enableFetch) setEnableFetch(false); + }, + enabled: enableFetch, + }); + + const handleFilterChange = (name: string, value: string) => { + setEnableFetch(false); + setFilters((prevFilters) => ({ + ...prevFilters, + [name]: value, + })); + }; + + const handleExport = () => { + mutate(); + }; + + const { mutate, isLoading: downloadLoading } = useMutation({ + mutationFn: async () => + await exportCorrectedTexts(filters.platform, exportFormat), + onSuccess: async (response) => { + handleDownload(response, exportFormat); + setIsModalOpen(true); + setModalTitle(t('correctedTexts.exportSuccessTitle') ?? ''); + setModalDiscription(t('correctedTexts.exportSuccessDesc') ?? ''); + setModalType('success'); + }, + onError: async () => { + setIsModalOpen(true); + setModalTitle(t('correctedTexts.exportDataUnsucessTitle') ?? ''); + setModalDiscription(t('correctedTexts.exportDataUnsucessDesc') ?? ''); + setModalType('error'); + }, + }); + + return ( +

    +
    +
    {t('correctedTexts.title')}
    + +
    + +
    +
    +
    + + handleFilterChange('platform', selection?.value as string) + } + /> + + handleFilterChange('sort', (selection?.value as string) ?? '') + } + defaultValue={filters.sort} + /> +
    + + +
    + + +
    + + setIsModalOpen(false)} + isOpen={isModalOpen} + title={modalTitle} + footer={ + modalType === 'export' && ( +
    + + +
    + ) + } + > + {modalType === 'export' ? ( +
    +

    + {t('datasetGroups.detailedView.modals.export.fileFormatlabel')} +

    +
    + +
    +
    + ) : ( + <> +

    {modalDiscription}

    + + )} +
    +
    + ); +}; + +export default CorrectedTexts; \ No newline at end of file diff --git a/GUI/src/pages/DataModels/ConfigureDataModel.tsx b/GUI/src/pages/DataModels/ConfigureDataModel.tsx new file mode 100644 index 00000000..7b854c6d --- /dev/null +++ b/GUI/src/pages/DataModels/ConfigureDataModel.tsx @@ -0,0 +1,411 @@ +import { FC, useRef, useState } from 'react'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { Link, useNavigate } from 'react-router-dom'; +import { Button, Card, Dialog } from 'components'; +import { useDialog } from 'hooks/useDialog'; +import BackArrowButton from 'assets/BackArrowButton'; +import { + deleteDataModel, + getMetadata, + retrainDataModel, + updateDataModel, +} from 'services/data-models'; +import DataModelForm from 'components/molecules/DataModelForm'; +import { getChangedAttributes } from 'utils/dataModelsUtils'; +import { Platform, UpdateType } from 'enums/dataModelsEnums'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import { DataModel, UpdatedDataModelPayload } from 'types/dataModels'; +import { dataModelsQueryKeys } from 'utils/queryKeys'; +import { useTranslation } from 'react-i18next'; + +type ConfigureDataModelType = { + id: number; + availableProdModels?: string[]; +}; + +const ConfigureDataModel: FC = ({ + id, + availableProdModels, +}) => { + const { t } = useTranslation(); + const { open, close } = useDialog(); + const navigate = useNavigate(); + const [enabled, setEnabled] = useState(true); + const [initialData, setInitialData] = useState>({ + modelName: '', + dgId: 0, + platform: '', + baseModels: [], + maturity: '', + version: '', + }); + const [dataModel, setDataModel] = useState({ + modelId: 0, + modelName: '', + dgId: 0, + platform: '', + baseModels: [], + maturity: '', + version: '', + }); + const [modalOpen, setModalOpen] = useState(false); + const [modalType, setModalType] = useState(''); + const [modalTitle, setModalTitle] = useState(''); + const [modalDiscription, setModalDiscription] = useState(''); + const modalFunciton = useRef(() => {}); + const { isLoading } = useQuery( + dataModelsQueryKeys.GET_META_DATA(id), + () => getMetadata(id), + { + enabled, + onSuccess: (data) => { + setDataModel({ + modelId: data?.modelId || 0, + modelName: data?.modelName || '', + dgId: data?.connectedDgId || 0, + platform: data?.deploymentEnv || '', + baseModels: data?.baseModels || [], + maturity: data?.maturityLabel || '', + version: `V${data?.majorVersion}.${data?.minorVersion}`, + }); + setInitialData({ + modelName: data?.modelName || '', + dgId: data?.connectedDgId || 0, + platform: data?.deploymentEnv || '', + baseModels: data?.baseModels || [], + maturity: data?.maturityLabel || '', + version: `V${data?.majorVersion}.${data?.minorVersion}`, + }); + setEnabled(false); + }, + } + ); + + const handleDataModelAttributesChange = ( + name: keyof DataModel, + value: any + ) => { + setDataModel((prevDataModel) => ({ + ...prevDataModel, + [name]: value, + })); + }; + + const handleSave = () => { + const payload = getChangedAttributes(initialData, dataModel); + let updateType: string | undefined; + if (payload.dgId) { + updateType = UpdateType.MAJOR; + } else if (payload.baseModels || payload.platform) { + updateType = UpdateType.MINOR; + } else if (payload.maturity) { + updateType = UpdateType.MATURITY_LABEL; + } + + const updatedPayload = { + modelId: dataModel.modelId, + connectedDgId: payload.dgId, + deploymentEnv: payload.platform, + baseModels: payload.baseModels, + maturityLabel: payload.maturity, + updateType: updateType, + }; + + if (updateType) { + if (availableProdModels?.includes(dataModel.platform)) { + openModal( + t('dataModels.createDataModel.replaceDesc'), + t('dataModels.createDataModel.replaceTitle'), + () => updateDataModelMutation.mutate(updatedPayload), + 'replace' + ); + // open({ + // title: t('dataModels.createDataModel.replaceTitle'), + // content: t('dataModels.createDataModel.replaceDesc'), + // footer: ( + //
    + // + // + //
    + // ), + // }); + } else { + updateDataModelMutation.mutate(updatedPayload); + } + } + }; + + const updateDataModelMutation = useMutation({ + mutationFn: (data: UpdatedDataModelPayload) => updateDataModel(data), + onSuccess: async () => { + open({ + title: t('dataModels.configureDataModel.saveChangesTitile'), + content:

    {t('dataModels.configureDataModel.saveChangesDesc')}

    , + footer: ( +
    + {' '} + +
    + ), + }); + }, + onError: () => { + open({ + title: t('dataModels.configureDataModel.updateErrorTitile'), + content:

    {t('dataModels.configureDataModel.updateErrorDesc')}

    , + }); + }, + }); + + const handleDelete = () => { + if ( + dataModel.platform === Platform.JIRA || + dataModel.platform === Platform.OUTLOOK + ) { + open({ + title: t('dataModels.configureDataModel.deleteErrorTitle'), + content:

    {t('dataModels.configureDataModel.deleteErrorDesc')}

    , + footer: ( +
    + +
    + ), + }); + } else { + openModal( + t('dataModels.configureDataModel.deleteConfirmationDesc'), + t('dataModels.configureDataModel.deleteConfirmation'), + () => deleteDataModelMutation.mutate(dataModel.modelId), + 'delete' + ); + // open({ + // title: t('dataModels.configureDataModel.deleteConfirmation'), + // content: ( + //

    {t('dataModels.configureDataModel.deleteConfirmationDesc')}

    + // ), + // footer: ( + //
    + // + // + //
    + // ), + // }); + } + }; + + const deleteDataModelMutation = useMutation({ + mutationFn: (modelId: number) => deleteDataModel(modelId), + onSuccess: async (response) => { + close(); + navigate(0); + }, + onError: () => { + open({ + title: t('dataModels.configureDataModel.deleteModalErrorTitle'), + content: ( +

    {t('dataModels.configureDataModel.deleteModalErrorDesc')}

    + ), + }); + }, + }); + + const retrainDataModelMutation = useMutation({ + mutationFn: (modelId: number) => retrainDataModel(modelId), + onSuccess: async () => { + close(); + navigate(0); + setModalOpen(false) + }, + onError: () => { + open({ + title: t('dataModels.configureDataModel.retrainDataModalErrorTitle'), + content: ( +

    {t('dataModels.configureDataModel.retrainDataModalErrorDesc')}

    + ), + }); + }, + }); + + const openModal = ( + content: string, + title: string, + onConfirm: () => void, + modalType: string + ) => { + setModalOpen(true); + setModalType(modalType); + setModalDiscription(content); + setModalTitle(title); + modalFunciton.current = onConfirm; + }; + return ( +
    +
    +
    + navigate(0)}> + + +
    + {t('dataModels.configureDataModel.title')} +
    +
    + + +
    +
    +

    {t('dataModels.configureDataModel.retrainCard')}

    + +
    +
    +
    + + {isLoading ? ( + + ) : ( + + )} +
    +
    + + + +
    + + setModalOpen(false)} + isOpen={modalOpen} + title={modalTitle} + footer={ +
    + + {modalType === 'retrain' ? ( + + ) : modalType === 'delete' ? ( + + ) : ( + + )} +
    + } + > +
    {modalDiscription}
    +
    +
    + ); +}; + +export default ConfigureDataModel; \ No newline at end of file diff --git a/GUI/src/pages/DataModels/CreateDataModel.tsx b/GUI/src/pages/DataModels/CreateDataModel.tsx new file mode 100644 index 00000000..c5bf4438 --- /dev/null +++ b/GUI/src/pages/DataModels/CreateDataModel.tsx @@ -0,0 +1,236 @@ +import { FC, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'components'; +import { Link, useNavigate } from 'react-router-dom'; +import './DataModels.scss'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { useDialog } from 'hooks/useDialog'; +import BackArrowButton from 'assets/BackArrowButton'; +import { extractedArray, validateDataModel } from 'utils/dataModelsUtils'; +import DataModelForm from 'components/molecules/DataModelForm'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { createDataModel, getDataModelsOverview } from 'services/data-models'; +import { dataModelsQueryKeys, integrationQueryKeys } from 'utils/queryKeys'; +import { getIntegrationStatus } from 'services/integration'; +import { + CreateDataModelPayload, + DataModel, + ErrorsType, +} from 'types/dataModels'; + +const CreateDataModel: FC = () => { + const { t } = useTranslation(); + const { open, close } = useDialog(); + const navigate = useNavigate(); + const [availableProdModels, setAvailableProdModels] = useState([]); + + const [dataModel, setDataModel] = useState>({ + modelName: '', + dgName: '', + dgId: 0, + platform: '', + baseModels: [], + maturity: '', + version: 'V1.0', + }); + + useQuery( + dataModelsQueryKeys.DATA_MODELS_OVERVIEW( + 0, + 'all', + -1, + -1, + 'all', + -1, + 'all', + 'all', + 'asc', + true + ), + () => + getDataModelsOverview( + 1, + 'all', + -1, + -1, + 'all', + -1, + 'all', + 'all', + 'asc', + true + ), + { + onSuccess: (data) => { + setAvailableProdModels(extractedArray(data?.data, 'deploymentEnv')); + }, + } + ); + + const { data: integrationStatus } = useQuery( + integrationQueryKeys.INTEGRATION_STATUS(), + () => getIntegrationStatus() + ); + + const handleDataModelAttributesChange = (name: string, value: string) => { + setDataModel((prevFilters) => ({ + ...prevFilters, + [name]: value, + })); + + setErrors((prevErrors) => { + const updatedErrors = { ...prevErrors }; + + if (name === 'modelName' && value !== '') { + delete updatedErrors.modelName; + } + if (name === 'platform' && value !== '') { + delete updatedErrors.platform; + } + if (name === 'baseModels' && value !== '') { + delete updatedErrors.baseModels; + } + if (name === 'maturity' && value !== '') { + delete updatedErrors.maturity; + } + if (name === 'dgId') { + delete updatedErrors.dgId; + } + + return updatedErrors; + }); + }; + + const [errors, setErrors] = useState({ + modelName: '', + dgName: '', + platform: '', + baseModels: '', + maturity: '', + }); + const validateData = () => { + const validationErrors = validateDataModel(dataModel); + setErrors(validationErrors); + return Object.keys(validationErrors)?.length === 0; + }; + + const handleCreate = () => { + if (validateData()) { + const payload = { + modelName: dataModel.modelName, + dgId: dataModel.dgId, + baseModels: dataModel.baseModels, + deploymentPlatform: dataModel.platform, + maturityLabel: dataModel.maturity, + }; + + if (availableProdModels?.includes(dataModel.platform ?? '')) { + open({ + title: t('dataModels.createDataModel.replaceTitle'), + content: ( +
    + {t('dataModels.createDataModel.replaceDesc')} + {!integrationStatus[ + `${dataModel.platform}_connection_status` + ] && ( +
    + {t('dataModels.createDataModel.replaceWarning', { + platform: dataModel.platform, + })} +
    + )} +
    + ), + footer: ( +
    + + +
    + ), + }); + } else { + createDataModelMutation.mutate(payload); + } + } + }; + const createDataModelMutation = useMutation({ + mutationFn: (data: CreateDataModelPayload) => createDataModel(data), + onSuccess: async () => { + open({ + title: t('dataModels.createDataModel.successTitle'), + content:

    {t('dataModels.createDataModel.successDesc')}

    , + footer: ( +
    + {' '} + +
    + ), + }); + }, + onError: () => { + open({ + title: t('dataModels.createDataModel.errorTitle'), + content:

    {t('dataModels.createDataModel.errorDesc')}

    , + }); + }, + }); + + return ( +
    +
    +
    +
    + + + +
    {t('dataModels.createDataModel.title')}
    +
    +
    + +
    +
    + + +
    +
    + ); +}; + +export default CreateDataModel; \ No newline at end of file diff --git a/GUI/src/pages/DataModels/DataModels.scss b/GUI/src/pages/DataModels/DataModels.scss new file mode 100644 index 00000000..81bd42f7 --- /dev/null +++ b/GUI/src/pages/DataModels/DataModels.scss @@ -0,0 +1,51 @@ +.grey-card { + border: 1px solid #A6A8B1; + border-radius: 5px; + margin-bottom: 10px; + display: flex; + gap: 20px; + align-items: center; + background-color: #F9F9F9; + padding: 25px; +} + +.blue-card { + border-radius: 10px; + margin-bottom: 10px; + display: flex; + gap: 20px; + align-items: center; + background-color: #d7edff; + padding: 25px; + margin-top: 20px; +} + +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 20px; +} + +@keyframes scaleIn { + 0% { + transform: scale(1); + opacity: 0; + } + 100% { + transform: scale(1.02); + opacity: 1; + } +} + +.featured-content { + background-color: hsl(40, 100%, 96%); + border: 2px solid #f39c12; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + border-radius: 10px; + padding: 0px 30px; + margin: 20px auto; + transform: scale(1.02); + transition: transform 0.3s, box-shadow 0.3s; + animation: scaleIn 0.5s ease-out; +} diff --git a/GUI/src/pages/DataModels/index.tsx b/GUI/src/pages/DataModels/index.tsx new file mode 100644 index 00000000..4186c9d4 --- /dev/null +++ b/GUI/src/pages/DataModels/index.tsx @@ -0,0 +1,381 @@ +import { FC, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, FormSelect } from 'components'; +import Pagination from 'components/molecules/Pagination'; +import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router-dom'; +import { formattedArray, parseVersionString } from 'utils/commonUtilts'; +import { getDataModelsOverview, getFilterData } from 'services/data-models'; +import DataModelCard from 'components/molecules/DataModelCard'; +import ConfigureDataModel from './ConfigureDataModel'; +import { customFormattedArray, extractedArray } from 'utils/dataModelsUtils'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { + DataModelResponse, + DataModelsFilters, + FilterData, +} from 'types/dataModels'; +import { dataModelsQueryKeys } from 'utils/queryKeys'; +import NoDataView from 'components/molecules/NoDataView'; + +const DataModels: FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const [pageIndex, setPageIndex] = useState(1); + const [id, setId] = useState(0); + const [enableFetch, setEnableFetch] = useState(true); + const [enableProdModelsFetch, setEnableProdModelsFetch] = + useState(true); + + const [view, setView] = useState<'list' | 'individual'>('list'); + const [availableProdModels, setAvailableProdModels] = useState([]); + + useEffect(() => { + setEnableFetch(true); + }, [view]); + + const [filters, setFilters] = useState({ + modelName: 'all', + version: 'x.x.x', + platform: 'all', + datasetGroup: -1, + trainingStatus: 'all', + maturity: 'all', + sort: 'created_timestamp desc', + }); + + const { data: dataModelsData, isLoading: isModelDataLoading } = useQuery( + dataModelsQueryKeys.DATA_MODELS_OVERVIEW( + pageIndex, + filters.modelName, + parseVersionString(filters.version)?.major, + parseVersionString(filters.version)?.minor, + filters.platform, + filters.datasetGroup, + filters.trainingStatus, + filters.maturity, + filters.sort, + false + ), + () => + getDataModelsOverview( + pageIndex, + filters.modelName, + parseVersionString(filters.version)?.major, + parseVersionString(filters.version)?.minor, + filters.platform, + filters.datasetGroup, + filters.trainingStatus, + filters.maturity, + filters.sort, + false + ), + { + keepPreviousData: true, + enabled: enableFetch, + } + ); + + const { data: prodDataModelsData, isLoading: isProdModelDataLoading } = + useQuery( + dataModelsQueryKeys.DATA_MODELS_OVERVIEW( + 0, + 'all', + -1, + -1, + 'all', + -1, + 'all', + 'all', + 'created_timestamp desc', + true + ), + () => + getDataModelsOverview( + 1, + 'all', + -1, + -1, + 'all', + -1, + 'all', + 'all', + 'created_timestamp desc', + true + ), + { + onSuccess: (data) => { + setAvailableProdModels(extractedArray(data?.data, 'deploymentEnv')); + setEnableProdModelsFetch(false); + }, + keepPreviousData: true, + enabled: enableProdModelsFetch, + } + ); + + const { data: filterData } = useQuery( + dataModelsQueryKeys.DATA_MODEL_FILTERS(), + () => getFilterData() + ); + + const pageCount = dataModelsData?.data[0]?.totalPages || 1; + + const handleFilterChange = ( + name: keyof DataModelsFilters, + value: string | number | undefined | { name: string; id: string } + ) => { + setEnableFetch(false); + setFilters((prevFilters) => ({ + ...prevFilters, + [name]: value, + })); + }; + + return ( +
    + {view === 'list' && ( +
    + {!isModelDataLoading && !isProdModelDataLoading ? ( +
    +
    +
    +
    + {t('dataModels.productionModels')} +
    {' '} +
    + {prodDataModelsData?.data?.length > 0 ? ( +
    + {prodDataModelsData?.data?.map( + (dataset: DataModelResponse) => { + return ( + + ); + } + )} +
    + ) : ( + + )} +
    +
    +
    +
    {t('dataModels.dataModels')}
    + +
    +
    +
    + + handleFilterChange('modelName', selection?.value ?? '') + } + defaultValue={filters?.modelName} + style={{ fontSize: '1rem', width: '200px' }} + /> + + handleFilterChange('version', selection?.value ?? '') + } + defaultValue={filters?.version} + style={{ width: 'auto' }} + /> + + handleFilterChange('platform', selection?.value ?? '') + } + defaultValue={filters?.platform} + style={{ width: 'auto' }} + /> + + handleFilterChange('datasetGroup', selection?.value?.id) + } + defaultValue={filters?.datasetGroup} + style={{ width: '200px' }} + /> + + handleFilterChange('trainingStatus', selection?.value) + } + defaultValue={filters?.trainingStatus} + style={{ width: '150px' }} + /> + + handleFilterChange('maturity', selection?.value) + } + defaultValue={filters?.maturity} + style={{ width: '150px' }} + /> + + handleFilterChange('sort', selection?.value) + } + defaultValue={filters?.sort} + style={{ width: 'auto' }} + /> +
    +
    + + +
    +
    + + {dataModelsData?.data?.length > 0 ? ( +
    + {dataModelsData?.data?.map( + (dataset: DataModelResponse, index: number) => { + return ( + + ); + } + )} +
    + ) : ( + + )} +
    + 1} + canNextPage={pageIndex < 10} + onPageChange={setPageIndex} + /> +
    + ) : ( + + )} +
    + )} + {view === 'individual' && ( + + )} +
    + ); +}; + +export default DataModels; diff --git a/GUI/src/pages/DatasetGroups/CreateDatasetGroup.tsx b/GUI/src/pages/DatasetGroups/CreateDatasetGroup.tsx new file mode 100644 index 00000000..023ab4df --- /dev/null +++ b/GUI/src/pages/DatasetGroups/CreateDatasetGroup.tsx @@ -0,0 +1,183 @@ +import { FC, useCallback, useState } from 'react'; +import './DatasetGroups.scss'; +import { useTranslation } from 'react-i18next'; +import { Button, Card, FormInput } from 'components'; +import { v4 as uuidv4 } from 'uuid'; +import ClassHierarchy from 'components/molecules/ClassHeirarchy'; +import { + isValidationRulesSatisfied, + transformClassHierarchy, + transformValidationRules, + validateClassHierarchy, + validateValidationRules, +} from 'utils/datasetGroupsUtils'; +import { DatasetGroup, TreeNode, ValidationRule } from 'types/datasetGroups'; +import { useNavigate } from 'react-router-dom'; +import ValidationCriteriaCardsView from 'components/molecules/ValidationCriteria/CardsView'; +import { useMutation } from '@tanstack/react-query'; +import { createDatasetGroup } from 'services/datasets'; +import { useDialog } from 'hooks/useDialog'; +import { + CreateDatasetGroupModals, + ValidationErrorTypes, +} from 'enums/datasetEnums'; +import CreateDatasetGroupModalController from 'components/molecules/CreateDatasetGroupModals/CreateDatasetGroupModal'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; + +const CreateDatasetGroup: FC = () => { + const { t } = useTranslation(); + const { open } = useDialog(); + const navigate = useNavigate(); + + const initialValidationRules = [ + { id: uuidv4(), fieldName: '', dataType: '', isDataClass: false }, + { id: uuidv4(), fieldName: '', dataType: '', isDataClass: true }, + ]; + + const initialClass = [ + { id: uuidv4(), fieldName: '', level: 0, children: [] }, + { id: uuidv4(), fieldName: '', level: 0, children: [] }, + ]; + + // Properly destructure useState calls into value and setter pairs + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalType, setModalType] = useState( + CreateDatasetGroupModals.NULL + ); + const [datasetName, setDatasetName] = useState(''); + const [datasetNameError, setDatasetNameError] = useState(false); + const [validationRules, setValidationRules] = useState( + initialValidationRules + ); + const [validationRuleError, setValidationRuleError] = useState(false); + const [nodes, setNodes] = useState(initialClass); + const [nodesError, setNodesError] = useState(false); + const [validationErrorType, setValidationErrorType] = + useState(ValidationErrorTypes.NULL); + + const validateData = useCallback(() => { + + setNodesError(validateClassHierarchy(nodes)); + setDatasetNameError(!datasetName); + setValidationRuleError(validateValidationRules(validationRules)); + if ( + !validateClassHierarchy(nodes) && + datasetName && + !validateValidationRules(validationRules) && !nodesError && + !validationRuleError + ) { + if (!isValidationRulesSatisfied(validationRules)) { + setIsModalOpen(true); + setModalType(CreateDatasetGroupModals.VALIDATION_ERROR); + setValidationErrorType(ValidationErrorTypes.VALIDATION_CRITERIA); + } else if (nodes.length < 2) { + setIsModalOpen(true); + setModalType(CreateDatasetGroupModals.VALIDATION_ERROR); + setValidationErrorType(ValidationErrorTypes.CLASS_HIERARCHY); + } else { + const payload: DatasetGroup = { + groupName: datasetName, + validationCriteria: { ...transformValidationRules(validationRules) }, + ...transformClassHierarchy(nodes), + }; + createDatasetGroupMutation.mutate(payload); + } + } + }, [datasetName, nodes, validationRules]); + + const createDatasetGroupMutation = useMutation({ + mutationFn: (data: DatasetGroup) => createDatasetGroup(data), + onSuccess: async () => { + setIsModalOpen(true); + setModalType(CreateDatasetGroupModals.SUCCESS); + }, + onError: () => { + open({ + title: t('datasetGroups.modals.createDatasetUnsuccessTitle'), + content:

    {t('datasetGroups.modals.errorDesc')}

    , + }); + }, + }); + + return ( +
    +
    +
    +
    +
    + {t('datasetGroups.createDataset.title')} +
    +
    +
    + +
    + setDatasetName(e.target.value)} + error={ + !datasetName && datasetNameError + ? t( + 'datasetGroups.createDataset.datasetInputPlaceholder' + ) ?? '' + : '' + } + /> +
    +
    + + + +
    Class Hierarchy
    + + {' '} + + +
    + + + +
    + + +
    +
    +
    +
    + ); +}; + +export default CreateDatasetGroup; diff --git a/GUI/src/pages/DatasetGroups/DatasetGroups.scss b/GUI/src/pages/DatasetGroups/DatasetGroups.scss new file mode 100644 index 00000000..bcaa225d --- /dev/null +++ b/GUI/src/pages/DatasetGroups/DatasetGroups.scss @@ -0,0 +1,80 @@ +@import 'src/styles/tools/color'; +@import 'src/styles/tools/spacing'; + + +.search-panel { + background-color: #F9F9F9; + border-radius: 10px; + border: solid 1px get-color(black-coral-1); + display: flex; + align-items: center; + gap: 10px; + padding: 15px; +} + +.dataset-group-card { + width: 100%; + border: 1px solid #ccc; + border-radius: 8px; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 15px; + box-sizing: border-box; +} + +.grid-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + width: 100%; +} + +.banner{ + background-color: #fff3f3; + border: 1px solid #cf0000; + border-radius: 5px; + margin-bottom: 10px; + padding: 20px 200px; + text-align: center; + color: #cf0000; +} + +.footer-button-group { + display: flex; + align-items: flex-end; + justify-content: flex-end; + gap: 10px; + margin-top: 25px; +} + +.skeleton-container { + background-color: #fff; + padding: 20px; + margin-top: 20px; +} + +.container { + min-height: 100vh; + padding-bottom: 100px; + box-sizing: border-box; +} + +.button-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + margin-top: 25px; + padding: 20px 50px 20px 0px; + background-color: #fff; + z-index: 10; + margin-right: 15px; +} + +.content-wrapper { + padding-bottom: 60px; +} \ No newline at end of file diff --git a/GUI/src/pages/DatasetGroups/ViewDatasetGroup.tsx b/GUI/src/pages/DatasetGroups/ViewDatasetGroup.tsx new file mode 100644 index 00000000..4177f58a --- /dev/null +++ b/GUI/src/pages/DatasetGroups/ViewDatasetGroup.tsx @@ -0,0 +1,538 @@ +import { FC, PropsWithChildren, useEffect, useRef, useState } from 'react'; +import './DatasetGroups.scss'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'components'; +import { PaginationState } from '@tanstack/react-table'; +import { + DatasetGroup, + SelectedRowPayload, + ImportDataset, + MinorPayLoad, + PatchPayLoad, + TreeNode, + ValidationRule, +} from 'types/datasetGroups'; +import { useNavigate } from 'react-router-dom'; +import { + deleteDatasetGroup, + exportDataset, + getDatasets, + getMetadata, + importDataset, + majorUpdate, + minorUpdate, + patchUpdate, +} from 'services/datasets'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useDialog } from 'hooks/useDialog'; +import { + handleDownload, + isMajorUpdate, + reverseTransformClassHierarchy, + transformClassHierarchy, + transformObjectToArray, + transformValidationRules, + validateClassHierarchy, + validateValidationRules, +} from 'utils/datasetGroupsUtils'; +import { datasetQueryKeys } from 'utils/queryKeys'; +import { + DatasetViewEnum, + UpdatePriority, + ViewDatasetGroupModalContexts, +} from 'enums/datasetEnums'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import ViewDatasetGroupModalController from 'components/molecules/ViewDatasetGroupModalController/ViewDatasetGroupModalController'; +import { FileUploadHandle } from 'components/FileUpload'; +import DatasetDetailedViewTable from 'components/molecules/DatasetDetailedViewTable/DatasetDetailedViewTable'; +import ValidationAndHierarchyCards from 'components/molecules/ValidationAndHierarchyCards/ValidationAndHierarchyCards'; + +type Props = { + dgId: number; + setView: React.Dispatch>; +}; + +const ViewDatasetGroup: FC> = ({ dgId, setView }) => { + const { t } = useTranslation(); + const { open, close } = useDialog(); + const queryClient = useQueryClient(); + + const [validationRuleError, setValidationRuleError] = useState(false); + const [nodesError, setNodesError] = useState(false); + const [importFormat, setImportFormat] = useState(''); + const [exportFormat, setExportFormat] = useState(''); + const [importStatus, setImportStatus] = useState(''); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const fileUploadRef = useRef(null); + const [fetchEnabled, setFetchEnabled] = useState(true); + const [file, setFile] = useState(); + const [selectedRow, setSelectedRow] = useState(); + const [bannerMessage, setBannerMessage] = useState(''); + const [minorPayload, setMinorPayload] = useState(); + const [patchPayload, setPatchPayload] = useState(); + const [deletedDataRows, setDeletedDataRows] = useState([]); + const [updatePriority, setUpdatePriority] = useState( + UpdatePriority.NULL + ); + const [confirmationTitle, setConfirmationTitle] = useState(''); + const [confirmationDesc, setConfirmationDesc] = useState(''); + const [openedModalContext, setOpenedModalContext] = + useState(ViewDatasetGroupModalContexts.NULL); + const [isModalOpen, setIsModalOpen] = useState(false); + const navigate = useNavigate(); + const changeConfirmationFunction = useRef(() => {}); + const [confirmationFLow, setConfirmationFlow] = useState(''); + useEffect(() => { + setFetchEnabled(false); + }, []); + + useEffect(() => { + if (updatePriority === UpdatePriority.MAJOR) + setBannerMessage(t('datasetGroups.detailedView.majorUpdateBanner') ?? ''); + else if (updatePriority === UpdatePriority.MINOR) + setBannerMessage(t('datasetGroups.detailedView.minorUpdateBanner') ?? ''); + else if (updatePriority === UpdatePriority.PATCH) + setBannerMessage(t('datasetGroups.detailedView.patchUpdateBanner') ?? ''); + else setBannerMessage(''); + }, [updatePriority]); + + const { data: datasets, isLoading } = useQuery( + datasetQueryKeys.GET_DATA_SETS(dgId, pagination), + () => getDatasets(pagination, dgId), + { + keepPreviousData: true, + } + ); + + const { data: metadata, isLoading: isMetadataLoading } = useQuery( + datasetQueryKeys.GET_META_DATA(dgId), + () => getMetadata(dgId), + { enabled: fetchEnabled } + ); + + const [updatedDataset, setUpdatedDataset] = useState(datasets?.dataPayload); + + useEffect(() => { + setUpdatedDataset(datasets?.dataPayload); + }, [datasets]); + + const [nodes, setNodes] = useState( + reverseTransformClassHierarchy(metadata?.[0]?.classHierarchy) + ); + const [validationRules, setValidationRules] = useState< + ValidationRule[] | undefined + >(transformObjectToArray(metadata?.[0]?.validationCriteria?.validationRules)); + + useEffect(() => { + setNodes(reverseTransformClassHierarchy(metadata?.[0]?.classHierarchy)); + }, [metadata]); + + useEffect(() => { + setValidationRules( + transformObjectToArray(metadata?.[0]?.validationCriteria?.validationRules) + ); + }, [metadata]); + + useEffect(() => { + if ( + metadata && + isMajorUpdate( + { + validationRules: metadata?.[0]?.validationCriteria?.validationRules, + classHierarchy: metadata?.[0]?.classHierarchy, + }, + { + validationRules: + transformValidationRules(validationRules)?.validationRules, + ...transformClassHierarchy(nodes), + } + ) + ) { + setUpdatePriority(UpdatePriority.MAJOR); + } else { + setUpdatePriority(UpdatePriority.NULL); + } + }, [validationRules, nodes]); + + const deleteRow = (dataRow: SelectedRowPayload) => { + setDeletedDataRows((prevDeletedDataRows) => [ + ...prevDeletedDataRows, + dataRow?.rowId, + ]); + const payload = updatedDataset?.filter( + (row) => row.rowId !== selectedRow?.rowId + ); + setUpdatedDataset(payload); + + const updatedPayload = { + dgId, + updateDataPayload: { + deletedDataRows: [...deletedDataRows, dataRow?.rowId], + editedData: payload, + }, + }; + setPatchPayload(updatedPayload); + handleCloseModals(); + if ( + updatePriority !== UpdatePriority.MAJOR && + updatePriority !== UpdatePriority.MINOR + ) + setUpdatePriority(UpdatePriority.PATCH); + }; + + const patchDataUpdate = (dataRow: SelectedRowPayload) => { + const payload = updatedDataset?.map((row) => + row.rowId === selectedRow?.rowId ? dataRow : row + ); + setUpdatedDataset(payload); + + const updatedPayload = { + dgId, + updateDataPayload: { + deletedDataRows, + editedData: payload, + }, + }; + setPatchPayload(updatedPayload); + handleCloseModals(); + if ( + updatePriority !== UpdatePriority.MAJOR && + updatePriority !== UpdatePriority.MINOR + ) + setUpdatePriority(UpdatePriority.PATCH); + }; + + const patchUpdateMutation = useMutation({ + mutationFn: (data: PatchPayLoad) => patchUpdate(data), + onSuccess: async () => { + await queryClient.invalidateQueries(datasetQueryKeys.GET_DATA_SETS()); + close(); + setView(DatasetViewEnum.LIST); + }, + onError: () => { + handleCloseModals(); + open({ + title: t('datasetGroups.detailedView.patchDataUnsuccessfulTitle') ?? '', + content: ( +

    + {t('datasetGroups.detailedView.patchDataUnsuccessfulDesc') ?? ''} +

    + ), + }); + }, + }); + + const handleExport = () => { + exportDataMutation.mutate({ dgId, exportType: exportFormat }); + }; + + const exportDataMutation = useMutation({ + mutationFn: (data: { dgId: number; exportType: string }) => + exportDataset(data?.dgId, data?.exportType), + onSuccess: async (response) => { + handleDownload(response, exportFormat); + open({ + title: t('datasetGroups.detailedView.exportDataSuccessTitle') ?? '', + content: ( +

    {t('datasetGroups.detailedView.exportDataSuccessDesc') ?? ''}

    + ), + }); + handleCloseModals(); + }, + onError: () => { + open({ + title: t('datasetGroups.detailedView.exportDataUnsucessTitle') ?? '', + content: ( +

    {t('datasetGroups.detailedView.exportDataUnsucessDesc') ?? ''}

    + ), + }); + }, + }); + + const handleFileSelect = (file: File | undefined) => { + setFile(file); + }; + + const handleImport = () => { + setImportStatus('STARTED'); + const payload = { + dgId, + dataFile: file as File, + }; + + importDataMutation.mutate(payload); + }; + + const importDataMutation = useMutation({ + mutationFn: (data: ImportDataset) => + importDataset(data?.dataFile, data?.dgId), + onSuccess: async (response) => { + setMinorPayload({ + dgId, + s3FilePath: response?.saved_file_path, + }); + if (updatePriority !== UpdatePriority.MAJOR) + setUpdatePriority(UpdatePriority.MINOR); + + handleCloseModals(); + }, + onError: () => { + handleCloseModals(); + setImportStatus('ABORTED'); + open({ + title: t('datasetGroups.detailedView.ImportDataUnsucessTitle') ?? '', + content: ( +

    {t('datasetGroups.detailedView.importDataUnsucessDesc') ?? ''}

    + ), + }); + }, + }); + + const minorUpdateMutation = useMutation({ + mutationFn: (data: MinorPayLoad) => minorUpdate(data), + onSuccess: async () => { + open({ + title: t('datasetGroups.detailedView.validationInitiatedTitle') ?? '', + content: ( +

    {t('datasetGroups.detailedView.validationInitiatedDesc') ?? ''}

    + ), + footer: ( +
    + + +
    + ), + }); + setIsModalOpen(false); + setOpenedModalContext(ViewDatasetGroupModalContexts.NULL); + }, + onError: () => { + open({ + title: t('datasetGroups.detailedView.ImportDataUnsucessTitle') ?? '', + content: ( +

    {t('datasetGroups.detailedView.importDataUnsucessDesc') ?? ''}

    + ), + }); + }, + }); + + const handleMajorUpdate = () => { + const payload: DatasetGroup = { + dgId, + validationCriteria: { ...transformValidationRules(validationRules) }, + ...transformClassHierarchy(nodes), + }; + majorUpdateDatasetGroupMutation.mutate(payload); + }; + + const openConfirmationModal = ( + content: string, + title: string, + onConfirm: () => void, + flow: string + ) => { + setOpenedModalContext(ViewDatasetGroupModalContexts.CONFIRMATION_MODAL); + setIsModalOpen(true); + setConfirmationDesc(content); + setConfirmationTitle(title); + changeConfirmationFunction.current = onConfirm; + setConfirmationFlow(flow); + }; + + const datasetGroupUpdate = () => { + const classHierarchyError = validateClassHierarchy(nodes) || nodesError; + const validationRulesError = validateValidationRules(validationRules); + + setNodesError(classHierarchyError); + setValidationRuleError(validationRulesError); + + const isMajorUpdateDetected = isMajorUpdate( + { + validationRules: metadata?.[0]?.validationCriteria?.validationRules, + classHierarchy: metadata?.[0]?.classHierarchy, + }, + { + validationRules: + transformValidationRules(validationRules)?.validationRules, + ...transformClassHierarchy(nodes), + } + ); + + if (classHierarchyError || validationRulesError || nodesError) { + return; + } + + if (isMajorUpdateDetected) { + openConfirmationModal( + t('datasetGroups.detailedView.confirmMajorUpdatesDesc'), + t('datasetGroups.detailedView.confirmMajorUpdatesTitle'), + handleMajorUpdate, + 'update' + ); + } else if (minorPayload) { + openConfirmationModal( + t('datasetGroups.detailedView.confirmMinorUpdatesDesc'), + t('datasetGroups.detailedView.confirmMinorUpdatesTitle'), + () => minorUpdateMutation.mutate(minorPayload), + 'update' + ); + } else if (patchPayload) { + openConfirmationModal( + t('datasetGroups.detailedView.confirmPatchUpdatesDesc'), + t('datasetGroups.detailedView.confirmPatchUpdatesTitle'), + () => patchUpdateMutation.mutate(patchPayload), + 'update' + ); + } + }; + + const majorUpdateDatasetGroupMutation = useMutation({ + mutationFn: (data: DatasetGroup) => majorUpdate(data), + onSuccess: async () => { + await queryClient.invalidateQueries(datasetQueryKeys.DATASET_OVERVIEW()); + setView(DatasetViewEnum.LIST); + close(); + }, + onError: () => { + open({ + title: t('datasetGroups.detailedView.modals.edit.error'), + content:

    {t('datasetGroups.modals.delete.errorDesc')}

    , + }); + }, + }); + + const handleDeleteDataset = () => { + deleteDatasetMutation.mutate(dgId); + }; + + const deleteDatasetMutation = useMutation({ + mutationFn: (dgId: number) => deleteDatasetGroup(dgId), + onSuccess: async () => { + navigate(0); + close(); + }, + onError: () => { + open({ + title: t('datasetGroups.detailedView.modals.delete.error'), + content:

    {t('datasetGroups.modals.delete.errorDesc')}

    , + }); + }, + }); + + const handleOpenModals = (context: ViewDatasetGroupModalContexts) => { + setIsModalOpen(true); + setOpenedModalContext(context); + }; + + const handleCloseModals = () => { + setIsModalOpen(false); + setOpenedModalContext(ViewDatasetGroupModalContexts.NULL); + }; + + return ( +
    +
    +
    + + + + +
    + + +
    +
    +
    + changeConfirmationFunction.current()} + majorUpdateLoading={majorUpdateDatasetGroupMutation.isLoading} + patchUpdateLoading={patchUpdateMutation.isLoading} + minorUpdateLoading={minorUpdateMutation.isLoading} + confirmationFLow={confirmationFLow} + deleteDatasetMutationLoading={deleteDatasetMutation.isLoading} + /> +
    + ); +}; + +export default ViewDatasetGroup; \ No newline at end of file diff --git a/GUI/src/pages/DatasetGroups/index.tsx b/GUI/src/pages/DatasetGroups/index.tsx new file mode 100644 index 00000000..d28da457 --- /dev/null +++ b/GUI/src/pages/DatasetGroups/index.tsx @@ -0,0 +1,229 @@ +import { FC, useEffect, useState } from 'react'; +import './DatasetGroups.scss'; +import { useTranslation } from 'react-i18next'; +import { Button, FormSelect } from 'components'; +import DatasetGroupCard from 'components/molecules/DatasetGroupCard'; +import Pagination from 'components/molecules/Pagination'; +import { getDatasetsOverview, getFilterData } from 'services/datasets'; +import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router-dom'; +import { formattedArray, parseVersionString } from 'utils/commonUtilts'; +import { FilterData, SingleDatasetType } from 'types/datasetGroups'; +import ViewDatasetGroup from './ViewDatasetGroup'; +import { datasetQueryKeys } from 'utils/queryKeys'; +import { DatasetViewEnum } from 'enums/datasetEnums'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import NoDataView from 'components/molecules/NoDataView'; + +const DatasetGroups: FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const [pageIndex, setPageIndex] = useState(1); + const [id, setId] = useState(0); + const [enableFetch, setEnableFetch] = useState(true); + const [view, setView] = useState(DatasetViewEnum.LIST); + + const [filters, setFilters] = useState({ + datasetGroupName: 'all', + version: 'x.x.x', + validationStatus: 'all', + sort: 'created_timestamp desc', + }); + + useEffect(() => { + setEnableFetch(true); + }, [view]); + + const { data: datasetGroupsData, isLoading } = useQuery( + datasetQueryKeys.DATASET_OVERVIEW( + pageIndex, + filters.datasetGroupName, + parseVersionString(filters?.version)?.major, + parseVersionString(filters?.version)?.minor, + parseVersionString(filters?.version)?.patch, + filters.validationStatus, + filters.sort + ), + () => + getDatasetsOverview( + pageIndex, + filters.datasetGroupName, + parseVersionString(filters?.version)?.major, + parseVersionString(filters?.version)?.minor, + parseVersionString(filters?.version)?.patch, + filters.validationStatus, + filters.sort + ), + { + keepPreviousData: true, + enabled: enableFetch, + } + ); + + const { data: filterData } = useQuery( + datasetQueryKeys.DATASET_FILTERS(), + () => getFilterData() + ); + + const pageCount = datasetGroupsData?.response?.data?.[0]?.totalPages || 1; + + const handleFilterChange = (name: string, value: string) => { + setEnableFetch(false); + setFilters((prevFilters) => ({ + ...prevFilters, + [name]: value, + })); + }; + + return ( +
    + {view === DatasetViewEnum.LIST && ( +
    +
    +
    {t('datasetGroups.title')}
    + +
    +
    +
    + + handleFilterChange( + 'datasetGroupName', + (selection?.value as string) ?? '' + ) + } + defaultValue={filters.datasetGroupName} + /> + + handleFilterChange( + 'version', + (selection?.value as string) ?? '' + ) + } + defaultValue={filters.version} + /> + + handleFilterChange( + 'validationStatus', + (selection?.value as string) ?? '' + ) + } + defaultValue={filters.validationStatus} + /> + + handleFilterChange('sort', (selection?.value as string) ?? '') + } + defaultValue={filters.sort} + /> + + +
    + {isLoading && ( +
    + +
    + )} + {datasetGroupsData?.response?.data?.length > 0 ? ( +
    + {datasetGroupsData?.response?.data?.map( + (dataset: SingleDatasetType, index: number) => { + return ( + + ); + } + )} +
    + ) : ( + + )} + + 1} + canNextPage={pageIndex < pageCount} + onPageChange={setPageIndex} + /> +
    +
    + )} + {view === DatasetViewEnum.INDIVIDUAL && ( + + )} +
    + ); +}; + +export default DatasetGroups; diff --git a/GUI/src/pages/Integrations/Integrations.scss b/GUI/src/pages/Integrations/Integrations.scss new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/GUI/src/pages/Integrations/Integrations.scss @@ -0,0 +1,2 @@ + + diff --git a/GUI/src/pages/Integrations/index.tsx b/GUI/src/pages/Integrations/index.tsx new file mode 100644 index 00000000..4ec4d965 --- /dev/null +++ b/GUI/src/pages/Integrations/index.tsx @@ -0,0 +1,44 @@ +import { FC } from 'react'; +import './Integrations.scss'; +import { useTranslation } from 'react-i18next'; +import IntegrationCard from 'components/molecules/IntegrationCard'; +import Outlook from 'assets/Outlook'; +import Jira from 'assets/Jira'; +import { useQuery } from '@tanstack/react-query'; +import { getIntegrationStatus } from 'services/integration'; +import { integrationQueryKeys } from 'utils/queryKeys'; + +const Integrations: FC = () => { + const { t } = useTranslation(); + + const { data: integrationStatus } = useQuery( + integrationQueryKeys.INTEGRATION_STATUS(), + () => getIntegrationStatus() + ); + + return ( +
    +
    +
    +
    {t('integration.title')}
    +
    +
    + } + channel={t('integration.jira') ?? ''} + channelDescription={t('integration.jiraDesc') ?? ''} + isActive={integrationStatus?.jira_connection_status} + /> + } + channel={t('integration.outlook') ?? ''} + channelDescription={t('integration.outlookDesc') ?? ''} + isActive={integrationStatus?.outlook_connection_status} + /> +
    +
    +
    + ); +}; + +export default Integrations; diff --git a/GUI/src/pages/LoadingScreen/LoadingScreen.scss b/GUI/src/pages/LoadingScreen/LoadingScreen.scss new file mode 100644 index 00000000..c45e573a --- /dev/null +++ b/GUI/src/pages/LoadingScreen/LoadingScreen.scss @@ -0,0 +1,20 @@ +/* Loader container */ +.loader { + position: fixed; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + border: 8px solid #f3f3f3; /* Light grey */ + border-top: 8px solid #3498db; /* Blue */ + border-radius: 50%; + width: 60px; + height: 60px; + animation: spin 1.5s linear infinite; + } + + /* Spin animation */ + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + \ No newline at end of file diff --git a/GUI/src/pages/LoadingScreen/LoadingScreen.tsx b/GUI/src/pages/LoadingScreen/LoadingScreen.tsx new file mode 100644 index 00000000..3f8add92 --- /dev/null +++ b/GUI/src/pages/LoadingScreen/LoadingScreen.tsx @@ -0,0 +1,12 @@ +import { FC } from 'react'; +import './LoadingScreen.scss' + +const LoadingScreen: FC = () => { + return ( +
    +
    +
    + ); +}; + +export default LoadingScreen; \ No newline at end of file diff --git a/GUI/src/pages/LoadingSxreen/LoadingScreen.tsx b/GUI/src/pages/LoadingSxreen/LoadingScreen.tsx new file mode 100644 index 00000000..679ec206 --- /dev/null +++ b/GUI/src/pages/LoadingSxreen/LoadingScreen.tsx @@ -0,0 +1,11 @@ +import { FC } from 'react'; + +const LoadingScreen: FC = () => { + return ( +
    +

    Loading...

    +
    + ); +}; + +export default LoadingScreen; \ No newline at end of file diff --git a/GUI/src/pages/StopWords/index.tsx b/GUI/src/pages/StopWords/index.tsx new file mode 100644 index 00000000..fc7c20cc --- /dev/null +++ b/GUI/src/pages/StopWords/index.tsx @@ -0,0 +1,248 @@ +import { FC, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, Card, Dialog, FormInput, FormRadios } from 'components'; +import LabelChip from 'components/LabelChip'; +import FileUpload, { FileUploadHandle } from 'components/FileUpload'; +import { useForm } from 'react-hook-form'; +import importOptions from '../../config/importOptionsConfig.json'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + addStopWord, + deleteStopWord, + deleteStopWords, + getStopWords, + importStopWords, +} from 'services/datasets'; +import { stopWordsQueryKeys } from 'utils/queryKeys'; +import { ButtonAppearanceTypes } from 'enums/commonEnums'; +import { StopWordImportOptions } from 'enums/datasetEnums'; +import { useDialog } from 'hooks/useDialog'; +import { StopWordsImportResponse } from 'types/datasetGroups'; +import useOptionLists from 'hooks/useOptionLists'; + +const StopWords: FC = () => { + const { t } = useTranslation(); + const queryClient = useQueryClient(); + + const { open } = useDialog(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [importOption, setImportOption] = useState(''); + const [file, setFile] = useState(); + const fileUploadRef = useRef(null); + const { importOptionsConfigs } = useOptionLists(); + const { register, setValue, watch } = useForm({ + defaultValues: { + stopWord: '', + }, + }); + + const { data: stopWordsData, refetch: stopWordRefetch } = useQuery( + stopWordsQueryKeys.GET_ALL_STOP_WORDS(), + () => getStopWords() + ); + + const watchedStopWord = watch('stopWord'); + + const removeStopWord = (wordToRemove: string) => { + deleteStopWordMutation.mutate({ stopWords: [wordToRemove] }); + }; + + const addStopWordMutation = useMutation({ + mutationFn: (data: { stopWords: string[] }) => addStopWord(data), + onSuccess: async () => { + await queryClient.invalidateQueries( + stopWordsQueryKeys.GET_ALL_STOP_WORDS() + ); + }, + onError: () => {}, + }); + + const deleteStopWordMutation = useMutation({ + mutationFn: (data: { stopWords: string[] }) => deleteStopWord(data), + onSuccess: async () => { + await queryClient.invalidateQueries( + stopWordsQueryKeys.GET_ALL_STOP_WORDS() + ); + }, + onError: () => {}, + }); + + const importMutationSuccessFunc = async ( + response: StopWordsImportResponse + ) => { + setIsModalOpen(false); + if (response.response?.operationSuccessful) { + open({ + title: t('stopWords.importModal.successTitle') ?? '', + content:

    {t('stopWords.importModal.successDesc') ?? ''}

    , + }); + setFile(null); + await queryClient.invalidateQueries( + stopWordsQueryKeys.GET_ALL_STOP_WORDS() + ); + + if (importOption === StopWordImportOptions.DELETE) { + stopWordRefetch(); + } + } else { + open({ + title: t('stopWords.importModal.unsuccessTitle') ?? '', + content:

    {t('stopWords.importModal.unsuccessDesc') ?? ''}

    , + }); + } + setImportOption(''); + }; + + const importStopWordsMutation = useMutation({ + mutationFn: (file: File) => importStopWords(file), + onSuccess: async (response) => { + importMutationSuccessFunc(response); + }, + onError: async () => { + setIsModalOpen(true); + open({ + title: t('stopWords.importModal.unsuccessTitle') ?? '', + content:

    {t('stopWords.importModal.unsuccessDesc') ?? ''}

    , + }); + }, + }); + + const deleteStopWordsMutation = useMutation({ + mutationFn: (file: File) => deleteStopWords(file), + onSuccess: async (response) => { + importMutationSuccessFunc(response); + }, + onError: async () => { + setIsModalOpen(true); + open({ + title: t('stopWords.importModal.unsuccessTitle') ?? '', + content:

    {t('stopWords.importModal.unsuccessDesc') ?? ''}

    , + }); + }, + }); + + const handleFileSelect = (file: File | undefined) => { + setFile(file); + }; + + const handleStopWordFileOperations = () => { + if ( + importOption === StopWordImportOptions.ADD && + file && + file !== undefined + ) { + importStopWordsMutation.mutate(file); + } else if ( + importOption === StopWordImportOptions.DELETE && + file && + file !== undefined + ) + deleteStopWordsMutation.mutate(file); + + setIsModalOpen(false); + open({ + title: t('stopWords.importModal.inprogressTitle') ?? '', + content:

    {t('stopWords.importModal.inprogressDesc') ?? ''}

    , + }); + }; + + return ( +
    +
    +
    +
    {t('stopWords.title') ?? ''}
    + +
    + + {stopWordsData?.map((word: string) => ( + removeStopWord(word)} + /> + ))} +
    + + +
    +
    + {isModalOpen && ( + { + setIsModalOpen(false); + setImportOption(''); + }} + title={t('stopWords.importModal.title') ?? ''} + footer={ +
    + + +
    + } + > +
    +

    {t('stopWords.importModal.selectionLabel') ?? ''}

    + +
    +

    {t('stopWords.importModal.attachements') ?? ''}

    + +
    +
    + )} +
    +
    + ); +}; + +export default StopWords; diff --git a/GUI/src/pages/TestModel/index.tsx b/GUI/src/pages/TestModel/index.tsx new file mode 100644 index 00000000..59588215 --- /dev/null +++ b/GUI/src/pages/TestModel/index.tsx @@ -0,0 +1,145 @@ +import { useMutation, useQuery } from '@tanstack/react-query'; +import { Button, FormSelect, FormTextarea } from 'components'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import { FC, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import apiDev from 'services/api-dev'; +import { + ClassifyTestModalPayloadType, + ClassifyTestModalResponseType, + TestModalDropdownSelectionType, + TestModelType, +} from 'types/testModelTypes'; +import { formatClassHierarchyArray } from 'utils/commonUtilts'; +import { testModelsEndpoints } from 'utils/endpoints'; +import { testModelsQueryKeys } from 'utils/queryKeys'; +import { formatPredictions } from 'utils/testModelUtil'; +import './testModelStyles.scss'; + +const TestModel: FC = () => { + const { t } = useTranslation(); + + const [modelOptions, setModelOptions] = useState< + TestModalDropdownSelectionType[] + >([]); + + const [testModel, setTestModel] = useState({ + modelId: null, + text: '', + }); + const { isLoading } = useQuery({ + queryKey: testModelsQueryKeys.GET_TEST_MODELS(), + queryFn: async () => { + const response = await apiDev.get(testModelsEndpoints.GET_MODELS()); + return response?.data?.response?.data ?? ([] as TestModelType[]); + }, + onSuccess: (data: TestModelType[]) => { + if (data && data.length > 0) { + setModelOptions( + data?.map((options) => ({ + label: options.modelName, + value: options.modelId, + })) + ); + } + }, + }); + + const { + data: classifyData, + isLoading: classifyLoading, + mutate, + } = useMutation({ + mutationFn: async (data: ClassifyTestModalPayloadType) => { + const response = await apiDev.post( + testModelsEndpoints.CLASSIFY_TEST_MODELS(), + data + ); + return response?.data?.response?.data as ClassifyTestModalResponseType; + }, + }); + + const handleChange = (key: string, value: string | number) => { + setTestModel((prev) => ({ + ...prev, + [key]: value, + })); + }; + + return ( +
    + {isLoading ? ( + + ) : ( +
    +
    +
    {t('testModels.title')}
    +
    +
    + { + handleChange('modelId', selection?.value as string); + }} + /> +
    + +
    +

    {t('testModels.classifyTextLabel')}

    + handleChange('text', e.target.value)} + showMaxLength={true} + /> +
    +
    + +
    + + {!classifyLoading && classifyData && ( +
    +
    +
    + {t('testModels.predictedHierarchy')} +

    + {classifyData?.predictedClasses && + formatClassHierarchyArray(classifyData?.predictedClasses)} +

    +
    +
    +
    +
    + {t('testModels.averageConfidence')} +

    {classifyData?.averageConfidence ?? ''}

    +
    +
    +
    +
    + {t('testModels.classProbabilities')} +
      + {formatPredictions(classifyData)?.map((prediction, index) => { + return
    • {prediction}
    • ; + })} +
    +
    +
    +
    + )} +
    + )} +
    + ); +}; + +export default TestModel; \ No newline at end of file diff --git a/GUI/src/pages/TestModel/testModelStyles.scss b/GUI/src/pages/TestModel/testModelStyles.scss new file mode 100644 index 00000000..f6224ff9 --- /dev/null +++ b/GUI/src/pages/TestModel/testModelStyles.scss @@ -0,0 +1,13 @@ +.testModalFormTextArea { + margin-top: 30px; +} + +.testModalClassifyButton { + text-align: right; + margin-top: 20px; +} + +.testModalList { + list-style: disc; + margin-left: 30px; +} \ No newline at end of file diff --git a/GUI/src/pages/TrainingSessions/index.tsx b/GUI/src/pages/TrainingSessions/index.tsx new file mode 100644 index 00000000..ec88abd7 --- /dev/null +++ b/GUI/src/pages/TrainingSessions/index.tsx @@ -0,0 +1,32 @@ +import { FC } from 'react'; +import { useTranslation } from 'react-i18next'; +import TrainingSessionCard from 'components/molecules/TrainingSessionCard'; +import { useTrainingSessions } from 'hooks/useTrainingSessions'; + +const TrainingSessions: FC = () => { + const { t } = useTranslation(); + const progresses = useTrainingSessions(); + + return ( +
    +
    +
    +
    {t('trainingSessions.title')}
    +
    + {progresses?.map((session) => ( + + ))} +
    +
    + ); +}; + +export default TrainingSessions; diff --git a/GUI/src/pages/Unauthorized/unauthorized.scss b/GUI/src/pages/Unauthorized/unauthorized.scss new file mode 100644 index 00000000..3c1bb0ff --- /dev/null +++ b/GUI/src/pages/Unauthorized/unauthorized.scss @@ -0,0 +1,30 @@ +.unauthorized-container { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + background-color: #f0f2f5; + padding: 20px; + box-sizing: border-box; + } + + .unauthorized-card { + background-color: #fff; + padding: 40px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + text-align: center; + max-width: 400px; + width: 100%; + } + + .unauthorized-header { + font-size: 2.5em; + color: #333; + margin-bottom: 20px; + } + + .unauthorized-message { + font-size: 1.2em; + color: #555; + } diff --git a/GUI/src/pages/Unauthorized/unauthorized.tsx b/GUI/src/pages/Unauthorized/unauthorized.tsx new file mode 100644 index 00000000..088fd80b --- /dev/null +++ b/GUI/src/pages/Unauthorized/unauthorized.tsx @@ -0,0 +1,17 @@ +import { FC } from 'react'; +import './unauthorized.scss'; +import { useTranslation } from 'react-i18next'; + +const Unauthorized: FC = () => { + const { t } = useTranslation(); + return ( +
    +
    +

    {t('global.unAuthorized')}

    +

    {t('global.unAuthorizedDesc')}

    +
    +
    + ); +}; + +export default Unauthorized; diff --git a/GUI/src/pages/UserManagement/SettingsUsers.scss b/GUI/src/pages/UserManagement/SettingsUsers.scss new file mode 100644 index 00000000..37e5f63b --- /dev/null +++ b/GUI/src/pages/UserManagement/SettingsUsers.scss @@ -0,0 +1,48 @@ +@import 'src/styles/tools/spacing'; +@import 'src/styles/tools/color'; +@import 'src/styles/settings/variables/other'; +@import 'src/styles/settings/variables/typography'; + +.multiSelect { + $self: &; + display: flex; + align-items: center; + gap: get-spacing(paldiski); + width: 100%; + &::placeholder { + color: get-color(black-coral-6); + font-size: small; + } + + &__label { + flex: 0 0 185px; + font-size: $veera-font-size-100; + line-height: 24px; + } + + + &__wrapper { + width: 390px; + flex: 1; + display: block; + flex-direction: column; + gap: 7px; + position: relative; + border: 0.15px solid get-color(black-coral-6); + border-radius: $veera-radius-s; + } +} + +.footer-button-wrapper { + display: flex; + gap: 10px; +} + +.button-wrapper { + display: flex; + gap: 10px; +} + +.error-span { + color: get-color(jasper-10); +} \ No newline at end of file diff --git a/GUI/src/pages/UserManagement/UserManagement.scss b/GUI/src/pages/UserManagement/UserManagement.scss new file mode 100644 index 00000000..969be2a6 --- /dev/null +++ b/GUI/src/pages/UserManagement/UserManagement.scss @@ -0,0 +1,28 @@ + +.button { + background-color: #007bff; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; +} + +.button:hover { + background-color: #0056b3; +} + +.form-group { + margin-bottom: 20px; +} + +.table-header { + display: flex; + width: 100%; + justify-content: end; +} + +.action-button-container { + display: flex; + gap: 10px; +} diff --git a/GUI/src/pages/UserManagement/UserModal.tsx b/GUI/src/pages/UserManagement/UserModal.tsx new file mode 100644 index 00000000..43235b72 --- /dev/null +++ b/GUI/src/pages/UserManagement/UserModal.tsx @@ -0,0 +1,296 @@ +import { useForm, Controller } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { AxiosError } from 'axios'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { Button, Dialog, FormInput, Track } from 'components'; +import { User, UserDTO } from 'types/user'; +import { checkIfUserExists, createUser, editUser } from 'services/users'; +import { useToast } from 'hooks/useToast'; +import Select, { components } from 'react-select'; +import './SettingsUsers.scss'; +import { FC, useMemo, useState } from 'react'; +import { ROLES } from 'enums/roles'; +import { userManagementQueryKeys } from 'utils/queryKeys'; +import { ButtonAppearanceTypes, ToastTypes } from 'enums/commonEnums'; +import { FaChevronDown, FaChevronUp } from 'react-icons/fa'; + +type UserModalProps = { + onClose: () => void; + user?: User; + isModalOpen?: boolean; +}; + +const DropdownIndicator = (props: any) => { + return ( + + {props.selectProps.menuIsOpen ? : } + + ); +}; + +const UserModal: FC = ({ onClose, user, isModalOpen }) => { + const { t } = useTranslation(); + const toast = useToast(); + const queryClient = useQueryClient(); + const [isValidIdentification, setIsValidIdentification] = + useState(false); + + const { + register, + control, + handleSubmit, + formState: { errors, isDirty }, + getValues, + } = useForm({ + defaultValues: { + useridcode: user?.useridcode, + authorities: user?.authorities, + displayName: user?.fullName, + csaTitle: user?.csaTitle, + csaEmail: user?.csaEmail, + fullName: user?.fullName, + }, + }); + + const roles = useMemo( + () => [ + { label: t('roles.ROLE_ADMINISTRATOR'), value: ROLES.ROLE_ADMINISTRATOR }, + { + label: t('roles.ROLE_MODEL_TRAINER'), + value: ROLES.ROLE_MODEL_TRAINER, + }, + ], + [t] + ); + + const userCreateMutation = useMutation({ + mutationFn: (data: UserDTO) => createUser(data), + onSuccess: async () => { + await queryClient.invalidateQueries( + userManagementQueryKeys.getAllEmployees() + ); + toast.open({ + type: ToastTypes.SUCCESS, + title: t('global.notification'), + message: t('toast.success.newUserAdded'), + }); + onClose(); + }, + onError: (error: AxiosError) => { + toast.open({ + type: ToastTypes.ERROR, + title: t('global.notificationError'), + message: error?.message ?? '', + }); + }, + }); + + const userEditMutation = useMutation({ + mutationFn: ({ + id, + userData, + }: { + id: string | number; + userData: UserDTO; + }) => editUser(id, userData), + onSuccess: async () => { + await queryClient.invalidateQueries( + userManagementQueryKeys.getAllEmployees() + ); + toast.open({ + type: ToastTypes.SUCCESS, + title: t('global.notification'), + message: t('toast.success.userUpdated'), + }); + onClose(); + }, + onError: (error: AxiosError) => { + toast.open({ + type: ToastTypes.ERROR, + title: t('global.notificationError'), + message: error?.message ?? '', + }); + }, + }); + + const checkIfUserExistsMutation = useMutation({ + mutationFn: ({ userData }: { userData: UserDTO }) => + checkIfUserExists(userData), + onSuccess: async (data) => { + if (data.response === 'true') { + setIsValidIdentification(false); + toast.open({ + type: ToastTypes.ERROR, + title: t('global.notificationError'), + message: t('userManagement.addUser.userExists'), + }); + } else { + createNewUser(); + } + }, + onError: (error: AxiosError) => { + toast.open({ + type: ToastTypes.ERROR, + title: t('global.notificationError'), + message: error?.message, + }); + }, + }); + + const createNewUser = handleSubmit((userData) => + userCreateMutation.mutate(userData) + ); + + const handleUserSubmit = handleSubmit((data) => { + if (user) userEditMutation.mutate({ id: user.useridcode, userData: data }); + else checkIfUserExistsMutation.mutate({ userData: data }); + }); + + const hasChangedFields = () => { + const currentValues = getValues(); + return ( + currentValues.useridcode !== user?.useridcode || + currentValues.authorities?.join(',') !== user?.authorities?.join(',') || + currentValues.displayName !== user?.fullName || + currentValues.csaTitle !== user?.csaTitle || + currentValues.csaEmail !== user?.csaEmail || + currentValues.fullName !== user?.fullName + ); + }; + + return ( + + + + + } + > + + + {errors?.fullName && ( + {errors?.fullName?.message} + )} + + ( +
    + +
    + + +
    + ); +}; + +export default CopyableTextField; \ No newline at end of file diff --git a/outlook-consent-app/src/app/components/LoginButton/index.tsx b/outlook-consent-app/src/app/components/LoginButton/index.tsx new file mode 100644 index 00000000..8dd6902c --- /dev/null +++ b/outlook-consent-app/src/app/components/LoginButton/index.tsx @@ -0,0 +1,18 @@ +'use client'; + +import React from 'react'; +import styles from "../../page.module.css"; + +const LoginButton = () => { + const handleLogin = () => { + window.location.href = '/api/auth'; + }; + + return ( + + ); +}; + +export default LoginButton; diff --git a/outlook-consent-app/src/app/favicon.ico b/outlook-consent-app/src/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/outlook-consent-app/src/app/favicon.ico differ diff --git a/outlook-consent-app/src/app/globals.css b/outlook-consent-app/src/app/globals.css new file mode 100644 index 00000000..2748dd3e --- /dev/null +++ b/outlook-consent-app/src/app/globals.css @@ -0,0 +1,92 @@ +:root { + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", + "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", + "Fira Mono", "Droid Sans Mono", "Courier New", monospace; + + --foreground-rgb: 0, 0, 0; + --background-color: white; + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} diff --git a/outlook-consent-app/src/app/layout.tsx b/outlook-consent-app/src/app/layout.tsx new file mode 100644 index 00000000..3314e478 --- /dev/null +++ b/outlook-consent-app/src/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/outlook-consent-app/src/app/page.module.css b/outlook-consent-app/src/app/page.module.css new file mode 100644 index 00000000..b3cd538c --- /dev/null +++ b/outlook-consent-app/src/app/page.module.css @@ -0,0 +1,251 @@ +.main { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 6rem; + min-height: 100vh; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.description a { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; +} + +.description p { + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.grid { + display: grid; + grid-template-columns: repeat(4, minmax(25%, auto)); + max-width: 100%; + width: var(--max-width); +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; + text-wrap: balance; +} + +.center { + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; + gap: 5px; +} + + +.logo { + position: relative; +} + +.copyableTextField { + display: flex; + align-items: center; + justify-content: center; +} + +.copyableTextField input { + margin-right: 10px; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + width: 450px; +} + + +/* Enable hover only on non-touch devices */ +@media (hover: hover) and (pointer: fine) { + .card:hover { + background: rgba(var(--card-rgb), 0.1); + border: 1px solid rgba(var(--card-border-rgb), 0.15); + } + + .card:hover span { + transform: translateX(4px); + } +} + +@media (prefers-reduced-motion) { + .card:hover span { + transform: none; + } +} + +/* Mobile */ +@media (max-width: 700px) { + .content { + padding: 4rem; + } + + .grid { + grid-template-columns: 1fr; + margin-bottom: 120px; + max-width: 320px; + text-align: center; + } + + .card { + padding: 1rem 2.5rem; + } + + .card h2 { + margin-bottom: 0.5rem; + } + + .center { + padding: 4rem 0 4 rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } +} + +.btn { + appearance: none; + align-items: center; + background: rgb(0, 0, 93); + border: 0; + color: white; + cursor: pointer; + font: inherit; + overflow: visible; + padding: 8px 20px; + text-decoration: none; + font-size: 14px; + line-height: 24px; + border-radius: 20px; + white-space: nowrap; + text-align: center; + min-width: 100px; + + &:focus { + outline: none; + } + + &--disabled { + cursor: not-allowed; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + .grid { + grid-template-columns: repeat(2, 50%); + } +} + +@media (prefers-color-scheme: dark) { + .vercelLogo { + filter: invert(1); + } + + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } +} + +@keyframes rotate { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/outlook-consent-app/src/app/page.tsx b/outlook-consent-app/src/app/page.tsx new file mode 100644 index 00000000..ec6349c4 --- /dev/null +++ b/outlook-consent-app/src/app/page.tsx @@ -0,0 +1,15 @@ +import Image from "next/image"; +import styles from "./page.module.css"; +import LoginButton from "./components/LoginButton"; + +export default function Home() { + return ( +
    +
    +
    + +
    +
    +
    + ); +} diff --git a/outlook-consent-app/tsconfig.json b/outlook-consent-app/tsconfig.json new file mode 100644 index 00000000..7b285893 --- /dev/null +++ b/outlook-consent-app/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/src/unitTesting.sh b/src/unitTesting.sh new file mode 100755 index 00000000..23221242 --- /dev/null +++ b/src/unitTesting.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo "True" diff --git a/token.sh b/token.sh new file mode 100755 index 00000000..21e9d865 --- /dev/null +++ b/token.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Define the path where the SQL file will be generated +SQL_FILE="DSL/Liquibase/data/update_refresh_token.sql" + +# Read the OUTLOOK_REFRESH_KEY value from the INI file +OUTLOOK_REFRESH_KEY=$(awk -F '=' '/OUTLOOK_REFRESH_KEY/ {print $2}' constants.ini | xargs) + +# Function to Base64 encode +base64_encode() { + echo -n "$1" | base64 +} + +# Encrypt the refresh token +encrypted_refresh_token=$(base64_encode "$OUTLOOK_REFRESH_KEY") + +# Generate a SQL script with the encrypted value +cat << EOF > "$SQL_FILE" +-- Update the refresh token in the database +UPDATE integration_status +SET token = '$encrypted_refresh_token' +WHERE platform='OUTLOOK'; +EOF + +echo "SQL file created at $SQL_FILE with encrypted token."