@designcrowd/library.brand-page 6.2.1 → 6.2.2-live-preview-1

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 (123) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CtvPtYF6.js → CreatedOnBrandCrowd-de-de-Cn0M-eZ1.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-eGF-EjAX.js → CreatedOnBrandCrowd-en-us-DRLMSDZr.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CSemsOtT.js → CreatedOnBrandCrowd-es-es-BglTu73U.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DzylX-AT.js → CreatedOnBrandCrowd-fr-ca-Bj6YZ_el.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DzylX-AT.js → CreatedOnBrandCrowd-fr-fr-Bj6YZ_el.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-QMUxXQsE.js → CreatedOnBrandCrowd-pt-br-DEoOmQ1F.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-QMUxXQsE.js → CreatedOnBrandCrowd-pt-pt-DEoOmQ1F.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-M7OoJt-1.js → CreatedOnDcom-de-de-BZ7gTt4Z.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-D7hMM0AI.js → CreatedOnDcom-en-us-CD6Km5sH.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-CLw-BLp2.js → CreatedOnDcom-es-es-CNpZHvEl.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BRu_kmeu.js → CreatedOnDcom-fr-ca-URS-ydJ1.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-IMnEv_yM.js → CreatedOnDcom-fr-fr-D2AVeA3H.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-Bt6v1p27.js → CreatedOnDcom-pt-br-C32frnFs.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-BVsFJIJg.js → CreatedOnDcom-pt-pt-TDYGjRXS.js} +1 -1
  15. package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -2
  16. package/lib/src/brand-page/models/brand-page-context.model.js +0 -12
  17. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +0 -1
  18. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.js +0 -9
  19. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +7 -43
  20. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +6 -42
  21. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +0 -14
  22. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -42
  23. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +0 -42
  24. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +0 -42
  25. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +0 -42
  26. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +0 -42
  27. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +0 -42
  28. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +3 -0
  29. package/lib/src/brand-page-maker/composables/useEventTracker.js +10 -0
  30. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +0 -42
  31. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +2 -4
  32. package/lib/src/{bundled-translations-KY0aSmMp.js → bundled-translations-BYUfWBT3.js} +9 -9
  33. package/lib/src/{bundled-translations.de-DE-BIFi4bYj.js → bundled-translations.de-DE-MmzUu1tt.js} +13 -13
  34. package/lib/src/{bundled-translations.es-ES-CHlKuPLO.js → bundled-translations.es-ES-D-tG33XL.js} +1 -1
  35. package/lib/src/{bundled-translations.fr-CA-DR1fvm8e.js → bundled-translations.fr-CA-CNc1H1h3.js} +1 -1
  36. package/lib/src/{bundled-translations.fr-FR-DnpWVIQg.js → bundled-translations.fr-FR-DX0-yepT.js} +1 -1
  37. package/lib/src/{bundled-translations.pt-BR-p8szTOiT.js → bundled-translations.pt-BR-ih1-LRgy.js} +9 -9
  38. package/lib/src/{bundled-translations.pt-PT-DEk39NhW.js → bundled-translations.pt-PT-Dq2CmD1p.js} +1 -1
  39. package/lib/src/bundles/bundled-translations.de-DE.json +3 -1
  40. package/lib/src/bundles/bundled-translations.es-ES.json +3 -1
  41. package/lib/src/bundles/bundled-translations.fr-CA.json +3 -1
  42. package/lib/src/bundles/bundled-translations.fr-FR.json +3 -1
  43. package/lib/src/bundles/bundled-translations.json +3 -1
  44. package/lib/src/bundles/bundled-translations.pt-BR.json +3 -1
  45. package/lib/src/bundles/bundled-translations.pt-PT.json +3 -1
  46. package/lib/src/composables/useTranslate.d.ts +1 -1
  47. package/lib/src/composables/useTranslate.js +1 -1
  48. package/lib/src/css/library.brand-page-brandCrowd.min.css +4 -4
  49. package/lib/src/css/library.brand-page-brandPage.min.css +4 -4
  50. package/lib/src/css/library.brand-page-designCom.min.css +4 -4
  51. package/lib/src/css/library.brand-page-preview.min.css +4 -4
  52. package/lib/src/index.mjs +2 -2
  53. package/lib/src/{lib-B38VVCSw.js → lib-Dm9AuOJL.js} +16909 -16783
  54. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +2 -0
  55. package/lib/src/metadata-builder/builders/item-id-utils.js +8 -2
  56. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +8 -1
  57. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +6 -0
  58. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +1 -0
  59. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +5 -0
  60. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +5 -42
  61. package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.d.ts +9 -1
  62. package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.js +31 -9
  63. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -0
  64. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +5 -42
  65. package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -1
  66. package/lib/src/shared/page-navigation/store/page-navigation-store.js +22 -9
  67. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +7 -1
  68. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +10 -43
  69. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.js +12 -35
  70. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +6 -42
  71. package/lib/src/shared/page-settings/duplicate-page.util.d.ts +4 -0
  72. package/lib/src/shared/page-settings/duplicate-page.util.js +82 -0
  73. package/lib/src/shared/page-settings/models.d.ts +1 -0
  74. package/lib/src/shared/page-settings/store/page-settings-store.d.ts +5 -0
  75. package/lib/src/shared/page-settings/store/page-settings-store.js +90 -7
  76. package/lib/src/shared/page-settings/utils.d.ts +3 -0
  77. package/lib/src/shared/page-settings/utils.js +6 -0
  78. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -56
  79. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +0 -42
  80. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +1 -0
  81. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -42
  82. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -0
  83. package/lib/src/tracker.mjs +1 -1
  84. package/lib/src/{useEventTracker-C5Ql5p7u.js → useEventTracker-DwPTmzpa.js} +13 -3
  85. package/package.json +1 -1
  86. package/src/brand-page/models/brand-page-context.model.ts +0 -23
  87. package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -10
  88. package/src/brand-page-maker/components/action-bar/ActionBarNavigation.test.ts +8 -49
  89. package/src/brand-page-maker/components/action-bar/ActionBarNavigation.vue +1 -1
  90. package/src/brand-page-maker/composables/useEventTracker.ts +13 -0
  91. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +2 -4
  92. package/src/bundles/bundled-translations.de-DE.json +3 -1
  93. package/src/bundles/bundled-translations.es-ES.json +3 -1
  94. package/src/bundles/bundled-translations.fr-CA.json +3 -1
  95. package/src/bundles/bundled-translations.fr-FR.json +3 -1
  96. package/src/bundles/bundled-translations.json +3 -1
  97. package/src/bundles/bundled-translations.pt-BR.json +3 -1
  98. package/src/bundles/bundled-translations.pt-PT.json +3 -1
  99. package/src/composables/useTranslate.ts +2 -1
  100. package/src/metadata-builder/builders/item-id-utils.ts +9 -2
  101. package/src/shared/ecommerce-settings/components/product-config-field/ProductConfigOption.vue +0 -2
  102. package/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.ts +32 -10
  103. package/src/shared/page-navigation/store/page-navigation-store.ts +41 -13
  104. package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +3 -0
  105. package/src/shared/page-settings/components/page-settings/PageElement.vue +2 -0
  106. package/src/shared/page-settings/components/page-settings/PageSettingMenu.test.ts +105 -0
  107. package/src/shared/page-settings/components/page-settings/PageSettingMenu.vue +16 -0
  108. package/src/shared/page-settings/components/page-settings/PageSettings.duplicate.test.ts +108 -0
  109. package/src/shared/page-settings/components/page-settings/PageSettings.mixin.ts +22 -50
  110. package/src/shared/page-settings/components/page-settings/PageSettings.vue +1 -0
  111. package/src/shared/page-settings/components/page-settings/i18n/page-settings.de-DE.json +3 -1
  112. package/src/shared/page-settings/components/page-settings/i18n/page-settings.es-ES.json +3 -1
  113. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-CA.json +3 -1
  114. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-FR.json +3 -1
  115. package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +3 -1
  116. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-BR.json +3 -1
  117. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-PT.json +3 -1
  118. package/src/shared/page-settings/duplicate-page.util.test.ts +382 -0
  119. package/src/shared/page-settings/duplicate-page.util.ts +124 -0
  120. package/src/shared/page-settings/models.ts +1 -0
  121. package/src/shared/page-settings/store/page-settings-store.test.ts +441 -1
  122. package/src/shared/page-settings/store/page-settings-store.ts +145 -11
  123. package/src/shared/page-settings/utils.ts +10 -0
