File

libs/core/devtools/src/lib/journey-details/journey-details.component.ts

Index

Properties

Properties

bbId
Type string | undefined
form
Type DynamicFormConfiguration
pageId
Type string
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  effect,
  HostBinding,
  inject,
  input,
  isSignal,
  output,
  Signal,
  signal,
  untracked
} from '@angular/core';
import { NxGridModule } from '@allianz/ng-aquila/grid';
import { PAGES_CONFIGURATION_RUNTIME_CONFIG_MODE } from '@allianz/taly-core';
import { PAGES_CONFIGURATION } from '@allianz/taly-core';
import {
  BuildingBlockConfiguration,
  DynamicFormBBConfiguration,
  DynamicFormConfiguration,
  Frame,
  HeaderAction,
  isDynamicFormBbConfig,
  PageConfiguration,
  PageConfigurationWithTransformedDynamicForms
} from '@allianz/taly-core/schemas';

import { NxActionComponent, NxActionIconDirective } from '@allianz/ng-aquila/action';
import { NxIconComponent } from '@allianz/ng-aquila/icon';
import {
  NxFlatTreeControl,
  NxFlatTreeNode,
  NxTreeComponent,
  NxTreeFlatDataSource,
  NxTreeNode,
  NxTreeNodeActionItem,
  NxTreeNodeComponent,
  NxTreeNodeDefDirective,
  NxTreeNodePaddingDirective,
  NxTreeNodeToggleDirective
} from '@allianz/ng-aquila/tree';
import { NxButtonComponent } from '@allianz/ng-aquila/button';
import {
  NxAccordionDirective,
  NxExpansionPanelComponent,
  NxExpansionPanelHeaderComponent,
  NxExpansionPanelTitleDirective
} from '@allianz/ng-aquila/accordion';
import { NGX_PFE_CONFIGURATION, PfeNavigationService } from '@allianz/ngx-pfe';
import { NxListComponent } from '@allianz/ng-aquila/list';
import { NxLinkComponent } from '@allianz/ng-aquila/link';
import { NormalizeUrlModule } from '@allianz/taly-core';

type PagesConfig = {
  pages: PageConfiguration[] | PageConfigurationWithTransformedDynamicForms[];
  frame?: Frame;
};

interface PageOrBBTreeNodeAction {
  handler: (node: PageOrBBTreeNode) => void;
  label: string;
  icon?: string;
}

type TreeNode = PageOrBBTreeNode | BuildingBlockDataTreeNode;

interface BuildingBlockDataTreeNode extends NxTreeNode {
  type:
    | 'building-block'
    | 'banner-block'
    | 'dynamic-form'
    | 'overarching-details-block'
    | 'header-action';
  selector?: string;
  module?: string;
  package?: string;
}

interface PageOrBBTreeNode extends NxTreeNode {
  isPage: boolean;
  label: string;
  icon?: string;
  form?: DynamicFormConfiguration;
  actions?: PageOrBBTreeNodeAction[];
  children?: TreeNode[];
}

export interface JourneyDetailsEditDynamicFormEvent {
  form: DynamicFormConfiguration;
  bbId: string | undefined;
  pageId: string;
}

