diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html index 41decd7d723f..01dd0ecbc8a5 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html @@ -31,19 +31,24 @@
- - @if ($toolbar().runningExperiment; as runningExperiment) { } + - Persona + Workflows
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts index bd5f9d06150d..24b8b490d289 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts @@ -1,4 +1,4 @@ -import { expect, describe } from '@jest/globals'; +import { expect, describe, it } from '@jest/globals'; import { byTestId, mockProvider, Spectator, createComponentFactory } from '@ngneat/spectator/jest'; import { MockComponent } from 'ng-mocks'; import { of } from 'rxjs'; @@ -9,7 +9,12 @@ import { By } from '@angular/platform-browser'; import { ConfirmationService, MessageService } from 'primeng/api'; -import { DotExperimentsService, DotLanguagesService, DotLicenseService } from '@dotcms/data-access'; +import { + DotExperimentsService, + DotLanguagesService, + DotLicenseService, + DotPersonalizeService +} from '@dotcms/data-access'; import { LoginService } from '@dotcms/dotcms-js'; import { DotExperimentsServiceMock, @@ -37,19 +42,23 @@ import { import { DotEmaBookmarksComponent } from '../dot-ema-bookmarks/dot-ema-bookmarks.component'; import { DotEmaRunningExperimentComponent } from '../dot-ema-running-experiment/dot-ema-running-experiment.component'; import { EditEmaLanguageSelectorComponent } from '../edit-ema-language-selector/edit-ema-language-selector.component'; +import { EditEmaPersonaSelectorComponent } from '../edit-ema-persona-selector/edit-ema-persona-selector.component'; const $apiURL = '/api/v1/page/json/123-xyz-567-xxl?host_id=123-xyz-567-xxl&language_id=1'; describe('DotUveToolbarComponent', () => { let spectator: Spectator; let messageService: MessageService; + let confirmationService: ConfirmationService; + let store: InstanceType; const createComponent = createComponentFactory({ component: DotUveToolbarComponent, imports: [ HttpClientTestingModule, MockComponent(DotEmaBookmarksComponent), - MockComponent(DotEmaRunningExperimentComponent) + MockComponent(DotEmaRunningExperimentComponent), + MockComponent(EditEmaPersonaSelectorComponent) ], providers: [ UVEStore, @@ -87,6 +96,14 @@ describe('DotUveToolbarComponent', () => { add: jest.fn() } } + ], + componentProviders: [ + { + provide: DotPersonalizeService, + useValue: { + getPersonalize: jest.fn() + } + } ] }); @@ -118,11 +135,7 @@ describe('DotUveToolbarComponent', () => { runningExperiment: null, workflowActionsInode: pageAPIResponse?.page.inode, unlockButton: null, - showInfoDisplay: shouldShowInfoDisplay, - personaSelector: { - pageId: pageAPIResponse?.page.identifier, - value: pageAPIResponse?.viewAs.persona ?? DEFAULT_PERSONA - } + showInfoDisplay: shouldShowInfoDisplay }; const baseUVEState = { @@ -132,6 +145,10 @@ describe('DotUveToolbarComponent', () => { pageParams: signal(params), pageAPIResponse: signal(MOCK_RESPONSE_VTL), $apiURL: signal($apiURL), + $personaSelector: signal({ + pageId: pageAPIResponse?.page.identifier, + value: pageAPIResponse?.viewAs.persona ?? DEFAULT_PERSONA + }), reloadCurrentPage: jest.fn(), loadPageAsset: jest.fn(), languages: signal([ @@ -148,6 +165,8 @@ describe('DotUveToolbarComponent', () => { }); messageService = spectator.inject(MessageService); + confirmationService = spectator.inject(ConfirmationService); + store = spectator.inject(UVEStore); }); describe('dot-ema-bookmarks', () => { @@ -244,6 +263,96 @@ describe('DotUveToolbarComponent', () => { expect(btn.getAttribute('href')).toBe($apiURL); }); }); + + describe('dot-edit-ema-persona-selector', () => { + it('should have attr', () => { + const personaSelector = spectator.query(EditEmaPersonaSelectorComponent); + + expect(personaSelector.pageId).toBe('123'); + expect(personaSelector.value).toEqual({ + archived: false, + baseType: 'PERSONA', + contentType: 'persona', + folder: 'SYSTEM_FOLDER', + hasLiveVersion: false, + hasTitleImage: false, + host: 'SYSTEM_HOST', + hostFolder: 'SYSTEM_HOST', + hostName: 'System Host', + identifier: 'modes.persona.no.persona', + inode: '', + keyTag: 'dot:persona', + languageId: 1, + live: false, + locked: false, + modDate: '0', + modUser: 'system', + modUserName: 'system user system user', + name: 'Default Visitor', + owner: 'SYSTEM_USER', + personalized: false, + sortOrder: 0, + stInode: 'c938b15f-bcb6-49ef-8651-14d455a97045', + title: 'Default Visitor', + titleImage: 'TITLE_IMAGE_NOT_FOUND', + url: 'demo.dotcms.com', + working: false + }); + }); + + it('should personalize - no confirmation', () => { + const spyloadPageAsset = jest.spyOn(store, 'loadPageAsset'); + spectator.triggerEventHandler(EditEmaPersonaSelectorComponent, 'selected', { + identifier: '123', + pageId: '123', + personalized: true + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + spectator.detectChanges(); + + expect(spyloadPageAsset).toHaveBeenCalledWith({ + 'com.dotmarketing.persona.id': '123' + }); + }); + + it('should personalize - confirmation', () => { + spectator.triggerEventHandler(EditEmaPersonaSelectorComponent, 'selected', { + identifier: '123', + pageId: '123', + personalized: false + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + spectator.detectChanges(); + + expect(confirmationService.confirm).toHaveBeenCalledWith({ + accept: expect.any(Function), + acceptLabel: 'dot.common.dialog.accept', + header: 'editpage.personalization.confirm.header', + message: 'editpage.personalization.confirm.message', + reject: expect.any(Function), + rejectLabel: 'dot.common.dialog.reject' + }); + }); + + it('should despersonalize', () => { + spectator.triggerEventHandler(EditEmaPersonaSelectorComponent, 'despersonalize', { + identifier: '123', + pageId: '123', + personalized: true + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + spectator.detectChanges(); + + expect(confirmationService.confirm).toHaveBeenCalledWith({ + accept: expect.any(Function), + acceptLabel: 'dot.common.dialog.accept', + header: 'editpage.personalization.delete.confirm.header', + message: 'editpage.personalization.delete.confirm.message', + rejectLabel: 'dot.common.dialog.reject' + }); + }); + }); }); describe('State changes', () => { diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts index 3ec216e00b77..c60adb1e5a09 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts @@ -12,15 +12,17 @@ import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { ToolbarModule } from 'primeng/toolbar'; -import { DotMessageService } from '@dotcms/data-access'; -import { DotLanguage } from '@dotcms/dotcms-models'; +import { DotMessageService, DotPersonalizeService } from '@dotcms/data-access'; +import { DotPersona, DotLanguage } from '@dotcms/dotcms-models'; +import { DEFAULT_PERSONA } from '../../../shared/consts'; import { DotPage } from '../../../shared/models'; import { UVEStore } from '../../../store/dot-uve.store'; import { DotEmaBookmarksComponent } from '../dot-ema-bookmarks/dot-ema-bookmarks.component'; import { DotEmaInfoDisplayComponent } from '../dot-ema-info-display/dot-ema-info-display.component'; import { DotEmaRunningExperimentComponent } from '../dot-ema-running-experiment/dot-ema-running-experiment.component'; import { EditEmaLanguageSelectorComponent } from '../edit-ema-language-selector/edit-ema-language-selector.component'; +import { EditEmaPersonaSelectorComponent } from '../edit-ema-persona-selector/edit-ema-persona-selector.component'; @Component({ selector: 'dot-uve-toolbar', @@ -31,22 +33,29 @@ import { EditEmaLanguageSelectorComponent } from '../edit-ema-language-selector/ DotEmaBookmarksComponent, DotEmaInfoDisplayComponent, DotEmaRunningExperimentComponent, - ClipboardModule, - EditEmaLanguageSelectorComponent + EditEmaPersonaSelectorComponent, + EditEmaLanguageSelectorComponent, + ClipboardModule ], + providers: [DotPersonalizeService], templateUrl: './dot-uve-toolbar.component.html', styleUrl: './dot-uve-toolbar.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) export class DotUveToolbarComponent { - languageSelector = viewChild('languageSelector'); + $personaSelector = viewChild('personaSelector'); + + $languageSelector = viewChild('languageSelector'); #store = inject(UVEStore); + readonly #messageService = inject(MessageService); readonly #dotMessageService = inject(DotMessageService); readonly #confirmationService = inject(ConfirmationService); + readonly #personalizeService = inject(DotPersonalizeService); readonly $toolbar = this.#store.$uveToolbar; readonly $apiURL = this.#store.$apiURL; + readonly $personaSelectorProps = this.#store.$personaSelector; @Output() translatePage = new EventEmitter<{ page: DotPage; newLanguage: number }>(); @@ -89,6 +98,75 @@ export class DotUveToolbarComponent { } /** + * Handle the persona selection + * + * @param {DotPersona} persona + * @memberof DotEmaComponent + */ + onPersonaSelected(persona: DotPersona & { pageId: string }) { + if (persona.identifier === DEFAULT_PERSONA.identifier || persona.personalized) { + this.#store.loadPageAsset({ + 'com.dotmarketing.persona.id': persona.identifier + }); + } else { + this.#confirmationService.confirm({ + header: this.#dotMessageService.get('editpage.personalization.confirm.header'), + message: this.#dotMessageService.get( + 'editpage.personalization.confirm.message', + persona.name + ), + acceptLabel: this.#dotMessageService.get('dot.common.dialog.accept'), + rejectLabel: this.#dotMessageService.get('dot.common.dialog.reject'), + accept: () => { + this.#personalizeService + .personalized(persona.pageId, persona.keyTag) + .subscribe(() => { + this.#store.loadPageAsset({ + 'com.dotmarketing.persona.id': persona.identifier + }); + + this.$personaSelector().fetchPersonas(); + }); // This does a take 1 under the hood + }, + reject: () => { + this.$personaSelector().resetValue(); + } + }); + } + } + + /** + * Handle the persona despersonalization + * + * @param {(DotPersona & { pageId: string })} persona + * @memberof EditEmaToolbarComponent + */ + onDespersonalize(persona: DotPersona & { pageId: string; selected: boolean }) { + this.#confirmationService.confirm({ + header: this.#dotMessageService.get('editpage.personalization.delete.confirm.header'), + message: this.#dotMessageService.get( + 'editpage.personalization.delete.confirm.message', + persona.name + ), + acceptLabel: this.#dotMessageService.get('dot.common.dialog.accept'), + rejectLabel: this.#dotMessageService.get('dot.common.dialog.reject'), + accept: () => { + this.#personalizeService + .despersonalized(persona.pageId, persona.keyTag) + .subscribe(() => { + this.$personaSelector().fetchPersonas(); + + if (persona.selected) { + this.#store.loadPageAsset({ + 'com.dotmarketing.persona.id': DEFAULT_PERSONA.identifier + }); + } + }); // This does a take 1 under the hood + } + }); + } + + /* * Asks the user for confirmation to create a new translation for a given language. * * @param {DotLanguage} language - The language to create a new translation for. @@ -116,7 +194,7 @@ export class DotUveToolbarComponent { }, reject: () => { // If is rejected, bring back the current language on selector - this.languageSelector().listbox.writeValue(this.$toolbar().currentLanguage); + this.$languageSelector().listbox.writeValue(this.$toolbar().currentLanguage); } }); } diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/edit-ema-persona-selector/edit-ema-persona-selector.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/edit-ema-persona-selector/edit-ema-persona-selector.component.ts index fbd23688f3da..a934c9cbed14 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/edit-ema-persona-selector/edit-ema-persona-selector.component.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/edit-ema-persona-selector/edit-ema-persona-selector.component.ts @@ -111,8 +111,7 @@ export class EditEmaPersonaSelectorComponent implements AfterViewInit, OnChanges * @memberof EditEmaPersonaSelectorComponent */ resetValue(): void { - this.listbox.value = this.value; - this.listbox.cd.detectChanges(); + this.listbox.writeValue(this.value); } /** diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts index 7dfaa010c8bb..450bc1f9e114 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts @@ -34,6 +34,7 @@ import { DotPropertiesService, DotSeoMetaTagsService, DotSeoMetaTagsUtilService, + DotSessionStorageService, DotTempFileUploadService, DotWorkflowActionsFireService, PushPublishService @@ -147,6 +148,7 @@ const createRouting = () => UVEStore, DotFavoritePageService, DotESContentService, + DotSessionStorageService, { provide: DotPropertiesService, useValue: { diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts index a51fb31413c4..fea55a82c03a 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts @@ -128,10 +128,6 @@ export interface UVEToolbarProps { hideSocialMedia: boolean; }; }; - personaSelector: { - pageId: string; - value: DotPersona; - }; runningExperiment?: DotExperiment; currentLanguage: DotLanguage; workflowActionsInode?: string; @@ -141,3 +137,8 @@ export interface UVEToolbarProps { }; showInfoDisplay?: boolean; } + +export interface PersonaSelectorProps { + pageId: string; + value: DotPersona; +} diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.spec.ts index 6a3b044ff152..445abbcf4d9c 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.spec.ts @@ -8,6 +8,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { withUVEToolbar } from './withUVEToolbar'; import { DotPageApiService } from '../../../../services/dot-page-api.service'; +import { DEFAULT_PERSONA } from '../../../../shared/consts'; import { UVE_STATUS } from '../../../../shared/enums'; import { MOCK_RESPONSE_HEADLESS } from '../../../../shared/mocks'; import { UVEState } from '../../../models'; @@ -47,7 +48,6 @@ describe('withEditor', () => { mockProvider(Router), mockProvider(ActivatedRoute), mockProvider(Router), - mockProvider(ActivatedRoute), { provide: DotPageApiService, useValue: { @@ -79,5 +79,12 @@ describe('withEditor', () => { expect(store.$apiURL()).toBe(expectURL); }); + + it('should return the personaSelector props', () => { + expect(store.$personaSelector()).toEqual({ + pageId: '123', + value: DEFAULT_PERSONA + }); + }); }); }); diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts index 047bb34bdbec..297b39476be4 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts @@ -22,7 +22,7 @@ import { sanitizeURL } from '../../../../utils'; import { UVEState } from '../../../models'; -import { EditorToolbarState, UVEToolbarProps } from '../models'; +import { EditorToolbarState, PersonaSelectorProps, UVEToolbarProps } from '../models'; /** * The initial state for the editor toolbar. @@ -109,14 +109,18 @@ export function withUVEToolbar() { : null, runningExperiment: isExperimentRunning ? experiment : null, workflowActionsInode: store.canEditPage() ? pageAPIResponse?.page.inode : null, - personaSelector: { - pageId: pageAPIResponse?.page.identifier, - value: pageAPIResponse?.viewAs.persona ?? DEFAULT_PERSONA - }, unlockButton: shouldShowUnlock ? unlockButton : null, showInfoDisplay: shouldShowInfoDisplay }; }), + $personaSelector: computed(() => { + const pageAPIResponse = store.pageAPIResponse(); + + return { + pageId: pageAPIResponse?.page.identifier, + value: pageAPIResponse?.viewAs.persona ?? DEFAULT_PERSONA + }; + }), $apiURL: computed(() => { const pageParams = store.pageParams(); const url = sanitizeURL(pageParams?.url); diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts index 60e16bc29bdb..82f5298bc27c 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts @@ -541,10 +541,6 @@ describe('withEditor', () => { urlContentMap: null, runningExperiment: null, workflowActionsInode: MOCK_RESPONSE_HEADLESS.page.inode, - personaSelector: { - pageId: MOCK_RESPONSE_HEADLESS.page.identifier, - value: MOCK_RESPONSE_HEADLESS.viewAs.persona ?? DEFAULT_PERSONA - }, unlockButton: null, showInfoDisplay: false }); diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts index 03a5affc8f18..5f643314740a 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts @@ -110,7 +110,10 @@ export function deleteContentletFromContainer(action: ActionPayload): { }; } - return currentContainer; + return { + ...currentContainer, + personaTag + }; }); return { diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts index e3d312039e26..9419124736a7 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts @@ -63,6 +63,11 @@ describe('utils functions', () => { identifier: 'test', uuid: 'test', contentletsId: ['test'] + }, + { + identifier: 'test-2', + uuid: 'test', + contentletsId: ['test'] } ], contentlet: { @@ -82,6 +87,12 @@ describe('utils functions', () => { uuid: 'test', contentletsId: [], personaTag: 'test' + }, + { + identifier: 'test-2', + uuid: 'test', + contentletsId: ['test'], + personaTag: 'test' // In the last version this was not being added and it lead to saving content to the default persona and messing up with the pages } ], contentletsId: []