import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { NgComponentOutlet } from '@angular/common'; import { PageRenderModel } from '../../dynamic-renderer/page-renderer/page-renderer.model'; import { WidgetRenderNode } from '../../dynamic-renderer/widget-host/widget-host.model'; import { WidgetHostService } from '../../dynamic-renderer/widget-host/widget-host.service'; import { RuntimeDiagnosticsService } from '../../core/runtime/runtime-diagnostics.service'; import { UnknownWidgetComponent } from '../../widgets/ui'; import { ConfigService } from '../../core/config/config.service'; @Component({ selector: 'app-dynamic-page-layout', standalone: true, imports: [CommonModule, NgComponentOutlet], template: ` @if (model) {
@for (section of model.sections; track section.id) {
@for (widget of section.widgets; track widget.id) {
@if (resolveWidget(widget, section.id); as resolved) { }
}
}
} `, styles: [ ` .dynamic-page-layout { display: grid; gap: 1rem; } .dynamic-section { display: grid; gap: 1rem; } ` ], changeDetection: ChangeDetectionStrategy.OnPush }) export class DynamicPageLayoutComponent { @Input() model: PageRenderModel | null = null; private readonly loggedUnknownWidgets = new Set(); constructor( private readonly widgetHost: WidgetHostService, private readonly diagnostics: RuntimeDiagnosticsService, private readonly configService: ConfigService ) {} resolveWidget(widget: WidgetRenderNode, sectionId?: string) { const resolved = this.widgetHost.resolveWidget(widget.source); if (resolved) { return resolved; } const diagnosticKey = `${this.model?.id ?? 'unknown'}:${sectionId ?? 'unknown'}:${widget.id}`; if (!this.loggedUnknownWidgets.has(diagnosticKey)) { this.loggedUnknownWidgets.add(diagnosticKey); const tenantId = this.configService.getBootstrapSnapshot()?.tenant.id ?? 'unknown'; this.diagnostics.logUnknownWidget({ tenant: tenantId, page: this.model?.key ?? 'unknown', section: sectionId ?? 'unknown', widget: `${widget.type}@${widget.version}`, reason: 'not_registered_in_widget_registry' }); } return { type: widget.type, version: widget.version, component: UnknownWidgetComponent, props: { widgetType: widget.type, widgetVersion: widget.version } }; } }