@designcrowd/library.brand-page 6.2.1 → 6.2.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 (106) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CtvPtYF6.js → CreatedOnBrandCrowd-de-de-7P2gNr-R.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-eGF-EjAX.js → CreatedOnBrandCrowd-en-us-Dl7BymW6.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CSemsOtT.js → CreatedOnBrandCrowd-es-es-CO4FvWVw.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DzylX-AT.js → CreatedOnBrandCrowd-fr-ca-DHNqakQu.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DzylX-AT.js → CreatedOnBrandCrowd-fr-fr-DHNqakQu.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-QMUxXQsE.js → CreatedOnBrandCrowd-pt-br-CGUC-ZbP.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-QMUxXQsE.js → CreatedOnBrandCrowd-pt-pt-CGUC-ZbP.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-M7OoJt-1.js → CreatedOnDcom-de-de-Dvds-Vmy.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-D7hMM0AI.js → CreatedOnDcom-en-us-fd3C-8Tp.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-CLw-BLp2.js → CreatedOnDcom-es-es-DTXZW35-.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BRu_kmeu.js → CreatedOnDcom-fr-ca-DRCYOKPs.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-IMnEv_yM.js → CreatedOnDcom-fr-fr-DpIB8x2x.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-Bt6v1p27.js → CreatedOnDcom-pt-br-Esnxy8wX.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-BVsFJIJg.js → CreatedOnDcom-pt-pt-CaiVHFXR.js} +1 -1
  15. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +7 -1
  16. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +6 -0
  17. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +3 -0
  18. package/lib/src/brand-page-maker/composables/useEventTracker.js +10 -0
  19. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +2 -4
  20. package/lib/src/{bundled-translations-KY0aSmMp.js → bundled-translations-BYUfWBT3.js} +9 -9
  21. package/lib/src/{bundled-translations.de-DE-BIFi4bYj.js → bundled-translations.de-DE-MmzUu1tt.js} +13 -13
  22. package/lib/src/{bundled-translations.es-ES-CHlKuPLO.js → bundled-translations.es-ES-D-tG33XL.js} +1 -1
  23. package/lib/src/{bundled-translations.fr-CA-DR1fvm8e.js → bundled-translations.fr-CA-CNc1H1h3.js} +1 -1
  24. package/lib/src/{bundled-translations.fr-FR-DnpWVIQg.js → bundled-translations.fr-FR-DX0-yepT.js} +1 -1
  25. package/lib/src/{bundled-translations.pt-BR-p8szTOiT.js → bundled-translations.pt-BR-ih1-LRgy.js} +9 -9
  26. package/lib/src/{bundled-translations.pt-PT-DEk39NhW.js → bundled-translations.pt-PT-Dq2CmD1p.js} +1 -1
  27. package/lib/src/bundles/bundled-translations.de-DE.json +3 -1
  28. package/lib/src/bundles/bundled-translations.es-ES.json +3 -1
  29. package/lib/src/bundles/bundled-translations.fr-CA.json +3 -1
  30. package/lib/src/bundles/bundled-translations.fr-FR.json +3 -1
  31. package/lib/src/bundles/bundled-translations.json +3 -1
  32. package/lib/src/bundles/bundled-translations.pt-BR.json +3 -1
  33. package/lib/src/bundles/bundled-translations.pt-PT.json +3 -1
  34. package/lib/src/composables/useTranslate.d.ts +1 -1
  35. package/lib/src/composables/useTranslate.js +1 -1
  36. package/lib/src/css/library.brand-page-brandCrowd.min.css +4 -4
  37. package/lib/src/css/library.brand-page-brandPage.min.css +4 -4
  38. package/lib/src/css/library.brand-page-designCom.min.css +4 -4
  39. package/lib/src/css/library.brand-page-preview.min.css +4 -4
  40. package/lib/src/index.mjs +2 -2
  41. package/lib/src/{lib-B38VVCSw.js → lib-V7aqfuiB.js} +18106 -17965
  42. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +2 -0
  43. package/lib/src/metadata-builder/builders/item-id-utils.js +8 -2
  44. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +8 -1
  45. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +6 -0
  46. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +1 -0
  47. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +5 -0
  48. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +5 -0
  49. package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.d.ts +9 -1
  50. package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.js +31 -9
  51. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -0
  52. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +5 -0
  53. package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -1
  54. package/lib/src/shared/page-navigation/store/page-navigation-store.js +22 -9
  55. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +7 -1
  56. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +10 -1
  57. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.js +12 -35
  58. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +6 -0
  59. package/lib/src/shared/page-settings/duplicate-page.util.d.ts +4 -0
  60. package/lib/src/shared/page-settings/duplicate-page.util.js +82 -0
  61. package/lib/src/shared/page-settings/models.d.ts +1 -0
  62. package/lib/src/shared/page-settings/store/page-settings-store.d.ts +5 -0
  63. package/lib/src/shared/page-settings/store/page-settings-store.js +90 -7
  64. package/lib/src/shared/page-settings/utils.d.ts +3 -0
  65. package/lib/src/shared/page-settings/utils.js +6 -0
  66. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -0
  67. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +1 -0
  68. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -0
  69. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -0
  70. package/lib/src/tracker.mjs +1 -1
  71. package/lib/src/{useEventTracker-C5Ql5p7u.js → useEventTracker-DwPTmzpa.js} +13 -3
  72. package/package.json +1 -1
  73. package/src/brand-page-maker/composables/useEventTracker.ts +13 -0
  74. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +2 -4
  75. package/src/bundles/bundled-translations.de-DE.json +3 -1
  76. package/src/bundles/bundled-translations.es-ES.json +3 -1
  77. package/src/bundles/bundled-translations.fr-CA.json +3 -1
  78. package/src/bundles/bundled-translations.fr-FR.json +3 -1
  79. package/src/bundles/bundled-translations.json +3 -1
  80. package/src/bundles/bundled-translations.pt-BR.json +3 -1
  81. package/src/bundles/bundled-translations.pt-PT.json +3 -1
  82. package/src/composables/useTranslate.ts +2 -1
  83. package/src/metadata-builder/builders/item-id-utils.ts +9 -2
  84. package/src/shared/ecommerce-settings/components/product-config-field/ProductConfigOption.vue +0 -2
  85. package/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.ts +32 -10
  86. package/src/shared/page-navigation/store/page-navigation-store.ts +41 -13
  87. package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +3 -0
  88. package/src/shared/page-settings/components/page-settings/PageElement.vue +2 -0
  89. package/src/shared/page-settings/components/page-settings/PageSettingMenu.test.ts +105 -0
  90. package/src/shared/page-settings/components/page-settings/PageSettingMenu.vue +16 -0
  91. package/src/shared/page-settings/components/page-settings/PageSettings.duplicate.test.ts +108 -0
  92. package/src/shared/page-settings/components/page-settings/PageSettings.mixin.ts +22 -50
  93. package/src/shared/page-settings/components/page-settings/PageSettings.vue +1 -0
  94. package/src/shared/page-settings/components/page-settings/i18n/page-settings.de-DE.json +3 -1
  95. package/src/shared/page-settings/components/page-settings/i18n/page-settings.es-ES.json +3 -1
  96. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-CA.json +3 -1
  97. package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-FR.json +3 -1
  98. package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +3 -1
  99. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-BR.json +3 -1
  100. package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-PT.json +3 -1
  101. package/src/shared/page-settings/duplicate-page.util.test.ts +382 -0
  102. package/src/shared/page-settings/duplicate-page.util.ts +124 -0
  103. package/src/shared/page-settings/models.ts +1 -0
  104. package/src/shared/page-settings/store/page-settings-store.test.ts +512 -1
  105. package/src/shared/page-settings/store/page-settings-store.ts +145 -11
  106. package/src/shared/page-settings/utils.ts +10 -0