@@ -69,40 +69,6 @@ export default defineComponent({
69
69
  },
70
70
  });
71
71
  const isDraggingPage = ref(false);
72
- const syncPagesWithOrders = () => {
73
- const pageIdOrder = [];
74
- const collectPageIds = (orders) => {
75
- for (const order of orders) {
76
- const route = pageNavigationStore.routes.find((r) => r.id === order.routeId);
77
- if (route?.pageId) {
78
- pageIdOrder.push(route.pageId);
79
- }
80
- if (order.subRoutes && order.subRoutes.length > 0) {
81
- collectPageIds(order.subRoutes);
82
- }
83
- }
84
- };
85
- collectPageIds(pageNavigationStore.orders);
86
- if (!pageIdOrder.length) {
87
- return;
88
- }
89
- const pageMap = new Map();
90
- for (const page of pageSettingsStore.pages) {
91
- pageMap.set(page.id, page);
92
- }
93
- const orderedPages = [];
94
- for (const pageId of pageIdOrder) {
95
- const page = pageMap.get(pageId);
96
- if (page) {
97
- orderedPages.push(page);
98
- pageMap.delete(pageId);
99
- }
100
- }
101
- for (const remainingPage of pageMap.values()) {
102
- orderedPages.push(remainingPage);
103
- }
104
- pageSettingsStore.setPages({ pages: orderedPages });
105
- };
106
72
  const onDraggingStart = () => {
107
73
  isDraggingPage.value = true;
108
74
  };
