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]
};
}