diff --git a/CHANGELOG.MD b/CHANGELOG.MD index e12ec525a..883380381 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,15 @@ All notable changes to this project will be documented in this file. This projec > **Feature**: Added the timeline widget. [🎟️DESENG-439](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-439) +## January 9, 2024 + +- **Task** Improvements from Epic [🎟️DESENG-468](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-468) + - Improvements to Survey Result Tracking analytics + - New Rejection Email Template for Closed Engagements + - Export Format for Proponent updated to be in excel format + - Formio Version Update + - Enable Survey Editing for Open Engagements + ## December 11, 2023 - **Task** Merge `gdx-sso`, `gdx-dev`, `gdx-main` into `main` [🎟️DESENG-442](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-442) diff --git a/analytics-api/migrations/versions/812b1f67015a_updating_available_response_options.py b/analytics-api/migrations/versions/812b1f67015a_updating_available_response_options.py new file mode 100644 index 000000000..faded1962 --- /dev/null +++ b/analytics-api/migrations/versions/812b1f67015a_updating_available_response_options.py @@ -0,0 +1,30 @@ +"""updating_available_response_options + +Revision ID: 812b1f67015a +Revises: e7fdf769e8ff +Create Date: 2023-11-30 09:50:55.874798 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '812b1f67015a' +down_revision = 'e7fdf769e8ff' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('available_response_option', 'request_key', type_=sa.Text()) + op.alter_column('available_response_option', 'request_id', type_=sa.Text()) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('available_response_option', 'request_key', type_=sa.String(length=100)) + op.alter_column('available_response_option', 'request_id', type_=sa.String(length=20)) + # ### end Alembic commands ### diff --git a/analytics-api/migrations/versions/e7fdf769e8ff_adding_available_response_options.py b/analytics-api/migrations/versions/e7fdf769e8ff_adding_available_response_options.py new file mode 100644 index 000000000..679276848 --- /dev/null +++ b/analytics-api/migrations/versions/e7fdf769e8ff_adding_available_response_options.py @@ -0,0 +1,41 @@ +"""adding_available_response_options + +Revision ID: e7fdf769e8ff +Revises: 3a705c422892 +Create Date: 2023-11-21 12:45:34.871602 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'e7fdf769e8ff' +down_revision = '3a705c422892' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('available_response_option', + sa.Column('created_date', sa.DateTime(), nullable=True), + sa.Column('updated_date', sa.DateTime(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=True), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('participant_id', sa.Integer(), nullable=True), + sa.Column('request_key', sa.String(length=100), nullable=False), + sa.Column('value', sa.Text(), nullable=True), + sa.Column('request_id', sa.String(length=20), nullable=True), + sa.Column('survey_id', sa.Integer(), nullable=False), + sa.Column('runcycle_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['survey_id'], ['survey.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', 'request_key') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('available_response_option') + # ### end Alembic commands ### diff --git a/analytics-api/src/analytics_api/config.py b/analytics-api/src/analytics_api/config.py index 269b4a411..ba931c0a1 100644 --- a/analytics-api/src/analytics_api/config.py +++ b/analytics-api/src/analytics_api/config.py @@ -22,13 +22,14 @@ import os import sys +from typing import Union from dotenv import find_dotenv, load_dotenv # this will load all the envars from a .env file located in the project root (api) load_dotenv(find_dotenv()) -def get_named_config(environment: 'str | None') -> '_Config': +def get_named_config(environment: Union[str, None]) -> '_Config': """ Retrieve a configuration object by name. Used by the Flask app factory. @@ -38,16 +39,16 @@ def get_named_config(environment: 'str | None') -> '_Config': """ config_mapping = { 'development': DevConfig, - 'default': ProdConfig, - 'staging': ProdConfig, + 'default': ProdConfig, + 'staging': ProdConfig, 'production': ProdConfig, - 'testing': TestConfig, + 'testing': TestConfig, } try: print(f'Loading configuration: {environment}...') return config_mapping[environment]() - except KeyError: - raise KeyError(f'Configuration "{environment}" not found.') + except KeyError as e: + raise KeyError(f'Configuration "{environment}" not found.') from e class _Config(): # pylint: disable=too-few-public-methods diff --git a/analytics-api/src/analytics_api/models/available_response_option.py b/analytics-api/src/analytics_api/models/available_response_option.py new file mode 100644 index 000000000..0e406a4ed --- /dev/null +++ b/analytics-api/src/analytics_api/models/available_response_option.py @@ -0,0 +1,12 @@ +"""available_response_option model class. + +Manages the Available Options for a option type questions on a survey +""" +from .base_model import BaseModel +from .response_mixin import ResponseMixin + + +class AvailableResponseOption(BaseModel, ResponseMixin): # pylint: disable=too-few-public-methods + """Definition of the Available Response Options entity.""" + + __tablename__ = 'available_response_option' diff --git a/analytics-api/src/analytics_api/models/request_type_option.py b/analytics-api/src/analytics_api/models/request_type_option.py index 4c48e0394..499291d11 100644 --- a/analytics-api/src/analytics_api/models/request_type_option.py +++ b/analytics-api/src/analytics_api/models/request_type_option.py @@ -4,6 +4,7 @@ """ from sqlalchemy import and_, func, or_ from sqlalchemy.sql.expression import true +from analytics_api.models.available_response_option import AvailableResponseOption as AvailableResponseOptionModel from analytics_api.models.survey import Survey as SurveyModel from analytics_api.models.response_type_option import ResponseTypeOption as ResponseTypeOptionModel from .base_model import BaseModel @@ -34,7 +35,7 @@ def get_survey_result( if can_view_all_survey_results: survey_question = (db.session.query(RequestTypeOption.position.label('position'), RequestTypeOption.label.label('label'), - RequestTypeOption.request_id) + RequestTypeOption.key) .filter(and_(RequestTypeOption.survey_id.in_(analytics_survey_id), RequestTypeOption.is_active == true())) .order_by(RequestTypeOption.position) @@ -42,7 +43,7 @@ def get_survey_result( else: survey_question = (db.session.query(RequestTypeOption.position.label('position'), RequestTypeOption.label.label('label'), - RequestTypeOption.request_id) + RequestTypeOption.key) .filter(and_(RequestTypeOption.survey_id.in_(analytics_survey_id), RequestTypeOption.is_active == true(), or_(RequestTypeOption.display == true(), @@ -50,26 +51,60 @@ def get_survey_result( .order_by(RequestTypeOption.position) .subquery()) + # Get all the available responses for each question within the survey. + available_response = (db.session.query(AvailableResponseOptionModel.request_key, + AvailableResponseOptionModel.value) + .filter(and_(AvailableResponseOptionModel.survey_id.in_( + analytics_survey_id), AvailableResponseOptionModel.is_active == true())) + .subquery()) # Get all the survey responses with the counts for each response specific to a survey id which # are in active status. - survey_response = (db.session.query(ResponseTypeOptionModel.request_id, ResponseTypeOptionModel.value, - func.count(ResponseTypeOptionModel.request_id).label('response')) + survey_response = (db.session.query(ResponseTypeOptionModel.request_key, ResponseTypeOptionModel.value, + func.count(ResponseTypeOptionModel.request_key).label('response')) .filter(and_(ResponseTypeOptionModel.survey_id.in_(analytics_survey_id), ResponseTypeOptionModel.is_active == true())) - .group_by(ResponseTypeOptionModel.request_id, ResponseTypeOptionModel.value) + .group_by(ResponseTypeOptionModel.request_key, ResponseTypeOptionModel.value) .subquery()) + survey_response_exists = db.session.query(survey_response.c.request_key).first() + available_response_exists = db.session.query(available_response.c.request_key).first() + # Combine the data fetched above such that the result has a format as below # - position: is a unique value for each question which helps to get the order of question on the survey # - label: is the the survey question # - value: user selected response for each question # - count: number of time the same value is selected as a response to each question - survey_result = (db.session.query((survey_question.c.position).label('position'), - (survey_question.c.label).label('question'), - func.json_agg(func.json_build_object('value', survey_response.c.value, - 'count', survey_response.c.response)) - .label('result')) - .join(survey_response, survey_response.c.request_id == survey_question.c.request_id) - .group_by(survey_question.c.position, survey_question.c.label)) - return survey_result.all() + # Check if there are records in survey_response and available_response before executing the final query + # which fetches all the available responses along with the corresponding responses. + if survey_response_exists and available_response_exists: + survey_result = (db.session.query((survey_question.c.position).label('position'), + (survey_question.c.label).label('question'), + func.json_agg(func.json_build_object( + 'value', available_response.c.value, + 'count', func.coalesce(survey_response.c.response, 0))) + .label('result')) + .outerjoin(available_response, survey_question.c.key == available_response.c.request_key) + .outerjoin(survey_response, + (available_response.c.value == survey_response.c.value) & + (available_response.c.request_key == survey_response.c.request_key), + full=True) + .group_by(survey_question.c.position, survey_question.c.label)) + + return survey_result.all() + # Check if there are records in survey_response before executing the final query which fetches reponses + # even if the available_response table is not yet populated. + if survey_response_exists: + survey_result = (db.session.query((survey_question.c.position).label('position'), + (survey_question.c.label).label('question'), + func.json_agg(func.json_build_object('value', + survey_response.c.value, + 'count', + survey_response.c.response)) + .label('result')) + .join(survey_response, survey_response.c.request_key == survey_question.c.key) + .group_by(survey_question.c.position, survey_question.c.label)) + + return survey_result.all() + + return None # Return None indicating no records diff --git a/met-api/sample.env b/met-api/sample.env index 15e938527..90a2b1d60 100644 --- a/met-api/sample.env +++ b/met-api/sample.env @@ -80,6 +80,7 @@ EMAIL_FROM_ADDRESS="met-example@gov.bc.ca" SUBSCRIBE_EMAIL_TEMPLATE_ID= SUBSCRIBE_EMAIL_SUBJECT= REJECTED_EMAIL_TEMPLATE_ID= +CLOSED_ENGAGEMENT_REJECTED_EMAIL_TEMPLATE_ID= REJECTED_EMAIL_SUBJECT= VERIFICATION_EMAIL_TEMPLATE_ID= VERIFICATION_EMAIL_SUBJECT= diff --git a/met-api/src/met_api/__init__.py b/met-api/src/met_api/__init__.py index d672b4bc2..2858d08dd 100644 --- a/met-api/src/met_api/__init__.py +++ b/met-api/src/met_api/__init__.py @@ -130,12 +130,13 @@ def setup_jwt_manager(app_context, jwt_manager): def get_roles(token_info): """ Consumes a token_info dictionary and returns a list of roles. + Uses a configurable path to the roles in the token_info dictionary. """ role_access_path = app_context.config['JWT_CONFIG']['ROLE_CLAIM'] for key in role_access_path.split('.'): token_info = token_info.get(key, {}) return token_info - + app_context.config['JWT_ROLE_CALLBACK'] = get_roles jwt_manager.init_app(app_context) diff --git a/met-api/src/met_api/config.py b/met-api/src/met_api/config.py index b022ca549..9b0b76216 100644 --- a/met-api/src/met_api/config.py +++ b/met-api/src/met_api/config.py @@ -24,6 +24,7 @@ import os +from typing import Union from dotenv import find_dotenv, load_dotenv from met_api.utils.constants import TestKeyConfig @@ -36,7 +37,7 @@ os.environ = {k: v for k, v in os.environ.items() if v} -def get_named_config(environment: 'str | None') -> 'Config': +def get_named_config(environment: Union[str, None]) -> 'Config': """ Retrieve a configuration object by name. Used by the Flask app factory. @@ -46,40 +47,46 @@ def get_named_config(environment: 'str | None') -> 'Config': """ config_mapping = { 'development': DevConfig, - 'default': ProdConfig, - 'staging': ProdConfig, + 'default': ProdConfig, + 'staging': ProdConfig, 'production': ProdConfig, - 'testing': TestConfig, - 'docker': DockerConfig, + 'testing': TestConfig, + 'docker': DockerConfig, } try: print(f'Loading configuration: {environment}...') - return config_mapping[environment]() - except KeyError: - raise KeyError(f'Configuration "{environment}" not found.') + return config_mapping.get(environment, ProdConfig)() + except KeyError as e: + raise KeyError(f'Configuration "{environment}" not found.') from e + def env_truthy(env_var, default: bool = False): """ Return True if the environment variable is set to a truthy value. + Accepts a default value, which is returned if the environment variable is not set. """ return is_truthy(os.getenv(env_var, str(default))) + class Config: # pylint: disable=too-few-public-methods """ - Base configuration that sets reasonable defaults for all other configs. + Base configuration that sets reasonable defaults for all other configs. + New configurations should inherit from this one where possible. Reference: https://flask.palletsprojects.com/en/3.0.x/config/ """ def __init__(self) -> None: """ - Initializes the configuration object. Performs more advanced - configuration logic that is not possible in the normal class definition. + Initialize the configuration object. + + Performs more advanced configuration logic that is not possible + in the normal class definition. """ # If extending this class, call super().__init__() in your constructor. - print(f'SQLAlchemy URL: {self.SQLALCHEMY_DATABASE_URI}') + print(f'SQLAlchemy URL: {self.sqlalchemy_database_uri}') # apply configs to _Config in the format that flask_jwt_oidc expects # this flattens the JWT_CONFIG dict into individual attributes @@ -87,14 +94,16 @@ def __init__(self) -> None: setattr(self, f'JWT_OIDC_{key}', value) # Enable live reload and interactive API debugger for developers - os.environ["FLASK_DEBUG"] = str(self.USE_DEBUG) + os.environ['FLASK_DEBUG'] = str(self.USE_DEBUG) @property - def SQLALCHEMY_DATABASE_URI(self) -> str: + def sqlalchemy_database_uri(self) -> str: """ Dynamically fetch the SQLAlchemy Database URI based on the DB config. + This avoids having to redefine the URI after setting the DB access - credentials in subclasses. Can be overridden by env variables.""" + credentials in subclasses. Can be overridden by env variables. + """ return os.environ.get( 'SQLALCHEMY_DATABASE_URI', f'postgresql://' @@ -103,21 +112,20 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: f'{self.DB_CONFIG.get("NAME")}' ) - # If enabled, Exceptions are propagated up, instead of being handled # by the the app’s error handlers. Enable this for tests. TESTING = env_truthy('FLASK_TESTING', default=False) - + # If enabled, the interactive debugger will be shown for any # unhandled Exceptions, and the server will be reloaded when code changes. USE_DEBUG = env_truthy('FLASK_DEBUG', default=False) - + # SQLAlchemy settings # Echoes the SQL queries generated - useful for debugging SQLALCHEMY_ECHO = env_truthy('SQLALCHEMY_ECHO') # Disable modification tracking for performance SQLALCHEMY_TRACK_MODIFICATIONS = env_truthy('SQLALCHEMY_TRACK_MODIFICATIONS') - + # Used for session management. Randomized by default for security, but # should be set to a fixed value in production to avoid invalidating sessions. SECRET_KEY = os.getenv('SECRET_KEY', os.urandom(24)) @@ -164,7 +172,6 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: 'KEYCLOAK_CLIENT_ID': os.getenv('EPIC_KC_CLIENT_ID'), } - # Keycloak configuration KEYCLOAK_CONFIG = KC = { 'BASE_URL': os.getenv('KEYCLOAK_BASE_URL', ''), @@ -173,7 +180,7 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: 'SERVICE_ACCOUNT_SECRET': os.getenv('MET_ADMIN_CLIENT_SECRET'), 'ADMIN_USERNAME': os.getenv('MET_ADMIN_CLIENT_ID'), 'ADMIN_SECRET': os.getenv('MET_ADMIN_CLIENT_SECRET'), - 'CONNECT_TIMEOUT': int(os.getenv('KEYCLOAK_CONNECT_TIMEOUT', 60)), + 'CONNECT_TIMEOUT': int(os.getenv('KEYCLOAK_CONNECT_TIMEOUT', '60')), } # JWT OIDC Settings (for Keycloak) @@ -184,14 +191,14 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: f'{KC["BASE_URL"]}/realms/{KC["REALMNAME"]}' )), 'WELL_KNOWN_CONFIG': os.getenv( - 'JWT_OIDC_WELL_KNOWN_CONFIG', + 'JWT_OIDC_WELL_KNOWN_CONFIG', f'{_issuer}/.well-known/openid-configuration', ), 'JWKS_URI': os.getenv('JWT_OIDC_JWKS_URI', f'{_issuer}/protocol/openid-connect/certs'), 'ALGORITHMS': os.getenv('JWT_OIDC_ALGORITHMS', 'RS256'), 'AUDIENCE': os.getenv('JWT_OIDC_AUDIENCE', 'account'), - 'CACHING_ENABLED': str(env_truthy('JWT_OIDC_CACHING_ENABLED', True)), - 'JWKS_CACHE_TIMEOUT': int(os.getenv('JWT_OIDC_JWKS_CACHE_TIMEOUT', 300)), + 'CACHING_ENABLED': str(env_truthy('JWT_OIDC_CACHING_ENABLED', 'True')), + 'JWKS_CACHE_TIMEOUT': int(os.getenv('JWT_OIDC_JWKS_CACHE_TIMEOUT', '300')), 'ROLE_CLAIM': os.getenv('JWT_OIDC_ROLE_CLAIM', 'realm_access.roles'), } @@ -230,11 +237,11 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: 'UNSUBSCRIBE': os.getenv( 'UNSUBSCRIBE_PATH', '/engagements/{engagement_id}/unsubscribe/{participant_id}' ), - "ENGAGEMENT": { + 'ENGAGEMENT': { 'VIEW': os.getenv('ENGAGEMENT_PATH', '/engagements/{engagement_id}/view'), 'SLUG': os.getenv('ENGAGEMENT_PATH_SLUG', '/{slug}'), 'DASHBOARD': os.getenv( - 'ENGAGEMENT_DASHBOARD_PATH','/engagements/{engagement_id}/comments/public' + 'ENGAGEMENT_DASHBOARD_PATH', '/engagements/{engagement_id}/comments/public' ), 'DASHBOARD_SLUG': os.getenv( 'ENGAGEMENT_DASHBOARD_PATH_SLUG', '/{slug}/comments/public' @@ -252,35 +259,40 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: # value in met-cron/cron/crontab 'CLOSING_TIME': os.getenv('ENGAGEMENT_END_TIME', '5 PM'), 'FROM_ADDRESS': os.getenv('EMAIL_FROM_ADDRESS'), - 'ENVIRONMENT' : os.getenv('EMAIL_ENVIRONMENT'), + 'ENVIRONMENT': os.getenv('EMAIL_ENVIRONMENT'), 'SUBSCRIBE': { 'ID': os.getenv('SUBSCRIBE_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('SUBSCRIBE_EMAIL_SUBJECT', - 'Confirm your subscription'), + 'SUBJECT': os.getenv('SUBSCRIBE_EMAIL_SUBJECT', + 'Confirm your subscription'), }, 'REJECTED': { 'ID': os.getenv('REJECTED_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('REJECTED_EMAIL_SUBJECT', - '{engagement_name} - About your Comments'), + 'SUBJECT': os.getenv('REJECTED_EMAIL_SUBJECT', + 'Your comment on {engagement_name} needs to be edited'), + }, + 'CLOSED_ENGAGEMENT_REJECTED': { + 'ID': os.getenv('CLOSED_ENGAGEMENT_REJECTED_EMAIL_TEMPLATE_ID'), + 'SUBJECT': os.getenv('CLOSED_ENGAGEMENT_REJECTED_EMAIL_SUBJECT', + 'Your comment on {engagement_name} has been rejected'), }, 'VERIFICATION': { 'ID': os.getenv('VERIFICATION_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('VERIFICATION_EMAIL_SUBJECT', + 'SUBJECT': os.getenv('VERIFICATION_EMAIL_SUBJECT', '{engagement_name} - Access link'), }, 'SUBMISSION_RESPONSE': { 'ID': os.getenv('SUBMISSION_RESPONSE_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('SUBMISSION_RESPONSE_EMAIL_SUBJECT', + 'SUBJECT': os.getenv('SUBMISSION_RESPONSE_EMAIL_SUBJECT', 'MET - Your feedback was successfully submitted'), }, - "CLOSEOUT":{ + 'CLOSEOUT': { 'ID': os.getenv('CLOSEOUT_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('CLOSEOUT_EMAIL_SUBJECT', + 'SUBJECT': os.getenv('CLOSEOUT_EMAIL_SUBJECT', 'MET - Engagement Closed'), }, 'ACCESS_REQUEST': { 'ID': os.getenv('ACCESS_REQUEST_EMAIL_TEMPLATE_ID'), - 'SUBJECT': os.getenv('ACCESS_REQUEST_EMAIL_SUBJECT', + 'SUBJECT': os.getenv('ACCESS_REQUEST_EMAIL_SUBJECT', 'MET - New User Access Request'), 'DEST_EMAIL_ADDRESS': os.getenv('ACCESS_REQUEST_EMAIL_ADDRESS'), } @@ -306,17 +318,25 @@ class DevConfig(Config): # pylint: disable=too-few-public-methods class TestConfig(TestKeyConfig, Config): # pylint: disable=too-few-public-methods """ The configuration used when running the test suite. + Extends TestKeyConfig, which contains some large constant keys that are used in the tests. It is stored in a separate file to avoid clutter. TestKeyConfig, in turn, extends the default Config class from above. """ def __init__(self) -> None: + """ + Initialize the object. + + This method is called when an object is created. It sets up the initial + state of the object. + + """ super().__init__() - + # Override Keycloak variables here self.KC['ADMIN_USERNAME'] = os.getenv( - 'KEYCLOAK_TEST_ADMIN_CLIENTID', + 'KEYCLOAK_TEST_ADMIN_CLIENTID', self.KC['ADMIN_USERNAME'] ) self.KC['ADMIN_SECRET'] = os.getenv( @@ -329,12 +349,12 @@ def __init__(self) -> None: # Propagate exceptions up to the test runner TESTING = env_truthy('TESTING', default=True) - # explicitly disable the debugger; we want the tests to fail if an + # explicitly disable the debugger; we want the tests to fail if an # unhandled exception occurs - USE_DEBUG = False + USE_DEBUG = False # JWT OIDC Settings for the test environment - JWT_OIDC_TEST_MODE = True # enables the test mode for flask_jwt_oidc + JWT_OIDC_TEST_MODE = True # enables the test mode for flask_jwt_oidc JWT_OIDC_TEST_AUDIENCE = os.getenv('JWT_OIDC_TEST_AUDIENCE') JWT_OIDC_TEST_CLIENT_SECRET = os.getenv('JWT_OIDC_TEST_CLIENT_SECRET') JWT_OIDC_TEST_ISSUER = os.getenv('JWT_OIDC_TEST_ISSUER') @@ -365,4 +385,3 @@ class DockerConfig(Config): # pylint: disable=too-few-public-methods class ProdConfig(Config): # pylint: disable=too-few-public-methods """Production Config.""" - diff --git a/met-api/src/met_api/generated_documents_carbone_templates/proponent_comments_sheet.xlsx b/met-api/src/met_api/generated_documents_carbone_templates/proponent_comments_sheet.xlsx index 1a568d5a1..dbb2a557f 100644 Binary files a/met-api/src/met_api/generated_documents_carbone_templates/proponent_comments_sheet.xlsx and b/met-api/src/met_api/generated_documents_carbone_templates/proponent_comments_sheet.xlsx differ diff --git a/met-api/src/met_api/models/comment.py b/met-api/src/met_api/models/comment.py index a3d0339eb..2aaac8022 100644 --- a/met-api/src/met_api/models/comment.py +++ b/met-api/src/met_api/models/comment.py @@ -112,7 +112,7 @@ def get_accepted_comments_by_survey_id_paginated( if search_text: query = query.filter(Comment.text.ilike('%' + search_text + '%')) - query = query.order_by(Comment.id.desc()) + query = query.order_by(Comment.id.asc()) no_pagination_options = not pagination_options or not pagination_options.page or not pagination_options.size if no_pagination_options: diff --git a/met-api/src/met_api/models/engagement.py b/met-api/src/met_api/models/engagement.py index 487120ab8..5315937c6 100644 --- a/met-api/src/met_api/models/engagement.py +++ b/met-api/src/met_api/models/engagement.py @@ -15,7 +15,7 @@ from met_api.constants.engagement_status import EngagementDisplayStatus, Status from met_api.constants.user import SYSTEM_USER -from met_api.models.engagement_metadata import EngagementMetadataModel +# from met_api.models.engagement_metadata import EngagementMetadataModel from met_api.models.membership import Membership as MembershipModel from met_api.models.staff_user import StaffUser from met_api.models.pagination_options import PaginationOptions @@ -260,12 +260,11 @@ def _filter_by_internal(query, search_options): query = query.filter(Engagement.is_internal.is_(False)) return query - @staticmethod - def _filter_by_project_metadata(query, search_options): - query = query.outerjoin(EngagementMetadataModel, EngagementMetadataModel.engagement_id == Engagement.id) - - # TODO: Populate or remove this method dependent on changes resulting from adding the new Engagement metadata - return query + # TODO: Populate or remove this method dependent on changes resulting from adding the new Engagement metadata + # @staticmethod + # def _filter_by_project_metadata(query, search_options): + # query = query.outerjoin(EngagementMetadataModel, EngagementMetadataModel.engagement_id == Engagement.id) + # return query @staticmethod def _filter_by_assigned_engagements(query, external_user_id: int, exception_status_ids: Optional[list[int]] = None): diff --git a/met-api/src/met_api/models/engagement_metadata.py b/met-api/src/met_api/models/engagement_metadata.py index c1fab56fd..eb1b95cc7 100644 --- a/met-api/src/met_api/models/engagement_metadata.py +++ b/met-api/src/met_api/models/engagement_metadata.py @@ -7,7 +7,7 @@ from typing import Optional -from sqlalchemy.dialects import postgresql +# from sqlalchemy.dialects import postgresql from sqlalchemy.sql.schema import ForeignKey from .base_model import BaseModel diff --git a/met-api/src/met_api/models/new_engagement_metadata.py b/met-api/src/met_api/models/new_engagement_metadata.py index 751596b8e..edc012728 100644 --- a/met-api/src/met_api/models/new_engagement_metadata.py +++ b/met-api/src/met_api/models/new_engagement_metadata.py @@ -1,21 +1,19 @@ -""" -The Engagement Metadata models. -""" +"""The Engagement Metadata models.""" -from sqlalchemy.dialects import postgresql +# from sqlalchemy.dialects import postgresql from sqlalchemy.sql.schema import ForeignKey from .base_model import BaseModel from .db import db + class MetadataModel(BaseModel): - """ - Metadata for an Engagement. Can be used to store any arbitrary data. - """ + """Metadata for an Engagement. Can be used to store any arbitrary data.""" + __tablename__ = 'metadata_relationship' tenant_id = db.Column( db.Integer, - ForeignKey('tenant.id', ondelete='CASCADE'), + ForeignKey('tenant.id', ondelete='CASCADE'), primary_key=True ) engagement_id = db.Column( @@ -24,7 +22,7 @@ class MetadataModel(BaseModel): primary_key=True ) category_id = db.Column( - db.Integer, + db.Integer, ForeignKey('metadata_category.category_id', ondelete='CASCADE'), primary_key=True ) @@ -33,10 +31,10 @@ class MetadataModel(BaseModel): category = db.relationship('MetadataTaxonomy', backref='metadata') engagements = db.relationship('Engagement', backref='metadata') + class MetadataTaxonomyModel(BaseModel): - """ - Defines a category of metadata fields. - """ + """Defines a category of metadata fields.""" + __tablename__ = 'metadata_category' category_id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column( @@ -50,4 +48,4 @@ class MetadataTaxonomyModel(BaseModel): name = db.Column(db.String(100), unique=False, nullable=True) data_type = db.Column(db.String(100), unique=False, nullable=True) description = db.Column(db.String(100), unique=False, nullable=True) - metadata = db.relationship(MetadataModel, backref='metadata_category') \ No newline at end of file + metadata = db.relationship(MetadataModel, backref='metadata_category') diff --git a/met-api/src/met_api/services/cdogs_api_service.py b/met-api/src/met_api/services/cdogs_api_service.py index d4ad9850d..3d325b40c 100644 --- a/met-api/src/met_api/services/cdogs_api_service.py +++ b/met-api/src/met_api/services/cdogs_api_service.py @@ -16,7 +16,6 @@ """Service for receipt generation.""" import base64 import json -import os import re from http import HTTPStatus @@ -32,9 +31,9 @@ class CdogsApiService: def __init__(self): """Initiate class.""" # we can't use current_app.config here because it isn't initialized yet - config = Config().CDOGS_CONFIG + config = Config().CDOGS_CONFIG self.base_url = config['BASE_URL'] - + self.access_token = self._get_access_token() def generate_document(self, template_hash_code: str, data, options): @@ -111,9 +110,9 @@ def check_template_cached(self, template_hash_code: str): @staticmethod def _get_access_token(): - token_url = CdogsApiService.config['TOKEN_URL'] - service_client = CdogsApiService.config['SERVICE_CLIENT'] - service_client_secret = CdogsApiService.config['SERVICE_CLIENT_SECRET'] + token_url = Config().CDOGS_CONFIG['TOKEN_URL'] + service_client = Config().CDOGS_CONFIG['SERVICE_CLIENT'] + service_client_secret = Config().CDOGS_CONFIG['SERVICE_CLIENT_SECRET'] basic_auth_encoded = base64.b64encode( bytes(f'{service_client}:{service_client_secret}', 'utf-8')).decode('utf-8') diff --git a/met-api/src/met_api/services/comment_service.py b/met-api/src/met_api/services/comment_service.py index e629a29d3..805e47d3a 100644 --- a/met-api/src/met_api/services/comment_service.py +++ b/met-api/src/met_api/services/comment_service.py @@ -6,7 +6,7 @@ from met_api.constants.export_comments import RejectionReason from met_api.models import Survey as SurveyModel from met_api.models.comment import Comment -from met_api.models.engagement_metadata import EngagementMetadataModel +# from met_api.models.engagement_metadata import EngagementMetadataModel from met_api.models.membership import Membership as MembershipModel from met_api.models.pagination_options import PaginationOptions from met_api.models.submission import Submission as SubmissionModel @@ -82,7 +82,7 @@ def get_comments_paginated(cls, survey_id, pagination_options: PaginationOptions """Get comments paginated.""" include_unpublished = CommentService.can_view_unapproved_comments(survey_id) - comment_schema = CommentSchema(many=True, only=('text', 'submission_date')) + comment_schema = CommentSchema(many=True, only=('text', 'submission_date', 'label', 'submission_id')) items, total = Comment.get_accepted_comments_by_survey_id_paginated( survey_id, pagination_options, search_text, include_unpublished) return { @@ -157,7 +157,7 @@ def export_comments_to_spread_sheet_staff(cls, survey_id): # metadata_model = EngagementMetadataModel.find_by_id(survey.engagement_id) titles = cls.get_titles(survey) - data_rows = cls.get_data_rows(titles, comments) + data_rows = cls.get_data_rows(titles, comments, None) formatted_comments = { 'titles': titles, @@ -253,11 +253,11 @@ def export_comments_to_spread_sheet_proponent(cls, survey_id): ) authorization.check_auth(one_of_roles=one_of_roles, engagement_id=survey.engagement_id) comments = Comment.get_public_viewable_comments_by_survey_id(survey_id) - formatted_comments = cls.format_comments(survey, comments) + formatted_comments = cls.format_comments(comments) document_options = { 'document_type': GeneratedDocumentTypes.COMMENT_SHEET_PROPONENT.value, 'template_name': 'proponent_comments_sheet.xlsx', - 'convert_to': 'csv', + 'convert_to': 'xlsx', 'report_name': 'proponent_comments_sheet' } return DocumentGenerationService().generate_document(data=formatted_comments, options=document_options) @@ -312,11 +312,58 @@ def sort_comments_by_titles(cls, titles, grouped_comments): return grouped_comments @classmethod - def format_comments(cls, survey, comments): + def format_comments(cls, comments): """Format comments.""" - grouped_comments = cls.group_comments_by_submission_id(comments) - visible_titles = cls.get_visible_titles(survey, comments) - titles = [title for title in cls.get_titles(survey) if title in visible_titles] - sorted_comments = cls.sort_comments_by_titles(titles, grouped_comments) + # Create a dictionary to store comments grouped by labels + comments_by_label = {} + + # Create a list to store unique titles in order of appearance + unique_titles = [] + + # Iterate over the input data + for comment in comments: + # Get the submission_id, or an empty string if it's missing + submission_id = comment.get('submission_id', '') + label = comment['label'] + text = comment.get('text', '') # Get the text, or an empty string if it's missing + + # If the label is not already in unique_titles, add it + if label not in unique_titles: + unique_titles.append(label) + + # If label is not in comments_by_label, create an empty list for it + if label not in comments_by_label: + comments_by_label[label] = [] + + # Append the comment to the corresponding label in comments_by_label + comments_by_label[label].append({'text': text, 'submission_id': submission_id}) + + # Create a list of titles with label information in order of appearance + titles = [{'label': title, 'proponent_answers': 'Proponent Answer'} for title in unique_titles] + + # Create a list of comments organized by label order + formatted_comments = [] + row_id = 1 + + # Iterate over each row_id until there are no more comments + while any(comments_by_label.get(title['label']) for title in titles): + comment_row = {'row_id': row_id, 'commentText': []} + + for title in titles: + label = title['label'] + label_comments = comments_by_label.get(label, [{'text': '', 'submission_id': ''}]) + # If there are comments for this label, pop the first one + if label_comments: + comment_text = label_comments.pop(0) + else: + # If there are no comments for this label, use a default empty text + comment_text = {'text': '', 'submission_id': ''} + + comment_row['commentText'].append(comment_text) + + # Append the comment row to the list of comments + formatted_comments.append(comment_row) + row_id += 1 - return {'titles': titles, 'comments': sorted_comments} + # Create the final output structure + return {'titles': titles, 'comments': formatted_comments} diff --git a/met-api/src/met_api/services/document_generation_service.py b/met-api/src/met_api/services/document_generation_service.py index cf965c355..f1aa4a4b5 100644 --- a/met-api/src/met_api/services/document_generation_service.py +++ b/met-api/src/met_api/services/document_generation_service.py @@ -70,7 +70,7 @@ def generate_document(self, data, options): generator_options = { 'cachereport': False, - 'convertTo': options.get('convert_to', 'csv'), + 'convertTo': options.get('convert_to', 'xlsx'), 'overwrite': True, 'reportName': options.get('report_name', 'report') } diff --git a/met-api/src/met_api/services/email_verification_service.py b/met-api/src/met_api/services/email_verification_service.py index 0405b2c47..d361e73bb 100644 --- a/met-api/src/met_api/services/email_verification_service.py +++ b/met-api/src/met_api/services/email_verification_service.py @@ -13,7 +13,7 @@ from met_api.models import Survey as SurveyModel from met_api.models import Tenant as TenantModel from met_api.models.email_verification import EmailVerification -from met_api.models.engagement_metadata import EngagementMetadataModel +# from met_api.models.engagement_metadata import EngagementMetadataModel from met_api.schemas.email_verification import EmailVerificationSchema from met_api.services.participant_service import ParticipantService from met_api.utils import notification @@ -193,9 +193,7 @@ def _render_survey_email_template(survey: SurveyModel, token): engagement_name = engagement.name paths = current_app.config['PATH_CONFIG'] templates = current_app.config['EMAIL_TEMPLATES'] - template_id = templates['VERIFICATION']['ID'] subject_template = templates['VERIFICATION']['SUBJECT'] - email_environment = templates['ENVIRONMENT'] template = Template.get_template('email_verification.html') survey_path = paths['SURVEY'].format(survey_id=survey.id, token=token) engagement_path = EmailVerificationService.get_engagement_path(engagement) @@ -207,7 +205,7 @@ def _render_survey_email_template(survey: SurveyModel, token): 'engagement_url': f'{site_url}{engagement_path}', 'tenant_name': tenant_name, 'end_date': datetime.strftime(engagement.end_date, EmailVerificationService.full_date_format), - 'email_environment': email_environment, + 'email_environment': templates['ENVIRONMENT'], } subject = subject_template.format(engagement_name=engagement_name) body = template.render( @@ -218,7 +216,7 @@ def _render_survey_email_template(survey: SurveyModel, token): end_date=args.get('end_date'), email_environment=args.get('email_environment'), ) - return subject, body, args, template_id + return subject, body, args, templates['VERIFICATION']['ID'] @staticmethod def get_engagement_path(engagement: EngagementModel, is_public_url=True): @@ -237,7 +235,7 @@ def _get_tenant_name(tenant_id): @staticmethod def _get_project_name(subscription_type, tenant_name, engagement): - metadata_model: EngagementMetadataModel = EngagementMetadataModel.find_by_id(engagement.id) + # metadata_model: EngagementMetadataModel = EngagementMetadataModel.find_by_id(engagement.id) if subscription_type == SubscriptionTypes.TENANT.value: return tenant_name diff --git a/met-api/src/met_api/services/engagement_service.py b/met-api/src/met_api/services/engagement_service.py index 7f1ef3d6a..be7c562d4 100644 --- a/met-api/src/met_api/services/engagement_service.py +++ b/met-api/src/met_api/services/engagement_service.py @@ -27,6 +27,7 @@ from met_api.utils.token_info import TokenInfo from met_api.models import Tenant as TenantModel + class EngagementService: """Engagement management service.""" @@ -68,7 +69,7 @@ def get_engagements_paginated( user_roles = TokenInfo.get_user_roles() has_team_access = search_options.get('has_team_access') scope_options = self._get_scope_options(user_roles, has_team_access) - + items, total = EngagementModel.get_engagements_paginated( external_user_id, pagination_options, diff --git a/met-api/src/met_api/services/keycloak.py b/met-api/src/met_api/services/keycloak.py index 815976ad9..76740b2ed 100644 --- a/met-api/src/met_api/services/keycloak.py +++ b/met-api/src/met_api/services/keycloak.py @@ -109,8 +109,8 @@ def _get_admin_token(): headers = { 'Content-Type': 'application/x-www-form-urlencoded' } - TOKEN_ISSUER = current_app.config['JWT_CONFIG']['ISSUER'] - token_url = f'{TOKEN_ISSUER}/protocol/openid-connect/token' + token_issuer = current_app.config['JWT_CONFIG']['ISSUER'] + token_url = f'{token_issuer}/protocol/openid-connect/token' response = requests.post( token_url, diff --git a/met-api/src/met_api/services/object_storage_service.py b/met-api/src/met_api/services/object_storage_service.py index 3d6b88e75..be5500bf1 100644 --- a/met-api/src/met_api/services/object_storage_service.py +++ b/met-api/src/met_api/services/object_storage_service.py @@ -1,4 +1,3 @@ - """Service for object storage management.""" import os import uuid @@ -18,15 +17,15 @@ class ObjectStorageService: def __init__(self): """Initialize the service.""" # initialize s3 config from environment variables - s3 = current_app.config['S3_CONFIG'] + s3_client = current_app.config['S3_CONFIG'] self.s3_auth = AWSRequestsAuth( - aws_access_key=s3["ACCESS_KEY_ID"], - aws_secret_access_key=s3["SECRET_ACCESS_KEY"], - aws_host=s3["HOST"], - aws_region=s3["REGION"], - aws_service=s3["SERVICE"] + aws_access_key=s3_client['ACCESS_KEY_ID'], + aws_secret_access_key=s3_client['SECRET_ACCESS_KEY'], + aws_host=s3_client['HOST'], + aws_region=s3_client['REGION'], + aws_service=s3_client['SERVICE'] ) - self.s3_bucket = s3["BUCKET"] + self.s3_bucket = s3_client['BUCKET'] def get_url(self, filename: string): """Get the object url.""" @@ -36,16 +35,15 @@ def get_url(self, filename: string): ): return '' - return f'https://{self.s3_auth.aws_host}/{self.s3_bucket}/{filename}' def get_auth_headers(self, documents: List[Document]): """Get the S3 auth headers for the provided documents.""" if ( - self.s3_auth.aws_access_key is None - or self.s3_auth.aws_secret_access_key is None - or self.s3_auth.aws_host is None - or self.s3_bucket is None + self.s3_auth.aws_access_key is None or + self.s3_auth.aws_secret_access_key is None or + self.s3_auth.aws_host is None or + self.s3_bucket is None ): return { 'status': 'Configuration Issue', @@ -58,7 +56,7 @@ def get_auth_headers(self, documents: List[Document]): uniquefilename = f'{uuid.uuid4()}{filenamesplittext[1]}' s3uri = s3sourceuri if s3sourceuri is not None else self.get_url(uniquefilename) - + if s3sourceuri is None: response = requests.put(s3uri, data=None, auth=self.s3_auth) else: diff --git a/met-api/src/met_api/services/slug_generation_service.py b/met-api/src/met_api/services/slug_generation_service.py index c91df42f2..96de62b78 100644 --- a/met-api/src/met_api/services/slug_generation_service.py +++ b/met-api/src/met_api/services/slug_generation_service.py @@ -2,6 +2,7 @@ from slugify import UniqueSlugify from met_api.config import Config + class SlugGenerationService: """Service for generating slugs.""" diff --git a/met-api/src/met_api/services/submission_service.py b/met-api/src/met_api/services/submission_service.py index 41e28352b..a3ccdc666 100644 --- a/met-api/src/met_api/services/submission_service.py +++ b/met-api/src/met_api/services/submission_service.py @@ -328,9 +328,7 @@ def _send_rejected_email(staff_review_details: dict, submission: SubmissionModel """Send an verification email.Throws error if fails.""" participant_id = submission.participant_id participant = ParticipantModel.find_by_id(participant_id) - templates = current_app.config.get('EMAIL_TEMPLATES') - template_id = templates['REJECTED']['ID'] - subject, body, args = SubmissionService._render_email_template( + template_id, subject, body, args = SubmissionService._render_email_template( staff_review_details, submission, review_note, token) try: notification.send_email(subject=subject, @@ -349,13 +347,23 @@ def _send_rejected_email(staff_review_details: dict, submission: SubmissionModel @staticmethod # pylint: disable-msg=too-many-locals def _render_email_template(staff_review_details: dict, submission: SubmissionModel, review_note, token): - template = Template.get_template('email_rejected_comment.html') engagement: EngagementModel = EngagementModel.find_by_id( submission.engagement_id) - survey: SurveyModel = SurveyModel.find_by_id(submission.survey_id) templates = current_app.config['EMAIL_TEMPLATES'] paths = current_app.config['PATH_CONFIG'] engagement_name = engagement.name + templates = current_app.config.get('EMAIL_TEMPLATES') + if engagement.status_id == EngagementStatus.Closed.value: + template_id = templates['CLOSED_ENGAGEMENT_REJECTED']['ID'] + template = Template.get_template('email_rejected_comment_closed.html') + subject = templates['CLOSED_ENGAGEMENT_REJECTED']['SUBJECT']. \ + format(engagement_name=engagement_name) + else: + template_id = templates['REJECTED']['ID'] + template = Template.get_template('email_rejected_comment.html') + subject = templates['REJECTED']['SUBJECT']. \ + format(engagement_name=engagement_name) + survey: SurveyModel = SurveyModel.find_by_id(submission.survey_id) survey_name = survey.name tenant_name = SubmissionService._get_tenant_name( engagement.tenant_id) @@ -365,7 +373,6 @@ def _render_email_template(staff_review_details: dict, submission: SubmissionMod ) submission_url = notification.get_tenant_site_url( engagement.tenant_id, submission_path) - subject = templates['REJECTED']['SUBJECT'] email_environment = templates['ENVIRONMENT'] args = { 'engagement_name': engagement_name, @@ -391,7 +398,7 @@ def _render_email_template(staff_review_details: dict, submission: SubmissionMod end_date=args.get('end_date'), email_environment=args.get('email_environment'), ) - return subject, body, args + return template_id, subject, body, args @staticmethod def _send_submission_response_email(participant_id, engagement_id) -> None: diff --git a/met-api/src/met_api/services/survey_service.py b/met-api/src/met_api/services/survey_service.py index 2e4b9dc59..61cc05acc 100644 --- a/met-api/src/met_api/services/survey_service.py +++ b/met-api/src/met_api/services/survey_service.py @@ -180,7 +180,7 @@ def update(cls, data: SurveySchema): is_template = survey.get('is_template', None) cls.validate_template_surveys_edit_access(is_template, user_roles) - if engagement and engagement.get('status_id', None) != Status.Draft.value: + if engagement and engagement.get('status_id', None) not in [Status.Draft.value, Status.Published.value]: raise ValueError('Engagement already published') updated_survey = SurveyModel.update_survey(data) diff --git a/met-api/src/met_api/utils/constants.py b/met-api/src/met_api/utils/constants.py index d0a81c7de..4ef88d2fc 100644 --- a/met-api/src/met_api/utils/constants.py +++ b/met-api/src/met_api/utils/constants.py @@ -39,9 +39,11 @@ def get_name_by_value(value): TENANT_ID_JWT_CLAIM = 'tenant_id' + class TestKeyConfig: # pylint: disable=too-few-public-methods - """This config is used to hold bulky test keys so they don't clutter up the - main configuration file for the app. + """ + This config is used to hold bulky test keys so they don't clutter up the main configuration file for the app. + These keys are publicly available and can be used for testing purposes. """ diff --git a/met-api/src/met_api/utils/tenant_validator.py b/met-api/src/met_api/utils/tenant_validator.py index 2696b6433..80d64b6e4 100644 --- a/met-api/src/met_api/utils/tenant_validator.py +++ b/met-api/src/met_api/utils/tenant_validator.py @@ -23,7 +23,6 @@ from flask import abort, current_app, g from met_api.auth import jwt as _jwt -from met_api.utils.constants import TENANT_ID_JWT_CLAIM from met_api.utils.roles import Role from met_api.models.staff_user import StaffUser diff --git a/met-api/src/met_api/utils/user_context.py b/met-api/src/met_api/utils/user_context.py index f09bcf38a..9ca283659 100644 --- a/met-api/src/met_api/utils/user_context.py +++ b/met-api/src/met_api/utils/user_context.py @@ -16,7 +16,7 @@ import functools from typing import Dict -from flask import g, request, current_app +from flask import current_app, g, request from met_api.utils.constants import TENANT_ID_JWT_CLAIM from met_api.utils.roles import Role diff --git a/met-api/src/met_api/utils/util.py b/met-api/src/met_api/utils/util.py index 91a0d1e5d..fdfb642c4 100644 --- a/met-api/src/met_api/utils/util.py +++ b/met-api/src/met_api/utils/util.py @@ -23,6 +23,7 @@ from humps.main import camelize, decamelize + def cors_preflight(methods): """Render an option method on the class.""" @@ -40,6 +41,7 @@ def options(self, *args, **kwargs): # pylint: disable=unused-argument return wrapper + def is_truthy(value: str) -> bool: """ Check if a value is truthy or not. @@ -50,7 +52,6 @@ def is_truthy(value: str) -> bool: return str(value).lower() in ('1', 'true', 'yes', 'y', 'on') - def camelback2snake(camel_dict: dict): """Convert the passed dictionary's keys from camelBack case to snake_case.""" return decamelize(camel_dict) @@ -60,6 +61,7 @@ def snake2camelback(snake_dict: dict): """Convert the passed dictionary's keys from snake_case to camelBack case.""" return camelize(snake_dict) + def allowedorigins(): """Return the allowed origins for CORS.""" return os.getenv('CORS_ORIGINS', '').split(',') diff --git a/met-api/templates/email_rejected_comment_closed.html b/met-api/templates/email_rejected_comment_closed.html new file mode 100644 index 000000000..79179dc09 --- /dev/null +++ b/met-api/templates/email_rejected_comment_closed.html @@ -0,0 +1,30 @@ +

