import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ProjectEditorFacade } from '../facade/project-editor.facade'; import { TranslatePipe } from '../../../i18n/translate.pipe'; import { TranslateService } from '../../../i18n/translate.service'; import { InputComponent } from '../../../shared/ui/input/input.component'; import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component'; import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component'; @Component({ selector: 'app-project-editor-widgets-section', standalone: true, imports: [FormsModule, TranslatePipe, InputComponent, SectionCardComponent, ToggleComponent], templateUrl: './widgets-section.component.html', styleUrls: ['./section.shared.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class ProjectEditorWidgetsSectionComponent { private readonly facade = inject(ProjectEditorFacade); private readonly translate = inject(TranslateService); readonly widgets = this.facade.homepageWidgets; readonly fieldError = (key: string): string | null => { const messageKey = this.facade.fieldError(key); return messageKey ? this.translate.t(messageKey) : null; }; updateWidget(widgetId: string, updater: (props: Record) => Record): void { this.facade.updateBootstrap(current => ({ ...current, pages: current.pages.map(page => ({ ...page, sections: page.sections.map(section => ({ ...section, widgets: section.widgets.map(widget => widget.id !== widgetId ? widget : ({ ...widget, props: updater(widget.props ?? {}) })) })) })) })); } updateProp(widgetId: string, key: string, value: unknown): void { this.updateWidget(widgetId, props => ({ ...props, [key]: value })); } private readonly jsonDrafts = signal>({}); private readonly jsonErrors = signal>({}); /** * While the textarea holds invalid JSON, keep showing the user's own draft * (not the last-committed props) so their in-progress edit isn't silently * overwritten on the next change-detection pass. */ widgetJsonValue(widgetId: string, props: Record): string { return this.jsonDrafts()[widgetId] ?? this.propsJson(props); } widgetJsonError(widgetId: string): string | null { return this.jsonErrors()[widgetId] ?? null; } updateJson(widgetId: string, raw: string): void { try { const parsed = JSON.parse(raw); this.updateWidget(widgetId, () => parsed); this.jsonDrafts.update(({ [widgetId]: _removed, ...rest }) => rest); this.jsonErrors.update(({ [widgetId]: _removed, ...rest }) => rest); } catch { this.jsonDrafts.update(drafts => ({ ...drafts, [widgetId]: raw })); this.jsonErrors.update(errors => ({ ...errors, [widgetId]: this.translate.t('builder.widgetJsonInvalid') })); } } propsJson(props: Record): string { return JSON.stringify(props ?? {}, null, 2); } }