File

libs/nx/src/generators/journey-src/lib/create-pages/single-page/merge-blocks-and-panels-into-sections.ts

Extends

PageSectionDefinition

Index

Properties

Properties

backgroundColor
Type string | undefined
compact
Type boolean
isConfiguredSection
Type boolean
isSimpleSection
Type boolean
sectionItems
Type (BuildingBlockConfiguration | PanelRuntimeDefinition)[]
dividerLineBelow (Optional)
Type boolean
Default value false
Description

Set to true if you want to add a divider line below this section. By default, the section does not contain a divider line. false

id
Type string
Description

Unique ID for the Page Section to be referred to by Building Blocks.

title (Optional)
Type string
Description

The provided title will appear at the top of the section.

Example :
import {
  BuildingBlockConfiguration,
  PageConfigurationWithTransformedDynamicForms,
  PageSectionDefinition,
  PanelDefinition,
  StyleOfThePanel
} from '@allianz/taly-core/schemas';
import { logger } from '@nx/devkit';

export type SectionOrBlock = SectionRuntimeDefinition | BuildingBlockConfiguration;
type PanelOrSectionOrBlock = PanelRuntimeDefinition | SectionOrBlock;

export function isPanel(item: PanelOrSectionOrBlock): item is PanelRuntimeDefinition {
  return (
    Object.prototype.hasOwnProperty.call(item, 'blocks') &&
    Object.prototype.hasOwnProperty.call(item, 'style')
  );
}

// separate panel type to aggregate blocks for the runtime processing
export interface PanelRuntimeDefinition extends PanelDefinition {
  i18nKey: string;
  blocks: BuildingBlockConfiguration[];
  style: StyleOfThePanel;
  expanded: string | boolean;
}

export interface SectionRuntimeDefinition extends PageSectionDefinition {
  sectionItems: (BuildingBlockConfiguration | PanelRuntimeDefinition)[];
  isSimpleSection: boolean;
  isConfiguredSection: boolean;
  backgroundColor: string | undefined;
  compact: boolean;
}

/**
 * Given a list of blocks, sections and panels merge everything except standalone BBs into sections configuration.
 *
 * Important: This method have to retain the order of elements, so we don't calculate two lists of panels and unbound blocks
 * but a mixed list to allow `panel, block, panel, block` instead of `panel, panel, panel, block, block`
 */
export function mergeBlocksAndPanelsIntoSections(
  page: PageConfigurationWithTransformedDynamicForms,
  isExpertApp = false
): SectionRuntimeDefinition[] {
  const { sections = [], panels = [], blocks = [] } = page;

  const processedSections = blocks.reduce((accu: SectionRuntimeDefinition[], block, index) => {
    if (block.section) {
      // find configured section
      const section = sections.find((item) => item.id === block.section);
      if (!section) {
        logger.warn(
          `The Building Block "${block.id}" is configured as part of section "${block.section}", but "${block.section}" is not defined. "${block.id}" will be displayed as a standalone Building Block. If you want "${block.id}" to be part of a section, please add "${block.section}" to the "sections" list in the page configuration.\n`
        );
        accu.push(createNewRuntimeSection(block, index));
        return accu;
      }

      // find existing section in the accu list
      const sectionInAccu = accu.find((item) => item.id === section.id);
      if (!sectionInAccu) {
        accu.push(createRuntimeSectionFromConfiguration(section, block));
        return accu;
      }

      sectionInAccu.sectionItems.push(block);
      return accu;
    } else if (block.panel) {
      // find configured panel
      const panel = panels.find((panel) => panel.id === block.panel);
      if (!panel) {
        logger.warn(
          `The Building Block "${block.id}" is configured as part of panel "${block.panel}", but "${block.panel}" is not defined. "${block.id}" will be displayed as a standalone Building Block. If you want "${block.id}" to be part of a panel, please add "${block.panel}" to the "panels" list in the page configuration.\n`
        );
        accu.push(createNewRuntimeSection(block, index));
        return accu;
      }

      // find existing panel in the accu list
      const panelInAccu = findPanelInSections(accu, panel.id);
      if (panelInAccu) {
        panelInAccu.blocks.push(block);
        return accu;
      }

      const newPanel = createRuntimePanelFromConfiguration(panel, block, page.id, isExpertApp);

      if (!newPanel.section) {
        // check if it's a consecutive panel. If so, put it in the same section
        const lastSectionWithStandalonePanel = findLastSectionWithStandalonePanel(accu);
        if (lastSectionWithStandalonePanel) {
          lastSectionWithStandalonePanel.sectionItems.push(newPanel);
          return accu;
        }

        accu.push(createNewRuntimeSection(newPanel, index));
        return accu;
      }

      const section = sections.find((item) => item.id === newPanel.section);
      if (!section) {
        logger.warn(
          `The panel "${panel.id}" is configured as part of section "${panel.section}", but "${panel.section}" is not defined. "${panel.id}" will be displayed as a standalone panel. If you want "${panel.id}" to be part of a section, please add "${panel.section}" to the "sections" list in the page configuration.\n`
        );
        // check if it's a consecutive panel. If so, put it in the same section
        const lastSectionWithStandalonePanel = findLastSectionWithStandalonePanel(accu);
        if (lastSectionWithStandalonePanel) {
          lastSectionWithStandalonePanel.sectionItems.push(newPanel);
          return accu;
        }

        accu.push(createNewRuntimeSection(newPanel, index));
        return accu;
      }

      // find existing section in the accu list
      const sectionInAccu = accu.find((item) => item.id === section.id);
      if (!sectionInAccu) {
        accu.push(createRuntimeSectionFromConfiguration(section, newPanel));
        return accu;
      }

      sectionInAccu.sectionItems.push(newPanel);
      return accu;
    } else {
      accu.push(createNewRuntimeSection(block, index));
      return accu;
    }
  }, []);

  for (const section of processedSections) {
    section.isSimpleSection =
      section.sectionItems.length === 1 ||
      section.sectionItems.every((subSection) => isPanel(subSection));
  }

  return processedSections;
}

