From 012fd2cfeba6f3ecce8097b7db9c2e77d1db43f5 Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 16 Nov 2023 05:22:38 -0800 Subject: [PATCH 1/3] Added Migration and Replaced text editor in frontend --- .../1700109002096-RichTextStoringPI.ts | 277 +++++++++++++++ .../section-contract.ts | 7 +- .../section-controls.ts | 7 +- .../section-storage.ts | 10 +- .../section-track-access.ts | 7 +- .../personal-information.ts | 7 +- .../mocks/create-pia-intake.mock.ts | 14 +- .../test/util/mocks/data/pia-intake.mock.ts | 14 +- .../storingPersonalInformation/index.tsx | 325 ++++++++---------- .../storingPersonalInformation/interfaces.ts | 13 +- 10 files changed, 469 insertions(+), 212 deletions(-) create mode 100644 src/backend/src/migrations/1700109002096-RichTextStoringPI.ts diff --git a/src/backend/src/migrations/1700109002096-RichTextStoringPI.ts b/src/backend/src/migrations/1700109002096-RichTextStoringPI.ts new file mode 100644 index 000000000..03338924a --- /dev/null +++ b/src/backend/src/migrations/1700109002096-RichTextStoringPI.ts @@ -0,0 +1,277 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RichTextStoringPI1700109002096 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + // Add temporary columns to store the existing text + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_whereDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_disclosureDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_contractualTerms" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_enterpriseServiceAccessDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_unauthorizedAccessMeasures" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_trackAccessDetails" text`, + ); + + // Copy the data from the old columns to the temporary columns + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_whereDetails" = "storing_personal_information"->'personalInformation'->>'whereDetails'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_disclosureDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'storage'->>'disclosureDetails'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_contractualTerms" = "storing_personal_information"->'disclosuresOutsideCanada'->'storage'->>'contractualTerms'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_enterpriseServiceAccessDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'contract'->>'enterpriseServiceAccessDetails'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_unauthorizedAccessMeasures" = "storing_personal_information"->'disclosuresOutsideCanada'->'controls'->>'unauthorizedAccessMeasures'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_trackAccessDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'trackAccess'->>'trackAccessDetails'`, + ); + + // Change jsonb properties from type string to object + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{personalInformation, whereDetails}', + '{"content": ""}'::jsonb + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, disclosureDetails}', + '{"content": ""}'::jsonb + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, contractualTerms}', + '{"content": ""}'::jsonb + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, contract, enterpriseServiceAccessDetails}', + '{"content": ""}'::jsonb + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, controls, unauthorizedAccessMeasures}', + '{"content": ""}'::jsonb + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, trackAccess, trackAccessDetails}', + '{"content": ""}'::jsonb + )`, + ); + + // Populate the jsonb with the data from the temporary columns + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{personalInformation, whereDetails, content}', + to_jsonb("temp_storingPI_whereDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, disclosureDetails, content}', + to_jsonb("temp_storingPI_disclosureDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, contractualTerms, content}', + to_jsonb("temp_storingPI_contractualTerms") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, contract, enterpriseServiceAccessDetails, content}', + to_jsonb("temp_storingPI_enterpriseServiceAccessDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, controls, unauthorizedAccessMeasures, content}', + to_jsonb("temp_storingPI_unauthorizedAccessMeasures") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, trackAccess, trackAccessDetails, content}', + to_jsonb("temp_storingPI_trackAccessDetails") + )`, + ); + + // Drop the temporary columns + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_whereDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_disclosureDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_contractualTerms"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_enterpriseServiceAccessDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_unauthorizedAccessMeasures"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_trackAccessDetails"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // Re-add temporary columns + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_whereDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_disclosureDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_contractualTerms" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_enterpriseServiceAccessDetails" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_unauthorizedAccessMeasures" text`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" ADD "temp_storingPI_trackAccessDetails" text`, + ); + + // Extract the content from JSONB and store in temporary columns + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_whereDetails" = "storing_personal_information"->'personalInformation'->'whereDetails'->>'content'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_disclosureDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'storage'->'disclosureDetails'->>'content'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_contractualTerms" = "storing_personal_information"->'disclosuresOutsideCanada'->'storage'->'contractualTerms'->>'content'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_enterpriseServiceAccessDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'contract'->'enterpriseServiceAccessDetails'->>'content'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_unauthorizedAccessMeasures" = "storing_personal_information"->'disclosuresOutsideCanada'->'controls'->'unauthorizedAccessMeasures'->>'content'`, + ); + await queryRunner.query( + `UPDATE "pia-intake" SET "temp_storingPI_trackAccessDetails" = "storing_personal_information"->'disclosuresOutsideCanada'->'trackAccess'->'trackAccessDetails'->>'content'`, + ); + + // Revert the JSONB properties to type string using the temporary column values + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{personalInformation, whereDetails}', + to_jsonb("temp_storingPI_whereDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, disclosureDetails}', + to_jsonb("temp_storingPI_disclosureDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, storage, contractualTerms}', + to_jsonb("temp_storingPI_contractualTerms") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, contract, enterpriseServiceAccessDetails}', + to_jsonb("temp_storingPI_enterpriseServiceAccessDetails") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, controls, unauthorizedAccessMeasures}', + to_jsonb("temp_storingPI_unauthorizedAccessMeasures") + )`, + ); + await queryRunner.query( + `UPDATE "pia-intake" + SET "storing_personal_information" = jsonb_set( + "storing_personal_information", + '{disclosuresOutsideCanada, trackAccess, trackAccessDetails}', + to_jsonb("temp_storingPI_trackAccessDetails") + )`, + ); + + // Drop the temporary columns + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_whereDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_disclosureDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_contractualTerms"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_enterpriseServiceAccessDetails"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_unauthorizedAccessMeasures"`, + ); + await queryRunner.query( + `ALTER TABLE "pia-intake" DROP COLUMN "temp_storingPI_trackAccessDetails"`, + ); + } +} diff --git a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-contract.ts b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-contract.ts index 5273966c0..0c94193cf 100644 --- a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-contract.ts +++ b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-contract.ts @@ -1,12 +1,13 @@ -import { IsEnum, IsOptional, IsString } from '@nestjs/class-validator'; +import { IsEnum, IsObject, IsOptional } from '@nestjs/class-validator'; import { YesNoInput } from 'src/common/enums/yes-no-input.enum'; +import { RichTextContent } from '../../rich-text-content'; export class DisclosureContract { @IsEnum(YesNoInput) @IsOptional() relyOnExistingContract?: YesNoInput; - @IsString() + @IsObject() @IsOptional() - enterpriseServiceAccessDetails?: string; + enterpriseServiceAccessDetails?: RichTextContent; } diff --git a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-controls.ts b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-controls.ts index 95a9a49f8..ecfab0c8b 100644 --- a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-controls.ts +++ b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-controls.ts @@ -1,7 +1,8 @@ -import { IsOptional, IsString } from '@nestjs/class-validator'; +import { IsObject, IsOptional } from '@nestjs/class-validator'; +import { RichTextContent } from '../../rich-text-content'; export class DisclosureControls { - @IsString() + @IsObject() @IsOptional() - unauthorizedAccessMeasures?: string; + unauthorizedAccessMeasures?: RichTextContent; } diff --git a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-storage.ts b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-storage.ts index 356a93d0a..e3937920e 100644 --- a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-storage.ts +++ b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-storage.ts @@ -1,12 +1,14 @@ import { IsArray, IsEnum, + IsObject, IsOptional, IsString, ValidateNested, } from '@nestjs/class-validator'; import { Type } from 'class-transformer'; import { YesNoInput } from 'src/common/enums/yes-no-input.enum'; +import { RichTextContent } from '../../rich-text-content'; class ServiceProviderDetails { @IsString() @@ -31,11 +33,11 @@ export class DisclosureStorage { @Type(() => ServiceProviderDetails) serviceProviderList: Array; - @IsString() + @IsObject() @IsOptional() - disclosureDetails?: string; + disclosureDetails?: RichTextContent; - @IsString() + @IsObject() @IsOptional() - contractualTerms?: string; + contractualTerms?: RichTextContent; } diff --git a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-track-access.ts b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-track-access.ts index 362c3ad28..abffd8329 100644 --- a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-track-access.ts +++ b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/disclosures-outside-canda/section-track-access.ts @@ -1,7 +1,8 @@ -import { IsOptional, IsString } from '@nestjs/class-validator'; +import { IsObject, IsOptional } from '@nestjs/class-validator'; +import { RichTextContent } from '../../rich-text-content'; export class DisclosureTrackAccess { - @IsString() + @IsObject() @IsOptional() - trackAccessDetails?: string; + trackAccessDetails?: RichTextContent; } diff --git a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/personal-information.ts b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/personal-information.ts index ebd79ed0f..c6ceeb791 100644 --- a/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/personal-information.ts +++ b/src/backend/src/modules/pia-intake/jsonb-classes/storing-personal-information/personal-information.ts @@ -1,12 +1,13 @@ -import { IsEnum, IsOptional, IsString } from '@nestjs/class-validator'; +import { IsEnum, IsObject, IsOptional } from '@nestjs/class-validator'; import { YesNoInput } from 'src/common/enums/yes-no-input.enum'; +import { RichTextContent } from '../rich-text-content'; export class PersonalInformation { @IsEnum(YesNoInput) @IsOptional() storedOutsideCanada?: YesNoInput; - @IsString() + @IsObject() @IsOptional() - whereDetails?: string; + whereDetails?: RichTextContent; } diff --git a/src/backend/src/modules/pia-intake/mocks/create-pia-intake.mock.ts b/src/backend/src/modules/pia-intake/mocks/create-pia-intake.mock.ts index 13ac0e627..d906dd06d 100644 --- a/src/backend/src/modules/pia-intake/mocks/create-pia-intake.mock.ts +++ b/src/backend/src/modules/pia-intake/mocks/create-pia-intake.mock.ts @@ -57,7 +57,7 @@ export const piaIntakeEntityMock: CreatePiaIntakeDto = { storingPersonalInformation: { personalInformation: { storedOutsideCanada: YesNoInput.YES, - whereDetails: 'USA', + whereDetails: { content: 'USA' }, }, sensitivePersonalInformation: { doesInvolve: YesNoInput.YES, @@ -73,18 +73,20 @@ export const piaIntakeEntityMock: CreatePiaIntakeDto = { details: 'Stored in cloud', }, ], - disclosureDetails: 'S3 storage in us-east-1: US East (N. Virginia)', - contractualTerms: 'None', + disclosureDetails: { + content: 'S3 storage in us-east-1: US East (N. Virginia)', + }, + contractualTerms: { content: 'None' }, }, contract: { relyOnExistingContract: YesNoInput.YES, - enterpriseServiceAccessDetails: 'S3', + enterpriseServiceAccessDetails: { content: 'S3' }, }, controls: { - unauthorizedAccessMeasures: 'IAM rules are in effect', + unauthorizedAccessMeasures: { content: 'IAM rules are in effect' }, }, trackAccess: { - trackAccessDetails: 'IAM', + trackAccessDetails: { content: 'IAM' }, }, risks: { privacyRisks: [ diff --git a/src/backend/test/util/mocks/data/pia-intake.mock.ts b/src/backend/test/util/mocks/data/pia-intake.mock.ts index 4573522c6..671d73657 100644 --- a/src/backend/test/util/mocks/data/pia-intake.mock.ts +++ b/src/backend/test/util/mocks/data/pia-intake.mock.ts @@ -64,7 +64,7 @@ const piaIntakeDataMock = { storingPersonalInformation: { personalInformation: { storedOutsideCanada: YesNoInput.YES, - whereDetails: 'USA', + whereDetails: { content: 'USA' }, }, sensitivePersonalInformation: { doesInvolve: YesNoInput.YES, @@ -80,18 +80,20 @@ const piaIntakeDataMock = { details: 'Stored in cloud', }, ], - disclosureDetails: 'S3 storage in us-east-1: US East (N. Virginia)', - contractualTerms: 'None', + disclosureDetails: { + content: 'S3 storage in us-east-1: US East (N. Virginia)', + }, + contractualTerms: { content: 'None' }, }, contract: { relyOnExistingContract: YesNoInput.YES, - enterpriseServiceAccessDetails: 'S3', + enterpriseServiceAccessDetails: { content: 'S3' }, }, controls: { - unauthorizedAccessMeasures: 'IAM rules are in effect', + unauthorizedAccessMeasures: { content: 'IAM rules are in effect' }, }, trackAccess: { - trackAccessDetails: 'IAM', + trackAccessDetails: { content: 'IAM' }, }, risks: { privacyRisks: [ diff --git a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx index b616894d9..f2eac388f 100644 --- a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx +++ b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx @@ -1,4 +1,3 @@ -import MDEditor from '@uiw/react-md-editor'; import { useContext, useEffect, useMemo, useState } from 'react'; import { YesNoInput } from '../../../../types/enums/yes-no.enum'; import { isMPORole } from '../../../../utils/user'; @@ -21,6 +20,7 @@ import { import ViewComments from '../../../common/ViewComment'; import { PiaSections } from '../../../../types/enums/pia-sections.enum'; import Callout from '../../../common/Callout'; +import { RichTextEditor } from '@bcgov/citz-imb-richtexteditor'; const StoringPersonalInformation = ({ showComments = true, @@ -39,7 +39,7 @@ const StoringPersonalInformation = ({ const personalInformation = useMemo( () => ({ storedOutsideCanada: YesNoInput.YES, - whereDetails: '', + whereDetails: { content: '' }, }), [], ); @@ -57,18 +57,18 @@ const StoringPersonalInformation = ({ storage: { sensitiveInfoStoredByServiceProvider: YesNoInput.YES, serviceProviderList: [{ name: '', cloudInfraName: '', details: '' }], - disclosureDetails: '', - contractualTerms: '', + disclosureDetails: { content: '' }, + contractualTerms: { content: '' }, }, contract: { relyOnExistingContract: YesNoInput.YES, - enterpriseServiceAccessDetails: '', + enterpriseServiceAccessDetails: { content: '' }, }, controls: { - unauthorizedAccessMeasures: '', + unauthorizedAccessMeasures: { content: '' }, }, trackAccess: { - trackAccessDetails: '', + trackAccessDetails: { content: '' }, }, risks: { privacyRisks: [ @@ -111,15 +111,95 @@ const StoringPersonalInformation = ({ setNestedReactState(setStoringPersonalInformationForm, path, value); }; - // passing updated data to parent for auto-save to work efficiently only if there are changes + // State for rich text editors. + const [whereDetails, setWhereDetails] = useState( + storingPersonalInformationForm?.personalInformation.whereDetails?.content ?? + '', + ); + const [disclosureDetails, setDisclosureDetails] = useState( + storingPersonalInformationForm?.disclosuresOutsideCanada?.storage + .disclosureDetails?.content ?? '', + ); + const [contractualTerms, setContractualTerms] = useState( + storingPersonalInformationForm?.disclosuresOutsideCanada?.storage + .contractualTerms?.content ?? '', + ); + const [enterpriseServiceAccessDetails, setEnterpriseServiceAccessDetails] = + useState( + storingPersonalInformationForm?.disclosuresOutsideCanada?.contract + .enterpriseServiceAccessDetails?.content ?? '', + ); + const [unauthorizedAccessMeasures, setUnauthorizedAccessMeasures] = useState( + storingPersonalInformationForm?.disclosuresOutsideCanada?.controls + .unauthorizedAccessMeasures?.content ?? '', + ); + const [trackAccessDetails, setTrackAccessDetails] = useState( + storingPersonalInformationForm?.disclosuresOutsideCanada?.trackAccess + .trackAccessDetails.content ?? '', + ); + + // Update form state on rich text editor changes. useEffect(() => { - if (!deepEqual(initialFormState, storingPersonalInformationForm)) { - piaStateChangeHandler( - storingPersonalInformationForm, - 'storingPersonalInformation', - ); - } - }, [piaStateChangeHandler, storingPersonalInformationForm, initialFormState]); + stateChangeHandler( + whereDetails, + 'personalInformation.whereDetails.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [whereDetails]); + useEffect(() => { + stateChangeHandler( + disclosureDetails, + 'disclosuresOutsideCanada.storage.disclosureDetails.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [disclosureDetails]); + useEffect(() => { + stateChangeHandler( + contractualTerms, + 'disclosuresOutsideCanada.storage.contractualTerms.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [contractualTerms]); + useEffect(() => { + stateChangeHandler( + enterpriseServiceAccessDetails, + 'disclosuresOutsideCanada.contract.enterpriseServiceAccessDetails.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [enterpriseServiceAccessDetails]); + useEffect(() => { + stateChangeHandler( + unauthorizedAccessMeasures, + 'disclosuresOutsideCanada.controls.unauthorizedAccessMeasures.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [unauthorizedAccessMeasures]); + useEffect(() => { + stateChangeHandler( + trackAccessDetails, + 'disclosuresOutsideCanada.trackAccess.trackAccessDetails.content', + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [trackAccessDetails]); + + // Show the editor unless isReadOnly and whereDetails is empty. + const showEditorWhereDetails = !(isReadOnly && whereDetails === ''); + //Show the editor unless isReadOnly and disclosureDetails is empty. + const showEditorDisclosureDetails = !(isReadOnly && disclosureDetails === ''); + // Show the editor unless isReadOnly and contractualTerm is empty. + const showEditorContractualTerms = !(isReadOnly && contractualTerms === ''); + // Show the editor unless isReadOnly and EnterpriseServiceAccessDetail is empty. + const showEditorEnterpriseServiceAccessDetails = !( + isReadOnly && enterpriseServiceAccessDetails === '' + ); + // Show the editor unless isReadOnly and disclosureDetails is empty. + const showEditorUnauthorizedAccessMeasures = !( + isReadOnly && unauthorizedAccessMeasures === '' + ); + // Show the editor unless isReadOnly and disclosureDetails is empty. + const showEditorTrackAccessDetails = !( + isReadOnly && trackAccessDetails === '' + ); const disclosuresOutsideCanadaStorageServiceProviderListColumns: Array = [ @@ -335,8 +415,18 @@ const StoringPersonalInformation = ({ }, ]; + // passing updated data to parent for auto-save to work efficiently only if there are changes + useEffect(() => { + if (!deepEqual(initialFormState, storingPersonalInformationForm)) { + piaStateChangeHandler( + storingPersonalInformationForm, + 'storingPersonalInformation', + ); + } + }, [piaStateChangeHandler, storingPersonalInformationForm, initialFormState]); + return ( -
+ <>