@@ -28,6 +28,7 @@
28
28
  :delete-page-selected="onDeletePageSelected"
29
29
  :change-page-visibility="onChangePageVisibility"
30
30
  :on-add-sub-page-selected="onAddSubPageSelected"
31
+ :on-duplicate-page-selected="onDuplicatePageSelected"
31
32
  :total-pages="totalPages"
32
33
  :visible-page-count="visiblePageCount"
33
34
  />
@@ -41,6 +42,7 @@
41
42
  :on-delete-page-selected="onDeletePageSelected"
42
43
  :on-change-page-visibility="onChangePageVisibility"
43
44
  :on-add-sub-page-selected="onAddSubPageSelected"
45
+ :on-duplicate-page-selected="onDuplicatePageSelected"
44
46
  :move="handleMove"
45
47
  @drag-start="handleStart"
46
48
  @drag-end="handleEnd"
@@ -92,6 +94,7 @@ interface Props {
92
94
  onDeletePageSelected: (pageId: string) => void;
93
95
  onChangePageVisibility: (pageId: string) => void;
94
96
  onAddSubPageSelected: (pageId: string) => void;
97
+ onDuplicatePageSelected: (pageId: string) => void;
95
98
  }
96
99
 
97
100
  const props = defineProps<Props>();
@@ -45,6 +45,7 @@
45
45
  :on-delete-page-selected="deletePageSelected"