@Component({
  selector: 'taly-journey-details',
  templateUrl: './journey-details.component.html',
  styleUrl: './journey-details.component.scss',
  imports: [
    NxGridModule,
    NxTreeComponent,
    NxTreeNodeDefDirective,
    NxTreeNodeComponent,
    NxActionComponent,
    NxTreeNodeActionItem,
    NxTreeNodePaddingDirective,
    NxIconComponent,
    NxActionIconDirective,
    NxTreeNodeToggleDirective,
    NxButtonComponent,
    NxListComponent,
    NxAccordionDirective,
    NxExpansionPanelComponent,
    NxExpansionPanelHeaderComponent,
    NxExpansionPanelTitleDirective,
    NxLinkComponent,
    NormalizeUrlModule
  ],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TalyDevtoolsJourneyDetailsComponent {
  private pagesConfig = signal(inject<PagesConfig>(PAGES_CONFIGURATION));
  private pagesConfigRuntime = inject<Signal<PagesConfig> | null>(
    PAGES_CONFIGURATION_RUNTIME_CONFIG_MODE,
    {
      optional: true
    }
  );
  private pfeNavigationService = inject(PfeNavigationService);
  private pfeConfiguration = inject(NGX_PFE_CONFIGURATION);

  dynamicFormEditEnabled = input(true);
  darkMode = input(false);

  editDynamicForm = output<JourneyDetailsEditDynamicFormEvent>();

  @HostBinding('class.taly-journey-details--dark') get darkModeClass() {
    return this.darkMode();
  }

  tenantName = computed(() =>
    isSignal(this.pfeConfiguration) ? this.pfeConfiguration().tenant : this.pfeConfiguration.tenant
  );

  data = signal<TreeNode[]>([]);
  _hasChild = (_: number, node: NxFlatTreeNode) => node.expandable;
  _treeControl = new NxFlatTreeControl();
  _dataSource: NxTreeFlatDataSource<TreeNode, NxFlatTreeNode>;

  uniqueBuildingBlocks = computed(() => {
    if (this.data().length === 0) {
      return 0;
    }

    const buildingBlockSet = new Set<string>();

    for (const page of this.data()) {
      for (const bb of page.children ?? []) {
        if (bb.children) {
          const child = bb.children[0] as BuildingBlockDataTreeNode;
          if (child.package && child.module) {
            buildingBlockSet.add(`${child.package}-${child.module}`);
          } else if (child.type === 'dynamic-form') {
            buildingBlockSet.add(`dynamic-form`);
          }
        }
      }
    }
    return buildingBlockSet.size;
  });

  constructor() {
    this.data.set(this.computeTreeData());
    this._dataSource = new NxTreeFlatDataSource(this._treeControl, this.data());

    effect(() => {
      if (this.pagesConfigRuntime?.()) {
        this.pagesConfig.set(this.pagesConfigRuntime());
      }
      this.data.set(this.computeTreeData());
      this._dataSource.data = untracked(() => this.data());
    });
  }

  private computeTreeData(): TreeNode[] {
    const headerActions = this.pagesConfig().frame?.header?.actions ?? [];
    const headerActionNodes: TreeNode[] = headerActions.length
      ? [
          {
            label: 'Header Actions',
            icon: 'setting-o',
            isPage: false,
            children: headerActions.map((action) => this.getHeaderActionData(action))
          }
        ]
      : [];

    const pageNodes: TreeNode[] = this.pagesConfig().pages.map((page) => ({
      label: page.id,
      icon: 'file',
      isPage: true,
      children: [
        ...(page.bannerBlock
          ? [this.getBuildingBlockData(page.bannerBlock, page.id, 'banner-block')]
          : []),
        ...(page.overarchingDetailsBlock
          ? [
              this.getBuildingBlockData(
                page.overarchingDetailsBlock,
                page.id,
                'overarching-details-block'
              )
            ]
          : []),
        ...(page.blocks?.map((block) => this.getBuildingBlockData(block, page.id)) ?? [])
      ],
      actions: [
        {
          label: 'Navigate',
          icon: 'arrow-right',
          handler: (node: PageOrBBTreeNode) => {
            if (node.isPage) {
              this.pfeNavigationService.navigate(node.label);
            }
          }
        }
      ]
    }));

    return [...headerActionNodes, ...pageNodes];
  }

  private getBuildingBlockData(
    block: BuildingBlockConfiguration | DynamicFormBBConfiguration,
    pageId: string,
    type: BuildingBlockDataTreeNode['type'] = 'building-block'
  ): PageOrBBTreeNode {
    let icon = 'product-puzzle';
    let form: DynamicFormConfiguration | undefined;

    if (isDynamicFormBbConfig(block)) {
      type = 'dynamic-form';
      icon = 'fountain-pen';
      form = block.form;
    } else if (
      block.package === '@allianz/taly-core/building-blocks' &&
      block.module === 'DynamicFormBuildingBlockModule'
    ) {
      type = 'dynamic-form';
      icon = 'fountain-pen';
      form = block.resources?.['talyDynamicFormConfig'] as DynamicFormConfiguration;
    }

    return {
      isPage: false,
      label: block.id,
      icon,
      form,
      children: [
        {
          type,
          ...('selector' in block && type != 'dynamic-form' ? { selector: block.selector } : {}),
          ...('module' in block && type != 'dynamic-form' ? { module: block.module } : {}),
          ...('package' in block && type != 'dynamic-form' ? { package: block.package } : {})
        }
      ],
      actions:
        form && this.dynamicFormEditEnabled()
          ? this.getDynamicFormEditActions(form, block.id, pageId)
          : undefined
    };
  }

  private getHeaderActionData(action: HeaderAction): PageOrBBTreeNode {
    return {
      isPage: false,
      label: action.id,
      icon: 'product-puzzle',
      children: [
        {
          type: 'header-action' as const,
          selector: action.selector,
          module: action.module,
          package: action.package
        }
      ]
    };
  }

  private getDynamicFormEditActions(
    form: DynamicFormConfiguration,
    bbId: string,
    pageId: string
  ): PageOrBBTreeNodeAction[] {
    return [
      {
        label: 'Edit',
        icon: 'edit',
        handler: () => {
          if (form) {
            const cleanedForm: DynamicFormConfiguration = JSON.parse(
              JSON.stringify(form).replaceAll('@localize.', '')
            );
            this.editDynamicForm.emit({
              form: cleanedForm,
              bbId: this.pagesConfigRuntime ? undefined : bbId,
              pageId
            });
          }
        }
      }
    ];
  }
}

results matching ""

    No results matching ""