Skip to content

Commit

Permalink
implementation ( Edit Content): #30212 Display Toast Message on Conte…
Browse files Browse the repository at this point in the history
…ntlet Save (#30615)

### Proposed Changes
* Add a toast when a workflow is executed. 



### Screenshots
<img width="1279" alt="image"
src="https://github.com/user-attachments/assets/71165ed7-364b-49e6-8ac1-77bff65c4758">
  • Loading branch information
hmoreras authored Nov 11, 2024
1 parent 65c8ebb commit 4548b16
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {
Spectator,
SpyObject
} from '@ngneat/spectator/jest';
import { patchState } from '@ngrx/signals';
import { of } from 'rxjs';

import { Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { MessageService } from 'primeng/api';
import { TabPanel, TabView } from 'primeng/tabview';

import {
Expand All @@ -21,6 +23,7 @@ import {
DotWorkflowsActionsService,
DotWorkflowService
} from '@dotcms/data-access';
import { ComponentStatus } from '@dotcms/dotcms-models';
import { DotWorkflowActionsComponent } from '@dotcms/ui';
import { DotFormatDateServiceMock } from '@dotcms/utils-testing';

Expand All @@ -40,7 +43,7 @@ import { MockResizeObserver } from '../../utils/mocks';
describe('DotFormComponent', () => {
let spectator: Spectator<DotEditContentFormComponent>;
let component: DotEditContentFormComponent;
let store: SpyObject<InstanceType<typeof DotEditContentStore>>;
let store: InstanceType<typeof DotEditContentStore>;
let dotContentTypeService: SpyObject<DotContentTypeService>;
let workflowActionsService: SpyObject<DotWorkflowsActionsService>;
let workflowActionsFireService: SpyObject<DotWorkflowActionsFireService>;
Expand All @@ -62,6 +65,7 @@ describe('DotFormComponent', () => {
mockProvider(DotMessageService),
mockProvider(Router),
mockProvider(DotWorkflowService),
mockProvider(MessageService),
{
provide: ActivatedRoute,
useValue: {
Expand Down Expand Up @@ -125,6 +129,21 @@ describe('DotFormComponent', () => {
expect(component.form.get('modUserName')).toBeFalsy();
expect(component.form.get('publishDate')).toBeFalsy();
});

it('should disable the form when loading and enable it when not loading', () => {
spectator.detectChanges();

// // Initially, the form should be enabled
expect(component.form.enabled).toBe(true);

patchState(store, {
state: ComponentStatus.SAVING
});

spectator.flushEffects();

expect(component.form.enabled).toBe(false);
});
});

describe('New Content', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Component,
computed,
DestroyRef,
effect,
inject,
OnInit,
output
Expand Down Expand Up @@ -142,10 +143,21 @@ export class DotEditContentFormComponent implements OnInit {
ngOnInit(): void {
if (this.$store.tabs().length) {
this.initializeForm();
this.initializeFormListenter();
this.initializeFormListener();
}
}

constructor() {
effect(() => {
const isLoading = this.$store.isLoading();
if (isLoading) {
this.form.disable();
} else {
this.form.enable();
}
});
}

/**
* Initializes a listener for form value changes.
* When the form value changes, it calls the onFormChange method with the new value.
Expand All @@ -154,7 +166,7 @@ export class DotEditContentFormComponent implements OnInit {
* @private
* @memberof DotEditContentFormComponent
*/
private initializeFormListenter() {
private initializeFormListener() {
this.form.valueChanges.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe((value) => {
const processedValue = this.processFormValue(value);
this.changeValue.emit(processedValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { of } from 'rxjs';

import { ActivatedRoute, Router } from '@angular/router';

import { MessageService } from 'primeng/api';
import { TabView } from 'primeng/tabview';

import {
Expand Down Expand Up @@ -49,6 +50,7 @@ describe('DotEditContentSidebarComponent', () => {
mockProvider(DotMessageService),
mockProvider(Router),
mockProvider(DotWorkflowService),
mockProvider(MessageService),
{
provide: ActivatedRoute,
useValue: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--Todo: handle Loading and Saving state-->
@if ($store.isLoaded()) {
@if ($store.isLoaded() || $store.isSaving()) {
@let contentType = $store.contentType();
@let variable = contentType.variable;
@let showSidebar = $store.showSidebar();
Expand Down Expand Up @@ -38,9 +38,5 @@
}
}

@if ($store.hasError()) {
{{ 'edit.content.layout.no.content.to.show ' | dm }}
}

<p-toast />
<p-confirmDialog />
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { HttpErrorResponse } from '@angular/common/http';
import { fakeAsync, tick } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';

import { MessageService } from 'primeng/api';

import {
DotContentTypeService,
DotFireActionOptions,
DotHttpErrorManagerService,
DotMessageService,
DotRenderMode,
DotWorkflowActionsFireService,
DotWorkflowsActionsService,
Expand All @@ -25,13 +28,18 @@ import {
DotCMSContentType,
DotCMSWorkflowAction
} from '@dotcms/dotcms-models';
import { mockWorkflowsActions } from '@dotcms/utils-testing';
import { MockDotMessageService, mockWorkflowsActions } from '@dotcms/utils-testing';

import { DotEditContentStore } from './edit-content.store';

import { DotEditContentService } from '../../../services/dot-edit-content.service';
import { CONTENT_TYPE_MOCK } from '../../../utils/mocks';

const messageServiceMock = new MockDotMessageService({
'edit.content.success.workflow.message': 'Your changes have being applied.',
success: 'Success'
});

describe('DotEditContentStore', () => {
let spectator: SpectatorService<InstanceType<typeof DotEditContentStore>>;
let store: InstanceType<typeof DotEditContentStore>;
Expand All @@ -46,6 +54,7 @@ describe('DotEditContentStore', () => {

let workflowActionsService: SpyObject<DotWorkflowsActionsService>;
let workflowActionsFireService: SpyObject<DotWorkflowActionsFireService>;
let messageService: SpyObject<MessageService>;

const createService = createServiceFactory({
service: DotEditContentStore,
Expand All @@ -55,7 +64,8 @@ describe('DotEditContentStore', () => {
DotEditContentService,
DotHttpErrorManagerService,
DotWorkflowsActionsService,
DotWorkflowService
DotWorkflowService,
MessageService
],
providers: [
{
Expand All @@ -69,7 +79,11 @@ describe('DotEditContentStore', () => {

mockProvider(Router, {
navigate: jest.fn().mockReturnValue(Promise.resolve(true))
})
}),
{
provide: DotMessageService,
useValue: messageServiceMock
}
]
});

Expand All @@ -84,6 +98,7 @@ describe('DotEditContentStore', () => {
workflowActionsService = spectator.inject(DotWorkflowsActionsService);
workflowActionsFireService = spectator.inject(DotWorkflowActionsFireService);
dotEditContentService = spectator.inject(DotEditContentService);
messageService = spectator.inject(MessageService);

router = spectator.inject(Router);
});
Expand Down Expand Up @@ -212,6 +227,12 @@ describe('DotEditContentStore', () => {
replaceUrl: true,
queryParamsHandling: 'preserve'
});

expect(messageService.add).toHaveBeenCalledWith({
severity: 'success',
summary: 'Success',
detail: 'Your changes have being applied.'
});
}));

it('should handle error when firing workflow action', fakeAsync(() => {
Expand All @@ -225,7 +246,7 @@ describe('DotEditContentStore', () => {
store.fireWorkflowAction(mockOptions);
tick();

expect(store.state()).toBe(ComponentStatus.ERROR);
expect(store.state()).toBe(ComponentStatus.LOADED);
expect(store.error()).toBe('Error firing workflow action');
expect(dotHttpErrorManagerService.handle).toHaveBeenCalled();
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { HttpErrorResponse } from '@angular/common/http';
import { computed, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { MessageService } from 'primeng/api';

import { switchMap, tap } from 'rxjs/operators';

import { DotCMSContentlet } from '@dotcms/angular';
Expand All @@ -23,7 +25,8 @@ import {
DotHttpErrorManagerService,
DotRenderMode,
DotWorkflowActionsFireService,
DotWorkflowsActionsService
DotWorkflowsActionsService,
DotMessageService
} from '@dotcms/data-access';
import {
ComponentStatus,
Expand Down Expand Up @@ -97,6 +100,11 @@ export const DotEditContentStore = signalStore(
*/
isLoaded: computed(() => store.state() === ComponentStatus.LOADED),

/**
* Computed property that determines if the store's status is equal to ComponentStatus.SAVING.
*/
isSaving: computed(() => store.state() === ComponentStatus.SAVING),

/**
* A computed property that checks if an error exists in the store.
*
Expand Down Expand Up @@ -132,6 +140,8 @@ export const DotEditContentStore = signalStore(
dotContentTypeService = inject(DotContentTypeService),
dotEditContentService = inject(DotEditContentService),
dotHttpErrorManagerService = inject(DotHttpErrorManagerService),
messageService = inject(MessageService),
dotMessageService = inject(DotMessageService),

router = inject(Router)
) => ({
Expand Down Expand Up @@ -262,10 +272,17 @@ export const DotEditContentStore = signalStore(
state: ComponentStatus.LOADED,
error: null
});
messageService.add({
severity: 'success',
summary: dotMessageService.get('success'),
detail: dotMessageService.get(
'edit.content.success.workflow.message'
)
});
},
error: (error: HttpErrorResponse) => {
patchState(store, {
state: ComponentStatus.ERROR,
state: ComponentStatus.LOADED,
error: 'Error firing workflow action'
});
dotHttpErrorManagerService.handle(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ControlContainer, FormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute } from '@angular/router';

import { ConfirmationService } from 'primeng/api';
import { ConfirmationService, MessageService } from 'primeng/api';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { DropdownModule } from 'primeng/dropdown';

Expand Down Expand Up @@ -139,6 +139,7 @@ describe('DotEditContentWYSIWYGFieldComponent', () => {
mockProvider(DotHttpErrorManagerService),
mockProvider(ActivatedRoute),
mockProvider(DotWorkflowService),
mockProvider(MessageService),
provideHttpClient(),
provideHttpClientTesting(),
ConfirmationService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5820,6 +5820,7 @@ edit.content.wysiwyg-field.language-variable-tooltip=Start typing to see matchin

edit.content.sidebar.information.references-with.pages.tooltip=Used in {0} pages
edit.content.sidebar.information.references-with.pages.not.used=Not used on any page yet
edit.content.success.workflow.message=Your changes have being applied.

lts.expired.message = This version of dotCMS is no longer supported. Please contact your customer success manager to schedule an upgrade.
lts.expires.soon.message = Your dotCMS version will no longer be supported in {0} days. Please contact your customer success manager to schedule an upgrade.
Expand Down

0 comments on commit 4548b16

Please sign in to comment.