46
46
  :on-change-page-visibility="changePageVisibility"
47
47
  :on-add-sub-page-selected="onAddSubPageSelected"
48
+ :on-duplicate-page-selected="onDuplicatePageSelected"
48
49
  :is-root-page="isRootPage"
49
50
  :total-pages="totalPages"
50
51
  :visible-page-count="visiblePageCount"
@@ -80,6 +81,7 @@ interface Props {
80
81
  deletePageSelected: (pageId: string) => void;
81
82
  changePageVisibility: (pageId: string) => void;
82
83
  onAddSubPageSelected: (pageId: string) => void;
84
+ onDuplicatePageSelected: (pageId: string) => void;
83
85
  }
84
86
 
85
87
  defineProps<Props>();
@@ -0,0 +1,105 @@
1
+ import { describe, test, expect, beforeAll, vi } from 'vitest';
2
+ import { createPinia, setActivePinia } from 'pinia';
3
+ import { mount } from '@vue/test-utils';
4
+
5
+ import { setLocaleAsync } from '@src/composables/useTranslate';
6
+ import PageSettingMenu from './PageSettingMenu.vue';
7
+
8
+ // Menu order is a product requirement, so it is asserted rather than just
9
+ // membership.
10
+ const ITEM_TAGS = [
11
+ 'add-subpage',
12
+ 'duplicate-page',
13
+ 'toggle-page-visibility',
14
+ 'delete-page',
15
+ ];
16
+
17
+ const mountMenu = (overrides = {}) =>
18
+ mount(PageSettingMenu, {
19
+ props: {
20
+ index: 1,
21
+ pageId: 'page-1',
22
+ isHidden: false,
23
+ isDesignCom: false,
24
+ isRootPage: true,
25
+ hasSubPages: false,
26
+ totalPages: 3,
27
+ visiblePageCount: 3,
28
+ onDeletePageSelected: vi.fn(),
29
+ onChangePageVisibility: vi.fn(),
30
+ onAddSubPageSelected: vi.fn(),
31
+ onDuplicatePageSelected: vi.fn(),
32
+ ...overrides,
33
+ },
34
+ });
35
+
36
+ const openMenu = async (wrapper: ReturnType<typeof mountMenu>) => {
37
+ await wrapper.find('[data-test-tag="page-menu-trigger"]').trigger('click');
38
+
39
+ return wrapper;
40
+ };
41
+
42
+ const renderedTags = (wrapper: ReturnType<typeof mountMenu>) =>
43
+ wrapper
44
+ .findAll('li')
45
+ .map((li) => li.attributes('data-test-tag'))
46
+ .filter((tag): tag is string => Boolean(tag));
47
+
48
+ describe('PageSettingMenu', () => {
49
+ beforeAll(async () => {
50
+ setActivePinia(createPinia());
51
+
52
+ await setLocaleAsync('en-US');
53
+ });
54
+
55
+ test('a root page shows every action in order', async () => {
56
+ const wrapper = await openMenu(mountMenu());
57
+
58
+ expect(renderedTags(wrapper)).toEqual(ITEM_TAGS);
59
+ });
60
+
61
+ test('a sub-page shows duplicate but not add-subpage', async () => {
62
+ const wrapper = await openMenu(mountMenu({ isRootPage: false }));
63
+
64
+ expect(renderedTags(wrapper)).toEqual([
65
+ 'duplicate-page',
66
+ 'toggle-page-visibility',
67
+ 'delete-page',
68
+ ]);
69
+ });
70
+
71
+ test('the home page can be duplicated but not hidden or deleted', async () => {
72
+ const wrapper = await openMenu(mountMenu({ index: 0 }));
73
+
74
+ expect(renderedTags(wrapper)).toEqual(['add-subpage', 'duplicate-page']);
75
+ });
76
+
77
+ test('a parent page can be duplicated even though it cannot be deleted', async () => {
78
+ const wrapper = await openMenu(mountMenu({ hasSubPages: true }));
79
+
80
+ expect(renderedTags(wrapper)).toContain('duplicate-page');
81
+ expect(renderedTags(wrapper)).not.toContain('delete-page');
82
+ });
83
+
84
+ test('clicking duplicate calls back with the page id and closes the menu', async () => {
85
+ const onDuplicatePageSelected = vi.fn();
86
+
87
+ const wrapper = await openMenu(mountMenu({ onDuplicatePageSelected }));
88
+
89
+ await wrapper.find('[data-test-tag="duplicate-page"]').trigger('click');
90
+
91
+ expect(onDuplicatePageSelected).toHaveBeenCalledTimes(1);
92
+ expect(onDuplicatePageSelected).toHaveBeenCalledWith('page-1');
93
+ expect(wrapper.find('[data-test-tag="duplicate-page"]').exists()).toBe(
94
+ false,
95
+ );
96
+ });
97
+
98
+ test('the duplicate row is labelled from the translation bundle', async () => {
99
+ const wrapper = await openMenu(mountMenu());
100
+
101
+ expect(wrapper.find('[data-test-tag="duplicate-page"]').text()).toEqual(
102
+ 'Duplicate page',
103
+ );
104
+ });
105
+ });
@@ -29,6 +29,16 @@
29
29
  pageSettingsTr('addSubPage')
