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