{Messages.Heading.H2Text.en}

{Messages.Heading.PText.en}

@@ -381,35 +471,15 @@ const StoringPersonalInformation = ({ ) : (

{Messages.PersonalInformation.StoredWhere.en}

)} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'personalInformation.whereDetails', - ) - } - aria-label="Personal Information storage location details" - /> - ) : storingPersonalInformationForm.personalInformation - .whereDetails ? ( - ) : ( -

- Not answered -

+ Not answered )} )} @@ -647,35 +717,16 @@ const StoringPersonalInformation = ({ {Messages.AssessmentOfDisclosures.DisclosureDetails.en} )} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'disclosuresOutsideCanada.storage.disclosureDetails', - ) - } - aria-label="Disclosures Details Textarea Input" - /> - ) : storingPersonalInformationForm.disclosuresOutsideCanada - .storage.disclosureDetails ? ( - ) : ( -

- Not answered -

+ Not answered )} )} @@ -752,35 +803,15 @@ const StoringPersonalInformation = ({ {Messages.AssessmentOfDisclosures.ContractualTerms.en} )} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'disclosuresOutsideCanada.storage.contractualTerms', - ) - } + {showEditorContractualTerms ? ( + - ) : storingPersonalInformationForm.disclosuresOutsideCanada - .storage.contractualTerms ? ( - ) : ( -

- Not answered -

+ Not answered )} {showComments && ( @@ -844,37 +875,15 @@ const StoringPersonalInformation = ({ ) : (

{Messages.Contract.EnterpriseService.en}

)} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'disclosuresOutsideCanada.contract.enterpriseServiceAccessDetails', - ) - } - aria-label="Enterprise Service Access Details Textarea Input" - /> - ) : storingPersonalInformationForm.disclosuresOutsideCanada - .contract.enterpriseServiceAccessDetails ? ( - ) : ( -

- Not answered -

+ Not answered )} {showComments && ( {Messages.Controls.WhatControlsAreInPlace.en} )} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'disclosuresOutsideCanada.controls.unauthorizedAccessMeasures', - ) - } - aria-label="Unauthorized Access Measures Textarea Input" - /> - ) : storingPersonalInformationForm.disclosuresOutsideCanada - .controls.unauthorizedAccessMeasures ? ( - ) : ( -

- Not answered -

+ Not answered )} {showComments && ( {Messages.TrackAccess.TrackAccessDetails.en} )} - {!isReadOnly ? ( - - stateChangeHandler( - value || '', - 'disclosuresOutsideCanada.trackAccess.trackAccessDetails', - ) - } - aria-label="Track Access Details Textarea Input" - /> - ) : storingPersonalInformationForm.disclosuresOutsideCanada - .trackAccess.trackAccessDetails ? ( - ) : ( -

- Not answered -

+ Not answered )} {showComments && ( {Messages.Risks.DescribePrivacyRisks.en}

- source={Messages.Risks.DescribePrivacyRisks.HelperText.en} - /> + ) : (

{Messages.Risks.DescribePrivacyRisks.en}

@@ -1081,7 +1050,7 @@ const StoringPersonalInformation = ({
)} -
+ ); }; diff --git a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/interfaces.ts b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/interfaces.ts index a5ac66295..a42d421bb 100644 --- a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/interfaces.ts +++ b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/interfaces.ts @@ -1,4 +1,5 @@ import { YesNoInput } from '../../../../types/enums/yes-no.enum'; +import { RichTextContent } from '../types'; export type PrivacyRisk = { risk: string; @@ -18,7 +19,7 @@ export type ServiceProviderDetails = { export interface IStoringPersonalInformation { personalInformation: { storedOutsideCanada: YesNoInput; - whereDetails: string; + whereDetails: RichTextContent; }; sensitivePersonalInformation: { doesInvolve: YesNoInput; @@ -28,18 +29,18 @@ export interface IStoringPersonalInformation { storage: { sensitiveInfoStoredByServiceProvider: YesNoInput; serviceProviderList: Array; - disclosureDetails: string; - contractualTerms: string; + disclosureDetails: RichTextContent; + contractualTerms: RichTextContent; }; contract: { relyOnExistingContract: YesNoInput; - enterpriseServiceAccessDetails: string; + enterpriseServiceAccessDetails: RichTextContent; }; controls: { - unauthorizedAccessMeasures: string; + unauthorizedAccessMeasures: RichTextContent; }; trackAccess: { - trackAccessDetails: string; + trackAccessDetails: RichTextContent; }; risks: { privacyRisks: Array; From 83fd392a80b011f037d726a45d5d87abd24c6eb0 Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 16 Nov 2023 05:30:00 -0800 Subject: [PATCH 2/3] removed code related md-editor --- .../_collectionUseAndDisclosure.scss | 3 --- .../PIAFormTabs/collectionUseAndDisclosure/index.tsx | 11 ----------- src/frontend/src/sass/common.scss | 1 - 3 files changed, 15 deletions(-) delete mode 100644 src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/_collectionUseAndDisclosure.scss diff --git a/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/_collectionUseAndDisclosure.scss b/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/_collectionUseAndDisclosure.scss deleted file mode 100644 index 38b7cccaf..000000000 --- a/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/_collectionUseAndDisclosure.scss +++ /dev/null @@ -1,3 +0,0 @@ -.w-md-editor-show-preview .w-md-editor-preview { - background-color: #e9ecef !important; -} diff --git a/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/index.tsx b/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/index.tsx index f73eb3e6a..95a81752e 100644 --- a/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/index.tsx +++ b/src/frontend/src/components/public/PIAFormTabs/collectionUseAndDisclosure/index.tsx @@ -125,17 +125,6 @@ const PIACollectionUseAndDisclosure = ({ 'collectionUseAndDisclosure', ); } - - // Disabled editor buttons if the user does not have the MPORole. - // Select button elements inside #collectionNoticeMPO .w-md-editor-toolbar li. - const buttons = document.querySelectorAll( - '#collectionNoticeMPO .w-md-editor-toolbar li > button', - ); - - // Iterate over all button elements and set the 'disabled' attribute based on the user's role. - buttons.forEach((button) => { - button.disabled = !isMPORole() ? true : false; - }); }, [piaStateChangeHandler, collectionUseAndDisclosureForm, initialFormState]); return ( diff --git a/src/frontend/src/sass/common.scss b/src/frontend/src/sass/common.scss index 7dee55ea8..93874ffe3 100644 --- a/src/frontend/src/sass/common.scss +++ b/src/frontend/src/sass/common.scss @@ -39,4 +39,3 @@ @import './collapsible'; @import './printPreview'; @import './comment_sidebar'; -@import '../components/public/PIAFormTabs/collectionUseAndDisclosure/collectionUseAndDisclosure'; From 866af2b5aa7bee844370d3452462dce6dd92658f Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 16 Nov 2023 09:20:47 -0800 Subject: [PATCH 3/3] added condition for mpo role --- .../public/PIAFormTabs/storingPersonalInformation/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx index f2eac388f..eb7f9e018 100644 --- a/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx +++ b/src/frontend/src/components/public/PIAFormTabs/storingPersonalInformation/index.tsx @@ -717,12 +717,11 @@ const StoringPersonalInformation = ({ {Messages.AssessmentOfDisclosures.DisclosureDetails.en} )} - {/* TODO: Implement logic to enable writing in the RichTextEditor only when the user has the MPO role. */} - {showEditorDisclosureDetails && isMPORole() ? ( + {showEditorDisclosureDetails ? ( ) : (