@designcrowd/library.brand-page 5.19.1-id-based-items-2 → 5.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-Ruu5Az32.js → CreatedOnBrandCrowd-de-de-Ekmn0O3s.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-DGUJtvld.js → CreatedOnBrandCrowd-en-us-BjeXlKTS.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Dg-1uOfV.js → CreatedOnBrandCrowd-es-es-rpNINAMY.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CRKraZl8.js → CreatedOnBrandCrowd-fr-ca-BL4nSaLj.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CRKraZl8.js → CreatedOnBrandCrowd-fr-fr-BL4nSaLj.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-bZ9VlEAu.js → CreatedOnBrandCrowd-pt-br-K0r4FyYq.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-bZ9VlEAu.js → CreatedOnBrandCrowd-pt-pt-K0r4FyYq.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CJYfwZx8.js → CreatedOnDcom-de-de-0XYyEKYc.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-rzA1lVxH.js → CreatedOnDcom-en-us-E8hvwiMd.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-CNwGPjik.js → CreatedOnDcom-es-es-pRuCd7xX.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-DKw7OGIz.js → CreatedOnDcom-fr-ca-Bu4V3jpn.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-WzOEaAZG.js → CreatedOnDcom-fr-fr-CCcrGNz2.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DkualVhm.js → CreatedOnDcom-pt-br-1GTvaLKd.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-w4Nnsdzq.js → CreatedOnDcom-pt-pt-CTIYW10X.js} +1 -1
  15. package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +1 -1
  16. package/lib/src/brand-page/models/index.d.ts +1 -12
  17. package/lib/src/brand-page/models/index.js +3 -0
  18. package/lib/src/brand-page/utils/personalisation.util.js +72 -14
  19. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +1 -1
  20. package/lib/src/brand-page-maker/components/admin-background-settings/AdminBackgroundSettings.mixin.d.ts +6 -0
  21. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +12 -1
  22. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +2 -0
  23. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +1 -0
  24. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +1 -1
  25. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +1 -1
  26. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +1 -1
  27. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +1 -1
  28. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +1 -1
  29. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +1 -1
  30. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +2 -3
  31. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +16 -5
  32. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +0 -5
  33. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +6 -18
  34. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +1 -0
  35. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +3 -0
  36. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +3 -1
  37. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +5 -1
  38. package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +6 -0
  39. package/lib/src/brand-page-maker/stores/ui/ui-store.js +8 -0
  40. package/lib/src/clients/design-template-client.d.ts +3 -0
  41. package/lib/src/clients/models.d.ts +2 -0
  42. package/lib/src/css/library.brand-page-brandCrowd.min.css +19 -7
  43. package/lib/src/css/library.brand-page-brandPage.min.css +19 -7
  44. package/lib/src/css/library.brand-page-designCom.min.css +19 -7
  45. package/lib/src/css/library.brand-page-preview.min.css +19 -7
  46. package/lib/src/index.mjs +1 -1
  47. package/lib/src/{lib-Dy7MYyqz.js → lib-BfgUqu0B.js} +25060 -24791
  48. package/lib/src/metadata-builder/builders/footer-builder.js +44 -34
  49. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +68 -57
  50. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +93 -58
  51. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +99 -66
  52. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +17 -57
  53. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +85 -51
  54. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +74 -49
  55. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +4 -13
  56. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +48 -66
  57. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +1 -1
  58. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +1 -1
  59. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +7 -1
  60. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +9 -0
  61. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +1 -1
  62. package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +6 -0
  63. package/lib/src/shared/models.d.ts +2 -0
  64. package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +6 -0
  65. package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +6 -0
  66. package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +6 -0
  67. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +7 -3
  68. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +7 -3
  69. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -1
  70. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +7 -1
  71. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +10 -439
  72. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.js +2 -2
  73. package/lib/src/shared/template-settings/models.d.ts +1 -0
  74. package/lib/src/shared/template-settings/store/template-settings-store.d.ts +4 -0
  75. package/lib/src/shared/template-settings/store/template-settings-store.js +79 -11
  76. package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +6 -0
  77. package/lib/src/utils/minify-brand-page-metadata.d.ts +0 -2
  78. package/lib/src/utils/minify-brand-page-metadata.js +1 -8
  79. package/package.json +1 -1
  80. package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +1 -2
  81. package/src/brand-page/models/index.ts +4 -12
  82. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -0
  83. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +1 -0
  84. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +2 -0
  85. package/src/brand-page/utils/personalisation.util.test.ts +35 -0
  86. package/src/brand-page/utils/personalisation.util.ts +119 -23
  87. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +4 -0
  88. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +1 -0
  89. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +22 -8
  90. package/src/brand-page-maker/components/template-preview/TemplatePreview.vue +5 -10
  91. package/src/brand-page-maker/components/template-preview/TemplatePreviewAiBadge.vue +12 -0
  92. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +0 -6
  93. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +5 -0
  94. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +10 -0
  95. package/src/brand-page-maker/stores/ui/ui-store.ts +16 -0
  96. package/src/clients/design-template-client.ts +3 -0
  97. package/src/clients/models.ts +2 -0
  98. package/src/metadata-builder/builders/footer-builder.test.ts +0 -298
  99. package/src/metadata-builder/builders/footer-builder.ts +65 -56
  100. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +88 -62
  101. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +129 -66
  102. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +160 -105
  103. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +20 -65
  104. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +146 -83
  105. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +122 -70
  106. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +4 -13
  107. package/src/metadata-builder/parsers/section-tool-arguments.ts +48 -66
  108. package/src/shared/models.ts +6 -0
  109. package/src/shared/template-settings/components/TemplatesSettings.mixin.ts +2 -2
  110. package/src/shared/template-settings/models.ts +1 -0
  111. package/src/shared/template-settings/store/template-settings-store.ts +105 -18
  112. package/src/utils/minify-brand-page-metadata.ts +2 -14
  113. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +0 -54
  114. package/lib/src/metadata-builder/builders/item-id-utils.js +0 -96
  115. package/src/metadata-builder/builders/item-id-utils.test.ts +0 -184
  116. package/src/metadata-builder/builders/item-id-utils.ts +0 -117