30
30
  }}</span>
31
31
  </li>
32
+ <li
33
+ data-test-tag="duplicate-page"
34
+ class="tw-cursor-pointer tw-flex tw-gap-2 tw-items-center tw-px-5 hover:tw-bg-grayscale-400 tw-mt-3"
35
+ @click="onDuplicateClick"
36
+ >
37
+ <Icon name="duplicate" :class="{ 'tw-pt-1': !isDesignCom }" />
38
+ <span class="tw-whitespace-nowrap">{{
39
+ pageSettingsTr('duplicatePage')
40
+ }}</span>
41
+ </li>
32
42
  <li
33
43
  v-if="props.index !== 0"
34
44
  data-test-tag="toggle-page-visibility"
@@ -74,6 +84,7 @@ interface Props {
74
84
  onDeletePageSelected: (pageId: string) => void;
75
85
  onChangePageVisibility: (pageId: string) => void;
76
86
  onAddSubPageSelected: (pageId: string) => void;
87
+ onDuplicatePageSelected: (pageId: string) => void;
77
88
  }
78
89
 
79
90
  const props = defineProps<Props>();
@@ -100,6 +111,11 @@ const onAddSubPageClick = () => {
100
111
  closeMenu();
101
112
  };
102
113
 
114
+ const onDuplicateClick = () => {
115
+ props.onDuplicatePageSelected(props.pageId);
116
+ closeMenu();
117
+ };
118
+
103
119
  const onChangePageVisibility = () => {
104
120
  props.onChangePageVisibility(props.pageId);
105
121
  closeMenu();
@@ -0,0 +1,108 @@
1
+ import { describe, test, expect, beforeAll, vi } from 'vitest';
2
+ import { createPinia, setActivePinia } from 'pinia';
3
+ import { shallowMount } from '@vue/test-utils';
4
+
5
+ import { setLocaleAsync } from '@src/composables/useTranslate';
6
+ import {
7
+ INITIALISE_PAYLOAD,
8
+ USER_FEATURES,
9
+ } from '@brand-page-maker/components/brand-page-maker/__fixtures__';
10
+ import {
11
+ UNLIMITED_MAX_NUM_OF_PAGES,
12
+ useBrandPageMakerStore,
13
+ useBrandPageMakerUserStore,
14
+ } from '@brand-page-maker/stores';
15
+ import { FabricRenderClient } from '../../../../clients/fabric-render-client';
16
+ import { useUiPageSettingsStore } from '../../store';
17
+ import { usePageNavigationStore } from '../../../page-navigation/store/page-navigation-store';
18
+ import PageSettings from './PageSettings.vue';
19
+
20
+ vi.mock('../../../../clients/brand-page-client', () => ({
21
+ default: {
22
+ getWebsiteContextualUpgradeTestBucketAsync: vi
23
+ .fn()
24
+ .mockResolvedValue(undefined),
25
+ },
26
+ }));
27
+
28
+ // The analytics contract lives in the mixin, not the store: the store returns
29
+ // the copy, the mixin decides what gets tracked. Nothing else covers it.
30
+ describe('PageSettings - duplicate page handler', () => {
31
+ const setupAsync = async () => {
32
+ const makerStore = useBrandPageMakerStore();
33
+ const userStore = useBrandPageMakerUserStore();
34
+
35
+ userStore.setUserState({
36
+ features: { ...USER_FEATURES, maxNumOfPages: UNLIMITED_MAX_NUM_OF_PAGES },
37
+ });
38
+
39
+ await makerStore.initialise(INITIALISE_PAYLOAD);
40
+
41
+ const wrapper = shallowMount(PageSettings);
42
+
43
+ return {
44
+ wrapper,
45
+ pageSettingsStore: useUiPageSettingsStore(),
46
+ pageNavigationStore: usePageNavigationStore(),
47
+ spy: vi.spyOn(
48
+ wrapper.vm.eventTracker,
49
+ 'sendWebsiteManageDuplicatePageEvent',
50
+ ),
51
+ };
52
+ };
53
+
54
+ beforeAll(async () => {
55
+ FabricRenderClient.setInstance({ baseUrl: 'http://localhost:3000' });
56
+ setActivePinia(createPinia());
57
+
58
+ await setLocaleAsync('en-US');
59
+ });
60
+
61
+ test('tracks the source page title, not the copy', async () => {
62
+ const { wrapper, pageSettingsStore, spy } = await setupAsync();
63
+
64
+ const source = pageSettingsStore.addNewPage({ title: 'About' });
65
+
66
+ await wrapper.vm.onDuplicatePageSelected(source.id);
67
+
68
+ expect(spy).toHaveBeenCalledWith('About', false);
69
+ });
70
+
71
+ test('flags a duplicated sub-page', async () => {
72
+ const { wrapper, pageSettingsStore, spy } = await setupAsync();
73
+
74
+ const parent = pageSettingsStore.addNewPage({ title: 'Portfolio' });
75
+ const child = pageSettingsStore.addNewPage({
76
+ title: 'Portfolio 1',
77
+ newPageParentId: parent.id,
78
+ });
79
+
80
+ await wrapper.vm.onDuplicatePageSelected(child.id);
81
+
82
+ expect(spy).toHaveBeenCalledWith('Portfolio 1', true);
83
+ });
84
+
85
+ test('tracks nothing when the page limit blocked the duplicate', async () => {
86
+ const { wrapper, pageSettingsStore, spy } = await setupAsync();
87
+ const userStore = useBrandPageMakerUserStore();
88
+
89
+ userStore.setUserState({
90
+ features: {
91
+ ...USER_FEATURES,
92
+ maxNumOfPages: pageSettingsStore.pages.length,
93
+ },
94
+ });
95
+
96
+ await wrapper.vm.onDuplicatePageSelected(pageSettingsStore.pages[0].id);
97
+
98
+ expect(spy).not.toHaveBeenCalled();
99
+ });
100
+
101
+ test('tracks nothing for an unknown page id', async () => {
102
+ const { wrapper, spy } = await setupAsync();
103
+
104
+ await wrapper.vm.onDuplicatePageSelected('does-not-exist');
105
+
106
+ expect(spy).not.toHaveBeenCalled();
107
+ });
108
+ });
@@ -124,55 +124,6 @@ export default defineComponent({
124
124
 
125
125
  const isDraggingPage = ref(false);
126
126
 
127
- const syncPagesWithOrders = (): void => {
128
- const pageIdOrder: string[] = [];
129
-
130
- const collectPageIds = (orders: NavOrder[]): void => {
131
- for (const order of orders) {
132
- const route = pageNavigationStore.routes.find(
133
- (r) => r.id === order.routeId,
134
- );
135
-
136
- if (route?.pageId) {
137
- pageIdOrder.push(route.pageId);
138
- }
139
-
140
- if (order.subRoutes && order.subRoutes.length > 0) {
141
- collectPageIds(order.subRoutes);
142
- }
143
- }
144
- };
145
-
146
- collectPageIds(pageNavigationStore.orders);
147
-
148
- if (!pageIdOrder.length) {
149
- return;
150
- }
151
-
152
- const pageMap = new Map<string, ContentPage>();
153
-
154
- for (const page of pageSettingsStore.pages) {
155
- pageMap.set(page.id, page);
156
- }
157
-
158
- const orderedPages: ContentPage[] = [];
159
-
160
- for (const pageId of pageIdOrder) {
161
- const page = pageMap.get(pageId);
162
-
163
- if (page) {
164
- orderedPages.push(page);
165
- pageMap.delete(pageId);
166
- }
167
- }
168
-
169
- for (const remainingPage of pageMap.values()) {
170
- orderedPages.push(remainingPage);
171
- }
172
-
173
- pageSettingsStore.setPages({ pages: orderedPages });
174
- };
175
-
176
127
  const onDraggingStart = (): void => {
177
128
  isDraggingPage.value = true;
178
129
  };
@@ -200,7 +151,7 @@ export default defineComponent({
200
151
  }
201
152
  }
202
153
 
203
- syncPagesWithOrders();
154
+ pageSettingsStore.syncPagesWithOrders();
204
155
 
205
156
  // Recalc the moved route's slug to ensure uniqueness at its new depth
206
157
  if (routeId) {
@@ -371,6 +322,27 @@ export default defineComponent({
371
322
  isNewPageModalVisible: true,
372
323
  });
373
324
  },
325
+ async onDuplicatePageSelected(pageId: string) {
326
+ const sourcePage = this.pageSettingsStore.pages.find(
327
+ (p) => p.id === pageId,
328
+ );
329
+ const isSubPage = Boolean(
330
+ this.pageNavigationStore.getParentRouteIdForPage(pageId),
331
+ );
332
+
333
+ const newPage = await this.pageSettingsStore.duplicatePage({ pageId });
334
+
335
+ // Nothing was created - paywalled, or the page is gone. The upsell is
336
+ // already tracked through the bridge's cta_location.
337
+ if (!newPage || !sourcePage) {
338
+ return;
339
+ }
340
+
341
+ this.eventTracker.sendWebsiteManageDuplicatePageEvent(
342
+ sourcePage.title,
343
+ isSubPage,
344
+ );
345
+ },
374
346
  onChangePageVisibility(pageId: string) {
375
347
  const page = this.pageSettingsStore.pages.find((p) => p.id === pageId);
376
348
 
@@ -38,6 +38,7 @@
38
38
  :on-delete-page-selected="onDeletePageSelected"
39
39
  :on-change-page-visibility="onChangePageVisibility"
40
40
  :on-add-sub-page-selected="onAddSubPageSelected"
41
+ :on-duplicate-page-selected="onDuplicatePageSelected"
41
42
  @drag-start="onDraggingStart"
42
43
  @drag-end="onDraggingEnd"
43
44
  />
@@ -19,6 +19,8 @@
19
19
  "showPage": "Seite anzeigen",
20
20
  "hidePage": "Seite ausblenden",
21
21
  "delete": "Seite löschen",
22
- "addSubPage": "Unterseite hinzufügen"
22
+ "addSubPage": "Unterseite hinzufügen",
23
+ "duplicatePage": "Seite duplizieren",
24
+ "duplicatePageTitle": "{{title}} Kopieren"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Mostrar página",
20
20
  "hidePage": "Ocultar página",
21
21
  "delete": "Eliminar página",
22
- "addSubPage": "Añadir subpágina"
22
+ "addSubPage": "Añadir subpágina",
23
+ "duplicatePage": "Duplicar página",
24
+ "duplicatePageTitle": "{{title}} copia"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Afficher la page",
20
20
  "hidePage": "Masquez la page",
21
21
  "delete": "Supprimer la page",
22
- "addSubPage": "Ajouter une sous-page"
22
+ "addSubPage": "Ajouter une sous-page",
23
+ "duplicatePage": "Dupliquer la page",
24
+ "duplicatePageTitle": "{{title}} Copie"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Afficher la page",
20
20
  "hidePage": "Masquez la page",
21
21
  "delete": "Supprimer la page",
22
- "addSubPage": "Ajouter une sous-page"
22
+ "addSubPage": "Ajouter une sous-page",
23
+ "duplicatePage": "Dupliquer la page",
24
+ "duplicatePageTitle": "Copie de {{title}}"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Show page",
20
20
  "hidePage": "Hide page",
21
21
  "delete": "Delete page",
22
- "addSubPage": "Add subpage"
22
+ "addSubPage": "Add subpage",
23
+ "duplicatePage": "Duplicate page",
24
+ "duplicatePageTitle": "{{title}} Copy"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Mostrar página",
20
20
  "hidePage": "Ocultar página",
21
21
  "delete": "Eliminar página",
22
- "addSubPage": "Adicionar subpágina"
22
+ "addSubPage": "Adicionar subpágina",
23
+ "duplicatePage": "Duplicar página",
24
+ "duplicatePageTitle": "{{title}} Copiar"
23
25
  }
24
26
  }
@@ -19,6 +19,8 @@
19
19
  "showPage": "Mostrar página",
20
20
  "hidePage": "Ocultar página",
21
21
  "delete": "Eliminar página",
22
- "addSubPage": "Adicionar subpágina"
22
+ "addSubPage": "Adicionar subpágina",
23
+ "duplicatePage": "Duplicar página",
24
+ "duplicatePageTitle": "{{title}} Cópia"
23
25
  }
24
26
  }