diff --git a/src/app/features/project-editor/sections/homepage-section.component.ts b/src/app/features/project-editor/sections/homepage-section.component.ts index 45ecae0..440fb67 100644 --- a/src/app/features/project-editor/sections/homepage-section.component.ts +++ b/src/app/features/project-editor/sections/homepage-section.component.ts @@ -44,7 +44,7 @@ export class ProjectEditorHomepageSectionComponent { return this.layoutStrategyOptions.find(option => option.value === strategy)?.descKey ?? ''; } - drop(event: CdkDragDrop): void { + drop(event: CdkDragDrop): void { const sections = [...this.sections()]; moveItemInArray(sections, event.previousIndex, event.currentIndex); this.facade.updateBootstrap(current => ({ diff --git a/src/app/features/project-editor/sections/widgets-section.component.html b/src/app/features/project-editor/sections/widgets-section.component.html index ff7e3dc..187f479 100644 --- a/src/app/features/project-editor/sections/widgets-section.component.html +++ b/src/app/features/project-editor/sections/widgets-section.component.html @@ -36,7 +36,10 @@ } } diff --git a/src/app/features/project-editor/sections/widgets-section.component.ts b/src/app/features/project-editor/sections/widgets-section.component.ts index b9a2bdc..4add87c 100644 --- a/src/app/features/project-editor/sections/widgets-section.component.ts +++ b/src/app/features/project-editor/sections/widgets-section.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +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'; @@ -44,11 +44,31 @@ export class ProjectEditorWidgetsSectionComponent { 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 { - this.updateWidget(widgetId, () => JSON.parse(raw)); + const parsed = JSON.parse(raw); + this.updateWidget(widgetId, () => parsed); + this.jsonDrafts.update(({ [widgetId]: _removed, ...rest }) => rest); + this.jsonErrors.update(({ [widgetId]: _removed, ...rest }) => rest); } catch { - // Ignore malformed draft until valid JSON is provided. + this.jsonDrafts.update(drafts => ({ ...drafts, [widgetId]: raw })); + this.jsonErrors.update(errors => ({ ...errors, [widgetId]: this.translate.t('builder.widgetJsonInvalid') })); } } diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts index 971ca6a..f3833ee 100644 --- a/src/app/i18n/en.ts +++ b/src/app/i18n/en.ts @@ -658,6 +658,7 @@ export const en: Translations = { widgetShowRatingDesc: 'Show the star rating on product cards.', widgetShowPriceDesc: 'Show the price on product cards.', widgetJsonDesc: 'Raw widget properties as JSON. Used when no dedicated editor exists for this widget type.', + widgetJsonInvalid: 'Invalid JSON - the last valid version is still saved.', wishlistFeatureDesc: 'Let shoppers save products to a wishlist.', compareFeatureDesc: 'Let shoppers compare multiple products side by side.', reviewsFeatureDesc: 'Let shoppers leave and read product reviews.', diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts index 2a764f5..19d3b2a 100644 --- a/src/app/i18n/hy.ts +++ b/src/app/i18n/hy.ts @@ -658,6 +658,7 @@ export const hy: Translations = { widgetShowRatingDesc: 'Ցույց տալ աստղանիշ վարկանիշը ապրանքի քարտերի վրա։', widgetShowPriceDesc: 'Ցույց տալ գինը ապրանքի քարտերի վրա։', widgetJsonDesc: 'Վիջեթի հատկությունները՝ հում JSON ձևաչափով։ Օգտագործվում է, երբ այս վիջեթի տեսակի համար հատուկ խմբագիր չկա։', + widgetJsonInvalid: 'Սխալ JSON՝ պահպանված է վերջին վավեր տարբերակը։', wishlistFeatureDesc: 'Թույլ տալ գնորդներին ապրանքներ պահել ընտրյալներում։', compareFeatureDesc: 'Թույլ տալ գնորդներին համեմատել մի քանի ապրանք միաժամանակ։', reviewsFeatureDesc: 'Թույլ տալ գնորդներին թողնել և կարդալ ապրանքի կարծիքներ։', diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts index cfadaee..11cbc7f 100644 --- a/src/app/i18n/ru.ts +++ b/src/app/i18n/ru.ts @@ -658,6 +658,7 @@ export const ru: Translations = { widgetShowRatingDesc: 'Показывать рейтинг звёздами на карточках товаров.', widgetShowPriceDesc: 'Показывать цену на карточках товаров.', widgetJsonDesc: 'Сырые свойства виджета в формате JSON. Используется, если для этого типа виджета нет отдельного редактора.', + widgetJsonInvalid: 'Некорректный JSON — сохранена последняя корректная версия.', wishlistFeatureDesc: 'Разрешить покупателям сохранять товары в избранное.', compareFeatureDesc: 'Разрешить покупателям сравнивать несколько товаров одновременно.', reviewsFeatureDesc: 'Разрешить покупателям оставлять и читать отзывы о товарах.', diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts index a2ef5c1..98bbd3b 100644 --- a/src/app/i18n/translations.ts +++ b/src/app/i18n/translations.ts @@ -656,6 +656,7 @@ export interface Translations { widgetShowRatingDesc: string; widgetShowPriceDesc: string; widgetJsonDesc: string; + widgetJsonInvalid: string; wishlistFeatureDesc: string; compareFeatureDesc: string; reviewsFeatureDesc: string;