@@ -142,15 +142,10 @@ export interface ProfileSectionArguments {
142
142
  }
143
143
 
144
144
  export interface TextSectionItem {
145
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
146
- item_id?: string;
147
- /**
148
- * Id of an existing item to position this item before (insert for new items,
149
- * move for existing ones). Use "__end__" for the end of the list. Omit to
150
- * leave an existing item in place / append a new item. Mirrors the
151
- * section-level move_before_content_id.
152
- */
153
- move_before_item_id?: string;
145
+ /** 0-based index of existing item to update in-place */
146
+ item_index?: number;
147
+ /** 0-based position to insert a new item at */
148
+ insert_at?: number;
154
149
  text?: RichTextField;
155
150
  text_style?: TextStyleArguments;
156
151
  }
@@ -164,8 +159,10 @@ export interface TextSectionArguments {
164
159
  /** Multiple text items (BrandSite) */
165
160
  items?: TextSectionItem[];
166
161
  _replace_items?: boolean;
167
- /** Stable ids of items to remove. Order-independent (no index shifting). */
168
- _remove_item_ids?: string[];
162
+ /** 0-based indices of items to remove */
163
+ _remove_item_indices?: number[];
164
+ /** New order as array of current 0-based indices */
165
+ _reorder_items?: number[];
169
166
  background_color?: string | null;
170
167
  }
171
168
 
@@ -175,15 +172,10 @@ export interface ImageSectionArguments {
175
172
  }
176
173
 
177
174
  export interface ImageGalleryItem {
178
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
179
- item_id?: string;
180
- /**
181
- * Id of an existing item to position this item before (insert for new items,
182
- * move for existing ones). Use "__end__" for the end of the list. Omit to
183
- * leave an existing item in place / append a new item. Mirrors the
184
- * section-level move_before_content_id.
185
- */
186
- move_before_item_id?: string;
175
+ /** 0-based index of existing item to update in-place */
176
+ item_index?: number;
177
+ /** 0-based position to insert a new item at */
178
+ insert_at?: number;
187
179
  image_url?: string;
188
180
  caption?: string;
189
181
  }
@@ -193,8 +185,10 @@ export interface ImageGallerySectionArguments {
193
185
  gallery_type?: 'grid' | 'carousel' | 'masonry';
194
186
  is_full_width?: boolean;
195
187
  _replace_items?: boolean;
196
- /** Stable ids of items to remove. Order-independent (no index shifting). */
197
- _remove_item_ids?: string[];
188
+ /** 0-based indices of items to remove */
189
+ _remove_item_indices?: number[];
190
+ /** New order as array of current 0-based indices */
191
+ _reorder_items?: number[];
198
192
  }
199
193
 