function findPanelInSections(sections: SectionRuntimeDefinition[], panelId: string) {
  const allPanels = sections
    .flatMap((section) => section.sectionItems)
    .filter((sectionItem) => isPanel(sectionItem)) as PanelRuntimeDefinition[];
  return allPanels.find((panel) => panel.id === panelId);
}

function findLastSectionWithStandalonePanel(sections: SectionRuntimeDefinition[]) {
  const lastSection = sections[sections.length - 1];
  if (!lastSection) {
    return;
  }

  if (lastSection.isConfiguredSection) {
    return;
  }
  const lastSectionItems = lastSection.sectionItems;
  const lastSectionItem = lastSectionItems[lastSectionItems.length - 1];
  if (isPanel(lastSectionItem)) {
    return lastSection;
  }
  return undefined;
}

function createNewRuntimeSection(
  sectionItem: BuildingBlockConfiguration | PanelRuntimeDefinition,
  index: number
): SectionRuntimeDefinition {
  return {
    id: `customId${index}`,
    sectionItems: [sectionItem],
    isConfiguredSection: false,
    isSimpleSection: true,
    backgroundColor:
      (sectionItem as BuildingBlockConfiguration)?.buildingBlockStyle?.backgroundColor ?? undefined,
    compact: (sectionItem as BuildingBlockConfiguration)?.buildingBlockStyle?.compact ?? false
  };
}

function createRuntimeSectionFromConfiguration(
  section: PageSectionDefinition,
  sectionItem: BuildingBlockConfiguration | PanelRuntimeDefinition
): SectionRuntimeDefinition {
  return {
    id: section.id,
    title: section.title,
    dividerLineBelow: section.dividerLineBelow,
    sectionItems: [sectionItem],
    isConfiguredSection: true,
    isSimpleSection: true,
    backgroundColor: undefined,
    compact: false
  };
}

function createRuntimePanelFromConfiguration(
  panelConfig: PanelDefinition,
  blockConfig: BuildingBlockConfiguration,
  pageId: string,
  isExpertApp: boolean
): PanelRuntimeDefinition {
  return {
    i18nKey: `@@${pageId}.panels.${panelConfig.id}`, // to yield this in the template: i18n-title="@@my-page.panels.panel1"
    ...panelConfig,
    expanded: getExpandedCondition(panelConfig.collapsed),
    style: panelConfig.style || (isExpertApp ? StyleOfThePanel.Regular : StyleOfThePanel.Light),
    blocks: [blockConfig]
  };
}

function getExpandedCondition(collapsed: string | boolean | undefined) {
  const PFE_EXPRESSION_CONDITION_REGEX = /({.*?})/gm;

  const isPfeStateKeyOrCondition = typeof collapsed === 'string';
  if (isPfeStateKeyOrCondition) {
    const escapedCollapsed = escapeForHtmlTemplate(collapsed);
    const isCondition = PFE_EXPRESSION_CONDITION_REGEX.test(escapedCollapsed);

    return isCondition ? `'!(${escapedCollapsed})'` : `'!{${escapedCollapsed}}'`;
  }
  return !collapsed;
}

function escapeForHtmlTemplate(condition: string) {
  return condition.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"');
}

results matching ""

    No results matching ""