@@ -117,7 +83,7 @@ export default defineComponent({
117
83
  }
118
84
  }
119
85
  }
120
- syncPagesWithOrders();
86
+ pageSettingsStore.syncPagesWithOrders();
121
87
  // Recalc the moved route's slug to ensure uniqueness at its new depth
122
88
  if (routeId) {
123
89
  pageNavigationStore.recalculateSlugForRoute(routeId, previousParentRouteId);
@@ -256,6 +222,17 @@ export default defineComponent({
256
222
  isNewPageModalVisible: true,
257
223
  });
258
224
  },
225
+ async onDuplicatePageSelected(pageId) {
226
+ const sourcePage = this.pageSettingsStore.pages.find((p) => p.id === pageId);
227
+ const isSubPage = Boolean(this.pageNavigationStore.getParentRouteIdForPage(pageId));
228
+ const newPage = await this.pageSettingsStore.duplicatePage({ pageId });
229
+ // Nothing was created - paywalled, or the page is gone. The upsell is
230
+ // already tracked through the bridge's cta_location.
231
+ if (!newPage || !sourcePage) {
232
+ return;
233
+ }
234
+ this.eventTracker.sendWebsiteManageDuplicatePageEvent(sourcePage.title, isSubPage);
235
+ },
259
236
  onChangePageVisibility(pageId) {
260
237
  const page = this.pageSettingsStore.pages.find((p) => p.id === pageId);
261
238
  const currentPageVisibility = Boolean(page?.isHidden);
@@ -3430,6 +3430,8 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
3430
3430
  setPages({ pages }: {
3431
3431
  pages: import("../../models").ContentPage[];
3432
3432
  }): void;
3433
+ syncPagesWithOrders(): void;
3434
+ showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
3433
3435
  setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
3434
3436
  isNewPageModalVisible: boolean;
3435
3437
  parentPageId?: string;
@@ -3479,6 +3481,9 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
3479
3481
  title: string;
3480
3482
  newPageParentId?: string;
3481
3483
  }): import("../../models").ContentPage;
3484
+ duplicatePage({ pageId, }: {
3485
+ pageId: string;
3486
+ }): Promise<import("../../models").ContentPage | undefined>;
3482
3487
  setSelectedSectionName({ sectionName, }: {
3483
3488
  sectionName?: import("../../store").PageSettingsSection;
3484
3489
  }): void;
@@ -4169,20 +4174,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
4169
4174
  isVariation: boolean;
4170
4175
  isExcluded: boolean;
4171
4176
  } | undefined;
4172
- bp_5873_live_preview_prepurchase?: {
4173
- key: string;
4174
- value: string;
4175
- isControl: boolean;
4176
- isVariation: boolean;
4177
- isExcluded: boolean;
4178
- } | undefined;
4179
- bp_5873_live_preview_postpurchase?: {
4180
- key: string;
4181
- value: string;
4182
- isControl: boolean;
4183
- isVariation: boolean;
4184
- isExcluded: boolean;
4185
- } | undefined;
4186
4177
  };
4187
4178
  isSubsequentPublish: boolean;
4188
4179
  isContextualUpgradeModalVisible: boolean;
@@ -4258,20 +4249,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
4258
4249
  isVariation: boolean;
4259
4250
  isExcluded: boolean;
4260
4251
  } | undefined;
4261
- bp_5873_live_preview_prepurchase?: {
4262
- key: string;
4263
- value: string;
4264
- isControl: boolean;
4265
- isVariation: boolean;
4266
- isExcluded: boolean;
4267
- } | undefined;
4268
- bp_5873_live_preview_postpurchase?: {
4269
- key: string;
4270
- value: string;
4271
- isControl: boolean;
4272
- isVariation: boolean;
4273
- isExcluded: boolean;
4274
- } | undefined;
4275
4252
  };
4276
4253
  isSubsequentPublish: boolean;
4277
4254
  isContextualUpgradeModalVisible: boolean;
@@ -4347,20 +4324,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
4347
4324
  isVariation: boolean;
4348
4325
  isExcluded: boolean;
4349
4326
  } | undefined;
4350
- bp_5873_live_preview_prepurchase?: {
4351
- key: string;
4352
- value: string;
4353
- isControl: boolean;
4354
- isVariation: boolean;
4355
- isExcluded: boolean;
4356
- } | undefined;
4357
- bp_5873_live_preview_postpurchase?: {
4358
- key: string;
4359
- value: string;
4360
- isControl: boolean;
4361
- isVariation: boolean;
4362
- isExcluded: boolean;
4363
- } | undefined;
4364
4327
  };