200
194
  export interface GeneralLinkSectionArguments {
@@ -262,15 +256,10 @@ export interface TiktokSectionArguments {
262
256
  }
263
257
 
264
258
  export interface SocialLink {
265
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
266
- item_id?: string;
267
- /**
268
- * Id of an existing item to position this item before (insert for new items,
269
- * move for existing ones). Use "__end__" for the end of the list. Omit to
270
- * leave an existing item in place / append a new item. Mirrors the
271
- * section-level move_before_content_id.
272
- */
273
- move_before_item_id?: string;
259
+ /** 0-based index of existing item to update in-place */
260
+ item_index?: number;
261
+ /** 0-based position to insert a new item at */
262
+ insert_at?: number;
274
263
  platform?:
275
264
  | 'facebook'
276
265
  | 'x'
@@ -291,8 +280,10 @@ export interface SocialLinksSectionArguments {
291
280
  /** Style for the icon container background (used when icon_style is 'square' or 'circle'). */
292
281
  icon_background_style?: TextStyleArguments;
293
282
  _replace_items?: boolean;
294
- /** Stable ids of items to remove. Order-independent (no index shifting). */
295
- _remove_item_ids?: string[];
283
+ /** 0-based indices of items to remove */
284
+ _remove_item_indices?: number[];
285
+ /** New order as array of current 0-based indices */
286
+ _reorder_items?: number[];
296
287
  }
297
288
 
298
289
  export interface MailingListSectionArguments {
@@ -380,15 +371,10 @@ export interface AboutSectionArguments {
380
371
  }
381
372
 
382
373
  export interface ImagesSectionItem {
383
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
384
- item_id?: string;
385
- /**
386
- * Id of an existing item to position this item before (insert for new items,
387
- * move for existing ones). Use "__end__" for the end of the list. Omit to
388
- * leave an existing item in place / append a new item. Mirrors the
389
- * section-level move_before_content_id.
390
- */
391
- move_before_item_id?: string;
374
+ /** 0-based index of existing item to update in-place */
375
+ item_index?: number;
376
+ /** 0-based position to insert a new item at */
377
+ insert_at?: number;
392
378
  image_url?: string;
393
379
  heading?: string;
394
380
  text?: string;
@@ -407,8 +393,10 @@ export interface ImagesSectionArguments {
407
393
  | 'center-card-aligned-image'
408
394
  | 'alternate-image';
409
395
  _replace_items?: boolean;
410
- /** Stable ids of items to remove. Order-independent (no index shifting). */
411
- _remove_item_ids?: string[];
396
+ /** 0-based indices of items to remove */
397
+ _remove_item_indices?: number[];
398
+ /** New order as array of current 0-based indices */
399
+ _reorder_items?: number[];
412
400
  title_style?: TextStyleArguments;
413
401
  description_style?: TextStyleArguments;
414
402
  heading_style?: TextStyleArguments;
@@ -421,15 +409,10 @@ export interface ImagesSectionArguments {
421
409
  }
422
410
 
423
411
  export interface TestimonialItem {
424
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
425
- item_id?: string;
426
- /**
427
- * Id of an existing item to position this item before (insert for new items,
428
- * move for existing ones). Use "__end__" for the end of the list. Omit to
429
- * leave an existing item in place / append a new item. Mirrors the
430
- * section-level move_before_content_id.
431
- */
432
- move_before_item_id?: string;
412
+ /** 0-based index of existing item to update in-place */
413
+ item_index?: number;
414
+ /** 0-based position to insert a new item at */
415
+ insert_at?: number;
433
416
  testimonial?: string;
434
417
  name?: string;
435
418
  position?: string;
@@ -443,8 +426,10 @@ export interface TestimonialSectionArguments {
443
426
  items?: TestimonialItem[];
444
427
  display_style?: 'centered' | 'big-image' | 'columns';
445
428
  _replace_items?: boolean;
446
- /** Stable ids of items to remove. Order-independent (no index shifting). */
447
- _remove_item_ids?: string[];
429
+ /** 0-based indices of items to remove */
430
+ _remove_item_indices?: number[];
431
+ /** New order as array of current 0-based indices */
432
+ _reorder_items?: number[];
448
433
  title_style?: TextStyleArguments;
449
434
  testimonial_style?: TextStyleArguments;
450
435
  name_style?: TextStyleArguments;
@@ -454,15 +439,10 @@ export interface TestimonialSectionArguments {
454
439
  }
455
440
 
456
441
  export interface ShapesSectionItem {
457
- /** Stable id of the existing item to update or move. Omit to ADD a new item. */
458
- item_id?: string;
459
- /**
460
- * Id of an existing item to position this item before (insert for new items,
461
- * move for existing ones). Use "__end__" for the end of the list. Omit to
462
- * leave an existing item in place / append a new item. Mirrors the
463
- * section-level move_before_content_id.
464
- */
465
- move_before_item_id?: string;
442
+ /** 0-based index of existing item to update in-place */
443
+ item_index?: number;
444
+ /** 0-based position to insert a new item at */
445
+ insert_at?: number;
466
446
  icon_image_url?: string;
467
447
  shape_url?: string;
468
448
  color_theme?: string;
@@ -482,8 +462,10 @@ export interface ShapesSectionArguments {
482
462
  | 'center-card-aligned-image'
483
463
  | 'alternate-image';
484
464
  _replace_items?: boolean;
485
- /** Stable ids of items to remove. Order-independent (no index shifting). */
486
- _remove_item_ids?: string[];
465
+ /** 0-based indices of items to remove */
466
+ _remove_item_indices?: number[];
467
+ /** New order as array of current 0-based indices */
468
+ _reorder_items?: number[];
487
469
  title_style?: TextStyleArguments;
488
470
  description_style?: TextStyleArguments;
489
471
  heading_style?: TextStyleArguments;
@@ -19,6 +19,12 @@ export interface UIRootState {
19
19
  thirdPartySettings: UIRootStateThirdPartySettings;
20
20
  personalisationData: UserPersonalisationContent[] | undefined;
21
21
  isPersonalisationEnabled: boolean;
22
+ // Pre-purchase AI content key used to personalise AI template previews when
23
+ // there is no post-purchase personalisationData (bp-5742).
24
+ userPromptContentKey: string | undefined;
25
+ // Controls visibility of the dedicated AI templates tab. Decoupled from personalisationData
26
+ // so the slug flow can personalise previews/apply without surfacing the AI tab (bp-5742).
27
+ showAiTemplatesTab: boolean;
22
28
  }
23
29
 
24
30
  export interface Endpoints {
@@ -60,7 +60,7 @@ export default defineComponent({
60
60
  templateCategories: computed(
61
61
  () => templateSettingsStore.templateCategories,
62
62
  ),
63
- personalisationData: computed(() => uiStore.personalisationData),
63
+ showAiTemplatesTab: computed(() => uiStore.showAiTemplatesTab),
64
64
  templateSettingsStore,
65
65
  uiStore,
66
66
  isDesignCom: computed(() => containerStore.isDesignCom),
@@ -155,7 +155,7 @@ export default defineComponent({
155
155
  },
156
156
 
157
157
  visibleTabs() {
158
- return this.personalisationData
158
+ return this.showAiTemplatesTab
159
159
  ? this.tabs
160
160
  : [...this.tabs].splice(
161
161
  0,
@@ -7,6 +7,7 @@ export interface TemplateSettingsTemplate {
7
7
  personalisationContentIndex?: number;
8
8
  brandPageModel: BrandPageModel;
9
9
  contentKey?: string;
10
+ usesUserPromptContentKey?: boolean;
10
11
  previewImageUrl?: string;
11
12
  }
12
13
 
@@ -14,6 +14,7 @@ import * as DesignTemplateClient from '../../../clients/design-template-client';
14
14
  import { type DesignTemplate } from '../../../clients/models';
15
15
  import {
16
16
  BP_DEFAULT_TEMPLATE_TYPE_NAME,
17
+ BP_BRAND_SITE_TEMPLATE_TYPE_NAME,
17
18
  SIDE_BAR_TAB_NAMES,
18
19
  } from '@brand-page-maker/constants';
19
20
  import { defineStore, storeToRefs } from 'pinia';
@@ -32,6 +33,7 @@ import {
32
33
  useUiEditableLogoStore,
33
34
  useUiLogoSettingsStore,
34
35
  } from '../../logo-settings/store';
36
+ import { useEventTracker } from '@brand-page-maker/composables/useEventTracker';
35
37
  import {
36
38
  templateSettingsTr,
37
39
  getCurrentLocale,
@@ -39,6 +41,17 @@ import {
39
41
  import { watch } from 'vue';
40
42
 
41
43
  export const DEFAULT_PAGE_SIZE = 6;
44
+
45
+ const POST_PURCHASE_PAGE_SECTION_BY_TAB: Record<string, string> = {
46
+ [TEMPLATE_TABS.all]: 'MakerPostPurchaseSidebarTemplatesAllTab',
47
+ [TEMPLATE_TABS.AI]: 'MakerPostPurchaseSidebarTemplatesAiTab',
48
+ [TEMPLATE_TABS.categories]: 'MakerPostPurchaseSidebarTemplatesCategoryTab',
49
+ };
50
+
51
+ const PRE_PURCHASE_PAGE_SECTION_BY_TAB: Record<string, string> = {
52
+ [TEMPLATE_TABS.all]: 'MakerPrePurchaseSidebarTemplatesAllTab',
53
+ [TEMPLATE_TABS.categories]: 'MakerPrePurchaseSidebarTemplatesCategoryTab',
54
+ };
42
55
  const LOAD_PAGE_RETRY_DELAY = 250;
43
56
 
44
57
  type SelectedTabType = {
@@ -66,6 +79,7 @@ export interface TemplatesSettingState {
66
79
  tabsWithTranslations: SelectedTabType[];
67
80
  logoChangeWatcherInitialized: boolean;
68
81
  initialSearchTerm: string | undefined;
82
+ aiTemplateKeywords: string | undefined;
69
83
  onSearchTermChanged: OnSearchTermChangedCallback | undefined;
70
84
  }
71
85
 
@@ -103,6 +117,7 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
103
117
  cachedTemplates: {},
104
118
  logoChangeWatcherInitialized: false,
105
119
  initialSearchTerm: undefined,
120
+ aiTemplateKeywords: undefined,
106
121
  onSearchTermChanged: undefined,
107
122
  };
108
123
  },
@@ -302,22 +317,60 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
302
317
  const editableLogoVariation =
303
318
  editableLogoStore.editableLogoVariations[logoVariation];
304
319
 
320
+ const isPrePurchase = brandPageContainerStore.isPrePurchase;
321
+ const isBrandSite =
322
+ this.templateType === BP_BRAND_SITE_TEMPLATE_TYPE_NAME;
323
+
324
+ // Pre-purchase has no logo draft for logo-based recommendation, so fall back to the seeded
325
+ // search term (the keyword from the URL/cookie - never the business name) so the All tab
326
+ // stays relevant instead of generic (bp-5454).
327
+ const effectiveSearchTerm =
328
+ !this.searchTerm &&
329
+ !this.selectedCategory &&
330
+ isPrePurchase &&
331
+ isBrandSite &&
332
+ this.selectedTab.key === TEMPLATE_TABS.all
333
+ ? this.initialSearchTerm
334
+ : this.searchTerm;
335
+
336
+ const pageSection = isBrandSite
337
+ ? (isPrePurchase
338
+ ? PRE_PURCHASE_PAGE_SECTION_BY_TAB
339
+ : POST_PURCHASE_PAGE_SECTION_BY_TAB)[this.selectedTab.key]
340
+ : undefined;
341
+
342
+ const pageName = isBrandSite
343
+ ? isPrePurchase
344
+ ? 'maker-pre-purchase'
345
+ : 'maker'
346
+ : undefined;
347
+
348
+ const baseParams = {
349
+ page: pageNo,
350
+ categoryName: this.selectedCategory,
351
+ searchTerm: effectiveSearchTerm,
352
+ keywords: this.aiTemplateKeywords,
353
+ logoDraftToken: logoSettingsStore.selectedLogoImage.logoDraftToken,
354
+ logoVariation: logoSettingsStore.selectedLogoImage.logoVariation,
355
+ selectedTemplateToken: designTemplateToken,
356
+ locale: currentLocale,
357
+ logoS3Token: !isByo ? editableLogoVariation.s3Token : undefined,
358
+ logoTemplateToken: isByo ? logoTemplateToken : undefined,
359
+ useLogoForRecommendedTemplates:
360
+ isBrandSite && this.selectedTab.key === TEMPLATE_TABS.all,
361
+ pageSection,
362
+ pageName,
363
+ };
364
+
365
+ // AI templates are interleaved server-side for the Brand Site All tab
366
+ // (bp-5306); the frontend just renders the returned order.
305
367
  const result =
306
368
  await designTemplateClient.getTemplateItemsByTemplateType(
307
369
  this.templateType,
308
370
  {
309
- page: pageNo,
371
+ ...baseParams,
310
372
  pageSize: DEFAULT_PAGE_SIZE,
311
- categoryName: this.selectedCategory,
312
- searchTerm: this.searchTerm,
313
- logoDraftToken:
314
- logoSettingsStore.selectedLogoImage.logoDraftToken,
315
- logoVariation: logoSettingsStore.selectedLogoImage.logoVariation,
316
373
  personalisable: this.selectedTab.key === TEMPLATE_TABS.AI,
317
- selectedTemplateToken: designTemplateToken,
318
- locale: currentLocale,
319
- logoS3Token: !isByo ? editableLogoVariation.s3Token : undefined,
320
- logoTemplateToken: isByo ? logoTemplateToken : undefined,
321
374
  },
322
375
  );
323
376
 
@@ -327,11 +380,27 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
327
380
 
328
381
  this._setPageCount({ pageCount: result.pageCount });
329
382
 
383
+ if (
384
+ pageNo === 1 &&
385
+ result.abTestValuesToBePushed &&
386
+ Object.keys(result.abTestValuesToBePushed).length > 0
387
+ ) {
388
+ const eventTracker = useEventTracker();
389
+
390
+ for (const [experimentName, experimentValue] of Object.entries(
391
+ result.abTestValuesToBePushed,
392
+ )) {
393
+ eventTracker.sendExperimentStartedEvent(
394
+ experimentName,
395
+ experimentValue,
396
+ );
397
+ }
398
+ }
399
+
330
400
  const templates = await Promise.all(
331
401
  result.paginatedItems.map(async (item) => {
332
402
  try {
333
403
  if (brandPageMakerUserStore.isAdmin) {
334
- // Pre-purchase: full template with brandPageModel for live rendering
335
404
  const template =
336
405
  await designTemplateClient.getTemplateByTemplateToken(
337
406
  item.designTemplateToken,
@@ -349,17 +418,24 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
349
418
  uiStore.personalisationData.length,
350
419
  );
351
420
 
352
- // Get the contentKey from the selected personalisation variation
353
- // This is needed for the backend preview URL to show the correct variation
354
- const selectedPersonalisation =
355
- uiStore.personalisationData[personalisationContentIndex];
356
-
357
- contentKey = selectedPersonalisation.contentKey;
421
+ contentKey =
422
+ uiStore.personalisationData[personalisationContentIndex]
423
+ .contentKey;
424
+ } else if (item.isPersonalisable) {
425
+ // Pre-purchase has no personalisationData; personalise AI
426
+ // template previews with the prompt content key (bp-5742).
427
+ contentKey = uiStore.userPromptContentKey;
358
428
  }
359
429
 
360
430
  return {
361
431
  id: item.designTemplateToken,
432
+ isPersonalisable: item.isPersonalisable,
362
433
  contentKey,
434
+ // A userPromptContentKey signals this design's real AI images live
435
+ // in the userPrompt bucket (post- or pre-purchase). The preview must
436
+ // then resolve via userPromptContentKey, not the regular bucket which
437
+ // holds placeholders post-purchase (bp-5454).
438
+ usesUserPromptContentKey: !!uiStore.userPromptContentKey,
363
439
  previewImageUrl: item.previewImageUrl,
364
440
  } as TemplateSettingsTemplate;
365
441
  }
@@ -374,7 +450,15 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
374
450
  (t): t is TemplateSettingsTemplate => t !== null,
375
451
  );
376
452
 
377
- this._addTemplates({ templates: validTemplates });
453
+ // The Categories tab must never surface AI templates or the AI badge
454
+ // (bp-5454 scenario 5). The badge is driven purely by isPersonalisable,
455
+ // so clear it for category results regardless of the template's catalog flag.
456
+ const displayTemplates =
457
+ this.selectedTab.key === TEMPLATE_TABS.categories
458
+ ? validTemplates.map((t) => ({ ...t, isPersonalisable: false }))
459
+ : validTemplates;
460
+
461
+ this._addTemplates({ templates: displayTemplates });
378
462
  this._setPageNoLoaded({ pageNoLoaded: pageNo });
379
463
  } finally {
380
464
  this._setIsBusy({ isBusy: false });
@@ -496,6 +580,9 @@ export const useUiTemplateSettingsStore = defineStore('uiTemplateSettings', {
496
580
  this.initialSearchTerm = payload.initialSearchTerm;
497
581
  this.onSearchTermChanged = payload.onSearchTermChanged;
498
582
  },
583
+ setAiTemplateKeywords(payload: { aiTemplateKeywords: string }) {
584
+ this.aiTemplateKeywords = payload.aiTemplateKeywords;
585
+ },
499
586
  notifySearchTermChanged(searchTerm: string | undefined) {
500
587
  this.onSearchTermChanged?.(searchTerm);
501
588
  },
@@ -15,12 +15,7 @@ import { useExternalRichTextEditor } from '../brand-page-maker/composables/rich-
15
15
  import type { Editor, JSONContent } from '@tiptap/vue-3';
16
16
  import { type AssetEntry, resolveLogoData } from './build-asset-manifest';
17
17
 
18
- type MinifiedFooterSocialLink = {
19
- position: number;
20
- id?: string;
21
- imageUrl: string;
22
- url: string;
23
- };
18
+ type MinifiedFooterSocialLink = { imageUrl: string; url: string };
24
19
 
25
20
  type MinifiedFooter = {
26
21
  displayStyle?: string;
@@ -297,7 +292,6 @@ const minifyContent = (
297
292
  ...(Array.isArray(c.items) && {
298
293
  items: c.items.map((item: any, index: number) => ({
299
294
  position: index + 1,
300
- ...(item.id && { id: item.id }),
301
295
  ...st('name', item.name, editor),
302
296
  ...st('jobTitle', item.position, editor),
303
297
  ...st('testimonials', item.testimonials, editor),
@@ -326,7 +320,6 @@ const minifyContent = (
326
320
  ...(Array.isArray(c.items) && {
327
321
  items: c.items.map((item: any, index: number) => ({
328
322
  position: index + 1,
329
- ...(item.id && { id: item.id }),
330
323
  ...includeImageWithKey(item.imageUrl),
331
324
  ...st('title', item.title, editor),
332
325
  ...st('paragraph', item.paragraph, editor),
@@ -357,7 +350,6 @@ const minifyContent = (
357
350
  ...(Array.isArray(c.textItems) && {
358
351
  textItems: c.textItems.map((item: any, index: number) => ({
359
352
  position: index + 1,
360
- ...(item.id && { id: item.id }),
361
353
  ...st('text', item.text, editor),
362
354
  ...(includeStyle(item.textStyle) && { textStyle: item.textStyle }),
363
355
  })),
@@ -447,7 +439,6 @@ const minifyContent = (
447
439
  ...(Array.isArray(c.links) && {
448
440
  links: c.links.map((link: any, index: number) => ({
449
441
  position: index + 1,
450
- ...(link.id && { id: link.id }),
451
442
  ...(includeString(link.url) && { url: link.url }),
452
443
  ...includeImageWithKey(link.imageUrl),
453
444
  })),
@@ -491,7 +482,6 @@ const minifyContent = (
491
482
  ...(Array.isArray(c.images) && {
492
483
  images: c.images.map((img: any, index: number) => ({
493
484
  position: index + 1,
494
- ...(img.id && { id: img.id }),
495
485
  ...includeImageWithKey(img.imageUrl),
496
486
  ...st('imageTitle', img.imageTitle, editor),
497
487
  })),
@@ -541,9 +531,7 @@ const minifyFooter = (
541
531
  logoImageWidth: footer.logoImageWidth || undefined,
542
532
  ...(logoAssetKey && { logoAssetKey }),
543
533
  socialLinks: footer.socialLinks?.length
544
- ? footer.socialLinks.map((l, index) => ({
545
- position: index + 1,
546
- ...(l.id && { id: l.id }),
534
+ ? footer.socialLinks.map((l) => ({
547
535
  imageUrl: l.imageUrl || '',
548
536
  url: l.url || '',
549
537
  }))
@@ -1,54 +0,0 @@
1
- import type { BrandPageModel } from '../../brand-page/models';
2
- /**
3
- * Sentinel anchor meaning "the end of the list", mirroring the section-level
4
- * `move_before_content_id: "__end__"` convention used in base-section-builder.
5
- */
6
- export declare const END_ITEM_ANCHOR = "__end__";
7
- /** Generate a stable item id. Mirrors section-level `uuid.v4()` id generation. */
8
- export declare const generateItemId: () => string;
9
- /**
10
- * Walk an entire BrandPageModel and backfill a stable uuid `id` onto every
11
- * nested array item that lacks one (gallery images, images/shapes/testimonial
12
- * items, text items, social links, and footer social links).
13
- *
14
- * BP-5603: this must run BEFORE the model is minified for the AI, so the same
15
- * id the AI is shown (and copies into `item_id`) is the one the executor later
16
- * matches. The maker holds a single live model instance for both the minify
17
- * and the apply step, so mutating it here makes the ids round-trip; they then
18
- * persist on the next save. Idempotent — existing ids are preserved.
19
- */
20
- export declare const ensureBrandPageItemIds: (model: BrandPageModel) => void;
21
- /**
22
- * Backfill ids onto any items missing one. Per BP-5603 this runs in the
23
- * metadata builder when a section is processed, so the persisted metadata
24
- * returned to the client gains stable ids that subsequent AI tool calls can
25
- * reference. Mutates in place.
26
- */
27
- export declare const ensureItemIds: <T extends {
28
- id?: string | undefined;
29
- }>(items: T[] | undefined) => void;
30
- /**
31
- * Remove items whose id appears in `ids`. Order-independent (no descending
32
- * index sort needed) — the whole point of the id-based migration. Items
33
- * without an id are never removed by id.
34
- */
35
- export declare const removeItemsByIds: <T extends {
36
- id?: string | undefined;
37
- }>(items: T[] | undefined, ids: string[] | undefined) => T[];
38
- /**
39
- * Position `item` within `items` relative to an anchor id — the item-level
40
- * mirror of base-section-builder's `moveContent`:
41
- *
42
- * - `moveBeforeItemId === undefined` → leave an existing item where it is;
43
- * append a new (not-yet-present) item to the end.
44
- * - `moveBeforeItemId === "__end__"` → move/insert at the very end.
45
- * - anchor id found → insert immediately before it.
46
- * - anchor id not found → fall through to the end (same
47
- * forgiving behaviour as `moveContent`).
48
- *
49
- * Mutates `items` in place. `item` may be an existing element of `items`
50
- * (a move) or a brand-new object (an insert).
51
- */
52
- export declare const repositionItemByAnchor: <T extends {
53
- id?: string | undefined;
54
- }>(items: T[], item: T, moveBeforeItemId: string | undefined) => void;
@@ -1,96 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
- /**
3
- * Sentinel anchor meaning "the end of the list", mirroring the section-level
4
- * `move_before_content_id: "__end__"` convention used in base-section-builder.
5
- */
6
- export const END_ITEM_ANCHOR = '__end__';
7
- /** Generate a stable item id. Mirrors section-level `uuid.v4()` id generation. */
8
- export const generateItemId = () => uuidv4();
9
- /**
10
- * Walk an entire BrandPageModel and backfill a stable uuid `id` onto every
11
- * nested array item that lacks one (gallery images, images/shapes/testimonial
12
- * items, text items, social links, and footer social links).
13
- *
14
- * BP-5603: this must run BEFORE the model is minified for the AI, so the same
15
- * id the AI is shown (and copies into `item_id`) is the one the executor later
16
- * matches. The maker holds a single live model instance for both the minify
17
- * and the apply step, so mutating it here makes the ids round-trip; they then
18
- * persist on the next save. Idempotent — existing ids are preserved.
19
- */
20
- export const ensureBrandPageItemIds = (model) => {
21
- const ensureContentItemIds = (content) => {
22
- // Every array field that the minifier surfaces as a list of items.
23
- for (const field of ['images', 'items', 'textItems', 'links']) {
24
- const arr = content[field];
25
- if (Array.isArray(arr))
26
- ensureItemIds(arr);
27
- }
28
- };
29
- for (const page of model.pages ?? []) {
30
- for (const content of page.contents ?? []) {
31
- ensureContentItemIds(content);
32
- }
33
- }
34
- ensureItemIds(model.pageFooterDesktop?.socialLinks);
35
- ensureItemIds(model.pageFooterMobile?.socialLinks);
36
- };
37
- /**
38
- * Backfill ids onto any items missing one. Per BP-5603 this runs in the
39
- * metadata builder when a section is processed, so the persisted metadata
40
- * returned to the client gains stable ids that subsequent AI tool calls can
41
- * reference. Mutates in place.
42
- */
43
- export const ensureItemIds = (items) => {
44
- if (!items)
45
- return;
46
- for (const item of items) {
47
- if (!item.id)
48
- item.id = generateItemId();
49
- }
50
- };
51
- /**
52
- * Remove items whose id appears in `ids`. Order-independent (no descending
53
- * index sort needed) — the whole point of the id-based migration. Items
54
- * without an id are never removed by id.
55
- */
56
- export const removeItemsByIds = (items, ids) => {
57
- if (!items?.length)
58
- return items ?? [];
59
- if (!ids?.length)
60
- return items;
61
- const toRemove = new Set(ids);
62
- return items.filter((item) => !item.id || !toRemove.has(item.id));
63
- };
64
- /**
65
- * Position `item` within `items` relative to an anchor id — the item-level
66
- * mirror of base-section-builder's `moveContent`:
67
- *
68
- * - `moveBeforeItemId === undefined` → leave an existing item where it is;
69
- * append a new (not-yet-present) item to the end.
70
- * - `moveBeforeItemId === "__end__"` → move/insert at the very end.
71
- * - anchor id found → insert immediately before it.
72
- * - anchor id not found → fall through to the end (same
73
- * forgiving behaviour as `moveContent`).
74
- *
75
- * Mutates `items` in place. `item` may be an existing element of `items`
76
- * (a move) or a brand-new object (an insert).
77
- */
78
- export const repositionItemByAnchor = (items, item, moveBeforeItemId) => {
79
- const currentIndex = items.indexOf(item);
80
- if (moveBeforeItemId === undefined) {
81
- if (currentIndex === -1)
82
- items.push(item);
83
- return;
84
- }
85
- if (currentIndex !== -1)
86
- items.splice(currentIndex, 1);
87
- const anchorIndex = moveBeforeItemId === END_ITEM_ANCHOR
88
- ? -1
89
- : items.findIndex((i) => i.id === moveBeforeItemId);
90
- if (anchorIndex !== -1) {
91
- items.splice(anchorIndex, 0, item);
92
- }
93
- else {
94
- items.push(item);
95
- }
96
- };