libs/core/frame/src/frame-parts/notification/notifications-group.component.ts

Implements

OnDestroy

Metadata

Relationships

Depends on

Index

Properties
Inputs
Accessors

Constructor

constructor()

Inputs

columnSpan
Type : number
Required :  true
notifications
Required :  true

Properties

transformNotifications
Type : unknown
Default value : () => {...}

Accessors

isExpert
getisExpert()
import { NxColComponent, NxLayoutComponent, NxRowComponent } from '@allianz/ng-aquila/grid';
import { CHANNEL, CHANNEL_TOKEN, InterpolateFromStorePipe } from '@allianz/taly-core';
import { MarkdownToHtmlComponent } from '@allianz/taly-core/ui';
import {
  BaseNotificationConfig,
  ErrorNotificationConfig,
  NotificationConfig
} from '@allianz/taly-core/schemas';
import { AsyncPipe } from '@angular/common';
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  computed,
  effect,
  inject,
  input,
  InputSignalWithTransform,
  OnDestroy
} from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { TalyFrameTopAreaService } from '../../services/taly-frame-top-area.service';
import { NotificationComponent } from './notification.component';

interface ErrorNotificationWithVisibility extends ErrorNotificationConfig {
  visibility$: Observable<boolean>;
}

interface OtherNotificationWithVisibility extends BaseNotificationConfig {
  visibility$: BehaviorSubject<boolean>;
}

interface StructuredNotifications {
  errors: ErrorNotificationWithVisibility[];
  others: OtherNotificationWithVisibility[];
}