4365
4328
  isSubsequentPublish: boolean;
4366
4329
  isContextualUpgradeModalVisible: boolean;
@@ -4790,6 +4753,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
4790
4753
  fields: Record<string, ContentItemFieldSettings>;
4791
4754
  };
4792
4755
  }[];
4756
+ isHidden?: boolean | undefined;
4793
4757
  }[];
4794
4758
  }, {
4795
4759
  PAGE_SECTION_STYLE_OPTIONS: {
@@ -0,0 +1,4 @@
1
+ import type { Content } from '@brand-page';
2
+ import type { ContentItem } from '../content-settings/models';
3
+ export declare const getDuplicatePageTitle: (sourceTitle: string, siblingTitles: string[], isSubPage: boolean) => string;
4
+ export declare const duplicatePageContents: (contents: ContentItem<Content>[]) => ContentItem<Content>[];
@@ -0,0 +1,82 @@
1
+ import cloneDeep from 'lodash.clonedeep';
2
+ import { v4 as uuid } from 'uuid';
3
+ import { rotateMonetizationIds } from '../ecommerce-settings/util/refreshDuplicatedMetadata';
4
+ import { generateItemId, ITEM_ID_BEARING_FIELDS, } from '@src/metadata-builder/builders/item-id-utils';
5
+ import { pageSettingsTr } from '@src/composables/useTranslate';
6
+ import { MAX_PAGE_TITLE_LENGTH, MAX_SUBPAGE_TITLE_LENGTH } from './utils';
7
+ const composeTitle = (baseTitle, index) => {
8
+ const copyTitle = pageSettingsTr('duplicatePageTitle', {
9
+ title: baseTitle,
10
+ // A page title is text, not markup, and i18next escapes interpolated values
11
+ // by default - "Tom & Jerry" would come back as "Tom &amp; Jerry".
12
+ interpolation: { escapeValue: false },
13
+ }).trim();
14
+ return index > 1 ? `${copyTitle} ${index}` : copyTitle;
15
+ };
16
+ // The localised affix may sit either side of the title and is not a fixed
17
+ // length, so fit by trimming the base and re-composing rather than by slicing
18
+ // the finished string - that would cut the affix off in some locales.
19
+ const composeTitleWithinLimit = (baseTitle, index, maxLength) => {
20
+ const candidate = composeTitle(baseTitle, index);
21
+ const overflow = candidate.length - maxLength;
22
+ if (overflow <= 0) {
23
+ return candidate;
24
+ }
25
+ const trimmedBase = baseTitle
26
+ .slice(0, Math.max(0, baseTitle.length - overflow))
27
+ .trimEnd();
28
+ const trimmedCandidate = composeTitle(trimmedBase, index);
29
+ if (trimmedCandidate.length <= maxLength) {
30
+ return trimmedCandidate;
31
+ }
32
+ // A locale whose affix alone exceeds the limit: keep trimming the base rather
33
+ // than slicing the finished title, which would cut the trailing index and let
34
+ // two indexes collapse to the same name.
35
+ const affixLength = composeTitle('', index).length;
36
+ return composeTitle(baseTitle.slice(0, Math.max(0, maxLength - affixLength)).trimEnd(), index);
37
+ };
38
+ export const getDuplicatePageTitle = (sourceTitle, siblingTitles, isSubPage) => {
39
+ const maxLength = isSubPage
40
+ ? MAX_SUBPAGE_TITLE_LENGTH
41
+ : MAX_PAGE_TITLE_LENGTH;
42
+ const takenTitles = new Set(siblingTitles);
43
+ // N siblings block at most N candidates, so one of the first N+1 is free.
44
+ const lastIndex = siblingTitles.length + 1;
45
+ for (let index = 1; index < lastIndex; index += 1) {
46
+ const candidate = composeTitleWithinLimit(sourceTitle, index, maxLength);
47
+ if (!takenTitles.has(candidate)) {
48
+ return candidate;
49
+ }
50
+ }
51
+ return composeTitleWithinLimit(sourceTitle, lastIndex, maxLength);
52
+ };
53
+ // These ids are what the AI builder is shown and what its executor matches on
54
+ // (see ensureBrandPageItemIds), so a copy sharing them would put two identical
55
+ // ids in front of it.
56
+ const renewItemIds = (content) => {
57
+ const fields = content;
58
+ for (const field of ITEM_ID_BEARING_FIELDS) {
59
+ const items = fields[field];
60
+ if (!Array.isArray(items)) {
61
+ continue;
62
+ }
63
+ for (const item of items) {
64
+ if (item.id) {
65
+ item.id = generateItemId();
66
+ }
67
+ }
68
+ }
69
+ };
70
+ export const duplicatePageContents = (contents) => cloneDeep(contents).map((item) => {
71
+ // ContentItem.id is re-derived from content.id on every reload
72
+ // (content-settings.util: mapBrandPageContentToContentSettingsItem), so the
73
+ // two must be minted together or itemStates drifts until the next load.
74
+ const newId = uuid();
75
+ item.id = newId;
76
+ item.settings.content.id = newId;
77
+ // Ids only. The copy keeps the source's payment connection - same owner, so it should carry on
78
+ // taking payments; it is website duplication that disconnects.
79
+ rotateMonetizationIds(item.settings.content);
80
+ renewItemIds(item.settings.content);
81
+ return item;
82
+ });
@@ -6,6 +6,7 @@ export interface ContentPage {
6
6
  id: string;
7
7
  title: string;
8
8
  contents: ContentItem<Content>[];
9
+ isHidden?: boolean;
9
10
  }
10
11
  export declare const PAGE_SECTIONS: {
11
12
  readonly header: "header";
@@ -54,6 +54,8 @@ export declare const useUiPageSettingsStore: import("pinia").StoreDefinition<"ui
54
54
  setPages({ pages }: {
55
55
  pages: ContentPage[];
56
56
  }): void;
57
+ syncPagesWithOrders(): void;
58
+ showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
57
59
  setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
58
60
  isNewPageModalVisible: boolean;
59
61
  parentPageId?: string;
@@ -103,6 +105,9 @@ export declare const useUiPageSettingsStore: import("pinia").StoreDefinition<"ui
103
105
  title: string;
104
106
  newPageParentId?: string;
105
107
  }): ContentPage;
108
+ duplicatePage({ pageId, }: {
109
+ pageId: string;
110
+ }): Promise<ContentPage | undefined>;
106
111
  setSelectedSectionName({ sectionName, }: {
107
112
  sectionName?: PageSettingsSection;
108
113
  }): void;
@@ -2,6 +2,8 @@ import { defineStore } from 'pinia';
2
2
  import { PageHeaderDesktop, PageHeaderMobile, PageFooterDesktop, PageFooterMobile, VIEWPORTS, getNavigationType, } from '@brand-page';
3
3
  import { v4 as uuid } from 'uuid';
4
4
  import { PAGE_SECTIONS } from '../models';
5
+ import { toPageModels } from '../utils';
6
+ import { duplicatePageContents, getDuplicatePageTitle, } from '../duplicate-page.util';
5
7
  import { navigateToPageByPageId } from '@brand-page-maker/routing';
6
8
  import { getPageSlugFromPageTitle } from '@brand-page-maker/utils';
7
9
  import { useUiContentSettingsStore } from '../../content-settings/store';
@@ -78,16 +80,55 @@ export const useUiPageSettingsStore = defineStore('uiPageSettings', {
78
80
  setPages({ pages }) {
79
81
  this.pages = pages;
80
82
  },
81
- async setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }) {
83
+ // `orders` is the hierarchy; `pages` is a flat list that has to match it
84
+ // depth-first, because row indexes drive the home check and the hide/delete
85
+ // gating. Anything reshaping orders should re-derive pages after - except
86
+ // addNewPage, which predates this and appends by visibility instead; the
87
+ // next drag-end catches it up.
88
+ syncPagesWithOrders() {
89
+ const pageNavigationStore = usePageNavigationStore();
90
+ const pageIdOrder = [];
91
+ const collectPageIds = (orders) => {
92
+ for (const order of orders) {
93
+ const route = pageNavigationStore.routes.find((r) => r.id === order.routeId);
94
+ if (route?.pageId) {
95
+ pageIdOrder.push(route.pageId);
96
+ }
97
+ if (order.subRoutes?.length) {
98
+ collectPageIds(order.subRoutes);
99
+ }
100
+ }
101
+ };
102
+ collectPageIds(pageNavigationStore.orders);
103
+ if (!pageIdOrder.length) {
104
+ return;
105
+ }
106
+ const pageMap = new Map(this.pages.map((page) => [page.id, page]));
107
+ const orderedPages = [];
108
+ for (const pageId of pageIdOrder) {
109
+ const page = pageMap.get(pageId);
110
+ if (page) {
111
+ orderedPages.push(page);
112
+ pageMap.delete(pageId);
113
+ }
114
+ }
115
+ // A page with no route would otherwise vanish.
116
+ orderedPages.push(...pageMap.values());
117
+ this.setPages({ pages: orderedPages });
118
+ },
119
+ async showPageLimitUpgradeModalAsync(ctaLocation) {
82
120
  const uiStore = useUiStore();
83
121
  const eventTracker = useEventTracker();
122
+ eventTracker.setUpsellBridgeState({
123
+ cta_location: ctaLocation,
124
+ trigger_type: TRIGGER_TYPE.AUTOMATIC,
125
+ content_type: null,
126
+ });
127
+ await uiStore.showUpgradeModalAndSaveAsync(WEBSITE_UPGRADE_CONTEXT_TYPES.AddPage);
128
+ },
129
+ async setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }) {
84
130
  if (isNewPageModalVisible && !this.canAddPage) {
85
- eventTracker.setUpsellBridgeState({
86
- cta_location: UPGRADE_CTA_LOCATIONS.ADD_NEW_PAGE,
87
- trigger_type: TRIGGER_TYPE.AUTOMATIC,
88
- content_type: null,
89
- });
90
- await uiStore.showUpgradeModalAndSaveAsync(WEBSITE_UPGRADE_CONTEXT_TYPES.AddPage);
131
+ await this.showPageLimitUpgradeModalAsync(UPGRADE_CTA_LOCATIONS.ADD_NEW_PAGE);
91
132
  return;
92
133
  }
93
134
  this.isNewPageModalVisible = isNewPageModalVisible;
@@ -231,6 +272,48 @@ export const useUiPageSettingsStore = defineStore('uiPageSettings', {
231
272
  }
232
273
  return newPage;
233
274
  },
275
+ async duplicatePage({ pageId, }) {
276
+ const pageNavigationStore = usePageNavigationStore();
277
+ // Gate first: a paywalled duplicate must not leave a half-created page.
278
+ if (!this.canAddPage) {
279
+ await this.showPageLimitUpgradeModalAsync(UPGRADE_CTA_LOCATIONS.DUPLICATE_PAGE);
280
+ return undefined;
281
+ }
282
+ const sourcePage = this.pages.find((page) => page.id === pageId);
283
+ const sourceRoute = pageNavigationStore.routes.find((route) => route.pageId === pageId);
284
+ if (!sourcePage || !sourceRoute) {
285
+ return undefined;
286
+ }
287
+ const parentRouteId = pageNavigationStore.getParentRouteIdForPage(pageId);
288
+ const title = getDuplicatePageTitle(sourcePage.title, pageNavigationStore.getDisplayNamesForDepth(parentRouteId), Boolean(parentRouteId));
289
+ const newPage = {
290
+ id: uuid(),
291
+ title,
292
+ contents: duplicatePageContents(sourcePage.contents),
293
+ // A copy of a hidden page stays hidden, or it publishes into the nav.
294
+ isHidden: sourcePage.isHidden,
295
+ };
296
+ // addNavRoute navigates to the copy and reads its contents, so the page
297
+ // has to exist before the route does. syncPagesWithOrders places it.
298
+ this.setPages({ pages: [...this.pages, newPage] });
299
+ const newRoute = {
300
+ id: uuid(),
301
+ pageId: newPage.id,
302
+ pageSlug: getPageSlugFromPageTitle(title, pageNavigationStore.getPageSlugsForDepth(parentRouteId)),
303
+ isHidden: Boolean(sourceRoute.isHidden),
304
+ displayName: title,
305
+ };
306
+ pageNavigationStore.addNavRoute(newRoute, parentRouteId, sourceRoute.id);
307
+ this.syncPagesWithOrders();
308
+ // shopConfig.products is DERIVED from the shop blocks, not updated with
309
+ // them - without this the copy's fresh product ids never reach the save.
310
+ const shopConfigStore = useShopConfigStore();
311
+ shopConfigStore.initialiseShopStore({
312
+ paymentConfig: shopConfigStore.paymentConfig,
313
+ pages: toPageModels(this.pages),
314
+ });
315
+ return newPage;
316
+ },
234
317
  setSelectedSectionName({ sectionName, }) {
235
318
  this.selectedSectionName = sectionName;
236
319
  if (!sectionName) {
@@ -1,3 +1,6 @@
1
+ import type { PageModel } from '@brand-page';
2
+ import type { ContentPage } from './models';
1
3
  export declare const MAX_PAGE_TITLE_LENGTH = 30;
2
4
  export declare const MAX_SUBPAGE_TITLE_LENGTH = 20;
5
+ export declare const toPageModels: (pages: ContentPage[]) => PageModel[];
3
6
  export declare const getPageTitleError: (pageTitle: string, isSubPage: boolean) => string | null;
@@ -1,6 +1,12 @@
1
1
  import { errorTr } from '@src/composables/useTranslate';
2
2
  export const MAX_PAGE_TITLE_LENGTH = 30;
3
3
  export const MAX_SUBPAGE_TITLE_LENGTH = 20;
4
+ // The maker holds a page's sections as ContentItems (settings wrapper + content);
5
+ // everything that consumes a BrandPageModel wants the bare content.
6
+ export const toPageModels = (pages) => pages.map((page) => ({
7
+ ...page,
8
+ contents: page.contents.map((item) => item.settings.content),
9
+ }));
4
10
  export const getPageTitleError = (pageTitle, isSubPage) => {
5
11
  if (!pageTitle) {
6
12
  return errorTr('pageTitleError');
@@ -70,20 +70,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
70
70
  isVariation: boolean;
71
71
  isExcluded: boolean;
72
72
  } | undefined;
73
- bp_5873_live_preview_prepurchase?: {
74
- key: string;
75
- value: string;
76
- isControl: boolean;
77
- isVariation: boolean;
78
- isExcluded: boolean;
79
- } | undefined;
80
- bp_5873_live_preview_postpurchase?: {
81
- key: string;
82
- value: string;
83
- isControl: boolean;
84
- isVariation: boolean;
85
- isExcluded: boolean;
86
- } | undefined;
87
73
  }>;
88
74
  brandPageType: import("vue").ComputedRef<"default" | "brandSite" | "brandContact">;
89
75
  isSubsequentPublish: import("vue").ComputedRef<boolean>;
@@ -301,20 +287,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
301
287
  isVariation: boolean;
302
288
  isExcluded: boolean;
303
289
  } | undefined;
304
- bp_5873_live_preview_prepurchase?: {
305
- key: string;
306
- value: string;
307
- isControl: boolean;
308
- isVariation: boolean;
309
- isExcluded: boolean;
310
- } | undefined;
311
- bp_5873_live_preview_postpurchase?: {
312
- key: string;
313
- value: string;
314
- isControl: boolean;
315
- isVariation: boolean;
316
- isExcluded: boolean;
317
- } | undefined;
318
290
  };
319
291
  isSubsequentPublish: boolean;
320
292
  isContextualUpgradeModalVisible: boolean;
@@ -390,20 +362,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
390
362
  isVariation: boolean;
391
363
  isExcluded: boolean;
392
364
  } | undefined;
