diff --git a/met-api/migrations/versions/42641011576a_.py b/met-api/migrations/versions/42641011576a_.py new file mode 100644 index 000000000..8fe6c244d --- /dev/null +++ b/met-api/migrations/versions/42641011576a_.py @@ -0,0 +1,24 @@ +"""Merge revision heads + +Revision ID: 42641011576a +Revises: c2a384ddfe6a, bd493dbd9e0e +Create Date: 2024-08-22 17:53:24.806016 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '42641011576a' +down_revision = ('c2a384ddfe6a', 'bd493dbd9e0e') +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/met-api/migrations/versions/bd493dbd9e0e_merge_engagement_content.py b/met-api/migrations/versions/bd493dbd9e0e_merge_engagement_content.py new file mode 100644 index 000000000..0d9899a14 --- /dev/null +++ b/met-api/migrations/versions/bd493dbd9e0e_merge_engagement_content.py @@ -0,0 +1,112 @@ +"""Merge the engagement_summary_content and engagement_custom_content tables into the engagement_content table + +Revision ID: bd493dbd9e0e +Revises: 901a6724bca2 +Create Date: 2024-08-21 10:06:44.377763 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql +from sqlalchemy.sql import table, column +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = 'bd493dbd9e0e' +down_revision = '901a6724bca2' +branch_labels = None +depends_on = None + + +def upgrade(): + # Create new columns in the engagement_content table + op.add_column('engagement_content', sa.Column('text_content', sa.Text(), nullable=True)) + op.add_column('engagement_content', sa.Column('json_content', sa.JSON(), nullable=True)) + + # Reference the existing tables + + engagement_content = table('engagement_content', + column('id', sa.Integer), + column('text_content', sa.Text), + column('json_content', sa.JSON) + ) + + engagement_summary_content = table('engagement_summary_content', + column('engagement_content_id', sa.Integer), + column('content', sa.Text), + column('rich_content', sa.JSON) + ) + + engagement_custom_content = table('engagement_custom_content', + column('engagement_content_id', sa.Integer), + column('custom_text_content', sa.Text), + column('custom_json_content', sa.JSON) + ) + + # Copy data from the old tables to the new columns in engagement_content + op.execute( + engagement_content.update() + .where(engagement_content.c.id == engagement_summary_content.c.engagement_content_id) + .values({ + 'text_content': engagement_summary_content.c.content, + 'json_content': engagement_summary_content.c.rich_content + }) + ) + + op.execute( + engagement_content.update() + .where(engagement_content.c.id == engagement_custom_content.c.engagement_content_id) + .values({ + 'text_content': engagement_custom_content.c.custom_text_content, + 'json_content': engagement_custom_content.c.custom_json_content + }) + ) + + # Drop old tables + op.drop_table('engagement_custom_content') + op.drop_table('engagement_summary_content') + + # Drop the content_type column and icon name as they're no longer needed + op.drop_column('engagement_content', 'content_type') + op.drop_column('engagement_content', 'icon_name') + + +def downgrade(): + # Recreate the old tables + op.create_table('engagement_summary_content', + sa.Column('created_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), + sa.Column('updated_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('content', sa.TEXT(), autoincrement=False, nullable=False), + sa.Column('rich_content', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=False), + sa.Column('engagement_content_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('engagement_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.VARCHAR(length=50), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.VARCHAR(length=50), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['engagement_content_id'], ['engagement_content.id'], name='engagement_summary_content_engagement_content_id_fkey', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], name='engagement_summary_content_engagement_id_fkey', ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name='engagement_summary_content_pkey') + ) + + op.create_table('engagement_custom_content', + sa.Column('created_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), + sa.Column('updated_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=True), + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('custom_text_content', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('custom_json_content', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True), + sa.Column('engagement_content_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('engagement_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.VARCHAR(length=50), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.VARCHAR(length=50), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['engagement_content_id'], ['engagement_content.id'], name='engagement_custom_content_engagement_content_id_fkey', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], name='engagement_custom_content_engagement_id_fkey', ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name='engagement_custom_content_pkey') + ) + + # Drop new columns in engagement_content table + op.drop_column('engagement_content', 'json_content') + op.drop_column('engagement_content', 'text_content') + + # Re-add the content_type and icon_name columns + op.add_column('engagement_content', sa.Column('icon_name', sa.Text(), autoincrement=False, nullable=True)) + op.add_column('engagement_content', sa.Column('content_type', postgresql.ENUM('Summary', 'Custom', name='engagementcontenttype'), autoincrement=False)) diff --git a/met-api/migrations/versions/e2625b0d07ab_add_tables_for_dynamic_eng_pages.py b/met-api/migrations/versions/e2625b0d07ab_add_tables_for_dynamic_eng_pages.py index 14f8ff695..040f695e7 100644 --- a/met-api/migrations/versions/e2625b0d07ab_add_tables_for_dynamic_eng_pages.py +++ b/met-api/migrations/versions/e2625b0d07ab_add_tables_for_dynamic_eng_pages.py @@ -11,9 +11,6 @@ from sqlalchemy import text from sqlalchemy.dialects import postgresql -from met_api.constants.engagement_content_type import EngagementContentType -from met_api.utils.enums import ContentTitle - # revision identifiers, used by Alembic. revision = 'e2625b0d07ab' down_revision = '37176ea4708d' @@ -90,9 +87,9 @@ def upgrade(): """ ), { - 'title': ContentTitle.DEFAULT.value, - 'icon_name': ContentTitle.DEFAULT_ICON.value, - 'content_type': EngagementContentType(1).name, + 'title': "Summary", + 'icon_name': "n/a", + 'content_type': "n/a", 'engagement_id': eng_id, }, ) diff --git a/met-api/src/met_api/constants/engagement_content_type.py b/met-api/src/met_api/constants/engagement_content_type.py deleted file mode 100644 index 82f89a9f7..000000000 --- a/met-api/src/met_api/constants/engagement_content_type.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright © 2021 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Constants of engagement content type. - -Each value in this corresponds to a specific section or content element. -""" -from enum import Enum, IntEnum - - -class EngagementContentType(IntEnum): - """Enum of engagement content type.""" - - Summary = 1 - Custom = 2 - - -class EngagementContentDefaultValues(Enum): - """Default enum of engagement content type.""" - - Title = 'Summary' - Icon = 'faRectangleList' - Type = 'Summary' diff --git a/met-api/src/met_api/models/__init__.py b/met-api/src/met_api/models/__init__.py index ae5a15033..075d7518b 100644 --- a/met-api/src/met_api/models/__init__.py +++ b/met-api/src/met_api/models/__init__.py @@ -24,8 +24,6 @@ from .engagement_status import EngagementStatus from .engagement_status_block import EngagementStatusBlock from .engagement_settings import EngagementSettingsModel -from .engagement_custom_content import EngagementCustom -from .engagement_summary_content import EngagementSummary from .event_item import EventItem from .subscribe_item import SubscribeItem from .feedback import Feedback diff --git a/met-api/src/met_api/models/engagement_content.py b/met-api/src/met_api/models/engagement_content.py index 870fe8652..743df1e02 100644 --- a/met-api/src/met_api/models/engagement_content.py +++ b/met-api/src/met_api/models/engagement_content.py @@ -9,7 +9,6 @@ from typing import Optional from sqlalchemy.sql.schema import ForeignKey -from met_api.constants.engagement_content_type import EngagementContentType from .base_model import BaseModel from .db import db @@ -21,24 +20,23 @@ class EngagementContent(BaseModel): __tablename__ = 'engagement_content' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(50), unique=False, nullable=False) - icon_name = db.Column(db.Text, unique=False, nullable=True) - content_type = db.Column(db.Enum(EngagementContentType), nullable=False, - default=EngagementContentType.Summary) + text_content = db.Column(db.Text, unique=False, nullable=True) + json_content = db.Column(db.JSON, unique=False, nullable=True) engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE')) sort_index = db.Column(db.Integer, nullable=False, default=1) is_internal = db.Column(db.Boolean, nullable=False) @classmethod - def get_contents_by_engagement_id(cls, engagement_id): - """Get contents by engagement id.""" + def find_by_engagement_id(cls, engagement_id): + """Get content by engagement id.""" return db.session.query(EngagementContent)\ .filter(EngagementContent.engagement_id == engagement_id)\ .order_by(EngagementContent.sort_index.asc())\ .all() @classmethod - def update_engagement_contents(cls, update_mappings: list) -> None: - """Update contents.""" + def bulk_update_engagement_content(cls, update_mappings: list) -> None: + """Update content.""" db.session.bulk_update_mappings(EngagementContent, update_mappings) db.session.commit() @@ -49,7 +47,7 @@ def save_engagement_content(cls, content: list) -> None: @classmethod def remove_engagement_content(cls, engagement_id, engagement_content_id,) -> EngagementContent: - """Remove engagement content from engagement.""" + """Remove content from an engagement.""" engagement_content = EngagementContent.query.filter_by(id=engagement_content_id, engagement_id=engagement_id).delete() db.session.commit() diff --git a/met-api/src/met_api/models/engagement_custom_content.py b/met-api/src/met_api/models/engagement_custom_content.py deleted file mode 100644 index 9f32409f5..000000000 --- a/met-api/src/met_api/models/engagement_custom_content.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Engagement custom model class. - -Manages the engagement custom content -""" - -from __future__ import annotations - -from sqlalchemy.dialects.postgresql import JSON -from sqlalchemy.sql.schema import ForeignKey - -from .base_model import BaseModel -from .db import db - - -class EngagementCustom(BaseModel): - """Definition of the Engagement custom content entity.""" - - __tablename__ = 'engagement_custom_content' - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - custom_text_content = db.Column(db.Text, unique=False, nullable=True) - custom_json_content = db.Column(JSON, unique=False, nullable=True) - engagement_content_id = db.Column(db.Integer, ForeignKey('engagement_content.id', ondelete='CASCADE')) - engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE')) - - @classmethod - def get_custom_content(cls, content_id) -> list[EngagementCustom]: - """Get engagement custom content.""" - custom_content = db.session.query(EngagementCustom) \ - .filter(EngagementCustom.engagement_content_id == content_id) \ - .all() - return custom_content - - @classmethod - def update_custom_content(cls, content_id, custom_content_data: dict) -> EngagementCustom: - """Update engagement custom content.""" - query = EngagementCustom.query.filter_by(engagement_content_id=content_id) - custom_content: EngagementCustom = query.first() - if not custom_content: - return custom_content_data - query.update(custom_content_data) - db.session.commit() - return custom_content - - @classmethod - def save_engagement_custom_content(cls, custom_content: list) -> None: - """Save custom content.""" - db.session.bulk_save_objects(custom_content) diff --git a/met-api/src/met_api/models/engagement_summary_content.py b/met-api/src/met_api/models/engagement_summary_content.py deleted file mode 100644 index 2eb52270f..000000000 --- a/met-api/src/met_api/models/engagement_summary_content.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Engagement summary model class. - -Manages the engagement summary content -""" - -from __future__ import annotations - -from sqlalchemy.dialects.postgresql import JSON -from sqlalchemy.sql.schema import ForeignKey - -from .base_model import BaseModel -from .db import db - - -class EngagementSummary(BaseModel): - """Definition of the Engagement summary content entity.""" - - __tablename__ = 'engagement_summary_content' - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - content = db.Column(db.Text, unique=False, nullable=False) - rich_content = db.Column(JSON, unique=False, nullable=False) - engagement_content_id = db.Column(db.Integer, ForeignKey('engagement_content.id', ondelete='CASCADE')) - engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE')) - - @classmethod - def get_summary_content(cls, content_id) -> list[EngagementSummary]: - """Get engagement summary content.""" - summary_content = db.session.query(EngagementSummary) \ - .filter(EngagementSummary.engagement_content_id == content_id) \ - .all() - return summary_content - - @classmethod - def get_summary_content_by_engagement_id(cls, engagement_id) -> list[EngagementSummary]: - """Get engagement summary content by engagement id.""" - summary_content = db.session.query(EngagementSummary) \ - .filter(EngagementSummary.engagement_id == engagement_id) \ - .first() - return summary_content - - @classmethod - def update_summary_content(cls, content_id, summary_content_data: dict) -> EngagementSummary: - """Update engagement summary content.""" - query = EngagementSummary.query.filter_by(engagement_content_id=content_id) - summary_content: EngagementSummary = query.first() - if not summary_content: - return summary_content_data - query.update(summary_content_data) - db.session.commit() - return summary_content - - @classmethod - def save_engagement_summary_content(cls, summary_content: list) -> None: - """Save summary content.""" - db.session.bulk_save_objects(summary_content) diff --git a/met-api/src/met_api/resources/__init__.py b/met-api/src/met_api/resources/__init__.py index d1c864e2f..f3494bf61 100644 --- a/met-api/src/met_api/resources/__init__.py +++ b/met-api/src/met_api/resources/__init__.py @@ -30,8 +30,6 @@ from .document import API as DOCUMENT_API from .email_verification import API as EMAIL_VERIFICATION_API from .engagement_content import API as ENGAGEMENT_CONTENT_API -from .engagement_custom_content import API as ENGAGEMENT_CUSTOM_CONTENT_API -from .engagement_summary_content import API as ENGAGEMENT_SUMMARY_CONTENT_API from .engagement import API as ENGAGEMENT_API from .engagement_metadata import API as ENGAGEMENT_METADATA_API from .metadata_taxon import API as METADATA_TAXON_API @@ -84,8 +82,6 @@ API.add_namespace(COMMENT_API) API.add_namespace(EMAIL_VERIFICATION_API) API.add_namespace(ENGAGEMENT_CONTENT_API, path='/engagement//content') -API.add_namespace(ENGAGEMENT_CUSTOM_CONTENT_API, path='/content//custom') -API.add_namespace(ENGAGEMENT_SUMMARY_CONTENT_API, path='/content//summary') API.add_namespace(FEEDBACK_API) API.add_namespace(WIDGET_API) API.add_namespace(CONTACT_API) diff --git a/met-api/src/met_api/resources/engagement_content.py b/met-api/src/met_api/resources/engagement_content.py index 8ca5f5a1c..a5f3f42f9 100644 --- a/met-api/src/met_api/resources/engagement_content.py +++ b/met-api/src/met_api/resources/engagement_content.py @@ -36,7 +36,7 @@ @cors_preflight('GET, POST, OPTIONS') @API.route('') class EngagementContent(Resource): - """Resource for managing a engagement content.""" + """Resource for managing an engagement's contents.""" @staticmethod @cross_origin(origins=allowedorigins()) @@ -113,9 +113,8 @@ def patch(engagement_id, engagement_content_id): try: user_id = TokenInfo.get_id() engagement_content_data = request.get_json() - valid_format, errors = schema_utils.validate(engagement_content_data, 'engagement_content_update') - if not valid_format: - return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST + if not engagement_content_data or not engagement_content_data.get('title'): + return 'No content provided', HTTPStatus.BAD_REQUEST updated_engagement_content = EngagementContentService().update_engagement_content(engagement_id, engagement_content_id, diff --git a/met-api/src/met_api/resources/engagement_content_translation.py b/met-api/src/met_api/resources/engagement_content_translation.py index 0a7d1e8c2..4aaded417 100644 --- a/met-api/src/met_api/resources/engagement_content_translation.py +++ b/met-api/src/met_api/resources/engagement_content_translation.py @@ -8,12 +8,13 @@ from marshmallow import ValidationError from met_api.auth import jwt as _jwt +from met_api.exceptions.business_exception import BusinessException from met_api.schemas import utils as schema_utils from met_api.schemas.engagement_content_translation import EngagementContentTranslationSchema from met_api.services.engagement_content_translation_service import EngagementContentTranslationService -from met_api.exceptions.business_exception import BusinessException from met_api.utils.util import allowedorigins, cors_preflight + API = Namespace('engagement_content_translation', description='Endpoints for Engagement Content Translation Management') diff --git a/met-api/src/met_api/resources/engagement_custom_content.py b/met-api/src/met_api/resources/engagement_custom_content.py deleted file mode 100644 index 5a33ff44b..000000000 --- a/met-api/src/met_api/resources/engagement_custom_content.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright © 2021 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""API endpoints for managing an engagement custom content resource.""" - -from http import HTTPStatus - -from flask import jsonify, request -from flask_cors import cross_origin -from flask_restx import Namespace, Resource - -from met_api.auth import jwt as _jwt -from met_api.exceptions.business_exception import BusinessException -from met_api.schemas.engagement_custom_content import EngagementCustomSchema -from met_api.services.engagement_custom_content_service import EngagementCustomContentService -from met_api.utils.util import allowedorigins, cors_preflight - - -API = Namespace('custom_content', description='Endpoints for Engagement Custom Content Management') -"""Custom exception messages -""" - - -@cors_preflight('GET, POST, PATCH, OPTIONS') -@API.route('') -class CustomContent(Resource): - """Resource for managing engagement custom content.""" - - @staticmethod - @cross_origin(origins=allowedorigins()) - def get(content_id): - """Get engagement custom content.""" - try: - custom_content = EngagementCustomContentService().get_custom_content(content_id) - return jsonify(EngagementCustomSchema().dump(custom_content, many=True)), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code - - @staticmethod - @cross_origin(origins=allowedorigins()) - @_jwt.requires_auth - def post(content_id): - """Create engagement custom content.""" - try: - request_json = request.get_json() - custom_content = EngagementCustomContentService().create_custom_content(content_id, - request_json) - return EngagementCustomSchema().dump(custom_content), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code - - @staticmethod - @cross_origin(origins=allowedorigins()) - @_jwt.requires_auth - def patch(content_id): - """Update engagement custom content.""" - request_json = request.get_json() - try: - custom_content = EngagementCustomContentService().update_custom_content(content_id, request_json) - return EngagementCustomSchema().dump(custom_content), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code diff --git a/met-api/src/met_api/resources/engagement_summary_content.py b/met-api/src/met_api/resources/engagement_summary_content.py deleted file mode 100644 index d2e504a0d..000000000 --- a/met-api/src/met_api/resources/engagement_summary_content.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright © 2021 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""API endpoints for managing an engagement summary content resource.""" - -from http import HTTPStatus - -from flask import jsonify, request -from flask_cors import cross_origin -from flask_restx import Namespace, Resource - -from met_api.auth import jwt as _jwt -from met_api.exceptions.business_exception import BusinessException -from met_api.schemas.engagement_summary_content import EngagementSummarySchema -from met_api.services.engagement_summary_content_service import EngagementSummaryContentService -from met_api.utils.util import allowedorigins, cors_preflight - - -API = Namespace('summary_content', description='Endpoints for Engagement Summary Content Management') -"""Custom exception messages -""" - - -@cors_preflight('GET, POST, PATCH, OPTIONS') -@API.route('') -class SummaryContent(Resource): - """Resource for managing engagement summary content.""" - - @staticmethod - @cross_origin(origins=allowedorigins()) - def get(content_id): - """Get engagement summary content.""" - try: - summary_content = EngagementSummaryContentService().get_summary_content(content_id) - return jsonify(EngagementSummarySchema().dump(summary_content, many=True)), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code - - @staticmethod - @cross_origin(origins=allowedorigins()) - @_jwt.requires_auth - def post(content_id): - """Create engagement summary content.""" - try: - request_json = request.get_json() - summary_content = EngagementSummaryContentService().create_summary_content(content_id, - request_json) - return EngagementSummarySchema().dump(summary_content), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code - - @staticmethod - @cross_origin(origins=allowedorigins()) - @_jwt.requires_auth - def patch(content_id): - """Update engagement summary content.""" - request_json = request.get_json() - try: - summary_content = EngagementSummaryContentService().update_summary_content(content_id, request_json) - return EngagementSummarySchema().dump(summary_content), HTTPStatus.OK - except BusinessException as err: - return str(err), err.status_code diff --git a/met-api/src/met_api/schemas/engagement_content.py b/met-api/src/met_api/schemas/engagement_content.py index ef92a0f72..559343c17 100644 --- a/met-api/src/met_api/schemas/engagement_content.py +++ b/met-api/src/met_api/schemas/engagement_content.py @@ -3,8 +3,6 @@ Manages the engagement content. """ from marshmallow import EXCLUDE, Schema, fields -from marshmallow_enum import EnumField -from met_api.constants.engagement_content_type import EngagementContentType class EngagementContentSchema(Schema): @@ -17,8 +15,8 @@ class Meta: # pylint: disable=too-few-public-methods id = fields.Int(data_key='id') title = fields.Str(data_key='title') - icon_name = fields.Str(data_key='icon_name') - content_type = EnumField(EngagementContentType) + text_content = fields.Str(data_key='text_content') + json_content = fields.Str(data_key='json_content') engagement_id = fields.Int(data_key='engagement_id') sort_index = fields.Int(data_key='sort_index') is_internal = fields.Bool(data_key='is_internal') diff --git a/met-api/src/met_api/schemas/engagement_custom_content.py b/met-api/src/met_api/schemas/engagement_custom_content.py deleted file mode 100644 index 96d70d954..000000000 --- a/met-api/src/met_api/schemas/engagement_custom_content.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Engagement custom model class. - -Manages the engagement custom -""" -from marshmallow import EXCLUDE, Schema, fields - - -class EngagementCustomSchema(Schema): - """Schema for engagement custom.""" - - class Meta: # pylint: disable=too-few-public-methods - """Exclude unknown fields in the deserialized output.""" - - unknown = EXCLUDE - - id = fields.Int(data_key='id') - custom_text_content = fields.Str(data_key='custom_text_content') - custom_json_content = fields.Str(data_key='custom_json_content') - engagement_content_id = fields.Int(data_key='engagement_content_id') - engagement_id = fields.Int(data_key='engagement_id') diff --git a/met-api/src/met_api/schemas/engagement_summary_content.py b/met-api/src/met_api/schemas/engagement_summary_content.py deleted file mode 100644 index f76e53b32..000000000 --- a/met-api/src/met_api/schemas/engagement_summary_content.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Engagement summary model class. - -Manages the engagement summary -""" -from marshmallow import EXCLUDE, Schema, fields - - -class EngagementSummarySchema(Schema): - """Schema for engagement summary.""" - - class Meta: # pylint: disable=too-few-public-methods - """Exclude unknown fields in the deserialized output.""" - - unknown = EXCLUDE - - id = fields.Int(data_key='id') - content = fields.Str(data_key='content') - rich_content = fields.Str(data_key='rich_content') - engagement_content_id = fields.Int(data_key='engagement_content_id') - engagement_id = fields.Int(data_key='engagement_id') diff --git a/met-api/src/met_api/schemas/schemas/engagement_content.json b/met-api/src/met_api/schemas/schemas/engagement_content.json index 814620abb..5c01b0d82 100644 --- a/met-api/src/met_api/schemas/schemas/engagement_content.json +++ b/met-api/src/met_api/schemas/schemas/engagement_content.json @@ -8,14 +8,12 @@ "examples": [ { "title": "Summary", - "icon_name": "SummarizeIcon", - "content_type": "Summary", "sort_index": 1, "is_internal": "False", "engagement_id": 1 } ], - "required": ["title", "content_type", "engagement_id"], + "required": ["title", "engagement_id"], "properties": { "title": { "$id": "#/properties/title", @@ -24,13 +22,6 @@ "description": "The title of this content.", "examples": [1] }, - "content_type": { - "$id": "#/properties/content_type", - "type": "string", - "title": "Summary", - "description": "The content type enum.", - "examples": [1] - }, "engagement_id": { "$id": "#/properties/engagement_id", "type": "number", diff --git a/met-api/src/met_api/schemas/schemas/engagement_content_update.json b/met-api/src/met_api/schemas/schemas/engagement_content_update.json deleted file mode 100644 index e6e3325c3..000000000 --- a/met-api/src/met_api/schemas/schemas/engagement_content_update.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema", - "$id": "https://met.gov.bc.ca/.well_known/schemas/engagement_content_update", - "type": "object", - "title": "The root schema", - "description": "The root schema comprises the entire JSON document.", - "default": {}, - "examples": [ - { - "title": "Summary" - } - ], - "required": ["title"], - "properties": { - "title": { - "$id": "#/properties/title", - "type": "string", - "title": "Summary", - "description": "The title of this content.", - "examples": [1] - } - } - } - \ No newline at end of file diff --git a/met-api/src/met_api/services/engagement_content_service.py b/met-api/src/met_api/services/engagement_content_service.py index 13371626b..c0af9ff65 100644 --- a/met-api/src/met_api/services/engagement_content_service.py +++ b/met-api/src/met_api/services/engagement_content_service.py @@ -1,13 +1,10 @@ """Service for engagement content management.""" from http import HTTPStatus -from flask import current_app -from met_api.constants.engagement_content_type import EngagementContentType from met_api.constants.membership_type import MembershipType from met_api.exceptions.business_exception import BusinessException from met_api.models.engagement_content import EngagementContent as EngagementContentModel from met_api.schemas.engagement_content import EngagementContentSchema -from met_api.services.engagement_custom_content_service import EngagementCustomContentService from met_api.services import authorization from met_api.utils.roles import Role @@ -27,20 +24,13 @@ def get_content_by_content_id(engagement_content_id): ) content_data['id'] = engagement_content_record.id content_data['title'] = engagement_content_record.title - if engagement_content_record.content_type == EngagementContentType.Custom.name: - custom_content_records = EngagementCustomContentService.get_custom_content(engagement_content_id) - if custom_content_records: - custom_record = custom_content_records[0] - content_data['title'] = custom_record.get('title') - content_data['custom_text_content'] = custom_record.get('custom_text_content') - content_data['custom_json_content'] = custom_record.get('custom_json_content') return content_data @staticmethod def get_contents_by_engagement_id(engagement_id): - """Get contents by engagement id.""" + """Get content by engagement id.""" engagement_content_schema = EngagementContentSchema(many=True) - engagement_content_records = EngagementContentModel.get_contents_by_engagement_id(engagement_id) + engagement_content_records = EngagementContentModel.find_by_engagement_id(engagement_id) engagement_contents = engagement_content_schema.dump(engagement_content_records) return engagement_contents @@ -63,30 +53,13 @@ def create_engagement_content(engagement_content_data, engagement_id): created_content = EngagementContentService._create_content(engagement_id, engagement_content_data) created_content.commit() - if engagement_content_data.get('content_type') == EngagementContentType.Custom.name: - EngagementContentService.create_default_custom_content(engagement_id, created_content.id) - return EngagementContentSchema().dump(created_content) - @staticmethod - def create_default_custom_content(eng_id: int, eng_content_id: int): - """Create default engagement custom content.""" - default_summary_content = { - 'engagement_id': eng_id - } - try: - EngagementCustomContentService.create_custom_content(eng_content_id, default_summary_content) - except Exception as exc: # noqa: B902 - current_app.logger.error('Failed to create default engagement summary content', exc) - raise BusinessException( - error='Failed to create default engagement summary content.', - status_code=HTTPStatus.INTERNAL_SERVER_ERROR) from exc - @staticmethod def _find_higest_sort_index(engagement_id): # find the highest sort order of the engagement content sort_index = 0 - contents = EngagementContentModel.get_contents_by_engagement_id(engagement_id) + contents = EngagementContentModel.find_by_engagement_id(engagement_id) if contents: # Find the largest in the existing engagement contents sort_index = max(content.sort_index for content in contents) @@ -97,8 +70,8 @@ def _create_content(engagement_id, engagement_content_data: dict): engagement_content_model: EngagementContentModel = EngagementContentModel() engagement_content_model.engagement_id = engagement_id engagement_content_model.title = engagement_content_data.get('title') - engagement_content_model.icon_name = engagement_content_data.get('icon_name') - engagement_content_model.content_type = EngagementContentType[engagement_content_data.get('content_type')] + engagement_content_model.text_content = engagement_content_data.get('text_content') + engagement_content_model.json_content = engagement_content_data.get('json_content') engagement_content_model.sort_index = engagement_content_data.get('sort_index') engagement_content_model.is_internal = engagement_content_data.get('is_internal', False) engagement_content_model.flush() @@ -122,12 +95,12 @@ def sort_engagement_content(engagement_id, engagement_contents: list, user_id=No } for index, engagement_content in enumerate(engagement_contents) ] - EngagementContentModel.update_engagement_contents(engagement_content_sort_mappings) + EngagementContentModel.bulk_update_engagement_content(engagement_content_sort_mappings) @staticmethod def _validate_engagement_content_ids(engagement_id, engagement_contents): """Validate if engagement content ids belong to the engagement.""" - eng_contents = EngagementContentModel.get_contents_by_engagement_id(engagement_id) + eng_contents = EngagementContentModel.find_by_engagement_id(engagement_id) content_ids = [content.id for content in eng_contents] input_content_ids = [engagement_content.get('id') for engagement_content in engagement_contents] if len(set(content_ids) - set(input_content_ids)) > 0: diff --git a/met-api/src/met_api/services/engagement_custom_content_service.py b/met-api/src/met_api/services/engagement_custom_content_service.py deleted file mode 100644 index 4f0b3c057..000000000 --- a/met-api/src/met_api/services/engagement_custom_content_service.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Service for engagement custom content management.""" -from met_api.constants.membership_type import MembershipType -from met_api.models.engagement_custom_content import EngagementCustom as EngagementCustomModel -from met_api.services import authorization -from met_api.utils.roles import Role - - -class EngagementCustomContentService: - """Engagement custom content management service.""" - - @staticmethod - def get_custom_content(content_id): - """Get content by engagement custom content id.""" - custom_content = EngagementCustomModel.get_custom_content(content_id) - return custom_content - - @staticmethod - def create_custom_content(content_id, custom_content_details: dict): - """Create engagement custom content.""" - custom_content = dict(custom_content_details) - eng_id = custom_content.get('engagement_id') - authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name, - Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id) - - engagement_custom_content = EngagementCustomContentService._create_custom_content_model(content_id, - custom_content) - engagement_custom_content.commit() - return engagement_custom_content - - @staticmethod - def _create_custom_content_model(content_id, custom_content: dict): - custom_content_model: EngagementCustomModel = EngagementCustomModel() - custom_content_model.engagement_content_id = content_id - custom_content_model.engagement_id = custom_content.get('engagement_id') - custom_content_model.custom_text_content = custom_content.get('custom_text_content') - custom_content_model.custom_json_content = custom_content.get('custom_json_content') - custom_content_model.flush() - return custom_content_model - - @staticmethod - def update_custom_content(content_id, request_json): - """Update engagement custom content.""" - custom_content_list: EngagementCustomModel = EngagementCustomModel.get_custom_content(content_id) - if not custom_content_list: - raise KeyError('Engagement custom content ' + content_id + ' does not exist') - - custom_content = custom_content_list[0] - eng_id = custom_content.engagement_id - - authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name, - Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id) - - return EngagementCustomModel.update_custom_content(content_id, request_json) diff --git a/met-api/src/met_api/services/engagement_service.py b/met-api/src/met_api/services/engagement_service.py index 4d093b524..ad2a687fa 100644 --- a/met-api/src/met_api/services/engagement_service.py +++ b/met-api/src/met_api/services/engagement_service.py @@ -4,7 +4,6 @@ from flask import current_app -from met_api.constants.engagement_content_type import EngagementContentDefaultValues from met_api.constants.engagement_status import Status from met_api.constants.membership_type import MembershipType from met_api.exceptions.business_exception import BusinessException @@ -19,7 +18,6 @@ from met_api.services.engagement_settings_service import EngagementSettingsService from met_api.services.engagement_slug_service import EngagementSlugService from met_api.services.engagement_content_service import EngagementContentService -from met_api.services.engagement_summary_content_service import EngagementSummaryContentService from met_api.services.object_storage_service import ObjectStorageService from met_api.services.project_service import ProjectService from met_api.utils import email_util, notification @@ -159,8 +157,8 @@ def create_engagement(request_json: dict): EngagementService.validate_fields(request_json) eng_model = EngagementService._create_engagement_model(request_json) - eng_content = EngagementService.create_default_engagement_content(eng_model.id) - EngagementService.create_default_summary_content(eng_model.id, eng_content['id'], request_json) + EngagementService.create_default_engagement_content(eng_model.id) + EngagementService.create_default_content(eng_model.id, request_json) if request_json.get('status_block'): EngagementService._create_eng_status_block(eng_model.id, request_json) @@ -200,9 +198,7 @@ def _create_engagement_model(engagement_data: dict) -> EngagementModel: def create_default_engagement_content(eng_id): """Create default engagement content for the given engagement ID.""" default_engagement_content = { - 'title': EngagementContentDefaultValues.Title.value, - 'icon_name': EngagementContentDefaultValues.Icon.value, - 'content_type': EngagementContentDefaultValues.Type.value, + 'title': 'Summary', 'engagement_id': eng_id } try: @@ -216,15 +212,17 @@ def create_default_engagement_content(eng_id): return eng_content @staticmethod - def create_default_summary_content(eng_id: int, eng_content_id: int, content_data: dict): - """Create default summary content for the engagement ID, mandatory for each engagement.""" + def create_default_content(eng_id: int, content_data: dict): + """Create default summary content for the engagement.""" default_summary_content = { + 'title': 'Summary', 'engagement_id': eng_id, 'content': content_data.get('content', None), - 'rich_content': content_data.get('rich_content', None) + 'text_content': content_data.get('content', None), + 'json_content': content_data.get('rich_content', None), } try: - EngagementSummaryContentService.create_summary_content(eng_content_id, default_summary_content) + EngagementContentService.create_engagement_content(default_summary_content, eng_id) except Exception as exc: # noqa: B902 current_app.logger.error('Failed to create default engagement summary content', exc) raise BusinessException( diff --git a/met-api/src/met_api/services/engagement_summary_content_service.py b/met-api/src/met_api/services/engagement_summary_content_service.py deleted file mode 100644 index 2874d3c51..000000000 --- a/met-api/src/met_api/services/engagement_summary_content_service.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Service for engagement summary content management.""" -from met_api.constants.membership_type import MembershipType -from met_api.models.engagement_summary_content import EngagementSummary as EngagementSummaryModel -from met_api.services import authorization -from met_api.utils.roles import Role - - -class EngagementSummaryContentService: - """Engagement summary content management service.""" - - @staticmethod - def get_summary_content(content_id): - """Get content by engagement summary content id.""" - summary_content = EngagementSummaryModel.get_summary_content(content_id) - return summary_content - - @staticmethod - def create_summary_content(content_id, summary_content_details: dict): - """Create engagement summary content.""" - summary_content = dict(summary_content_details) - eng_id = summary_content.get('engagement_id') - authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name, - Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id) - - engagement_summary_content = EngagementSummaryContentService._create_summary_content_model(content_id, - summary_content) - engagement_summary_content.commit() - return engagement_summary_content - - @staticmethod - def _create_summary_content_model(content_id, summary_content: dict): - summary_content_model: EngagementSummaryModel = EngagementSummaryModel() - summary_content_model.engagement_content_id = content_id - summary_content_model.engagement_id = summary_content.get('engagement_id') - summary_content_model.content = summary_content.get('content') - summary_content_model.rich_content = summary_content.get('rich_content') - summary_content_model.flush() - return summary_content_model - - @staticmethod - def update_summary_content(content_id, request_json): - """Update engagement summary content.""" - summary_content_list: EngagementSummaryModel = EngagementSummaryModel.get_summary_content(content_id) - if not summary_content_list: - raise KeyError('Engagement summary content ' + str(content_id) + ' does not exist') - - summary_content = summary_content_list[0] - eng_id = summary_content.engagement_id - - authorization.check_auth(one_of_roles=(MembershipType.TEAM_MEMBER.name, - Role.EDIT_ENGAGEMENT.value), engagement_id=eng_id) - - return EngagementSummaryModel.update_summary_content(content_id, request_json) diff --git a/met-api/src/met_api/services/engagement_translation_service.py b/met-api/src/met_api/services/engagement_translation_service.py index 89f87048e..d902c9b68 100644 --- a/met-api/src/met_api/services/engagement_translation_service.py +++ b/met-api/src/met_api/services/engagement_translation_service.py @@ -8,8 +8,8 @@ from met_api.models.engagement import Engagement as EngagementModel from met_api.models.engagement_slug import EngagementSlug as EngagementSlugModel from met_api.models.engagement_status_block import EngagementStatusBlock as EngagementStatusBlockModel -from met_api.models.engagement_summary_content import EngagementSummary as EngagementSummaryModel from met_api.models.engagement_translation import EngagementTranslation as EngagementTranslationModel +from met_api.models.engagement_content import EngagementContent as EngagementContentModel from met_api.models.language import Language as LanguageModel from met_api.schemas.language import LanguageSchema from met_api.schemas.engagement_translation import EngagementTranslationSchema @@ -43,8 +43,7 @@ def create_engagement_translation(translation_data, pre_populate=True): """Create engagement translation.""" try: engagement = EngagementModel.find_by_id(translation_data['engagement_id']) - summary_content = EngagementSummaryModel.get_summary_content_by_engagement_id( - translation_data['engagement_id']) + content = EngagementContentModel.find_by_engagement_id(translation_data['engagement_id'])[0] if not engagement: raise ValueError('Engagement to translate was not found') @@ -59,9 +58,9 @@ def create_engagement_translation(translation_data, pre_populate=True): raise ValueError('Language to translate was not found') if pre_populate: - # prepopulate translation with base language data + # prepopulate translation_date dict with base language data from engagement content EngagementTranslationService._get_default_language_values(engagement, - summary_content, + content, translation_data) created_engagement_translation = EngagementTranslationModel.create_engagement_translation( @@ -131,14 +130,14 @@ def _verify_engagement_translation(engagement_translation_id): return engagement_translation @staticmethod - def _get_default_language_values(engagement, summary_content, translation_data): + def _get_default_language_values(engagement, content, translation_data): """Populate the default values.""" engagement_id = engagement.id translation_data['name'] = engagement.name translation_data['description'] = engagement.description translation_data['rich_description'] = engagement.rich_description - translation_data['content'] = summary_content.content - translation_data['rich_content'] = summary_content.rich_content + translation_data['content'] = content.text_content + translation_data['rich_content'] = content.json_content translation_data['consent_message'] = engagement.consent_message translation_data['sponsor_name'] = engagement.sponsor_name translation_data['cta_message'] = engagement.cta_message diff --git a/met-api/src/met_api/utils/enums.py b/met-api/src/met_api/utils/enums.py index 1b1c3d67d..382e642df 100644 --- a/met-api/src/met_api/utils/enums.py +++ b/met-api/src/met_api/utils/enums.py @@ -102,10 +102,3 @@ class UserStatus(IntEnum): ACTIVE = 1 INACTIVE = 2 - - -class ContentTitle(Enum): - """User status.""" - - DEFAULT = 'Summary' - DEFAULT_ICON = 'SummarizeIcon' diff --git a/met-api/tests/unit/api/test_engagement_content.py b/met-api/tests/unit/api/test_engagement_content.py index 92d8c53fc..cfb6de419 100644 --- a/met-api/tests/unit/api/test_engagement_content.py +++ b/met-api/tests/unit/api/test_engagement_content.py @@ -24,7 +24,6 @@ import pytest from faker import Faker -from met_api.constants.engagement_content_type import EngagementContentType from met_api.services.engagement_content_service import EngagementContentService from met_api.utils.enums import ContentType from tests.utilities.factory_scenarios import TestEngagementContentInfo @@ -70,7 +69,7 @@ def test_create_engagement_content(client, jwt, session, engagement_content_info @pytest.mark.parametrize('engagement_content_info', [TestEngagementContentInfo.content1]) def test_get_engagement_content(client, jwt, session, engagement_content_info, setup_admin_user_and_claims): # pylint:disable=unused-argument - """Assert that a engagement content can be fetched.""" + """Assert that engagement content can be fetched.""" engagement = factory_engagement_model() engagement_content_info['engagement_id'] = engagement.id user, claims = setup_admin_user_and_claims @@ -117,20 +116,17 @@ def test_create_engagement_content_sort(client, jwt, session, assert rv.status_code == 200 assert len(rv.json) == 2, 'Two Contents Should exist.' engagement_contents = rv.json - summary_content = _find_engagement_content(engagement_contents, EngagementContentType.Summary.name) - assert summary_content.get('sort_index') == 1 - - custom_content = _find_engagement_content(engagement_contents, EngagementContentType.Custom.name) - assert custom_content.get('sort_index') == 2 + assert engagement_contents[0].get('title') == 'Summary' + assert engagement_contents[1].get('title') == 'Custom' # Do reorder reorder_dict = [ { - 'id': custom_content.get('id'), + 'id': engagement_contents[1].get('id'), }, { - 'id': summary_content.get('id'), + 'id': engagement_contents[0].get('id'), } ] @@ -142,16 +138,9 @@ def test_create_engagement_content_sort(client, jwt, session, rv = client.get(f'/api/engagement/{engagement.id}/content', headers=headers, content_type=ContentType.JSON.value) engagement_contents = rv.json - summary_content = _find_engagement_content(engagement_contents, EngagementContentType.Summary.name) - assert summary_content.get('sort_index') == 2 - - custom_content = _find_engagement_content(engagement_contents, EngagementContentType.Custom.name) - assert custom_content.get('sort_index') == 1 - -def _find_engagement_content(engagement_contents, content_type): - search_result = next(x for x in engagement_contents if x.get('content_type') == content_type) - return search_result + assert engagement_contents[1].get('title') == 'Summary' + assert engagement_contents[0].get('title') == 'Custom' def test_create_engagement_content_sort_invalid(client, jwt, session, diff --git a/met-api/tests/unit/api/test_engagement_custom_content.py b/met-api/tests/unit/api/test_engagement_custom_content.py deleted file mode 100644 index 16062dcd5..000000000 --- a/met-api/tests/unit/api/test_engagement_custom_content.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests to verify the engagement custom content API end-point. - -Test-Suite to ensure that the engagement custom content endpoint is working as expected. -""" -import json -from http import HTTPStatus -from unittest.mock import patch - -import pytest -from faker import Faker - -from met_api.exceptions.business_exception import BusinessException -from met_api.services.engagement_custom_content_service import EngagementCustomContentService -from met_api.utils.enums import ContentType -from tests.utilities.factory_scenarios import TestEngagementContentInfo -from tests.utilities.factory_utils import factory_auth_header, factory_engagement_model - - -fake = Faker() - - -@pytest.mark.parametrize('engagement_content_info', [TestEngagementContentInfo.content2]) -def test_default_engagement_custom_content_is_created(client, jwt, session, engagement_content_info, - setup_admin_user_and_claims): # pylint:disable=unused-argument - """Assert that a engagement custom content can be POSTed.""" - engagement = factory_engagement_model() - engagement_content_info['engagement_id'] = engagement.id - user, claims = setup_admin_user_and_claims - headers = factory_auth_header(jwt=jwt, claims=claims) - rv = client.post(f'/api/engagement/{engagement.id}/content', - data=json.dumps(engagement_content_info), - headers=headers, content_type=ContentType.JSON.value) - assert rv.status_code == 200 - response_json = rv.json - created_content_id = response_json.get('id') - - rv = client.get( - f'/api/content/{created_content_id}/custom', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - assert rv.json[0].get('custom_text_content') is None - - -@pytest.mark.parametrize('engagement_content_info', [TestEngagementContentInfo.content2]) -def test_engagement_custom_content(client, jwt, session, engagement_content_info, - setup_admin_user_and_claims): # pylint:disable=unused-argument - """Assert that a engagement custom content can be POSTed.""" - engagement = factory_engagement_model() - engagement_content_info['engagement_id'] = engagement.id - user, claims = setup_admin_user_and_claims - headers = factory_auth_header(jwt=jwt, claims=claims) - rv = client.post(f'/api/engagement/{engagement.id}/content', - data=json.dumps(engagement_content_info), - headers=headers, content_type=ContentType.JSON.value) - assert rv.status_code == 200 - response_json = rv.json - created_content_id = response_json.get('id') - - data = { - 'custom_text_content': 'Content Sample', - 'custom_json_content': '"{\"blocks\":[{\"key\":\"fclgj\",\"text\":\"Rich Content Sample\",\ - \"type\":\"unstyled\",\"depth\":0,\ - \"inlineStyleRanges\":[],\"entityRanges\":[],\"data\":{}}],\"entityMap\":{}}"', - 'engagement_id': engagement.id - } - - rv = client.post( - f'/api/content/{created_content_id}/custom', - data=json.dumps(data), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - - with patch.object(EngagementCustomContentService, 'create_custom_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.post( - f'/api/content/{created_content_id}/custom', - data=json.dumps(data), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST - - rv = client.get( - f'/api/content/{created_content_id}/custom', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - assert rv.json[1].get('custom_text_content') == data.get('custom_text_content') - - with patch.object(EngagementCustomContentService, 'get_custom_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.get( - f'/api/content/{created_content_id}/custom', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST - - data_edits = { - 'custom_text_content': fake.text(max_nb_chars=10) - } - - rv = client.patch( - f'/api/content/{created_content_id}/custom', - data=json.dumps(data_edits), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK - - rv = client.get( - f'/api/content/{created_content_id}/custom', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - assert rv.json[0].get('custom_text_content') == data_edits.get('custom_text_content') - - with patch.object(EngagementCustomContentService, 'update_custom_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.patch( - f'/api/content/{created_content_id}/custom', - data=json.dumps(data_edits), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST diff --git a/met-api/tests/unit/api/test_engagement_summary_content.py b/met-api/tests/unit/api/test_engagement_summary_content.py deleted file mode 100644 index 63095b32c..000000000 --- a/met-api/tests/unit/api/test_engagement_summary_content.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests to verify the engagement summary content API end-point. - -Test-Suite to ensure that the engagement summary content endpoint is working as expected. -""" -import json -from http import HTTPStatus -from unittest.mock import patch - -import pytest -from faker import Faker - -from met_api.exceptions.business_exception import BusinessException -from met_api.services.engagement_summary_content_service import EngagementSummaryContentService -from met_api.utils.enums import ContentType -from tests.utilities.factory_scenarios import TestEngagementContentInfo -from tests.utilities.factory_utils import factory_auth_header, factory_engagement_model - - -fake = Faker() - - -@pytest.mark.parametrize('engagement_content_info', [TestEngagementContentInfo.content1]) -def test_engagement_summary_content(client, jwt, session, engagement_content_info, - setup_admin_user_and_claims): # pylint:disable=unused-argument - """Assert that a engagement summary content can be POSTed.""" - engagement = factory_engagement_model() - engagement_content_info['engagement_id'] = engagement.id - user, claims = setup_admin_user_and_claims - headers = factory_auth_header(jwt=jwt, claims=claims) - rv = client.post(f'/api/engagement/{engagement.id}/content', - data=json.dumps(engagement_content_info), - headers=headers, content_type=ContentType.JSON.value) - assert rv.status_code == 200 - response_json = rv.json - created_content_id = response_json.get('id') - - data = { - 'content': 'Content Sample', - 'rich_content': '"{\"blocks\":[{\"key\":\"fclgj\",\"text\":\"Rich Content Sample\",\ - \"type\":\"unstyled\",\"depth\":0,\ - \"inlineStyleRanges\":[],\"entityRanges\":[],\"data\":{}}],\"entityMap\":{}}"', - 'engagement_id': engagement.id - } - - rv = client.post( - f'/api/content/{created_content_id}/summary', - data=json.dumps(data), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - - with patch.object(EngagementSummaryContentService, 'create_summary_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.post( - f'/api/content/{created_content_id}/summary', - data=json.dumps(data), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST - - rv = client.get( - f'/api/content/{created_content_id}/summary', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - assert rv.json[0].get('content') == data.get('content') - - with patch.object(EngagementSummaryContentService, 'get_summary_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.get( - f'/api/content/{created_content_id}/summary', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST - - data_edits = { - 'content': fake.text(max_nb_chars=10) - } - - rv = client.patch( - f'/api/content/{created_content_id}/summary', - data=json.dumps(data_edits), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK - - rv = client.get( - f'/api/content/{created_content_id}/summary', - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.OK.value - assert rv.json[0].get('content') == data_edits.get('content') - - with patch.object(EngagementSummaryContentService, 'update_summary_content', - side_effect=BusinessException('Test error', status_code=HTTPStatus.BAD_REQUEST)): - rv = client.patch( - f'/api/content/{created_content_id}/summary', - data=json.dumps(data_edits), - headers=headers, - content_type=ContentType.JSON.value - ) - assert rv.status_code == HTTPStatus.BAD_REQUEST diff --git a/met-api/tests/utilities/factory_scenarios.py b/met-api/tests/utilities/factory_scenarios.py index 438d3ea81..803428fc1 100644 --- a/met-api/tests/utilities/factory_scenarios.py +++ b/met-api/tests/utilities/factory_scenarios.py @@ -24,12 +24,11 @@ from met_api.config import get_named_config from met_api.constants.comment_status import Status as CommentStatus from met_api.constants.engagement_status import Status as EngagementStatus -from met_api.constants.engagement_content_type import EngagementContentType from met_api.constants.engagement_status import SubmissionStatus from met_api.constants.timeline_event_status import TimelineEventStatus from met_api.constants.feedback import CommentType, FeedbackSourceType, FeedbackStatusType, RatingType from met_api.constants.widget import WidgetType -from met_api.utils.enums import ContentTitle, LoginSource, UserStatus +from met_api.utils.enums import LoginSource, UserStatus fake = Faker() @@ -880,16 +879,20 @@ class TestEngagementContentInfo(dict, Enum): """Test scenarios of engagement content.""" content1 = { - 'title': ContentTitle.DEFAULT.value, - 'icon_name': ContentTitle.DEFAULT_ICON.value, - 'content_type': EngagementContentType.Summary.name, + 'title': 'Summary', 'is_internal': False, + 'text_content': fake.text(max_nb_chars=20), + 'json_content': '{"blocks":[{"key":"fclgj","text":"Rich Content Sample",\ + "type":"unstyled","depth":0,\ + "inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}' } content2 = { 'title': 'Custom', - 'icon_name': ContentTitle.DEFAULT_ICON.value, - 'content_type': EngagementContentType.Custom.name, 'is_internal': False, + 'text_content': fake.text(max_nb_chars=20), + 'json_content': '{"blocks":[{"key":"fclgj","text":"Rich Content Sample",\ + "type":"unstyled","depth":0,\ + "inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}' } diff --git a/met-api/tests/utilities/factory_utils.py b/met-api/tests/utilities/factory_utils.py index 61364f417..c21f85989 100644 --- a/met-api/tests/utilities/factory_utils.py +++ b/met-api/tests/utilities/factory_utils.py @@ -857,8 +857,8 @@ def factory_enagement_content_model( engagement_content = EngagementContentModel( title=engagement_content.get('title'), - icon_name=engagement_content.get('icon_name'), - content_type=engagement_content.get('content_type'), + text_content=engagement_content.get('text_content'), + json_content=engagement_content.get('json_content'), engagement_id=engagement_id, is_internal=engagement_content.get('is_internal', False), ) diff --git a/met-web/src/apiManager/endpoints/index.ts b/met-web/src/apiManager/endpoints/index.ts index dac9f9eb0..db80cd18a 100644 --- a/met-web/src/apiManager/endpoints/index.ts +++ b/met-web/src/apiManager/endpoints/index.ts @@ -45,16 +45,6 @@ const Endpoints = { UPDATE: `${AppConfig.apiUrl}/engagement/engagement_id/content/content_id`, DELETE: `${AppConfig.apiUrl}/engagement/engagement_id/content/content_id`, }, - EngagementCustomContent: { - GET: `${AppConfig.apiUrl}/content/content_id/custom`, - CREATE: `${AppConfig.apiUrl}/content/content_id/custom`, - UPDATE: `${AppConfig.apiUrl}/content/content_id/custom`, - }, - EngagementSummaryContent: { - GET: `${AppConfig.apiUrl}/content/content_id/summary`, - CREATE: `${AppConfig.apiUrl}/content/content_id/summary`, - UPDATE: `${AppConfig.apiUrl}/content/content_id/summary`, - }, User: { GET: `${AppConfig.apiUrl}/user/user_id`, CREATE_UPDATE: `${AppConfig.apiUrl}/user/`, diff --git a/met-web/src/components/engagement/form/ActionContext.tsx b/met-web/src/components/engagement/form/ActionContext.tsx index 3d5c5f5e9..2f3cac82f 100644 --- a/met-web/src/components/engagement/form/ActionContext.tsx +++ b/met-web/src/components/engagement/form/ActionContext.tsx @@ -14,6 +14,7 @@ import { EngagementStatus } from 'constants/engagementStatus'; import { EngagementContent, createDefaultEngagementContent } from 'models/engagementContent'; import { TenantState } from 'reduxSlices/tenantSlice'; import { getEngagementContent } from 'services/engagementContentService'; +import { EngagementLoaderData } from '../public/view'; const CREATE = 'create'; export const ActionContext = createContext({ @@ -71,16 +72,7 @@ export const ActionProvider = ({ children }: { children: JSX.Element }) => { const [savedBannerImageFileName, setSavedBannerImageFileName] = useState(''); const [contentTabs, setContentTabs] = useState([createDefaultEngagementContent()]); const isCreate = window.location.pathname.includes(CREATE); - const routeLoaderData = useRouteLoaderData('single-engagement') as - | { - engagement: Promise; - content: Promise; - metadata: Promise; - taxa: Promise; - } - | undefined; - - const { engagement, content, metadata, taxa } = routeLoaderData || {}; + const { engagement, content, metadata, taxa } = useRouteLoaderData('single-engagement') as EngagementLoaderData; // Load the engagement from the shared individual engagement loader and watch the engagement variable for any changes. useEffect(() => { @@ -90,7 +82,7 @@ export const ActionProvider = ({ children }: { children: JSX.Element }) => { if (isCreate && !engagementId) { return; } - if (engagementId && engagement) { + if (engagementId) { engagement.then((result) => { setEngagement(result); }); @@ -108,7 +100,7 @@ export const ActionProvider = ({ children }: { children: JSX.Element }) => { // Load the engagement's content from the shared individual engagement loader and watch the content variable for any changes. useEffect(() => { - if (engagementId && content) { + if (engagementId) { if (isCreate) { return; } @@ -116,14 +108,14 @@ export const ActionProvider = ({ children }: { children: JSX.Element }) => { setContentTabs(result); }); } - }, [content]); + }, [content, engagementId]); // Load the engagement's metadata and taxa from the shared individual engagement loader and watch the metadata and taxa variables for any changes. useEffect(() => { if (isCreate) { return; } - if (!isCreate && metadata && taxa) { + if (!isCreate) { metadata?.then((result) => setEngagementMetadata(result)); taxa?.then((result) => setTenantTaxa(Object.values(result))); } diff --git a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/ContentTabModal.tsx b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/ContentTabModal.tsx index f26a78ea2..3a6ffde3e 100644 --- a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/ContentTabModal.tsx +++ b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/ContentTabModal.tsx @@ -1,12 +1,12 @@ import React, { useContext, useEffect, useState } from 'react'; -import { MenuItem, Modal, Grid, Stack, TextField, Select } from '@mui/material'; +import { Modal, Grid, Stack, TextField } from '@mui/material'; import { modalStyle, MetHeader1Old, MetLabel, PrimaryButtonOld } from 'components/common'; import { useAppDispatch } from 'hooks'; import { openNotification } from 'services/notificationService/notificationSlice'; import { ActionContext } from '../../ActionContext'; -import { EngagementContentContext } from './EngagementContentContext'; import { EngagementContent } from 'models/engagementContent'; import { postEngagementContent, patchEngagementContent } from 'services/engagementContentService'; +import { EngagementContentContext } from './EngagementContentContext'; interface ContentTabModalProps { open: boolean; @@ -19,36 +19,27 @@ interface ContentTabModalProps { const ContentTabModal = ({ open, updateModal, tabs, setTabs, selectedTabType, tabIndex }: ContentTabModalProps) => { const { savedEngagement } = useContext(ActionContext); - const { isEditMode, setIsSummaryContentsLoading, setIsCustomContentsLoading } = - useContext(EngagementContentContext); + const { isEditMode } = useContext(EngagementContentContext); const dispatch = useAppDispatch(); const [tabTitle, setTabTitle] = useState(''); - const [tabIcon, setTabIcon] = useState(''); useEffect(() => { // Fetch tab details when modal is opened and selectedTabIndex changes if (open && isEditMode && typeof tabIndex === 'number' && tabs[tabIndex]) { setTabTitle(tabs[tabIndex].title); - setTabIcon(tabs[tabIndex].icon_name); } else { // If not in edit mode, initialize tabTitle and tabIcon with empty values setTabTitle(''); - setTabIcon(''); } }, [open, isEditMode, tabIndex, tabs]); const fetchData = async () => { - setIsSummaryContentsLoading(true); - setIsCustomContentsLoading(true); const newtab = isEditMode ? await patchEngagementContent(savedEngagement.id, tabs[tabIndex || 0].id, { title: tabTitle, - icon_name: tabIcon, }) : await postEngagementContent(savedEngagement.id, { title: tabTitle, - icon_name: tabIcon, - content_type: selectedTabType, engagement_id: savedEngagement.id, }); @@ -75,15 +66,12 @@ const ContentTabModal = ({ open, updateModal, tabs, setTabs, selectedTabType, ta } details.`, }), ); - setIsSummaryContentsLoading(false); - setIsCustomContentsLoading(false); handleModalClose(); }; const handleModalClose = () => { updateModal(false); setTabTitle(''); - setTabIcon(''); }; return ( @@ -135,33 +123,6 @@ const ContentTabModal = ({ open, updateModal, tabs, setTabs, selectedTabType, ta - - - - Tab Icon: - - - - - - - - { const { contentTabs, setContentTabs, savedEngagement } = useContext(ActionContext); - const { setIsEditMode, isSummaryContentsLoading } = useContext(EngagementContentContext); + const { setIsEditMode } = useContext(EngagementContentContext); const [tabIndex, setTabIndex] = useState(0); const [isModalOpen, setIsModalOpen] = useState(false); - const [selectedTabType, setSelectedTabType] = useState(''); const [anchorEl, setAnchorEl] = useState(null); - const [customTabAdded, setCustomTabAdded] = useState(false); - const isAllTabTypesPresent = () => { - const tabTypes = Object.values(CONTENT_TYPE); - for (const tabType of tabTypes) { - if (!contentTabs.some((tab) => tab.content_type === tabType)) { - return false; // At least one tab type is missing - } - } - return true; // All tab types are present - }; const handleSetTabIndex = (_event: React.SyntheticEvent, newValue: number) => { setTabIndex(newValue); @@ -53,11 +39,6 @@ export const ContentTabs: React.FC = () => { } await deleteEngagementContent(savedEngagement.id, tab_id); setTabIndex(0); - - // Check if the deleted tab was a custom tab and reset customTabAdded state - if (contentTabs[index].content_type === CONTENT_TYPE.CUSTOM) { - setCustomTabAdded(false); - } }; const handleMenuOpen = (event: React.MouseEvent) => { @@ -68,16 +49,10 @@ export const ContentTabs: React.FC = () => { setAnchorEl(null); }; - const handleMenuClick = (type: string) => { + const handleMenuClick = () => { setIsEditMode(false); - setSelectedTabType(type); handleModalOpen(); handleMenuClose(); - - // Set customTabAdded to true when a custom tab is added - if (type === CONTENT_TYPE.CUSTOM) { - setCustomTabAdded(true); - } }; const handleEditTab = (index: number) => { @@ -87,107 +62,66 @@ export const ContentTabs: React.FC = () => { }; return ( - - - - - - - - - - - - {contentTabs?.map((tab, index) => ( - - {tab.title} - - handleEditTab(index)} aria-label="edit"> - - - - {index !== 0 && ( - - handleDeleteTab(index)} - aria-label="delete" - > - - - - )} - - } - /> - ))} - - - - - - handleMenuClick(CONTENT_TYPE.CUSTOM)} - data-testid="add-new-custom-tab" - > - Add new custom tab - - - - - - {contentTabs && contentTabs.length > 0 ? ( - (() => { - if (contentTabs[tabIndex]) { - switch (contentTabs[tabIndex].content_type) { - case CONTENT_TYPE.SUMMARY: - return ; - case CONTENT_TYPE.CUSTOM: - return ; - default: - return ( -
- Custom tab content for {contentTabs[tabIndex].content_type} -
- ); - } - } else { - return
Invalid tab index
; - } - })() - ) : ( -
No tabs available
- )} -
-
- + + + + + {contentTabs?.map((tab, index) => ( + + {tab.title} + + handleEditTab(index)} aria-label="edit"> + + + + {index !== 0 && ( + + handleDeleteTab(index)} aria-label="delete"> + + + + )} + + } + /> + ))} + + -
-
+ + + handleMenuClick()} data-testid="add-new-custom-tab"> + Add new custom tab + + + + {contentTabs?.map((tab, index) => ( + + + + ))} + + ); }; diff --git a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/CustomTabContent.tsx b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/CustomTabContent.tsx deleted file mode 100644 index b3bc525ad..000000000 --- a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/CustomTabContent.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useEffect, useState, useContext } from 'react'; -import { Grid } from '@mui/material'; -import { MetDescription, MetLabel } from 'components/common'; -import RichTextEditor from 'components/common/RichTextEditor'; -import { EngagementTabsContext } from '../EngagementTabsContext'; - -const CustomTabContent = () => { - const [initialRichContent, setInitialRichContent] = useState(''); - - const { setCustomTextContent, customJsonContent, setCustomJsonContent } = useContext(EngagementTabsContext); - - const handleContentChange = (rawText: string) => { - setCustomTextContent(rawText); - }; - - const handleRichContentChange = (newState: string) => { - setCustomJsonContent(newState); - }; - - useEffect(() => { - setInitialRichContent(customJsonContent); - }, []); - - return ( - - - Engagement - Page Custom Content - - This is the additional content of the engagement page. - - - - - ); -}; - -export default CustomTabContent; diff --git a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/EngagementContentContext.tsx b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/EngagementContentContext.tsx index ad5b90fb8..05ba1083a 100644 --- a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/EngagementContentContext.tsx +++ b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementContent/EngagementContentContext.tsx @@ -1,29 +1,13 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; -import { CONTENT_TYPE } from 'models/engagementContent'; import { ActionContext } from '../../ActionContext'; import { EngagementTabsContext } from '../EngagementTabsContext'; -import { useRouteLoaderData } from 'react-router-dom'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; -import { EngagementCustomContent } from 'models/engagementCustomContent'; export interface EngagementContentProps { - isSummaryContentsLoading: boolean; - setIsSummaryContentsLoading: React.Dispatch>; - isCustomContentsLoading: boolean; - setIsCustomContentsLoading: React.Dispatch>; isEditMode: boolean; setIsEditMode: React.Dispatch>; } export const EngagementContentContext = createContext({ - isSummaryContentsLoading: false, - setIsSummaryContentsLoading: async () => { - /* empty default method */ - }, - isCustomContentsLoading: false, - setIsCustomContentsLoading: async () => { - /* empty default method */ - }, isEditMode: false, setIsEditMode: async () => { /* empty default method */ @@ -32,70 +16,25 @@ export const EngagementContentContext = createContext({ export const EngagementContextProvider = ({ children }: { children: JSX.Element | JSX.Element[] }) => { const { contentTabs, savedEngagement } = useContext(ActionContext); - const { - setRichContent, - engagementFormData, - setEngagementFormData, - setEngagementSummaryContent, - setEngagementCustomContent, - setCustomTextContent, - setCustomJsonContent, - } = useContext(EngagementTabsContext); - const [isSummaryContentsLoading, setIsSummaryContentsLoading] = useState(true); - const [isCustomContentsLoading, setIsCustomContentsLoading] = useState(true); + const { engagementFormData, setEngagementFormData } = useContext(EngagementTabsContext); const [isEditMode, setIsEditMode] = useState(false); - const summaryItem = contentTabs.find((item) => item.content_type === CONTENT_TYPE.SUMMARY); - const customItem = contentTabs.find((item) => item.content_type === CONTENT_TYPE.CUSTOM); - const routeLoaderData = useRouteLoaderData('single-engagement') as - | { - contentSummary: Promise; - customContent: Promise; - } - | undefined; - const { contentSummary, customContent } = routeLoaderData ?? {}; // Load the engagement's summary from the shared individual engagement loader and watch the summary item variable for any changes. useEffect(() => { - if (!savedEngagement.id || !summaryItem) { - setIsSummaryContentsLoading(false); + if (!savedEngagement.id) { return; } - if (savedEngagement && contentSummary) { - contentSummary.then((result: EngagementSummaryContent[]) => { - setEngagementSummaryContent(result[0]); - setRichContent(result[0].rich_content); - setEngagementFormData({ - ...engagementFormData, - content: result[0].content, - }); - setIsSummaryContentsLoading(false); - }); - } - }, [summaryItem, contentSummary, savedEngagement]); - - // Load the engagement's custom content from the shared individual engagement loader and watch the customItem variable for any changes. - useEffect(() => { - if (savedEngagement && customContent) { - customContent.then((result: EngagementCustomContent[]) => { - if (!savedEngagement.id || !customItem) { - setIsCustomContentsLoading(false); - return; - } - setEngagementCustomContent(result); - setCustomTextContent(result[1].custom_text_content); - setCustomJsonContent(result[1].custom_json_content); - setIsCustomContentsLoading(false); + if (savedEngagement && contentTabs) { + setEngagementFormData({ + ...engagementFormData, + content: contentTabs[0]?.json_content, }); } - }, [customItem, customContent, savedEngagement]); + }, [savedEngagement, contentTabs]); return ( { - const { savedEngagement } = useContext(ActionContext); +const TabContent = ({ index }: { index: number }) => { + const { savedEngagement, contentTabs, setContentTabs } = useContext(ActionContext); const [initialRichContent, setInitialRichContent] = useState(''); const [editorDisabled, setEditorDisabled] = useState(false); - const { engagementFormData, setEngagementFormData, richContent, setRichContent } = - useContext(EngagementTabsContext); + const { richContent, setRichContent } = useContext(EngagementTabsContext); const handleContentChange = (rawText: string) => { - setEngagementFormData({ - ...engagementFormData, - content: rawText, + setContentTabs((prevTabs) => { + const newTabs = [...prevTabs]; + newTabs[index].json_content = rawText; + return newTabs; }); }; @@ -42,10 +43,18 @@ const SummaryTabContent = () => { columnSpacing={2} > - - Engagement - Page Content - - This is the main content of the engagement page. + + + Engagement - Page Content + + This is the main content of the engagement page. + + + + Additional Tab {index} - {contentTabs[index].title} + + Additional content for the engagement page. +
{ )}
- - - + + + + +
); }; -export default SummaryTabContent; +export default TabContent; diff --git a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementTabsContext.tsx b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementTabsContext.tsx index 929c1ec98..d3dd5e320 100644 --- a/met-web/src/components/engagement/form/EngagementFormTabs/EngagementTabsContext.tsx +++ b/met-web/src/components/engagement/form/EngagementFormTabs/EngagementTabsContext.tsx @@ -8,8 +8,6 @@ import { getTeamMembers } from 'services/membershipService'; import { openNotification } from 'services/notificationService/notificationSlice'; import { useAppDispatch } from 'hooks'; import { EngagementSettings, createDefaultEngagementSettings } from 'models/engagement'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; -import { EngagementCustomContent } from 'models/engagementCustomContent'; import { updatedDiff } from 'deep-object-diff'; import { getSlugByEngagementId } from 'services/engagementSlugService'; import { @@ -17,8 +15,6 @@ import { getEngagementSettings, patchEngagementSettings, } from 'services/engagementSettingService'; -import { PatchSummaryContentRequest, patchSummaryContent } from 'services/engagementSummaryService'; -import { PatchCustomContentRequest, patchCustomContent } from 'services/engagementCustomService'; import { EngagementSlugPatchRequest, patchEngagementSlug } from 'services/engagementSlugService'; import { EngagementForm } from '../types'; import { SubmissionStatus } from 'constants/engagementStatus'; @@ -65,22 +61,6 @@ const initialEngagementSettingsSlugData = { slug: '', }; -const initialEngagementSummaryContent = { - id: 0, - content: '', - rich_content: '', - engagement_id: 0, - engagement_content_id: 0, -}; - -const initialEngagementCustomContent = { - id: 0, - custom_text_content: '', - custom_json_content: '', - engagement_id: 0, - engagement_content_id: 0, -}; - interface EngagementFormError { name: boolean; start_date: boolean; @@ -128,10 +108,6 @@ export interface EngagementTabsContextState { hasBeenOpened: boolean; slug: EngagementSettingsSlugData; setSlug: React.Dispatch>; - engagementSummaryContent: EngagementSummaryContent; - setEngagementSummaryContent: React.Dispatch>; - engagementCustomContent: EngagementCustomContent[]; - setEngagementCustomContent: React.Dispatch>; customTextContent: string; setCustomTextContent: React.Dispatch>; customJsonContent: string; @@ -205,14 +181,6 @@ export const EngagementTabsContext = createContext({ setSlug: () => { /* empty default method */ }, - engagementSummaryContent: initialEngagementSummaryContent, - setEngagementSummaryContent: () => { - /* empty default method */ - }, - engagementCustomContent: [initialEngagementCustomContent], - setEngagementCustomContent: () => { - /* empty default method */ - }, customTextContent: '', setCustomTextContent: () => { /* empty default method */ @@ -249,12 +217,6 @@ export const EngagementTabsContextProvider = ({ children }: { children: React.Re const [richConsentMessage, setRichConsentMessage] = useState(savedEngagement?.consent_message || ''); const [customTextContent, setCustomTextContent] = useState(''); const [customJsonContent, setCustomJsonContent] = useState(''); - const [engagementSummaryContent, setEngagementSummaryContent] = useState( - initialEngagementSummaryContent, - ); - const [engagementCustomContent, setEngagementCustomContent] = useState([ - initialEngagementCustomContent, - ]); const [engagementFormError, setEngagementFormError] = useState(initialFormError); const metadataFormRef = useRef(null); @@ -383,80 +345,6 @@ export const EngagementTabsContextProvider = ({ children }: { children: React.Re } }; - const updateSummaryContent = async () => { - setSaving(true); - try { - const updatedSummaryContent = updatedDiff( - { - content: engagementSummaryContent.content, - rich_content: engagementSummaryContent.rich_content, - }, - { - content: engagementFormData.content, - rich_content: richContent, - }, - ) as PatchSummaryContentRequest; - if (Object.keys(updatedSummaryContent).length === 0) { - setSaving(false); - return; - } - const result = await patchSummaryContent( - engagementSummaryContent.engagement_content_id, - updatedSummaryContent, - ); - setEngagementSummaryContent(result); - setRichContent(result.rich_content); - dispatch(openNotification({ severity: 'success', text: 'Engagement has been saved' })); - setSaving(false); - return; - } catch (error) { - dispatch( - openNotification({ - severity: 'error', - text: 'Error occurred while trying to update summary content, please refresh the page or try again at a later time', - }), - ); - setSaving(false); - } - }; - - const updateCustomContent = async () => { - setSaving(true); - try { - const updatedCustomContent = updatedDiff( - { - custom_text_content: engagementCustomContent[1].custom_text_content, - custom_json_content: engagementCustomContent[1].custom_json_content, - }, - { - custom_text_content: customTextContent, - custom_json_content: customJsonContent, - }, - ) as PatchCustomContentRequest; - if (Object.keys(updatedCustomContent).length === 0) { - setSaving(false); - return; - } - const result = await patchCustomContent( - engagementCustomContent[1].engagement_content_id, - updatedCustomContent, - ); - setEngagementCustomContent([result]); - setCustomJsonContent(result.custom_json_content); - dispatch(openNotification({ severity: 'success', text: 'Engagement has been saved' })); - setSaving(false); - return; - } catch (error) { - dispatch( - openNotification({ - severity: 'error', - text: 'Error occurred while trying to update custom content, please refresh the page or try again at a later time', - }), - ); - setSaving(false); - } - }; - const [savedSlug, setSavedSlug] = useState(''); const [slug, setSlug] = useState(initialEngagementSettingsSlugData); @@ -578,8 +466,6 @@ export const EngagementTabsContextProvider = ({ children }: { children: React.Re if (!isNewEngagement) { await updateEngagementSettings(sendReport); - await updateSummaryContent(); - await updateCustomContent(); await handleSaveSlug(slug); await handleSaveEngagementMetadata(); } @@ -648,10 +534,6 @@ export const EngagementTabsContextProvider = ({ children }: { children: React.Re hasBeenOpened, slug, setSlug, - engagementSummaryContent, - setEngagementSummaryContent, - engagementCustomContent, - setEngagementCustomContent, customTextContent, setCustomTextContent, customJsonContent, diff --git a/met-web/src/components/engagement/old-view/ActionContext.tsx b/met-web/src/components/engagement/old-view/ActionContext.tsx index ed60b3e9d..2bd5c76cb 100644 --- a/met-web/src/components/engagement/old-view/ActionContext.tsx +++ b/met-web/src/components/engagement/old-view/ActionContext.tsx @@ -9,7 +9,8 @@ import { SubmissionStatus } from 'constants/engagementStatus'; import { verifyEmailVerification } from 'services/emailVerificationService'; import { openNotificationModal } from 'services/notificationModalService/notificationModalSlice'; import { getEngagementIdBySlug } from 'services/engagementSlugService'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; +import { EngagementLoaderData } from '../public/view'; +import { EngagementContent } from 'models/engagementContent'; interface EngagementSchedule { id: number; @@ -31,8 +32,7 @@ export interface EngagementViewContext { engagementWidgets: Widget[]; mockStatus: SubmissionStatus; updateMockStatus: (status: SubmissionStatus) => void; - content: string; - richContent: string; + content: EngagementContent[]; } export type EngagementParams = { @@ -56,8 +56,7 @@ export const ActionContext = createContext({ updateMockStatus: (status: SubmissionStatus) => { /* nothing returned */ }, - content: '', - richContent: '', + content: [], }); export const ActionProvider = ({ children }: { children: JSX.Element | JSX.Element[] }) => { @@ -73,14 +72,13 @@ export const ActionProvider = ({ children }: { children: JSX.Element | JSX.Eleme const [engagementWidgets, setEngagementWidgets] = useState([]); const [isEngagementLoading, setEngagementLoading] = useState(true); const [isWidgetsLoading, setIsWidgetsLoading] = useState(true); - const [content, setContent] = useState(''); - const [richContent, setRichContent] = useState(''); + const [content, setContent] = useState([]); - const { engagement } = useRouteLoaderData('single-engagement') as { engagement: Promise }; - const { contentSummary } = useRouteLoaderData('single-engagement') as { - contentSummary: Promise; - }; - const { widgets } = useRouteLoaderData('single-engagement') as { widgets: Promise }; + const { + engagement, + widgets, + content: contentPromise, + } = useRouteLoaderData('single-engagement') as EngagementLoaderData; // Load the engagement from the shared individual engagement loader and watch the engagement variable for any changes. useEffect(() => { @@ -99,27 +97,11 @@ export const ActionProvider = ({ children }: { children: JSX.Element | JSX.Eleme // Load the widgets from the shared individual engagement loader and watch the engagement variable for any changes. useEffect(() => { - if (!engagementId && slug) { - return; - } widgets.then((result) => { setEngagementWidgets(result); setIsWidgetsLoading(false); }); - }, [widgets]); - - // Load the engagement's summary from the shared individual engagement loader and watch the summary variable for any changes. - useEffect(() => { - contentSummary.then((result: EngagementSummaryContent[]) => { - if ((!engagementId && slug) || !result.length) { - return; - } - const selectedEngagement = result[0]; - setContent(selectedEngagement.content); - setRichContent(selectedEngagement.rich_content); - setEngagementLoading(false); - }); - }, [contentSummary]); + }, [widgets, engagementId]); useEffect(() => { verifySubscribeToken(); @@ -129,6 +111,12 @@ export const ActionProvider = ({ children }: { children: JSX.Element | JSX.Eleme setMockStatus(savedEngagement.submission_status); }, [savedEngagement]); + useEffect(() => { + contentPromise.then((result) => { + setContent(result); + }); + }, [contentPromise]); + const verifySubscribeToken = async () => { try { if (!token) { @@ -217,7 +205,6 @@ export const ActionProvider = ({ children }: { children: JSX.Element | JSX.Eleme mockStatus, unpublishEngagement, content, - richContent, }} > {children} diff --git a/met-web/src/components/engagement/old-view/EngagementContent.tsx b/met-web/src/components/engagement/old-view/EngagementContent.tsx index 78fcc45b7..990bb9c83 100644 --- a/met-web/src/components/engagement/old-view/EngagementContent.tsx +++ b/met-web/src/components/engagement/old-view/EngagementContent.tsx @@ -5,16 +5,16 @@ import { ActionContext } from './ActionContext'; import { Skeleton } from '@mui/material'; import { getEditorStateFromRaw } from 'components/common/RichTextEditor/utils'; -export const EngagementContent = () => { - const { richContent, isEngagementLoading } = useContext(ActionContext); +export const EngagementContent = ({ index = 0 }: { index?: number }) => { + const { content, isEngagementLoading } = useContext(ActionContext); - if (isEngagementLoading) { + if (isEngagementLoading || !content.length) { return ; } return ( - + ); }; diff --git a/met-web/src/components/engagement/old-view/ScheduleModal/index.tsx b/met-web/src/components/engagement/old-view/ScheduleModal/index.tsx index a10153360..2f3be53df 100644 --- a/met-web/src/components/engagement/old-view/ScheduleModal/index.tsx +++ b/met-web/src/components/engagement/old-view/ScheduleModal/index.tsx @@ -28,11 +28,11 @@ interface ScheduleModalProps { const ScheduleModal = ({ reschedule, open, updateModal }: ScheduleModalProps) => { const [scheduledDate, setScheduledDate] = useState(dayjs(Date.now())); - const { content, savedEngagement, scheduleEngagement } = useContext(ActionContext); + const { savedEngagement, scheduleEngagement } = useContext(ActionContext); const dispatch = useAppDispatch(); const isEngagementReady = () => { - return content && savedEngagement.description && savedEngagement.banner_url; + return savedEngagement.description && savedEngagement.banner_url; }; const handleChange = (newDate: Dayjs | null) => { diff --git a/met-web/src/components/engagement/public/view/EngagementContentTabs.tsx b/met-web/src/components/engagement/public/view/EngagementContentTabs.tsx index e394acab9..0f2189dd3 100644 --- a/met-web/src/components/engagement/public/view/EngagementContentTabs.tsx +++ b/met-web/src/components/engagement/public/view/EngagementContentTabs.tsx @@ -3,24 +3,19 @@ import { Tab, Skeleton, Box } from '@mui/material'; import { TabContext, TabList, TabPanel } from '@mui/lab'; import { Await, useLoaderData } from 'react-router-dom'; import { EngagementContent } from 'models/engagementContent'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; import { getEditorStateFromRaw } from 'components/common/RichTextEditor/utils'; import { Header2 } from 'components/common/Typography'; import { colors } from 'components/common'; import { RichTextArea } from 'components/common/Input/RichTextArea'; +import { EngagementLoaderData } from './EngagementLoader'; export const EngagementContentTabs = () => { - const { content, contentSummary } = useLoaderData() as { - content: Promise; - contentSummary: Promise; - }; + const { content } = useLoaderData() as EngagementLoaderData; const [selectedTab, setSelectedTab] = useState('0'); const handleChange = (event: SyntheticEvent, newValue: string) => { setSelectedTab(newValue); }; - const panelContents = Promise.all([content, contentSummary]); - const tabListRef = useCallback((node: HTMLButtonElement) => { if (!node) return; const scroller = node.getElementsByClassName('MuiTabs-scroller')[0]; @@ -137,25 +132,22 @@ export const EngagementContentTabs = () => { }> - - {([content, contentSummary]: [ - content: EngagementContent[], - contentSummary: EngagementSummaryContent[], - ]) => { - return contentSummary.map((summary, index) => ( - + + {(resolvedContent: EngagementContent[]) => + resolvedContent.map((content, index) => ( + - {content[index].title} + {content.title} - )); - }} + )) + } diff --git a/met-web/src/components/engagement/public/view/EngagementLoader.tsx b/met-web/src/components/engagement/public/view/EngagementLoader.tsx index 792fea0d0..185488e73 100644 --- a/met-web/src/components/engagement/public/view/EngagementLoader.tsx +++ b/met-web/src/components/engagement/public/view/EngagementLoader.tsx @@ -1,17 +1,12 @@ -import { EngagementCustomContent } from 'models/engagementCustomContent'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; import { Params, defer } from 'react-router-dom'; import { getEngagementContent } from 'services/engagementContentService'; -import { getCustomContent } from 'services/engagementCustomService'; import { getEngagement } from 'services/engagementService'; import { getEngagementIdBySlug, getSlugByEngagementId } from 'services/engagementSlugService'; -import { getSummaryContent } from 'services/engagementSummaryService'; import { getWidgets } from 'services/widgetService'; import { getEngagementMetadata, getMetadataTaxa } from 'services/engagementMetadataService'; -import { Engagement, EngagementMetadata } from 'models/engagement'; +import { Engagement, EngagementMetadata, MetadataTaxon } from 'models/engagement'; import { Widget } from 'models/widget'; import { EngagementContent } from 'models/engagementContent'; -import { TaxonType } from 'components/metadataManagement/types'; import { getTeamMembers } from 'services/membershipService'; import { EngagementTeamMember } from 'models/engagementTeamMember'; @@ -20,10 +15,8 @@ export type EngagementLoaderData = { slug: Promise; widgets: Promise; content: Promise; - contentSummary: Promise; metadata: Promise; - taxa: Promise; - customContent: Promise; + taxa: Promise; teamMembers: Promise; }; @@ -58,51 +51,13 @@ export const engagementLoader = async ({ params }: { params: Params }) = const taxa = taxaData.then((taxa) => Object.values(taxa)); - const contentSummary = content.then((response) => - Promise.all( - response.map((content) => { - return content.content_type === 'Summary' - ? getSummaryContent(Number(content.id)).then(convertArrayToSingleEntry) - : getCustomContent(content.id).then(castCustomContentToSummaryContent); - }), - ), - ); - - const customContent = content.then((response) => - Promise.all( - response.map((content) => { - if (content.content_type === 'Custom') return getCustomContent(content.id).then((result) => result[0]); - }), - ), - ); - - const convertArrayToSingleEntry = ( - engagementSummaryContent: EngagementSummaryContent[], - ): EngagementSummaryContent => { - return engagementSummaryContent[0]; - }; - - const castCustomContentToSummaryContent = (customContent: EngagementCustomContent[]): EngagementSummaryContent => { - const mappedCustomContent = customContent.map((custom) => ({ - id: custom.id, - content: custom.custom_text_content, - rich_content: custom.custom_json_content, - engagement_id: custom.engagement_id, - engagement_content_id: custom.engagement_content_id, - })); - const finishedContent = mappedCustomContent[0]; - return finishedContent; - }; - return defer({ engagement, slug, widgets, content, - contentSummary, metadata, taxa, - customContent, teamMembers, }); }; diff --git a/met-web/src/models/engagementContent.ts b/met-web/src/models/engagementContent.ts index 14e705198..4287212e2 100644 --- a/met-web/src/models/engagementContent.ts +++ b/met-web/src/models/engagementContent.ts @@ -1,15 +1,8 @@ -export type EngagementContentTypes = 'Summary' | 'Custom'; - -export const CONTENT_TYPE: { [status: string]: EngagementContentTypes } = { - SUMMARY: 'Summary', - CUSTOM: 'Custom', -}; - export interface EngagementContent { id: number; title: string; - icon_name: string; - content_type: string; + text_content: string; + json_content: string; engagement_id: number; sort_index: number; is_internal: boolean; @@ -19,8 +12,8 @@ export const createDefaultEngagementContent = (): EngagementContent => { return { id: 0, title: 'Summary', - icon_name: '', - content_type: CONTENT_TYPE.SUMMARY, + text_content: '', + json_content: '{}', engagement_id: 0, sort_index: 1, is_internal: false, diff --git a/met-web/src/models/engagementCustomContent.ts b/met-web/src/models/engagementCustomContent.ts deleted file mode 100644 index d915f7702..000000000 --- a/met-web/src/models/engagementCustomContent.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface EngagementCustomContent { - id: number; - custom_text_content: string; - custom_json_content: string; - engagement_id: number; - engagement_content_id: number; -} diff --git a/met-web/src/models/engagementSummaryContent.ts b/met-web/src/models/engagementSummaryContent.ts deleted file mode 100644 index 7c58eaa08..000000000 --- a/met-web/src/models/engagementSummaryContent.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface EngagementSummaryContent { - id: number; - content: string; - rich_content: string; - engagement_id: number; - engagement_content_id: number; -} diff --git a/met-web/src/services/engagementContentService/index.ts b/met-web/src/services/engagementContentService/index.ts index d51356fe8..5157abeca 100644 --- a/met-web/src/services/engagementContentService/index.ts +++ b/met-web/src/services/engagementContentService/index.ts @@ -17,11 +17,11 @@ export const getEngagementContent = async (engagementId: number): Promise => { - const url = replaceUrl(Endpoints.EngagementCustomContent.GET, 'content_id', String(contentId)); - if (!contentId || isNaN(Number(contentId))) { - return Promise.reject('Invalid content Id ' + contentId); - } - const response = await http.GetRequest(url); // Notice the change here - if (response.data) { - return response.data; - } - return Promise.reject('Failed to fetch engagement Custom content'); -}; - -interface PostCustomContentRequest { - custom_text_content?: string; - custom_json_content?: string; - engagement_id?: number; - engagement_content_id?: number; -} -export const postCustomContent = async ( - contentId: number, - data: PostCustomContentRequest, -): Promise => { - try { - const url = replaceUrl(Endpoints.EngagementCustomContent.CREATE, 'content_id', String(contentId)); - const response = await http.PostRequest(url, data); - if (response.data) { - return response.data; - } - return Promise.reject('Failed to create engagement Custom content'); - } catch (err) { - return Promise.reject(err); - } -}; - -export interface PatchCustomContentRequest { - custom_text_content?: string; - custom_json_content?: string; -} -export const patchCustomContent = async ( - contentId: number, - data: PatchCustomContentRequest, -): Promise => { - const url = replaceAllInURL({ - URL: Endpoints.EngagementCustomContent.UPDATE, - params: { - content_id: String(contentId), - }, - }); - const response = await http.PatchRequest(url, data); - if (response.data) { - return response.data; - } - return Promise.reject('Failed to update engagement Custom content'); -}; diff --git a/met-web/src/services/engagementSummaryService/index.ts b/met-web/src/services/engagementSummaryService/index.ts deleted file mode 100644 index 7454f107b..000000000 --- a/met-web/src/services/engagementSummaryService/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import http from 'apiManager/httpRequestHandler'; -import { EngagementSummaryContent } from 'models/engagementSummaryContent'; -import Endpoints from 'apiManager/endpoints'; -import { replaceAllInURL, replaceUrl } from 'helper'; - -export const getSummaryContent = async (contentId: number): Promise => { - const url = replaceUrl(Endpoints.EngagementSummaryContent.GET, 'content_id', String(contentId)); - if (!contentId || isNaN(Number(contentId))) { - return Promise.reject('Invalid content Id ' + contentId); - } - const response = await http.GetRequest(url); - if (response.data) { - return response.data; - } - return Promise.reject('Failed to fetch engagement summary content'); -}; - -interface PostSummaryContentRequest { - content?: string; - rich_content?: string; - engagement_id?: number; -} -export const postSummaryContent = async ( - contentId: number, - data: PostSummaryContentRequest, -): Promise => { - try { - const url = replaceUrl(Endpoints.EngagementSummaryContent.CREATE, 'content_id', String(contentId)); - const response = await http.PostRequest(url, data); - if (response.data) { - return response.data; - } - return Promise.reject('Failed to create engagement summary content'); - } catch (err) { - return Promise.reject(err); - } -}; - -export interface PatchSummaryContentRequest { - content?: string; - rich_content?: string; -} -export const patchSummaryContent = async ( - contentId: number, - data: PatchSummaryContentRequest, -): Promise => { - const url = replaceAllInURL({ - URL: Endpoints.EngagementSummaryContent.UPDATE, - params: { - content_id: String(contentId), - }, - }); - const response = await http.PatchRequest(url, data); - if (response.data) { - return response.data; - } - return Promise.reject('Failed to update engagement summary content'); -}; diff --git a/met-web/tests/unit/components/engagement/EngagementFormUserTab.test.tsx b/met-web/tests/unit/components/engagement/EngagementFormUserTab.test.tsx index 7120ec46e..30745e80d 100644 --- a/met-web/tests/unit/components/engagement/EngagementFormUserTab.test.tsx +++ b/met-web/tests/unit/components/engagement/EngagementFormUserTab.test.tsx @@ -92,7 +92,6 @@ jest.mock('react-router-dom', () => ({ engagement: Promise.resolve(draftEngagement), metadata: Promise.resolve([]), widgets: Promise.resolve([]), - contentSummary: Promise.resolve([]), content: Promise.resolve([]), }; } @@ -101,7 +100,6 @@ jest.mock('react-router-dom', () => ({ engagement: Promise.resolve(draftEngagement), metadata: Promise.resolve([]), widgets: Promise.resolve([]), - contentSummary: Promise.resolve([]), content: Promise.resolve([]), }), })); diff --git a/met-web/tests/unit/components/engagement/form/edit/EngagementForm.Edit.Two.test.tsx b/met-web/tests/unit/components/engagement/form/edit/EngagementForm.Edit.Two.test.tsx index 5efe8d896..629cc4ada 100644 --- a/met-web/tests/unit/components/engagement/form/edit/EngagementForm.Edit.Two.test.tsx +++ b/met-web/tests/unit/components/engagement/form/edit/EngagementForm.Edit.Two.test.tsx @@ -367,7 +367,6 @@ describe('Engagement form page tests', () => { await waitFor(() => { expect(getByTestId('add-tab-button')).toBeVisible(); expect(getByText('Tab Title:')).toBeInTheDocument(); - expect(getByText('Tab Icon:')).toBeInTheDocument(); }); }); diff --git a/met-web/tests/unit/components/engagement/old-engagement.test.tsx b/met-web/tests/unit/components/engagement/old-engagement.test.tsx index f390fc3ef..76e10c25a 100644 --- a/met-web/tests/unit/components/engagement/old-engagement.test.tsx +++ b/met-web/tests/unit/components/engagement/old-engagement.test.tsx @@ -12,6 +12,7 @@ import { Widget, WidgetItem, WidgetType } from 'models/widget'; import { createDefaultSurvey, Survey } from 'models/survey'; import { draftEngagement } from '../factory'; import { createMemoryRouter, RouterProvider } from 'react-router-dom'; +import { EngagementContent } from 'models/engagementContent'; const survey: Survey = { ...createDefaultSurvey(), @@ -129,18 +130,22 @@ jest.mock('react-router-dom', () => ({ engagement: Promise.resolve(draftEngagement), metadata: Promise.resolve([]), widgets: Promise.resolve([whoIsListeningWidget]), - contentSummary: Promise.resolve([]), - content: Promise.resolve([]), + content: Promise.resolve([ + { + id: 1, + title: 'title', + text_content: 'text content', + json_content: + '{"blocks":[{"key":"fclgj","text":"Rich Content Sample","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}', + engagement_id: 1, + content: 'content', + sort_index: 1, + is_internal: false, + } as EngagementContent, + ]), }; } }, - useLoaderData: () => ({ - engagement: Promise.resolve(draftEngagement), - metadata: Promise.resolve([]), - widgets: Promise.resolve([whoIsListeningWidget]), - contentSummary: Promise.resolve([]), - content: Promise.resolve([]), - }), })); describe('Engagement View page tests', () => { @@ -187,7 +192,6 @@ describe('Engagement View page tests', () => { mockWidgetsRtkUnwrap.mockReturnValueOnce(Promise.resolve([whoIsListeningWidget])); mockContactRtkUnwrap.mockReturnValueOnce(Promise.resolve(mockContact)); const { container } = render(); - await waitFor(() => { expect(container.querySelector('span.MuiSkeleton-root')).toBeNull(); expect(screen.getByText('Who is Listening')).toBeVisible(); diff --git a/met-web/tests/unit/components/factory.ts b/met-web/tests/unit/components/factory.ts index de12572ae..99092cd52 100644 --- a/met-web/tests/unit/components/factory.ts +++ b/met-web/tests/unit/components/factory.ts @@ -290,11 +290,11 @@ const engagementSlugData = { const engagementContentData: EngagementContent = { id: 1, title: '', - icon_name: '', - content_type: '', engagement_id: 1, sort_index: 1, is_internal: true, + text_content: 'test', + json_content: '', }; const staffUserState: Partial = {