From d71a0db841ae2195f64c790f4753955177a30fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Wed, 7 Aug 2024 12:27:11 +0200 Subject: [PATCH 1/9] SED-3222 add custom toast --- .../toast-container.component.html | 5 +++ .../toast-container.component.scss | 9 +++++ .../toast-container.component.ts | 20 +++++++++++ .../lib/components/toast/toast.component.html | 14 ++++++++ .../lib/components/toast/toast.component.scss | 36 +++++++++++++++++++ .../lib/components/toast/toast.component.ts | 28 +++++++++++++++ .../src/lib/services/toast.service.ts | 22 ++++++++++++ .../src/lib/shared/toast-action.interface.ts | 4 +++ .../step-core/src/lib/step-core.module.ts | 9 +++++ .../lib/components/root/root.component.html | 1 + .../plan-list/plan-list.component.ts | 5 +-- 11 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 projects/step-core/src/lib/components/toast-container/toast-container.component.html create mode 100644 projects/step-core/src/lib/components/toast-container/toast-container.component.scss create mode 100644 projects/step-core/src/lib/components/toast-container/toast-container.component.ts create mode 100644 projects/step-core/src/lib/components/toast/toast.component.html create mode 100644 projects/step-core/src/lib/components/toast/toast.component.scss create mode 100644 projects/step-core/src/lib/components/toast/toast.component.ts create mode 100644 projects/step-core/src/lib/services/toast.service.ts create mode 100644 projects/step-core/src/lib/shared/toast-action.interface.ts diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html new file mode 100644 index 0000000000..3a8d210b38 --- /dev/null +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -0,0 +1,5 @@ +
+ @for (toast of toasts; track toast) { + + } +
diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.scss b/projects/step-core/src/lib/components/toast-container/toast-container.component.scss new file mode 100644 index 0000000000..89ec06de12 --- /dev/null +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.scss @@ -0,0 +1,9 @@ +.toast-container { + position: fixed; + top: 2rem; + right: 2rem; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 1rem; +} diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.ts b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts new file mode 100644 index 0000000000..67cc8cb4cb --- /dev/null +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { ToastService } from '../../services/toast.service'; +import { NotificationAction } from '../../shared/toast-action.interface'; + +@Component({ + selector: 'step-toast-container', + templateUrl: './toast-container.component.html', + styleUrls: ['./toast-container.component.scss'], +}) +export class ToastContainerComponent implements OnInit { + toasts: { message: string; actions?: NotificationAction[]; duration?: number }[] = []; + + constructor(private toastService: ToastService) {} + + ngOnInit(): void { + this.toastService.toastMessages.subscribe((toasts) => { + this.toasts = toasts; + }); + } +} diff --git a/projects/step-core/src/lib/components/toast/toast.component.html b/projects/step-core/src/lib/components/toast/toast.component.html new file mode 100644 index 0000000000..a9b595735b --- /dev/null +++ b/projects/step-core/src/lib/components/toast/toast.component.html @@ -0,0 +1,14 @@ +@if (message) { +
+ +

{{ message }}

+
+ @for (action of actions; track action) { + + {{ action.label }} + + } +
+ +
+} diff --git a/projects/step-core/src/lib/components/toast/toast.component.scss b/projects/step-core/src/lib/components/toast/toast.component.scss new file mode 100644 index 0000000000..7fe020e616 --- /dev/null +++ b/projects/step-core/src/lib/components/toast/toast.component.scss @@ -0,0 +1,36 @@ +.toast { + display: flex; + align-items: center; + background-color: #fff; + border: 1px solid #ccc; + padding: 10px; + margin-bottom: 10px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + position: relative; +} + +.success-icon { + margin-right: 10px; + color: #4caf50; +} + +.toast-message { + flex: 1; +} + +.toast-actions { + display: flex; + gap: 5px; +} + +.dismiss-btn { + background: none; + border: none; + cursor: pointer; +} + +.action-btn { + margin-left: 1rem; + cursor: pointer; +} diff --git a/projects/step-core/src/lib/components/toast/toast.component.ts b/projects/step-core/src/lib/components/toast/toast.component.ts new file mode 100644 index 0000000000..90ecd7c49c --- /dev/null +++ b/projects/step-core/src/lib/components/toast/toast.component.ts @@ -0,0 +1,28 @@ +import { Component, inject, Input, OnInit } from '@angular/core'; +import { ToastService } from '../../services/toast.service'; +import { NotificationAction } from '../../shared/toast-action.interface'; + +@Component({ + selector: 'step-toast', + templateUrl: 'toast.component.html', + styleUrls: ['toast.component.scss'], +}) +export class ToastComponent implements OnInit { + @Input() message: string = ''; + @Input() actions: NotificationAction[] | undefined = []; + @Input() duration: number | undefined = 3000; + private _toastService = inject(ToastService); + private timer: any; + + ngOnInit(): void { + this.timer = setTimeout(() => this.closeToast(), this.duration || 3000); + } + + ngOnDestroy(): void { + clearTimeout(this.timer); + } + + closeToast(): void { + this._toastService.removeToast(this.message); + } +} diff --git a/projects/step-core/src/lib/services/toast.service.ts b/projects/step-core/src/lib/services/toast.service.ts new file mode 100644 index 0000000000..b2c466df9e --- /dev/null +++ b/projects/step-core/src/lib/services/toast.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; +import { NotificationAction } from '../shared/toast-action.interface'; + +@Injectable({ + providedIn: 'root', +}) +export class ToastService { + private toastSubject = new Subject<{ message: string; actions?: NotificationAction[]; duration?: number }[]>(); + toastMessages = this.toastSubject.asObservable(); + private toasts: { message: string; actions: NotificationAction[]; duration: number }[] = []; + + showToast(message: string, actions?: NotificationAction[], duration?: number): void { + this.toasts.push({ message, actions: actions || [], duration: duration || 3000 }); + this.toastSubject.next(this.toasts); + } + + removeToast(message: string): void { + this.toasts = this.toasts.filter((toast) => toast.message !== message); + this.toastSubject.next(this.toasts); + } +} diff --git a/projects/step-core/src/lib/shared/toast-action.interface.ts b/projects/step-core/src/lib/shared/toast-action.interface.ts new file mode 100644 index 0000000000..5544e92e36 --- /dev/null +++ b/projects/step-core/src/lib/shared/toast-action.interface.ts @@ -0,0 +1,4 @@ +export interface NotificationAction { + label: string; + handler: () => void; +} diff --git a/projects/step-core/src/lib/step-core.module.ts b/projects/step-core/src/lib/step-core.module.ts index c9213fcfaf..6618c63961 100644 --- a/projects/step-core/src/lib/step-core.module.ts +++ b/projects/step-core/src/lib/step-core.module.ts @@ -74,6 +74,8 @@ import { TestIdDirective } from './directives/test-id.directive'; import { ExtractUrlPipe } from './pipes/extract-url.pipe'; import { ExtractQueryParamsPipe } from './pipes/extract-query-params.pipe'; import { INFO_BANNER_EXPORTS } from './modules/info-banner'; +import { ToastComponent } from './components/toast/toast.component'; +import { ToastContainerComponent } from './components/toast-container/toast-container.component'; @NgModule({ declarations: [ @@ -113,6 +115,8 @@ import { INFO_BANNER_EXPORTS } from './modules/info-banner'; TestIdDirective, ExtractUrlPipe, ExtractQueryParamsPipe, + ToastComponent, + ToastContainerComponent, ], imports: [ CommonModule, @@ -219,6 +223,8 @@ import { INFO_BANNER_EXPORTS } from './modules/info-banner'; INFO_BANNER_EXPORTS, ExtractUrlPipe, ExtractQueryParamsPipe, + ToastComponent, + ToastContainerComponent, ], providers: [ CORE_INITIALIZER, @@ -350,3 +356,6 @@ export { TestIdDirective } from './directives/test-id.directive'; export * from './modules/info-banner'; export * from './pipes/extract-url.pipe'; export * from './pipes/extract-query-params.pipe'; +export * from './services/toast.service'; +export * from './components/toast/toast.component'; +export * from './components/toast-container/toast-container.component'; diff --git a/projects/step-frontend/src/lib/components/root/root.component.html b/projects/step-frontend/src/lib/components/root/root.component.html index 72d3aaac6c..ace3431d74 100644 --- a/projects/step-frontend/src/lib/components/root/root.component.html +++ b/projects/step-frontend/src/lib/components/root/root.component.html @@ -1,2 +1,3 @@ + diff --git a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts index 4c024cffd8..7e028411fa 100644 --- a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts +++ b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts @@ -10,6 +10,7 @@ import { STORE_ALL, tablePersistenceConfigProvider, tableColumnsConfigProvider, + ToastService, } from '@exense/step-core'; import { map, of, pipe, switchMap, tap } from 'rxjs'; @@ -34,7 +35,7 @@ import { map, of, pipe, switchMap, tap } from 'rxjs'; export class PlanListComponent implements DialogParentService { private _isUsedByDialogs = inject(IsUsedByDialogService); private _dialogs = inject(DialogsService); - + private toast = inject(ToastService); readonly _plansApiService = inject(AugmentedPlansService); readonly dataSource = this._plansApiService.getPlansTableDataSource(); @@ -58,7 +59,7 @@ export class PlanListComponent implements DialogParentService { switchMap((clone) => this._plansApiService.savePlan(clone)), this.updateDataSourceAfterChange, ) - .subscribe(); + .subscribe(() => this.toast.showToast('Plan successfully duplicated', [], 3000)); } deletePlan(plan: Plan): void { From 6db75d49b5fdfbacbe8fb24e692ac0fb9c10ec4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Fri, 9 Aug 2024 11:43:34 +0200 Subject: [PATCH 2/9] SED-3222 set styles, fix bugs --- .../toast-container.component.html | 10 ++- .../toast-container.component.ts | 10 ++- .../lib/components/toast/toast.component.html | 26 ++++++- .../lib/components/toast/toast.component.scss | 68 +++++++++++++------ .../lib/components/toast/toast.component.ts | 37 +++++++++- .../src/lib/services/toast.service.ts | 32 +++++++-- .../src/lib/shared/toast-type.enum.ts | 6 ++ .../step-core/src/lib/step-core.module.ts | 1 + .../plan-list/plan-list.component.ts | 27 ++++++-- 9 files changed, 180 insertions(+), 37 deletions(-) create mode 100644 projects/step-core/src/lib/shared/toast-type.enum.ts diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html index 3a8d210b38..cdb910e599 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.html +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -1,5 +1,13 @@
@for (toast of toasts; track toast) { - + + }
diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.ts b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts index 67cc8cb4cb..6e8cf83565 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.ts +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ToastService } from '../../services/toast.service'; import { NotificationAction } from '../../shared/toast-action.interface'; +import { ToastType } from '../../shared/toast-type.enum'; @Component({ selector: 'step-toast-container', @@ -8,7 +9,14 @@ import { NotificationAction } from '../../shared/toast-action.interface'; styleUrls: ['./toast-container.component.scss'], }) export class ToastContainerComponent implements OnInit { - toasts: { message: string; actions?: NotificationAction[]; duration?: number }[] = []; + toasts: { + type: ToastType; + message: string; + values: string[]; + actions?: NotificationAction[]; + autoClose?: boolean; + duration?: number; + }[] = []; constructor(private toastService: ToastService) {} diff --git a/projects/step-core/src/lib/components/toast/toast.component.html b/projects/step-core/src/lib/components/toast/toast.component.html index a9b595735b..e87bd94ed2 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.html +++ b/projects/step-core/src/lib/components/toast/toast.component.html @@ -1,7 +1,28 @@ @if (message) {
- -

{{ message }}

+
+ @switch (type) { + @case (toastType.SUCCESS) { + + } + @case (toastType.ERROR) { + + } + @case (toastType.WARNING) { + + } + @case (toastType.INFO) { + + } + } + +
+
+ + {{ part.displayValue }} + {{ part }} + +
@for (action of actions; track action) { @@ -9,6 +30,5 @@ }
-
} diff --git a/projects/step-core/src/lib/components/toast/toast.component.scss b/projects/step-core/src/lib/components/toast/toast.component.scss index 7fe020e616..ef6ed584b6 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.scss +++ b/projects/step-core/src/lib/components/toast/toast.component.scss @@ -3,34 +3,58 @@ align-items: center; background-color: #fff; border: 1px solid #ccc; - padding: 10px; - margin-bottom: 10px; + padding: 0.5rem; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); position: relative; -} + flex-direction: column; + gap: 0.5rem; -.success-icon { - margin-right: 10px; - color: #4caf50; -} + .action-icons { + padding: 0 0.2rem; + display: flex; + width: 100%; + justify-content: space-between; + } -.toast-message { - flex: 1; -} + .success-icon { + margin-right: 10px; + color: #4caf50; + } -.toast-actions { - display: flex; - gap: 5px; -} + .error-icon, + .warning-icon { + margin-right: 10px; + color: red; + } -.dismiss-btn { - background: none; - border: none; - cursor: pointer; -} + .info-icon { + margin-right: 10px; + color: #000; + } + + .toast-message { + flex: 1; + padding: 0 2rem; + } + + .toast-actions { + display: flex; + width: 100%; + padding: 0 0.2rem; + justify-content: end; + gap: 1rem; + + .action-btn { + font-size: 12px; + cursor: pointer; + } + } -.action-btn { - margin-left: 1rem; - cursor: pointer; + .dismiss-btn { + background: none; + border: none; + cursor: pointer; + padding: 0; + } } diff --git a/projects/step-core/src/lib/components/toast/toast.component.ts b/projects/step-core/src/lib/components/toast/toast.component.ts index 90ecd7c49c..abce1d5fa5 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.ts +++ b/projects/step-core/src/lib/components/toast/toast.component.ts @@ -1,6 +1,7 @@ import { Component, inject, Input, OnInit } from '@angular/core'; import { ToastService } from '../../services/toast.service'; import { NotificationAction } from '../../shared/toast-action.interface'; +import { ToastType } from '../../shared/toast-type.enum'; @Component({ selector: 'step-toast', @@ -8,21 +9,53 @@ import { NotificationAction } from '../../shared/toast-action.interface'; styleUrls: ['toast.component.scss'], }) export class ToastComponent implements OnInit { + @Input() type!: ToastType; @Input() message: string = ''; @Input() actions: NotificationAction[] | undefined = []; @Input() duration: number | undefined = 3000; + @Input() autoClose: boolean = true; + @Input() values: string[] = []; private _toastService = inject(ToastService); private timer: any; + toastType = ToastType; + messageParts: any[] = []; ngOnInit(): void { - this.timer = setTimeout(() => this.closeToast(), this.duration || 3000); + this.formatMessage(); + if (this.autoClose) { + this.timer = setTimeout(() => this.closeToast(), this.duration || 3000); + } } ngOnDestroy(): void { - clearTimeout(this.timer); + if (this.timer) { + clearTimeout(this.timer); + } } closeToast(): void { this._toastService.removeToast(this.message); } + + private formatMessage(): void { + const formattedMessage = this.message.replace(/{(\d+)}/g, (match, index) => { + return `{${index}}`; + }); + + const parts = formattedMessage.split(/(\{\d+\})/g); + this.messageParts = parts.map((part) => { + const match = part.match(/\{(\d+)\}/); + if (match) { + const index = parseInt(match[1], 10); + const originalValue = this.values[index]; + const displayValue = this.truncate(originalValue, 50); + return { isPlaceholder: true, originalValue, displayValue }; + } + return part; + }); + } + + private truncate(value: string, maxLength: number): string { + return value.length > maxLength ? value.substring(0, maxLength) + '...' : value; + } } diff --git a/projects/step-core/src/lib/services/toast.service.ts b/projects/step-core/src/lib/services/toast.service.ts index b2c466df9e..80d7751597 100644 --- a/projects/step-core/src/lib/services/toast.service.ts +++ b/projects/step-core/src/lib/services/toast.service.ts @@ -1,17 +1,41 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { NotificationAction } from '../shared/toast-action.interface'; +import { ToastType } from '../shared/toast-type.enum'; @Injectable({ providedIn: 'root', }) export class ToastService { - private toastSubject = new Subject<{ message: string; actions?: NotificationAction[]; duration?: number }[]>(); + private toastSubject = new Subject< + { + type: ToastType; + message: string; + values: string[]; + actions?: NotificationAction[]; + autoClose?: boolean; + duration?: number; + }[] + >(); toastMessages = this.toastSubject.asObservable(); - private toasts: { message: string; actions: NotificationAction[]; duration: number }[] = []; + private toasts: { + type: ToastType; + message: string; + values: string[]; + actions: NotificationAction[]; + autoClose: boolean; + duration: number; + }[] = []; - showToast(message: string, actions?: NotificationAction[], duration?: number): void { - this.toasts.push({ message, actions: actions || [], duration: duration || 3000 }); + showToast( + type: ToastType, + message: string, + values: string[], + actions?: NotificationAction[], + auto?: boolean, + duration?: number, + ): void { + this.toasts.push({ type, message, values, actions: actions || [], autoClose: !!auto, duration: duration || 3000 }); this.toastSubject.next(this.toasts); } diff --git a/projects/step-core/src/lib/shared/toast-type.enum.ts b/projects/step-core/src/lib/shared/toast-type.enum.ts new file mode 100644 index 0000000000..effbdfedb1 --- /dev/null +++ b/projects/step-core/src/lib/shared/toast-type.enum.ts @@ -0,0 +1,6 @@ +export enum ToastType { + SUCCESS = 'success', + ERROR = 'error', + WARNING = 'warning', + INFO = 'info', +} diff --git a/projects/step-core/src/lib/step-core.module.ts b/projects/step-core/src/lib/step-core.module.ts index 6618c63961..f394b6a8b4 100644 --- a/projects/step-core/src/lib/step-core.module.ts +++ b/projects/step-core/src/lib/step-core.module.ts @@ -359,3 +359,4 @@ export * from './pipes/extract-query-params.pipe'; export * from './services/toast.service'; export * from './components/toast/toast.component'; export * from './components/toast-container/toast-container.component'; +export * from './shared/toast-type.enum'; diff --git a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts index 7e028411fa..1551645273 100644 --- a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts +++ b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts @@ -4,15 +4,17 @@ import { AutoDeselectStrategy, DialogParentService, DialogsService, - Plan, IsUsedByDialogService, + Plan, selectionCollectionProvider, STORE_ALL, - tablePersistenceConfigProvider, tableColumnsConfigProvider, + tablePersistenceConfigProvider, ToastService, + ToastType, } from '@exense/step-core'; import { map, of, pipe, switchMap, tap } from 'rxjs'; +import { Router } from '@angular/router'; @Component({ selector: 'step-plan-list', @@ -36,6 +38,7 @@ export class PlanListComponent implements DialogParentService { private _isUsedByDialogs = inject(IsUsedByDialogService); private _dialogs = inject(DialogsService); private toast = inject(ToastService); + private _router = inject(Router); readonly _plansApiService = inject(AugmentedPlansService); readonly dataSource = this._plansApiService.getPlansTableDataSource(); @@ -53,13 +56,29 @@ export class PlanListComponent implements DialogParentService { } duplicatePlan(id: string): void { + let clonedPlan: Plan; + const values: Array = []; this._plansApiService .clonePlan(id) .pipe( - switchMap((clone) => this._plansApiService.savePlan(clone)), + switchMap((clone) => { + clonedPlan = clone; + if (clonedPlan.root?.attributes!['name']) { + values.push(clonedPlan.root?.attributes!['name']); + } + return this._plansApiService.savePlan(clone); + }), this.updateDataSourceAfterChange, ) - .subscribe(() => this.toast.showToast('Plan successfully duplicated', [], 3000)); + .subscribe(() => + this.toast.showToast( + ToastType.SUCCESS, + `{0} successfully duplicated`, + values, + [{ label: 'Edit', handler: () => this._router.navigateByUrl(`/plans/editor/${clonedPlan.id}`) }], + false, + ), + ); } deletePlan(plan: Plan): void { From 155367c6d036c90b5ea2315554e71f6f7917cce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Fri, 9 Aug 2024 13:57:03 +0200 Subject: [PATCH 3/9] SED-3222 add icon --- .../toast-container.component.html | 2 ++ .../toast-container.component.ts | 3 +++ .../lib/components/toast/toast.component.html | 3 +++ .../lib/components/toast/toast.component.ts | 3 +++ .../src/lib/services/toast.service.ts | 24 +++++++++++++++---- .../plan-list/plan-list.component.ts | 2 ++ 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html index cdb910e599..cbae1adb8a 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.html +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -4,6 +4,8 @@ [type]="toast.type" [message]="toast.message" [values]="toast.values" + [entity]="toast.entity" + [entityName]="toast.entityName" [actions]="toast.actions" [autoClose]="!!toast.autoClose" [duration]="toast.duration" diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.ts b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts index 6e8cf83565..f7c081ce7c 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.ts +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ToastService } from '../../services/toast.service'; import { NotificationAction } from '../../shared/toast-action.interface'; import { ToastType } from '../../shared/toast-type.enum'; +import { Entity } from '../../modules/entity/types/entity'; @Component({ selector: 'step-toast-container', @@ -13,6 +14,8 @@ export class ToastContainerComponent implements OnInit { type: ToastType; message: string; values: string[]; + entity?: Entity; + entityName?: string; actions?: NotificationAction[]; autoClose?: boolean; duration?: number; diff --git a/projects/step-core/src/lib/components/toast/toast.component.html b/projects/step-core/src/lib/components/toast/toast.component.html index e87bd94ed2..7626249b52 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.html +++ b/projects/step-core/src/lib/components/toast/toast.component.html @@ -18,6 +18,9 @@
+ @if (entity) { +   + } {{ part.displayValue }} {{ part }} diff --git a/projects/step-core/src/lib/components/toast/toast.component.ts b/projects/step-core/src/lib/components/toast/toast.component.ts index abce1d5fa5..5737e7e366 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.ts +++ b/projects/step-core/src/lib/components/toast/toast.component.ts @@ -2,6 +2,7 @@ import { Component, inject, Input, OnInit } from '@angular/core'; import { ToastService } from '../../services/toast.service'; import { NotificationAction } from '../../shared/toast-action.interface'; import { ToastType } from '../../shared/toast-type.enum'; +import { Entity } from '../../modules/entity/types/entity'; @Component({ selector: 'step-toast', @@ -15,6 +16,8 @@ export class ToastComponent implements OnInit { @Input() duration: number | undefined = 3000; @Input() autoClose: boolean = true; @Input() values: string[] = []; + @Input() entity?: Entity; + @Input() entityName?: string; private _toastService = inject(ToastService); private timer: any; toastType = ToastType; diff --git a/projects/step-core/src/lib/services/toast.service.ts b/projects/step-core/src/lib/services/toast.service.ts index 80d7751597..5331c6e8ae 100644 --- a/projects/step-core/src/lib/services/toast.service.ts +++ b/projects/step-core/src/lib/services/toast.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { NotificationAction } from '../shared/toast-action.interface'; import { ToastType } from '../shared/toast-type.enum'; +import { Entity } from '../modules/entity/types/entity'; @Injectable({ providedIn: 'root', @@ -12,6 +13,8 @@ export class ToastService { type: ToastType; message: string; values: string[]; + entity?: Entity; + entityName?: string; actions?: NotificationAction[]; autoClose?: boolean; duration?: number; @@ -22,20 +25,33 @@ export class ToastService { type: ToastType; message: string; values: string[]; - actions: NotificationAction[]; - autoClose: boolean; - duration: number; + entity?: Entity; + entityName?: string; + actions?: NotificationAction[]; + autoClose?: boolean; + duration?: number; }[] = []; showToast( type: ToastType, message: string, values: string[], + entity?: Entity, + entityName?: string, actions?: NotificationAction[], auto?: boolean, duration?: number, ): void { - this.toasts.push({ type, message, values, actions: actions || [], autoClose: !!auto, duration: duration || 3000 }); + this.toasts.push({ + type, + message, + values, + entity, + entityName, + actions: actions || [], + autoClose: !!auto, + duration: duration || 3000, + }); this.toastSubject.next(this.toasts); } diff --git a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts index 1551645273..f09c454399 100644 --- a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts +++ b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts @@ -75,6 +75,8 @@ export class PlanListComponent implements DialogParentService { ToastType.SUCCESS, `{0} successfully duplicated`, values, + clonedPlan, + 'plans', [{ label: 'Edit', handler: () => this._router.navigateByUrl(`/plans/editor/${clonedPlan.id}`) }], false, ), From 8d68e1b7bc17b89502a597eb174654e8ca4cc946 Mon Sep 17 00:00:00 2001 From: lukasz-lepa <158443736+lukasz-lepa@users.noreply.github.com> Date: Sun, 11 Aug 2024 13:34:54 +0200 Subject: [PATCH 4/9] Update projects/step-core/src/lib/components/toast-container/toast-container.component.html Co-authored-by: Vladimir --- .../components/toast-container/toast-container.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html index cbae1adb8a..e1e4c83300 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.html +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -1,5 +1,5 @@
- @for (toast of toasts; track toast) { + @for (toast of toasts; track toast.message) { Date: Sun, 11 Aug 2024 13:46:34 +0200 Subject: [PATCH 5/9] Update projects/step-core/src/lib/components/toast/toast.component.html Co-authored-by: Vladimir --- .../step-core/src/lib/components/toast/toast.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/step-core/src/lib/components/toast/toast.component.html b/projects/step-core/src/lib/components/toast/toast.component.html index 7626249b52..fd908f9c3e 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.html +++ b/projects/step-core/src/lib/components/toast/toast.component.html @@ -27,7 +27,7 @@
- @for (action of actions; track action) { + @for (action of actions; track action.label) { {{ action.label }} From 49025bb2c9d38b965340a3b361fc70e47862c2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Sun, 11 Aug 2024 13:57:06 +0200 Subject: [PATCH 6/9] SED-3222 fixes after code review --- .../toast-container.component.html | 3 +-- .../lib/components/toast/toast.component.scss | 16 +++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html index cbae1adb8a..892c064302 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.html +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -9,7 +9,6 @@ [actions]="toast.actions" [autoClose]="!!toast.autoClose" [duration]="toast.duration" - > - + /> }
diff --git a/projects/step-core/src/lib/components/toast/toast.component.scss b/projects/step-core/src/lib/components/toast/toast.component.scss index ef6ed584b6..a1809e46cd 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.scss +++ b/projects/step-core/src/lib/components/toast/toast.component.scss @@ -1,11 +1,13 @@ +@use 'projects/step-core/styles/core-variables' as var; + .toast { display: flex; align-items: center; - background-color: #fff; - border: 1px solid #ccc; + background-color: var.$white; + border: 1px solid var.$light-white-gray; padding: 0.5rem; - border-radius: 5px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + border-radius: 1rem; + box-shadow: 0 0.2rem 1rem rgba(0, 0, 0, 0.1); position: relative; flex-direction: column; gap: 0.5rem; @@ -19,18 +21,18 @@ .success-icon { margin-right: 10px; - color: #4caf50; + color: var.$green-650; } .error-icon, .warning-icon { margin-right: 10px; - color: red; + color: var.$red-100; } .info-icon { margin-right: 10px; - color: #000; + color: black; } .toast-message { From 1760538bf5975d7575b0dad77fc3b6f401c420f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Sun, 11 Aug 2024 14:26:08 +0200 Subject: [PATCH 7/9] SED-3222 use signals --- .../toast-container.component.html | 2 +- .../toast-container.component.ts | 26 ++---------- .../src/lib/services/toast.service.ts | 42 +++++-------------- 3 files changed, 15 insertions(+), 55 deletions(-) diff --git a/projects/step-core/src/lib/components/toast-container/toast-container.component.html b/projects/step-core/src/lib/components/toast-container/toast-container.component.html index 1efc8aea80..c7d157795c 100644 --- a/projects/step-core/src/lib/components/toast-container/toast-container.component.html +++ b/projects/step-core/src/lib/components/toast-container/toast-container.component.html @@ -1,5 +1,5 @@
- @for (toast of toasts; track toast.message) { + @for (toast of _toasts(); track toast.message) { { - this.toasts = toasts; - }); - } +export class ToastContainerComponent { + protected readonly _toasts = inject(ToastService).toastMessages; } diff --git a/projects/step-core/src/lib/services/toast.service.ts b/projects/step-core/src/lib/services/toast.service.ts index 5331c6e8ae..46ed800ca5 100644 --- a/projects/step-core/src/lib/services/toast.service.ts +++ b/projects/step-core/src/lib/services/toast.service.ts @@ -1,5 +1,4 @@ -import { Injectable } from '@angular/core'; -import { Subject } from 'rxjs'; +import { Injectable, signal } from '@angular/core'; import { NotificationAction } from '../shared/toast-action.interface'; import { ToastType } from '../shared/toast-type.enum'; import { Entity } from '../modules/entity/types/entity'; @@ -8,7 +7,7 @@ import { Entity } from '../modules/entity/types/entity'; providedIn: 'root', }) export class ToastService { - private toastSubject = new Subject< + private toastsInternal = signal< { type: ToastType; message: string; @@ -19,18 +18,8 @@ export class ToastService { autoClose?: boolean; duration?: number; }[] - >(); - toastMessages = this.toastSubject.asObservable(); - private toasts: { - type: ToastType; - message: string; - values: string[]; - entity?: Entity; - entityName?: string; - actions?: NotificationAction[]; - autoClose?: boolean; - duration?: number; - }[] = []; + >([]); + readonly toastMessages = this.toastsInternal.asReadonly(); showToast( type: ToastType, @@ -38,25 +27,16 @@ export class ToastService { values: string[], entity?: Entity, entityName?: string, - actions?: NotificationAction[], - auto?: boolean, - duration?: number, + actions: NotificationAction[] = [], + autoClose: boolean = true, + duration: number = 3000, ): void { - this.toasts.push({ - type, - message, - values, - entity, - entityName, - actions: actions || [], - autoClose: !!auto, - duration: duration || 3000, - }); - this.toastSubject.next(this.toasts); + this.toastsInternal.update((toasts) => + toasts.concat({ type, message, values, entity, entityName, actions, autoClose, duration }), + ); } removeToast(message: string): void { - this.toasts = this.toasts.filter((toast) => toast.message !== message); - this.toastSubject.next(this.toasts); + this.toastsInternal.update((toasts) => toasts.filter((toast) => toast.message !== message)); } } From 0a5892e0eed308fae14336f35fea77ad7200104d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Sun, 11 Aug 2024 16:36:37 +0200 Subject: [PATCH 8/9] SED-3222 fix issue --- .../src/lib/components/toast/toast.component.ts | 2 +- projects/step-core/src/lib/services/toast.service.ts | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/projects/step-core/src/lib/components/toast/toast.component.ts b/projects/step-core/src/lib/components/toast/toast.component.ts index 5737e7e366..ed7f222f61 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.ts +++ b/projects/step-core/src/lib/components/toast/toast.component.ts @@ -37,7 +37,7 @@ export class ToastComponent implements OnInit { } closeToast(): void { - this._toastService.removeToast(this.message); + this._toastService.removeToast(this.message, this.values); } private formatMessage(): void { diff --git a/projects/step-core/src/lib/services/toast.service.ts b/projects/step-core/src/lib/services/toast.service.ts index 46ed800ca5..8ece074d40 100644 --- a/projects/step-core/src/lib/services/toast.service.ts +++ b/projects/step-core/src/lib/services/toast.service.ts @@ -36,7 +36,14 @@ export class ToastService { ); } - removeToast(message: string): void { - this.toastsInternal.update((toasts) => toasts.filter((toast) => toast.message !== message)); + removeToast(message: string, values: string[]): void { + this.toastsInternal.update((toasts) => + toasts.filter((toast) => toast.message !== message || !this.arraysEqual(toast.values, values)), + ); + } + + private arraysEqual(arr1: string[], arr2: string[]): boolean { + if (arr1.length !== arr2.length) return false; + return arr1.every((value, index) => value === arr2[index]); } } From f576aafb035bf6fc601c279847d52be86571fe0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lepa?= Date: Thu, 5 Sep 2024 12:20:09 +0200 Subject: [PATCH 9/9] SED-3222 modify styling --- .../lib/components/toast/toast.component.html | 34 ++++++------------- .../lib/components/toast/toast.component.scss | 26 +++++++++----- .../plan-list/plan-list.component.ts | 2 +- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/projects/step-core/src/lib/components/toast/toast.component.html b/projects/step-core/src/lib/components/toast/toast.component.html index fd908f9c3e..41abfe0559 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.html +++ b/projects/step-core/src/lib/components/toast/toast.component.html @@ -1,22 +1,5 @@ @if (message) {
-
- @switch (type) { - @case (toastType.SUCCESS) { - - } - @case (toastType.ERROR) { - - } - @case (toastType.WARNING) { - - } - @case (toastType.INFO) { - - } - } - -
@if (entity) {   @@ -26,12 +9,17 @@ {{ part }}
-
- @for (action of actions; track action.label) { - - {{ action.label }} - - } +
+
+ @for (action of actions; track action.label) { + + {{ action.label }} + + } +
+
+ +
} diff --git a/projects/step-core/src/lib/components/toast/toast.component.scss b/projects/step-core/src/lib/components/toast/toast.component.scss index a1809e46cd..d834caa618 100644 --- a/projects/step-core/src/lib/components/toast/toast.component.scss +++ b/projects/step-core/src/lib/components/toast/toast.component.scss @@ -37,19 +37,29 @@ .toast-message { flex: 1; - padding: 0 2rem; + padding: 1rem 2rem 1rem 1rem; } - .toast-actions { + .toast-bottom { display: flex; width: 100%; - padding: 0 0.2rem; - justify-content: end; - gap: 1rem; + justify-content: space-between; + + .toast-actions { + display: flex; + width: 100%; + padding: 0 0.2rem; + justify-content: start; + padding-left: 1rem; + gap: 1rem; + + .action-btn { + cursor: pointer; + } + } - .action-btn { - font-size: 12px; - cursor: pointer; + .toast-close { + padding-right: 1rem; } } diff --git a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts index f09c454399..6354fed8a0 100644 --- a/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts +++ b/projects/step-frontend/src/lib/modules/plan/components/plan-list/plan-list.component.ts @@ -77,7 +77,7 @@ export class PlanListComponent implements DialogParentService { values, clonedPlan, 'plans', - [{ label: 'Edit', handler: () => this._router.navigateByUrl(`/plans/editor/${clonedPlan.id}`) }], + [{ label: 'edit', handler: () => this._router.navigateByUrl(`/plans/editor/${clonedPlan.id}`) }], false, ), );