393
- bp_5873_live_preview_prepurchase?: {
394
- key: string;
395
- value: string;
396
- isControl: boolean;
397
- isVariation: boolean;
398
- isExcluded: boolean;
399
- } | undefined;
400
- bp_5873_live_preview_postpurchase?: {
401
- key: string;
402
- value: string;
403
- isControl: boolean;
404
- isVariation: boolean;
405
- isExcluded: boolean;
406
- } | undefined;
407
365
  };
408
366
  isSubsequentPublish: boolean;
409
367
  isContextualUpgradeModalVisible: boolean;
@@ -479,20 +437,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
479
437
  isVariation: boolean;
480
438
  isExcluded: boolean;
481
439
  } | undefined;
482
- bp_5873_live_preview_prepurchase?: {
483
- key: string;
484
- value: string;
485
- isControl: boolean;
486
- isVariation: boolean;
487
- isExcluded: boolean;
488
- } | undefined;
489
- bp_5873_live_preview_postpurchase?: {
490
- key: string;
491
- value: string;
492
- isControl: boolean;
493
- isVariation: boolean;
494
- isExcluded: boolean;
495
- } | undefined;
496
440
  };
497
441
  isSubsequentPublish: boolean;
498
442
  isContextualUpgradeModalVisible: boolean;