@Component({
  selector: 'frame-notifications-group',
  templateUrl: './notifications-group.component.html',
  imports: [
    MarkdownToHtmlComponent,
    InterpolateFromStorePipe,
    AsyncPipe,
    NotificationComponent,
    NxRowComponent,
    NxLayoutComponent,
    NxColComponent
  ],
  styleUrl: './notifications-group.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotificationsGroupComponent implements OnDestroy {
  private channel = inject(CHANNEL_TOKEN);
  private topAreaService = inject(TalyFrameTopAreaService);
  private cdr = inject(ChangeDetectorRef);
  private inputChanged$ = new Subject<void>();
  protected anyErrorNotificationDisplayed$!: Observable<boolean>;
  protected anyNotificationDisplayed$!: Observable<boolean>;
  protected errorMessage$!: Observable<string | undefined>;
  protected hasConfiguredNotifications = computed(() => {
    return this.notifications().errors.length > 0 || this.notifications().others.length > 0;
  });
  columnSpan = input.required<number>();
  notifications: InputSignalWithTransform<StructuredNotifications, NotificationConfig[]> =
    input.required({
      transform: (notifications) => this.transformNotifications(notifications)
    });

  constructor() {
    this.errorMessage$ = this.topAreaService.networkErrorMessage$;
    effect(() => {
      this.anyErrorNotificationDisplayed$ = combineLatest([
        ...this.notifications().errors.map((notification) => notification.visibility$),
        this.errorMessage$.pipe(map(Boolean))
      ]).pipe(
        map((visibilityFlags) => visibilityFlags.some((visibilityFlags) => visibilityFlags)),
        takeUntil(this.inputChanged$)
      );

      this.anyNotificationDisplayed$ = combineLatest([
        ...this.notifications().others.map((notification) => notification.visibility$),
        this.anyErrorNotificationDisplayed$
      ]).pipe(
        map((values) => values.some((value) => value)),
        takeUntil(this.inputChanged$)
      );

      this.cdr.markForCheck();
    });
  }

  ngOnDestroy() {
    this.inputChanged$.next();
    this.inputChanged$.complete();
  }

  get isExpert() {
    return this.channel === CHANNEL.EXPERT;
  }

  transformNotifications = (notifications: NotificationConfig[]): StructuredNotifications => {
    this.inputChanged$.next();
    const sortedNotifications = this.sortNotificationsByContext(notifications);
    return sortedNotifications.reduce(
      (acc: StructuredNotifications, notification) => {
        if (notification.context === 'error') {
          const notificationWithVisibility = {
            ...notification,
            visibility$: this.topAreaService.getNotificationVisibility$(notification.visibleIf)
          };
          acc.errors.push(notificationWithVisibility);
        } else {
          const notificationWithVisibility = this.createToggleableNotification(notification);
          acc.others.push(notificationWithVisibility);
        }
        return acc;
      },
      {
        errors: [],
        others: []
      }
    );
  };

  private createToggleableNotification(
    notification: BaseNotificationConfig
  ): OtherNotificationWithVisibility {
    const visibilitySubject$ = new BehaviorSubject(true);
    if (notification.visibleIf) {
      this.topAreaService
        .getNotificationVisibility$(notification.visibleIf)
        .pipe(takeUntil(this.inputChanged$))
        .subscribe(visibilitySubject$);
    }
    return {
      ...notification,
      visibility$: visibilitySubject$
    };
  }

  private sortNotificationsByContext(notifications: NotificationConfig[]) {
    const contextOrder = {
      error: 0,
      warning: 1,
      info: 2,
      success: 3
    };

    notifications.sort((a, b) => {
      const contextA = a.context;
      const contextB = b.context;
      return contextOrder[contextA] - contextOrder[contextB];
    });

    return notifications;
  }
}
@if (anyNotificationDisplayed$ | async) {
<div class="notifications-section-wrapper">
  <div class="notifications-block-wrapper">
    <div nxLayout="grid nopadding" [containerQuery]="true">
      <div nxRow [rowJustify]="!isExpert ? 'center' : ''">
        <div [nxCol]="'12,12,' + columnSpan()">
          <!-- Error Notification -->
          @if (anyErrorNotificationDisplayed$ | async) {
          <div class="notification-wrapper error-notification-wrapper">
            <taly-notification context="error" [closable]="false">
              @if (errorMessage$ | async) {
              <div class="js-notification-content error-notification-content">
                {{ errorMessage$ | async }}
              </div>
              } @for (notification of notifications().errors; track notification.message) { @if
              (notification.visibility$ | async) {
              <div class="js-notification-content error-notification-content">
                @if (notification?.title) {
                <span class="nx-font-weight-bold" data-testid="notification-title">
                  {{ notification.title | interpolateFromStore | async }}
                </span>
                <br />
                } @defer (when hasConfiguredNotifications()) {
                <taly-markdown-to-html
                  [markdown]="(notification.message | interpolateFromStore | async) || ''"
                />
                }
              </div>
              } }
            </taly-notification>
          </div>
          }

          <!-- Other Notifications -->
          @for (notification of notifications().others; track notification.message) { @if
          (notification.visibility$ | async) {
          <div class="notification-wrapper">
            <taly-notification
              [context]="notification.context"
              [closable]="notification.closable ?? true"
              (closeNotification)="notification.visibility$.next(false)"
            >
              <div class="js-notification-content">
                @if (notification?.title) {
                <span class="nx-font-weight-bold" data-testid="notification-title">
                  {{ notification.title | interpolateFromStore | async }}
                </span>
                <br />
                } @defer (when hasConfiguredNotifications()) {
                <taly-markdown-to-html
                  [markdown]="(notification.message | interpolateFromStore | async) || ''"
                />
                }
              </div>
            </taly-notification>
          </div>
          } }
        </div>
      </div>
    </div>
  </div>
</div>
}

./notifications-group.component.scss

:host {
  display: block;
}

.notification-wrapper + .notification-wrapper {
  padding-top: var(--vertical-inner-section-spacing);
}

.error-notification-content + .error-notification-content {
  padding-top: var(--vertical-helper-low-spacing);
}

:host-context(.is-stacked),
:host-context(.is-centered) {
  .notifications-block-wrapper {
    margin-inline: auto;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""