File

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

Extends

PanelDefinition

Index

Properties

Properties

blocks
Type BuildingBlockConfiguration[]
i18nKey
Type string
style
Type StyleOfThePanel
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;
}

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,
    style: panelConfig.style || (isExpertApp ? StyleOfThePanel.Regular : StyleOfThePanel.Light),
    blocks: [blockConfig]
  };
}

results matching ""

    No results matching ""