@@ -650,6 +594,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
650
594
  sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
651
595
  sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
652
596
  sendWebsiteManageReorderPageEvent: (pageName: string) => void;
597
+ sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
653
598
  sendPublishOptionsForFreeEvent: () => void;
654
599
  sendPublishOptionsForPremiumEvent: () => void;
655
600
  sendPublishOptionsForExitEvent: () => void;
@@ -84,20 +84,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
84
84
  isVariation: boolean;
85
85
  isExcluded: boolean;
86
86
  } | undefined;
87
- bp_5873_live_preview_prepurchase?: {
88
- key: string;
89
- value: string;
90
- isControl: boolean;
91
- isVariation: boolean;
92
- isExcluded: boolean;
93
- } | undefined;
94
- bp_5873_live_preview_postpurchase?: {
95
- key: string;
96
- value: string;
97
- isControl: boolean;
98
- isVariation: boolean;
99
- isExcluded: boolean;
100
- } | undefined;
101
87
  };
102
88
  isSubsequentPublish: boolean;
103
89
  isContextualUpgradeModalVisible: boolean;
@@ -173,20 +159,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
173
159
  isVariation: boolean;
174
160
  isExcluded: boolean;
175
161
  } | undefined;
176
- bp_5873_live_preview_prepurchase?: {
177
- key: string;
178
- value: string;
179
- isControl: boolean;
180
- isVariation: boolean;
181
- isExcluded: boolean;
182
- } | undefined;
183
- bp_5873_live_preview_postpurchase?: {
184
- key: string;
185
- value: string;
186
- isControl: boolean;
187
- isVariation: boolean;
188
- isExcluded: boolean;
189
- } | undefined;
190
162
  };