Thank you for taking the time to provide your feedback on {{ engagement_name }}.

+
+

We have reviewed your feedback and can't accept it for the following reason(s):

+
+ +
+

{{review_notes}}

+
+

+ We are sorry but the public commenting period for {{ engagement_name }} + is now closed and your feedback cannot be edited. +

+
+

Thank you,

+
+

The {{tenant_name}} Team

+

{{email_environment}}

\ No newline at end of file diff --git a/met-etl/src/etl_project/services/ops/submission_etl_service.py b/met-etl/src/etl_project/services/ops/submission_etl_service.py index 24c46d8e7..059ce4ffd 100644 --- a/met-etl/src/etl_project/services/ops/submission_etl_service.py +++ b/met-etl/src/etl_project/services/ops/submission_etl_service.py @@ -286,7 +286,7 @@ def _save_survey(met_etl_session, context, answer_key, component, survey, partic # id and the key radio_response = EtlResponseTypeOptionModel( survey_id=survey.id, - request_key=component['key'], + request_key=component['key']+'-'+key, value=answer_label, request_id=component['id']+'-'+key, participant_id=getattr(participant, 'id', None), diff --git a/met-etl/src/etl_project/services/ops/survey_etl_service.py b/met-etl/src/etl_project/services/ops/survey_etl_service.py index 151719ade..d0ab4085f 100644 --- a/met-etl/src/etl_project/services/ops/survey_etl_service.py +++ b/met-etl/src/etl_project/services/ops/survey_etl_service.py @@ -1,12 +1,13 @@ -from dagster import Out, Output, op -from sqlalchemy import func -from datetime import datetime - +from analytics_api.models.available_response_option import AvailableResponseOption as EtlAvailableResponseOption from analytics_api.models.etlruncycle import EtlRunCycle as EtlRunCycleModel from analytics_api.models.request_type_option import RequestTypeOption as EtlRequestTypeOption +from analytics_api.models.response_type_option import ResponseTypeOption as EtlResponseTypeOptionModel from analytics_api.models.survey import Survey as EtlSurveyModel -from met_api.models.survey import Survey as MetSurveyModel from analytics_api.utils.util import FormIoComponentType +from dagster import Out, Output, op +from datetime import datetime +from met_api.models.survey import Survey as MetSurveyModel +from sqlalchemy import func # get the last run cycle id for survey etl @@ -40,7 +41,8 @@ def get_survey_last_run_cycle_time(context, flag_to_run_step_after_engagement): # extract the surveys that have been created or updated after the last run -@op(required_resource_keys={"met_db_session", "met_etl_db_session"}, out={"new_survey": Out(), "updated_survey": Out(), "survey_new_runcycleid": Out()}) +@op(required_resource_keys={"met_db_session", "met_etl_db_session"}, + out={"new_survey": Out(), "updated_survey": Out(), "survey_new_runcycleid": Out()}) def extract_survey(context, survey_last_run_cycle_time, survey_new_runcycleid): session = context.resources.met_db_session default_datetime = datetime(1900, 1, 1, 0, 0, 0, 0) @@ -71,7 +73,7 @@ def extract_survey(context, survey_last_run_cycle_time, survey_new_runcycleid): # load the surveys created or updated after last run to the analytics database -@op(required_resource_keys={"met_db_session", "met_etl_db_session"},out={"survey_new_runcycleid": Out()}) +@op(required_resource_keys={"met_db_session", "met_etl_db_session"}, out={"survey_new_runcycleid": Out()}) def load_survey(context, new_survey, updated_survey, survey_new_runcycleid): session = context.resources.met_etl_db_session all_surveys = new_survey + updated_survey @@ -83,23 +85,29 @@ def load_survey(context, new_survey, updated_survey, survey_new_runcycleid): _do_etl_survey_data(session, survey, survey_new_runcycleid) + _update_survey_responses_with_active_survey_id(session, survey) + if survey.form_json is None: context.log.info('Survey Found without form_json: %s. Skipping it', survey.id) continue form_type = survey.form_json.get('display', None) + page_position = 0 # Initialize the page-level position + # check and load data for single page survey. if form_type == 'form': form_components = survey.form_json.get('components', None) - extract_survey_components(context, session, survey, survey_new_runcycleid, form_components) + page_position = extract_survey_components(context, session, survey, survey_new_runcycleid, + form_components, page_position) # check and load data for multi page survey. if form_type == 'wizard': pages = survey.form_json.get('components', None) for page in pages: form_components = page.get('components', None) - extract_survey_components(context, session, survey, survey_new_runcycleid, form_components) + page_position = extract_survey_components(context, session, survey, survey_new_runcycleid, + form_components, page_position) yield Output(survey_new_runcycleid, "survey_new_runcycleid") @@ -107,25 +115,24 @@ def load_survey(context, new_survey, updated_survey, survey_new_runcycleid): session.close() + # extract components within a survey -def extract_survey_components(context, session, survey, survey_new_runcycleid, form_components): +def extract_survey_components(context, session, survey, survey_new_runcycleid, form_components, position): if (form_components) is None: context.log.info('Survey Found without any component in form_json: %s. Skipping it', survey.id) - return + return position - _inactivate_old_questions(session, survey.id) - - position = 0 + _refresh_questions_and_available_option_status(session, survey.id) for component in form_components: position = position + 1 component_type = component.get('type', None) context.log.info('Survey: %s.%s Processing component with id %s and type: %s and label %s ', - survey.id, - survey.name, - component.get('id', None), - component_type, - component.get('label', None)) + survey.id, + survey.name, + component.get('id', None), + component_type, + component.get('label', None)) if not component_type: continue @@ -134,13 +141,18 @@ def extract_survey_components(context, session, survey, survey_new_runcycleid, f if has_valid_question_type: etl_survey = session.query(EtlSurveyModel.id).filter(EtlSurveyModel.source_survey_id == survey.id, - EtlSurveyModel.is_active == True) + EtlSurveyModel.is_active == True) for survey_id in etl_survey: - _do_etl_survey_inputs(session, survey_id, component, component_type, - survey_new_runcycleid, position) + position = _do_etl_survey_inputs(session, survey_id, component, component_type, + survey_new_runcycleid, position) + _load_available_response_option(context, session, survey_id, component, component_type, + survey_new_runcycleid) + + return position + # inactivate if record is existing in analytics database -def _inactivate_old_questions(session, source_survey_id): +def _refresh_questions_and_available_option_status(session, source_survey_id): etl_survey_model = session.query(EtlSurveyModel.id).filter(EtlSurveyModel.source_survey_id == source_survey_id, EtlSurveyModel.is_active == False) if not etl_survey_model: @@ -150,6 +162,8 @@ def _inactivate_old_questions(session, source_survey_id): for survey_id in etl_survey_model: session.query(EtlRequestTypeOption).filter(EtlRequestTypeOption.survey_id == survey_id).update(deactive_flag) + session.query(EtlAvailableResponseOption).filter( + EtlAvailableResponseOption.survey_id == survey_id).update(deactive_flag) def _do_etl_survey_data(session, survey, survey_new_runcycleid): @@ -171,37 +185,100 @@ def _do_etl_survey_inputs(session, survey_id, component, component_type, survey_ questions = component.get('questions', None) if not questions: - return + return position for question in questions: + position = position + 1 model_name = EtlRequestTypeOption(survey_id=survey_id, - request_id=component['id'] + '-' + question['value'], - label=question['label'], - is_active=True, - key=component['key'] + '-' + question['value'], - type=component['type'], - runcycle_id=survey_new_runcycleid, - position=position - ) + request_id=component['id'] + '-' + question['value'], + label=question['label'], + is_active=True, + key=component['key'] + '-' + question['value'], + type=component['type'], + runcycle_id=survey_new_runcycleid, + position=position + ) session.add(model_name) session.commit() else: model_name = EtlRequestTypeOption(survey_id=survey_id, - request_id=component['id'], - label=component['label'], - is_active=True, - key=component['key'], - type=component['type'], - runcycle_id=survey_new_runcycleid, - position=position - ) + request_id=component['id'], + label=component['label'], + is_active=True, + key=component['key'], + type=component['type'], + runcycle_id=survey_new_runcycleid, + position=position + ) session.add(model_name) session.commit() + return position + + +# load data to table available response option +def _load_available_response_option(context, session, survey_id, component, component_type, survey_new_runcycleid): + + if component_type == FormIoComponentType.SURVEY.value: + _load_survey_available_response(session, component, survey_id, survey_new_runcycleid) + elif component_type == FormIoComponentType.SELECTLIST.value: + _load_selectlist_available_response(session, component, survey_id, survey_new_runcycleid) + else: + _load_default_available_response(session, component, survey_id, survey_new_runcycleid) + + +def _load_survey_available_response(session, component, survey_id, survey_new_runcycleid): + values = component.get('values', None) + if not values: + return + + questions = component.get('questions', None) + if not questions: + return + + for question in questions: + request_key = component['key'] + '-' + question['value'] + _do_etl_available_response_data(session, component, survey_id, values, + request_key, survey_new_runcycleid) + +def _load_selectlist_available_response(session, component, survey_id, survey_new_runcycleid): + data = component.get('data', None) + values = data.get('values', None) + + if not values: + return + + request_key = component['key'] + _do_etl_available_response_data(session, component, survey_id, values, + request_key, survey_new_runcycleid) + +def _load_default_available_response(session, component, survey_id, survey_new_runcycleid): + values = component.get('values', None) + if not values: + return + + request_key = component['key'] + _do_etl_available_response_data(session, component, survey_id, values, + request_key, survey_new_runcycleid) + + +def _do_etl_available_response_data(session, component, survey_id, values, request_key, survey_new_runcycleid): + for value in values: + model_name = EtlAvailableResponseOption(survey_id=survey_id, + request_key=request_key, + value=value['label'], + request_id=component['id'], + is_active=True, + runcycle_id=survey_new_runcycleid) + + session.add(model_name) + + session.commit() + def _validate_form_type(context, component_type): component_type = component_type.lower() @@ -222,7 +299,8 @@ def survey_end_run_cycle(context, survey_new_runcycleid): met_etl_db_session.query(EtlRunCycleModel).filter( EtlRunCycleModel.id == survey_new_runcycleid, EtlRunCycleModel.packagename == 'survey', EtlRunCycleModel.success == False).update( - {'success': True, 'enddatetime': datetime.utcnow(), 'description': 'ended the load for tables survey and requests'}) + {'success': True, 'enddatetime': datetime.utcnow(), + 'description': 'ended the load for tables survey and requests'}) context.log.info("run cycle ended for survey table") @@ -231,3 +309,25 @@ def survey_end_run_cycle(context, survey_new_runcycleid): met_etl_db_session.close() yield Output("survey", "flag_to_run_step_after_survey") + + +def _update_survey_responses_with_active_survey_id(session, survey): + etl_active_survey_id = session.query(EtlSurveyModel.id).filter( + EtlSurveyModel.source_survey_id == survey.id, EtlSurveyModel.is_active == True).first() + + subquery = ( + session.query(EtlSurveyModel.id) + .filter(EtlSurveyModel.source_survey_id == survey.id) + .subquery() + ) + + # Fetch response records + response_records = session.query(EtlResponseTypeOptionModel).filter( + EtlResponseTypeOptionModel.survey_id.in_(subquery)).all() + + # Update each response record individually + for record in response_records: + session.query(EtlResponseTypeOptionModel).filter(EtlResponseTypeOptionModel.id == record.id).update( + {'survey_id': etl_active_survey_id}, synchronize_session='fetch') + + session.commit() diff --git a/met-web/package-lock.json b/met-web/package-lock.json index adc464edd..6ab3b942d 100644 --- a/met-web/package-lock.json +++ b/met-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "client", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "client", - "version": "1.0.1", + "version": "1.1.0", "dependencies": { "@arcgis/core": "^4.26.5", "@bcgov/bc-sans": "^2.0", @@ -50,7 +50,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.13.0", "maplibre-gl": "^1.15.3", - "met-formio": "^1.0.13-rc1", + "met-formio": "^1.0.14-rc1", "mui-sx": "^1.0.0", "node-sass": "^7.0.3", "react": "^18.0.0", @@ -3357,7 +3357,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -3369,7 +3369,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4175,6 +4175,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, "dependencies": { "@jest/console": "^28.1.3", "@jest/reporters": "^28.1.3", @@ -4222,6 +4223,7 @@ "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" @@ -4237,6 +4239,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -4251,6 +4254,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -4262,6 +4266,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, "dependencies": { "@jest/fake-timers": "^28.1.3", "@jest/types": "^28.1.3", @@ -4276,6 +4281,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, "dependencies": { "expect": "^28.1.3", "jest-snapshot": "^28.1.3" @@ -4288,6 +4294,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, "dependencies": { "jest-get-type": "^28.0.2" }, @@ -4299,6 +4306,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, "dependencies": { "@jest/types": "^28.1.3", "@sinonjs/fake-timers": "^9.1.2", @@ -4315,6 +4323,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -4328,6 +4337,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^28.1.3", @@ -4371,6 +4381,7 @@ "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" @@ -4397,6 +4408,7 @@ "version": "28.1.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.13", "callsites": "^3.0.0", @@ -4424,6 +4436,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, "dependencies": { "@jest/test-result": "^28.1.3", "graceful-fs": "^4.2.9", @@ -4438,6 +4451,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -4463,6 +4477,7 @@ "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" @@ -5283,6 +5298,7 @@ "version": "9.1.2", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, "dependencies": { "@sinonjs/commons": "^1.7.0" } @@ -5662,25 +5678,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "dev": true }, "node_modules/@turf/along": { "version": "6.5.0", @@ -8888,6 +8904,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, "dependencies": { "@jest/transform": "^28.1.3", "@types/babel__core": "^7.1.14", @@ -8908,6 +8925,7 @@ "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" @@ -9001,6 +9019,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -9130,6 +9149,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, "dependencies": { "babel-plugin-jest-hoist": "^28.1.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -10400,7 +10420,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "dev": true }, "node_modules/cross-fetch": { "version": "3.1.8", @@ -11518,7 +11538,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.3.1" } @@ -13026,6 +13046,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, "dependencies": { "@jest/expect-utils": "^28.1.3", "jest-get-type": "^28.0.2", @@ -13041,6 +13062,7 @@ "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" @@ -13056,6 +13078,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -13064,6 +13087,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -13078,6 +13102,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -13092,6 +13117,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -13106,6 +13132,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -15606,6 +15633,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -15631,6 +15659,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, "dependencies": { "execa": "^5.0.0", "p-limit": "^3.1.0" @@ -15643,6 +15672,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -15672,6 +15702,7 @@ "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" @@ -15687,6 +15718,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -15695,6 +15727,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -15709,6 +15742,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -15723,6 +15757,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -15737,6 +15772,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -15748,6 +15784,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^28.1.3", @@ -15792,6 +15829,7 @@ "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" @@ -15807,6 +15845,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -15821,6 +15860,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -15869,6 +15909,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -15880,6 +15921,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, "dependencies": { "@jest/types": "^28.1.3", "chalk": "^4.0.0", @@ -15895,6 +15937,7 @@ "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" @@ -15910,6 +15953,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -15924,6 +15968,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -15954,6 +15999,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -15995,6 +16041,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -16003,6 +16050,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -16485,6 +16533,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, "dependencies": { "jest-get-type": "^28.0.2", "pretty-format": "^28.1.3" @@ -16497,6 +16546,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -16508,6 +16558,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -16618,6 +16669,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*" @@ -16654,6 +16706,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -16673,6 +16726,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, "dependencies": { "jest-regex-util": "^28.0.2", "jest-snapshot": "^28.1.3" @@ -16685,6 +16739,7 @@ "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" @@ -16700,6 +16755,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, "dependencies": { "@jest/console": "^28.1.3", "@jest/environment": "^28.1.3", @@ -16731,6 +16787,7 @@ "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" @@ -16746,6 +16803,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -16778,6 +16836,7 @@ "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" @@ -16805,6 +16864,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -16838,6 +16898,7 @@ "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" @@ -16853,6 +16914,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -16861,6 +16923,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -16875,6 +16938,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -16889,6 +16953,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -16903,6 +16968,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -16951,6 +17017,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, "dependencies": { "@jest/types": "^28.1.3", "camelcase": "^6.2.0", @@ -16967,6 +17034,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "engines": { "node": ">=10" }, @@ -16978,6 +17046,7 @@ "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" @@ -16993,6 +17062,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -17007,6 +17077,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -17172,6 +17243,7 @@ "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" @@ -17187,6 +17259,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/test-result": "^28.1.3", @@ -17723,7 +17796,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "node_modules/make-fetch-happen": { "version": "9.1.0", @@ -17870,39 +17943,6 @@ "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" }, - "node_modules/maplibre-gl/node_modules/mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", - "peer": true, - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/geojson-types": "^1.0.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^1.5.0", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^1.1.1", - "@mapbox/unitbezier": "^0.0.0", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.2", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.2.1", - "grid-index": "^1.1.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.1", - "quickselect": "^2.0.0", - "rw": "^1.3.3", - "supercluster": "^7.1.0", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.1" - }, - "engines": { - "node": ">=6.4.0" - } - }, "node_modules/maplibre-gl/node_modules/potpack": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", @@ -18028,9 +18068,9 @@ } }, "node_modules/met-formio": { - "version": "1.0.13-rc1", - "resolved": "https://registry.npmjs.org/met-formio/-/met-formio-1.0.13-rc1.tgz", - "integrity": "sha512-nRLbWJyrvQUjr1da7i5FLe0k7IB84r05kMrDL7osBVTml64U9zVbKjtI/YTEoa6DlCiDHoUjtToZM1x9zvOhyA==", + "version": "1.0.14-rc1", + "resolved": "https://registry.npmjs.org/met-formio/-/met-formio-1.0.14-rc1.tgz", + "integrity": "sha512-JetCvoIhfzsVSzgn7/PgC83NTl5Nq19iM2ndq5tmV59y0Iul0hDBZJssxrHOFXasBldEeKsIES/ltIGAVJFXWw==", "dependencies": { "formiojs": "^4.14.6", "lodash": "^4.17.21", @@ -25130,7 +25170,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "devOptional": true, + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -25173,7 +25213,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.4.0" } @@ -25182,7 +25222,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.14.2", @@ -25630,12 +25670,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -26491,6 +26532,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -26615,7 +26657,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } @@ -27497,8 +27539,7 @@ "@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==", - "requires": {} + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==" }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.18.6", @@ -28982,7 +29023,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -28991,7 +29032,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -29116,14 +29157,12 @@ "@csstools/postcss-unset-value": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "requires": {} + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" }, "@csstools/selector-specificity": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "requires": {} + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==" }, "@date-io/core": { "version": "2.16.0", @@ -29456,8 +29495,7 @@ "@hookform/resolvers": { "version": "2.9.8", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.8.tgz", - "integrity": "sha512-iVVjH0USq+1TqDdGkWe2M1x7Wn5OFPgVRo7CbWFsXTqqXqCaZtZcnzJu+UhljCWbthFWxWGXKLGYUDPZ04oVvQ==", - "requires": {} + "integrity": "sha512-iVVjH0USq+1TqDdGkWe2M1x7Wn5OFPgVRo7CbWFsXTqqXqCaZtZcnzJu+UhljCWbthFWxWGXKLGYUDPZ04oVvQ==" }, "@humanwhocodes/config-array": { "version": "0.9.5", @@ -29526,6 +29564,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, "requires": { "@jest/console": "^28.1.3", "@jest/reporters": "^28.1.3", @@ -29562,6 +29601,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -29571,6 +29611,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -29581,7 +29622,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -29591,6 +29633,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, "requires": { "@jest/fake-timers": "^28.1.3", "@jest/types": "^28.1.3", @@ -29602,6 +29645,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, "requires": { "expect": "^28.1.3", "jest-snapshot": "^28.1.3" @@ -29611,6 +29655,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, "requires": { "jest-get-type": "^28.0.2" } @@ -29619,6 +29664,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, "requires": { "@jest/types": "^28.1.3", "@sinonjs/fake-timers": "^9.1.2", @@ -29632,6 +29678,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, "requires": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -29642,6 +29689,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^28.1.3", @@ -29674,6 +29722,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -29693,6 +29742,7 @@ "version": "28.1.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.13", "callsites": "^3.0.0", @@ -29714,6 +29764,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, "requires": { "@jest/test-result": "^28.1.3", "graceful-fs": "^4.2.9", @@ -29725,6 +29776,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -29747,6 +29799,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -29992,8 +30045,7 @@ "@mui/types": { "version": "7.2.4", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", - "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", - "requires": {} + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==" }, "@mui/utils": { "version": "5.13.7", @@ -30224,6 +30276,7 @@ "version": "9.1.2", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" } @@ -30461,25 +30514,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "dev": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "dev": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "dev": true }, "@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "dev": true }, "@turf/along": { "version": "6.5.0", @@ -32676,14 +32729,12 @@ "acorn-import-assertions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "requires": {} + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "acorn-walk": { "version": "7.2.0", @@ -33067,6 +33118,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, "requires": { "@jest/transform": "^28.1.3", "@types/babel__core": "^7.1.14", @@ -33081,6 +33133,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -33113,8 +33166,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -33149,6 +33201,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, "requires": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -33169,8 +33222,7 @@ "babel-plugin-named-asset-import": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "requires": {} + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" }, "babel-plugin-polyfill-corejs2": { "version": "0.4.4", @@ -33256,6 +33308,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, "requires": { "babel-plugin-jest-hoist": "^28.1.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -34243,7 +34296,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "dev": true }, "cross-fetch": { "version": "3.1.8", @@ -34333,8 +34386,7 @@ "css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", - "requires": {} + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==" }, "css-has-pseudo": { "version": "3.0.4", @@ -34427,8 +34479,7 @@ "css-prefers-color-scheme": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "requires": {} + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" }, "css-select": { "version": "2.1.0", @@ -34547,8 +34598,7 @@ "cssnano-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "requires": {} + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" }, "csso": { "version": "4.2.0", @@ -34766,8 +34816,7 @@ "date-fns-tz": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.6.tgz", - "integrity": "sha512-C8q7mErvG4INw1ZwAFmPlGjEo5Sv4udjKVbTc03zpP9cu6cp5AemFzKhz0V68LGcWEtX5mJudzzg3G04emIxLA==", - "requires": {} + "integrity": "sha512-C8q7mErvG4INw1ZwAFmPlGjEo5Sv4udjKVbTc03zpP9cu6cp5AemFzKhz0V68LGcWEtX5mJudzzg3G04emIxLA==" }, "dayjs": { "version": "1.11.6", @@ -35074,7 +35123,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true + "dev": true }, "diff-sequences": { "version": "27.5.1", @@ -35252,8 +35301,7 @@ "draftjs-utils": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz", - "integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==", - "requires": {} + "integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==" }, "dragula": { "version": "3.7.3", @@ -35720,8 +35768,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} + "dev": true }, "eslint-config-react-app": { "version": "7.0.1", @@ -35943,8 +35990,7 @@ "eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" }, "eslint-plugin-testing-library": { "version": "5.11.0", @@ -36197,6 +36243,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, "requires": { "@jest/expect-utils": "^28.1.3", "jest-get-type": "^28.0.2", @@ -36209,6 +36256,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -36217,12 +36265,14 @@ "diff-sequences": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==" + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true }, "jest-diff": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -36234,6 +36284,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -36245,6 +36296,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -36255,7 +36307,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -36642,8 +36695,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "chalk": { "version": "4.1.2", @@ -37375,8 +37427,7 @@ "html-to-draftjs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/html-to-draftjs/-/html-to-draftjs-1.5.0.tgz", - "integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==", - "requires": {} + "integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==" }, "html-to-image": { "version": "1.11.11", @@ -37576,8 +37627,7 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "idb": { "version": "6.1.5", @@ -38083,6 +38133,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, "requires": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -38094,6 +38145,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38103,6 +38155,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, "requires": { "@jest/core": "^28.1.3", "@jest/test-result": "^28.1.3", @@ -38124,6 +38177,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, "requires": { "execa": "^5.0.0", "p-limit": "^3.1.0" @@ -38133,6 +38187,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, "requires": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -38159,6 +38214,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38167,12 +38223,14 @@ "diff-sequences": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==" + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true }, "jest-diff": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -38184,6 +38242,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -38195,6 +38254,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -38205,7 +38265,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -38215,6 +38276,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, "requires": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^28.1.3", @@ -38244,6 +38306,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38253,6 +38316,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -38263,7 +38327,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -38300,6 +38365,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, "requires": { "detect-newline": "^3.0.0" } @@ -38308,6 +38374,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, "requires": { "@jest/types": "^28.1.3", "chalk": "^4.0.0", @@ -38320,6 +38387,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38329,6 +38397,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -38339,7 +38408,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -38365,6 +38435,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, "requires": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -38395,12 +38466,14 @@ "jest-get-type": { "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==" + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true }, "jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, "requires": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -38793,6 +38866,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, "requires": { "jest-get-type": "^28.0.2", "pretty-format": "^28.1.3" @@ -38801,12 +38875,14 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true }, "pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -38892,6 +38968,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, "requires": { "@jest/types": "^28.1.3", "@types/node": "*" @@ -38900,8 +38977,7 @@ "jest-pnp-resolver": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "requires": {} + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" }, "jest-regex-util": { "version": "28.0.2", @@ -38912,6 +38988,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -38928,6 +39005,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38939,6 +39017,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, "requires": { "jest-regex-util": "^28.0.2", "jest-snapshot": "^28.1.3" @@ -38948,6 +39027,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, "requires": { "@jest/console": "^28.1.3", "@jest/environment": "^28.1.3", @@ -38976,6 +39056,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -38987,6 +39068,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, "requires": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -39016,6 +39098,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -39036,6 +39119,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, "requires": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -39066,6 +39150,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -39074,12 +39159,14 @@ "diff-sequences": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==" + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true }, "jest-diff": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^28.1.1", @@ -39091,6 +39178,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^28.1.3", @@ -39102,6 +39190,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -39112,7 +39201,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -39152,6 +39242,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, "requires": { "@jest/types": "^28.1.3", "camelcase": "^6.2.0", @@ -39164,12 +39255,14 @@ "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -39179,6 +39272,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "requires": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -39189,7 +39283,8 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } } @@ -39712,7 +39807,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "make-fetch-happen": { "version": "9.1.0", @@ -39828,8 +39923,7 @@ "@mapbox/mapbox-gl-supported": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", - "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", - "requires": {} + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==" }, "@mapbox/tiny-sdf": { "version": "1.2.5", @@ -39841,36 +39935,6 @@ "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" }, - "mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", - "peer": true, - "requires": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/geojson-types": "^1.0.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^1.5.0", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^1.1.1", - "@mapbox/unitbezier": "^0.0.0", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.2", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.2.1", - "grid-index": "^1.1.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.1", - "quickselect": "^2.0.0", - "rw": "^1.3.3", - "supercluster": "^7.1.0", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.1" - } - }, "potpack": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", @@ -39973,9 +40037,9 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "met-formio": { - "version": "1.0.13-rc1", - "resolved": "https://registry.npmjs.org/met-formio/-/met-formio-1.0.13-rc1.tgz", - "integrity": "sha512-nRLbWJyrvQUjr1da7i5FLe0k7IB84r05kMrDL7osBVTml64U9zVbKjtI/YTEoa6DlCiDHoUjtToZM1x9zvOhyA==", + "version": "1.0.14-rc1", + "resolved": "https://registry.npmjs.org/met-formio/-/met-formio-1.0.14-rc1.tgz", + "integrity": "sha512-JetCvoIhfzsVSzgn7/PgC83NTl5Nq19iM2ndq5tmV59y0Iul0hDBZJssxrHOFXasBldEeKsIES/ltIGAVJFXWw==", "requires": { "formiojs": "^4.14.6", "lodash": "^4.17.21", @@ -40937,8 +41001,7 @@ "postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "requires": {} + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" }, "postcss-calc": { "version": "8.2.4", @@ -41036,26 +41099,22 @@ "postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "requires": {} + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "requires": {} + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "requires": {} + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "requires": {} + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" }, "postcss-double-position-gradients": { "version": "3.1.2", @@ -41077,8 +41136,7 @@ "postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "requires": {} + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" }, "postcss-focus-visible": { "version": "6.0.4", @@ -41099,14 +41157,12 @@ "postcss-font-variant": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "requires": {} + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "requires": {} + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" }, "postcss-image-set-function": { "version": "4.0.7", @@ -41129,8 +41185,7 @@ "postcss-initial": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "requires": {} + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" }, "postcss-js": { "version": "4.0.1", @@ -41178,14 +41233,12 @@ "postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "requires": {} + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "requires": {} + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" }, "postcss-merge-longhand": { "version": "5.1.7", @@ -41246,8 +41299,7 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "requires": {} + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { "version": "4.0.3", @@ -41305,8 +41357,7 @@ "postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "requires": {} + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -41377,8 +41428,7 @@ "postcss-opacity-percentage": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "requires": {} + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==" }, "postcss-ordered-values": { "version": "5.1.3", @@ -41400,8 +41450,7 @@ "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "requires": {} + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" }, "postcss-place": { "version": "7.0.5", @@ -41495,8 +41544,7 @@ "postcss-replace-overflow-wrap": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "requires": {} + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" }, "postcss-selector-not": { "version": "6.0.1", @@ -42150,8 +42198,7 @@ "react-hook-form": { "version": "7.37.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.37.0.tgz", - "integrity": "sha512-6NFTxsnw+EXSpNNvLr5nFMjPdYKRryQcelTHg7zwBB6vAzfPIcZq4AExP4heVlwdzntepQgwiOQW4z7Mr99Lsg==", - "requires": {} + "integrity": "sha512-6NFTxsnw+EXSpNNvLr5nFMjPdYKRryQcelTHg7zwBB6vAzfPIcZq4AExP4heVlwdzntepQgwiOQW4z7Mr99Lsg==" }, "react-i18next": { "version": "13.0.2", @@ -42175,8 +42222,7 @@ "react-if": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/react-if/-/react-if-4.1.4.tgz", - "integrity": "sha512-bjufPfCdPBiBy9EO/BeoxaqGc/xCwTu0coKtHfjpJw+v85DLMbpG43IUPISh+m3DzENx1rOYLpqbp2KaDmEYlg==", - "requires": {} + "integrity": "sha512-bjufPfCdPBiBy9EO/BeoxaqGc/xCwTu0coKtHfjpJw+v85DLMbpG43IUPISh+m3DzENx1rOYLpqbp2KaDmEYlg==" }, "react-is": { "version": "18.2.0", @@ -43148,8 +43194,7 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" }, "xml-name-validator": { "version": "3.0.0", @@ -43360,8 +43405,7 @@ "redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "requires": {} + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==" }, "regenerate": { "version": "1.4.2", @@ -43690,8 +43734,7 @@ "rifm": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.12.1.tgz", - "integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==", - "requires": {} + "integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==" }, "rimraf": { "version": "3.0.2", @@ -43869,8 +43912,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -44581,8 +44623,7 @@ "style-loader": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", - "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", - "requires": {} + "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==" }, "stylehacks": { "version": "5.1.1", @@ -45116,7 +45157,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "devOptional": true, + "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -45137,13 +45178,13 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "devOptional": true + "dev": true }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true } } }, @@ -45409,14 +45450,12 @@ "use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", - "requires": {} + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==" }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "requires": {} + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" }, "util": { "version": "0.12.5", @@ -45479,12 +45518,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "dev": true }, "v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -46174,6 +46214,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -46182,8 +46223,7 @@ "ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "requires": {} + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==" }, "xml-name-validator": { "version": "4.0.0", @@ -46256,7 +46296,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true + "dev": true }, "yocto-queue": { "version": "0.1.0", diff --git a/met-web/package.json b/met-web/package.json index 24cf52945..77c937b91 100644 --- a/met-web/package.json +++ b/met-web/package.json @@ -45,7 +45,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.13.0", "maplibre-gl": "^1.15.3", - "met-formio": "^1.0.13-rc1", + "met-formio": "^1.0.14-rc1", "mui-sx": "^1.0.0", "node-sass": "^7.0.3", "react": "^18.0.0", diff --git a/met-web/src/apiManager/httpRequestHandler/index.ts b/met-web/src/apiManager/httpRequestHandler/index.ts index 9f5f5a1d1..f6c5ad8b7 100644 --- a/met-web/src/apiManager/httpRequestHandler/index.ts +++ b/met-web/src/apiManager/httpRequestHandler/index.ts @@ -1,16 +1,29 @@ -import axios from 'axios'; +import axios, { AxiosRequestConfig } from 'axios'; import UserService from 'services/userService'; -const GetRequest = (url: string, params = {}, headers = {}) => { - return axios.get(url, { +const GetRequest = (url: string, params = {}, headers = {}, responseType?: string) => { + const defaultHeaders = { + 'Content-type': 'application/json', + Authorization: `Bearer ${UserService.getToken()}`, + 'tenant-id': `${sessionStorage.getItem('tenantId')}`, + }; + + const finalHeaders = { + ...defaultHeaders, + ...headers, + }; + + const requestOptions: AxiosRequestConfig = { params: params, - headers: { - 'Content-type': 'application/json', - Authorization: `Bearer ${UserService.getToken()}`, - 'tenant-id': `${sessionStorage.getItem('tenantId')}`, - ...headers, - }, - }); + headers: finalHeaders, + }; + + // Conditionally add responseType to requestOptions if provided + if (responseType) { + requestOptions.responseType = responseType as AxiosRequestConfig['responseType']; + } + + return axios.get(url, requestOptions); }; const PostRequest = (url: string, data = {}, params = {}) => { diff --git a/met-web/src/components/comments/admin/review/emailPreview/EmailPreview.tsx b/met-web/src/components/comments/admin/review/emailPreview/EmailPreview.tsx index 82cbaa7b1..820321499 100644 --- a/met-web/src/components/comments/admin/review/emailPreview/EmailPreview.tsx +++ b/met-web/src/components/comments/admin/review/emailPreview/EmailPreview.tsx @@ -6,6 +6,7 @@ import { Survey } from 'models/survey'; import { formatDate } from 'components/common/dateHelper'; import { useAppSelector } from 'hooks'; import { TenantState } from 'reduxSlices/tenantSlice'; +import { EngagementStatus } from 'constants/engagementStatus'; export default function EmailPreview({ survey, @@ -18,6 +19,8 @@ export default function EmailPreview({ }) { const scheduledDate = formatDate(survey.engagement?.scheduled_date || '', 'MMM DD YYYY'); const tenant: TenantState = useAppSelector((state) => state.tenant); + const isClosed = survey.engagement?.engagement_status.id === EngagementStatus.Closed; + const engagementName = survey.engagement?.name || ''; return ( @@ -34,27 +37,38 @@ export default function EmailPreview({ - Thank you for taking the time to provide your feedback on {survey.engagement?.name || ''}. + Thank you for taking the time to provide your feedback on {engagementName}. {children} - You can edit and re-submit your feedback. The comment period is open until {scheduledDate}. You - must re-submit your feedback before the comment period closes. + {!isClosed ? ( + <> + You can edit and re-submit your feedback. The comment period is open until {''} + {scheduledDate}. You must re-submit your feedback before the comment period closes. + + ) : ( + <> + We are sorry but the public commenting period for {engagementName} is now closed and + your feedback cannot be edited. + + )} - - Edit your feedback - + {!isClosed ? ( + + Edit your feedback + + ) : null} Thank you, diff --git a/met-web/src/components/comments/admin/textListing/index.tsx b/met-web/src/components/comments/admin/textListing/index.tsx index b8bb41af8..5f071ffd5 100644 --- a/met-web/src/components/comments/admin/textListing/index.tsx +++ b/met-web/src/components/comments/admin/textListing/index.tsx @@ -94,7 +94,7 @@ const CommentTextListing = () => { try { setIsExporting(true); const response = await getProponentCommentSheet({ survey_id: survey.id }); - downloadFile(response, `PUBLIC - ${survey.engagement?.name || ''} - ${formatToUTC(Date())}.csv`); + downloadFile(response, `PUBLIC - ${survey.engagement?.name || ''} - ${formatToUTC(Date())}.xlsx`); setIsExporting(false); handleExportToCSVClose(); // Close the menu after export } catch (error) { diff --git a/met-web/src/components/engagement/dashboard/comment/CommentTable.tsx b/met-web/src/components/engagement/dashboard/comment/CommentTable.tsx index b9e663180..54b4efa0f 100644 --- a/met-web/src/components/engagement/dashboard/comment/CommentTable.tsx +++ b/met-web/src/components/engagement/dashboard/comment/CommentTable.tsx @@ -1,22 +1,61 @@ import React, { useContext } from 'react'; import MetTable from 'components/common/Table'; -import { Comment } from 'models/comment'; import { HeadCell } from 'components/common/Table/types'; -import { Skeleton, Typography } from '@mui/material'; -import { CommentViewContext } from './CommentViewContext'; +import { MetLabel, MetParagraph } from 'components/common'; +import { Skeleton } from '@mui/material'; +import { CommentViewContext, TransformedComment } from './CommentViewContext'; + +export interface CommentType { + label: string; + submission_date: string; + submission_id: number; + text: string; +} const CommentTable = () => { const { isCommentsListLoading, comments, paginationOptions, pageInfo, handleChangePagination, tableLoading } = useContext(CommentViewContext); - const headCells: HeadCell[] = [ + const transformedArray: TransformedComment[] = comments.reduce((acc: TransformedComment[], comment) => { + const existingSubmission = acc.find((submission) => submission.submission_id === comment.submission_id); + + if (existingSubmission) { + existingSubmission.comments.push({ label: comment.label, text: comment.text }); + } else { + acc.push({ + submission_id: comment.submission_id, + submission_date: comment.submission_date, + comments: [{ label: comment.label, text: comment.text }], + }); + } + + return acc; + }, []); + + // Sort transformedArray in descending order based on submission_id + transformedArray.sort((a, b) => b.submission_id - a.submission_id); + + const headCells: HeadCell[] = [ { - key: 'text', + key: 'comments', numeric: false, disablePadding: false, - label: 'Content', - allowSort: true, - renderCell: (row: Comment) => row.text, + label: 'Comment', + allowSort: false, + customStyle: { width: '80%' }, + align: 'left', + renderCell: (row: TransformedComment) => ( + <> + {row.comments.map((comment) => ( +
+ {comment.label} +
+ {comment.text} +
+
+ ))} + + ), }, { key: 'submission_date', @@ -26,7 +65,7 @@ const CommentTable = () => { allowSort: false, customStyle: { width: '20%' }, align: 'right', - renderCell: (row: Comment) => {row.submission_date}, + renderCell: (row: TransformedComment) => {row.submission_date}, }, ]; @@ -38,7 +77,7 @@ const CommentTable = () => { <> ; + paginationOptions: PaginationOptions; pageInfo: PageInfo; - handleChangePagination: (_paginationOptions: PaginationOptions) => void; + handleChangePagination: (_paginationOptions: PaginationOptions) => void; tableLoading: boolean; } @@ -34,7 +40,7 @@ export const CommentViewContext = createContext({ comments: [], paginationOptions: { page: 0, size: 10 }, pageInfo: { total: 0 }, - handleChangePagination: (_paginationOptions: PaginationOptions) => { + handleChangePagination: (_paginationOptions: PaginationOptions) => { throw new Error('Unimplemented'); }, tableLoading: false, @@ -52,7 +58,7 @@ export const CommentViewProvider = ({ children }: { children: JSX.Element | JSX. const [isEngagementLoading, setEngagementLoading] = useState(true); const [isCommentsListLoading, setIsCommentsListLoading] = useState(true); const [comments, setComments] = useState([]); - const [paginationOptions, setPaginationOptions] = useState>({ + const [paginationOptions, setPaginationOptions] = useState>({ page: 1, size: 10, }); @@ -140,7 +146,7 @@ export const CommentViewProvider = ({ children }: { children: JSX.Element | JSX. } }; - const handleChangePagination = (paginationOptions: PaginationOptions) => { + const handleChangePagination = (paginationOptions: PaginationOptions) => { setPaginationOptions(paginationOptions); }; diff --git a/met-web/src/components/publicDashboard/KPI/SurveyEmailsSent.tsx b/met-web/src/components/publicDashboard/KPI/SurveyEmailsSent.tsx index 347d5e0da..ea6038a53 100644 --- a/met-web/src/components/publicDashboard/KPI/SurveyEmailsSent.tsx +++ b/met-web/src/components/publicDashboard/KPI/SurveyEmailsSent.tsx @@ -112,9 +112,9 @@ const SurveyEmailsSent = ({ engagement, engagementIsLoading }: SurveyEmailsSentP barSize={circleSize / 4} data={[data]} startAngle={225} - endAngle={-270} + endAngle={-225} > - + { const [data, setData] = useState(null); + const [emailVerificationData, setEmailVerificationData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); const isTablet = useMediaQuery((theme: Theme) => theme.breakpoints.down('md')); @@ -40,6 +41,11 @@ const SurveysCompleted = ({ engagement, engagementIsLoading }: SurveysCompletedP count_for: 'survey_completed', }); setData(response); + const emailVerification = await getAggregatorData({ + engagement_id: Number(engagement.id), + count_for: 'email_verification', + }); + setEmailVerificationData(emailVerification); } catch (error) { if (axios.isAxiosError(error)) { setErrors(error); @@ -113,9 +119,14 @@ const SurveysCompleted = ({ engagement, engagementIsLoading }: SurveysCompletedP barSize={circleSize / 4} data={[data]} startAngle={225} - endAngle={-270} + endAngle={-225} > - + { dispatch( openNotification({ severity: 'warning', - text: 'Engagement already published. Saving is disabled.', + text: 'Engagement already published. Please be careful while editing the survey.', }), ); } @@ -410,12 +410,8 @@ const SurveyFormBuilder = () => {
- - {'Save & Continue'} + + {'Report Settings'} navigate('/surveys')}>Cancel diff --git a/met-web/src/components/survey/listing/ActionsDropDown.tsx b/met-web/src/components/survey/listing/ActionsDropDown.tsx index 0841ee1c5..61b9a08e5 100644 --- a/met-web/src/components/survey/listing/ActionsDropDown.tsx +++ b/met-web/src/components/survey/listing/ActionsDropDown.tsx @@ -20,10 +20,11 @@ export const ActionsDropDown = ({ survey }: { survey: Survey }) => { const engagementId = engagement?.id ?? 0; const submissionHasBeenOpened = !!engagement && [SubmissionStatus.Open, SubmissionStatus.Closed].includes(engagement.submission_status); + const submissionIsClosed = !!engagement && [SubmissionStatus.Closed].includes(engagement.submission_status); const isEngagementDraft = !!engagement && engagement.engagement_status.id === EngagementStatus.Draft; const canEditSurvey = (): boolean => { - if (submissionHasBeenOpened) { + if (submissionIsClosed) { return false; } diff --git a/met-web/src/components/survey/report/SettingsForm.tsx b/met-web/src/components/survey/report/SettingsForm.tsx index 24515a4a1..354bacd9d 100644 --- a/met-web/src/components/survey/report/SettingsForm.tsx +++ b/met-web/src/components/survey/report/SettingsForm.tsx @@ -1,5 +1,6 @@ import React, { useContext, useState } from 'react'; -import { ClickAwayListener, Grid, InputAdornment, TextField, Tooltip } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import { ClickAwayListener, Grid, Stack, InputAdornment, TextField, Tooltip } from '@mui/material'; import { MetHeader3, MetLabel, @@ -18,6 +19,8 @@ const SettingsForm = () => { const { setSavingSettings, savingSettings, engagementSlug, loadingEngagementSlug, survey } = useContext(ReportSettingsContext); + const navigate = useNavigate(); + const [copyTooltip, setCopyTooltip] = useState(false); const baseUrl = getBaseUrl(); @@ -111,13 +114,18 @@ const SettingsForm = () => { - setSavingSettings(true)} - loading={savingSettings} - > - Save - + + setSavingSettings(true)} + loading={savingSettings} + > + Save + + navigate(`/surveys/${survey?.id}/build`)}> + Back + + diff --git a/met-web/src/services/commentService/index.tsx b/met-web/src/services/commentService/index.tsx index b079db233..246b912a5 100644 --- a/met-web/src/services/commentService/index.tsx +++ b/met-web/src/services/commentService/index.tsx @@ -44,8 +44,9 @@ export const getStaffCommentSheet = async ({ survey_id }: GenerateCommentsSheetP export const getProponentCommentSheet = async ({ survey_id }: GenerateCommentsSheetParams) => { const url = replaceUrl(Endpoints.Comment.GET_PROPONENT_SPREAD_SHEET, 'survey_id', String(survey_id)); + const responseType = 'arraybuffer'; const headers = { - 'Content-type': 'text/csv; charset=utf-8', + 'Content-type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }; - return http.GetRequest(url, {}, headers); + return http.GetRequest(url, {}, headers, responseType); }; diff --git a/openshift/api.dc.yml b/openshift/api.dc.yml index e94e8a9a1..fed696112 100644 --- a/openshift/api.dc.yml +++ b/openshift/api.dc.yml @@ -206,7 +206,8 @@ objects: SITE_URL: ${SITE_URL} VERIFICATION_EMAIL_TEMPLATE_ID: ${VERIFICATION_EMAIL_TEMPLATE_ID} SUBSCRIBE_EMAIL_TEMPLATE_ID: ${SUBSCRIBE_EMAIL_TEMPLATE_ID} - REJECTED_EMAIL_TEMPLATE_ID: ${REJECTED_EMAIL_TEMPLATE_ID} + REJECTED_EMAIL_TEMPLATE_ID: ${REJECTED_EMAIL_TEMPLATE_ID} + CLOSED_ENGAGEMENT_REJECTED_EMAIL_TEMPLATE_ID: ${CLOSED_ENGAGEMENT_REJECTED_EMAIL_TEMPLATE_ID} ACCESS_REQUEST_EMAIL_TEMPLATE_ID: ${ACCESS_REQUEST_EMAIL_TEMPLATE_ID} KEYCLOAK_REALMNAME: ${KEYCLOAK_REALMNAME} KEYCLOAK_BASE_URL: ${KEYCLOAK_BASE_URL} @@ -272,6 +273,10 @@ parameters: description: "The rejected comment email template id" required: true value: '8410c055-587b-4788-bd2f-b563562bcb2d' + - name: CLOSED_ENGAGEMENT_REJECTED_EMAIL_TEMPLATE_ID + description: "The rejected comment email template id for closed engagement" + required: true + value: 'e942dea1-094e-4021-9aac-21a0ac1f240d' - name: ACCESS_REQUEST_EMAIL_TEMPLATE_ID description: "The access request email template id" required: true