191
163
  isSubsequentPublish: boolean;
192
164
  isContextualUpgradeModalVisible: boolean;
@@ -262,20 +234,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
262
234
  isVariation: boolean;
263
235
  isExcluded: boolean;
264
236
  } | undefined;
265
- bp_5873_live_preview_prepurchase?: {
266
- key: string;
267
- value: string;
268
- isControl: boolean;
269
- isVariation: boolean;
270
- isExcluded: boolean;
271
- } | undefined;
272
- bp_5873_live_preview_postpurchase?: {
273
- key: string;
274
- value: string;
275
- isControl: boolean;
276
- isVariation: boolean;
277
- isExcluded: boolean;
278
- } | undefined;
279
237
  };
280
238
  isSubsequentPublish: boolean;
281
239
  isContextualUpgradeModalVisible: boolean;
@@ -60,6 +60,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
60
60
  sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
61
61
  sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
62
62
  sendWebsiteManageReorderPageEvent: (pageName: string) => void;
63
+ sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
63
64
  sendPublishOptionsForFreeEvent: () => void;
64
65
  sendPublishOptionsForPremiumEvent: () => void;
65
66
  sendPublishOptionsForExitEvent: () => void;
@@ -592,20 +592,6 @@ declare const _default: import("vue").DefineComponent<{}, {
592
592
  isVariation: boolean;
593
593
  isExcluded: boolean;
594
594
  } | undefined;
595
- bp_5873_live_preview_prepurchase?: {
596
- key: string;
597
- value: string;
598
- isControl: boolean;
599
- isVariation: boolean;
600
- isExcluded: boolean;
601
- } | undefined;
602
- bp_5873_live_preview_postpurchase?: {
603
- key: string;
604
- value: string;
605
- isControl: boolean;
606
- isVariation: boolean;
607
- isExcluded: boolean;
608
- } | undefined;
609
595
  };
610
596
  isSubsequentPublish: boolean;
611
597
  isContextualUpgradeModalVisible: boolean;
@@ -681,20 +667,6 @@ declare const _default: import("vue").DefineComponent<{}, {
681
667
  isVariation: boolean;
682
668
  isExcluded: boolean;
683
669
  } | undefined;
684
- bp_5873_live_preview_prepurchase?: {
685
- key: string;
686
- value: string;
687
- isControl: boolean;
688
- isVariation: boolean;
689
- isExcluded: boolean;
690
- } | undefined;
691
- bp_5873_live_preview_postpurchase?: {
692
- key: string;
693
- value: string;
694
- isControl: boolean;
695
- isVariation: boolean;
696
- isExcluded: boolean;
697
- } | undefined;
698
670
  };
699
671
  isSubsequentPublish: boolean;
700
672
  isContextualUpgradeModalVisible: boolean;
@@ -770,20 +742,6 @@ declare const _default: import("vue").DefineComponent<{}, {
770
742
  isVariation: boolean;
771
743
  isExcluded: boolean;
772
744
  } | undefined;
773
- bp_5873_live_preview_prepurchase?: {
774
- key: string;
775
- value: string;
776
- isControl: boolean;
777
- isVariation: boolean;
778
- isExcluded: boolean;
779
- } | undefined;
780
- bp_5873_live_preview_postpurchase?: {
781
- key: string;
782
- value: string;
783
- isControl: boolean;
784
- isVariation: boolean;
785
- isExcluded: boolean;
786
- } | undefined;
787
745
  };
788
746
  isSubsequentPublish: boolean;
789
747
  isContextualUpgradeModalVisible: boolean;
@@ -994,6 +952,7 @@ declare const _default: import("vue").DefineComponent<{}, {
994
952
  sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
995
953
  sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
996
954
  sendWebsiteManageReorderPageEvent: (pageName: string) => void;
955
+ sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
997
956
  sendPublishOptionsForFreeEvent: () => void;
998
957
  sendPublishOptionsForPremiumEvent: () => void;
999
958
  sendPublishOptionsForExitEvent: () => void;
@@ -175,6 +175,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
175
175
  sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
176
176
  sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
177
177
  sendWebsiteManageReorderPageEvent: (pageName: string) => void;
178
+ sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
178
179
  sendPublishOptionsForFreeEvent: () => void;
179
180
  sendPublishOptionsForPremiumEvent: () => void;
180
181
  sendPublishOptionsForExitEvent: () => void;
@@ -1,4 +1,4 @@
1
- import { A as N, E as O, G as _, P as I, a as A, b as a, c as S, S as s, T as C, U as L, u as P } from "./useEventTracker-C5Ql5p7u.js";
1
+ import { A as N, E as O, G as _, P as I, a as A, b as a, c as S, S as s, T as C, U as L, u as P } from "./useEventTracker-DwPTmzpa.js";
2
2
  export {
3
3
  N as AI_CTA_LOCATIONS,
4
4
  O as EVENTS,