@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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-CtvPtYF6.js → CreatedOnBrandCrowd-de-de-7P2gNr-R.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-eGF-EjAX.js → CreatedOnBrandCrowd-en-us-Dl7BymW6.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-CSemsOtT.js → CreatedOnBrandCrowd-es-es-CO4FvWVw.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-DzylX-AT.js → CreatedOnBrandCrowd-fr-ca-DHNqakQu.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-DzylX-AT.js → CreatedOnBrandCrowd-fr-fr-DHNqakQu.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-QMUxXQsE.js → CreatedOnBrandCrowd-pt-br-CGUC-ZbP.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-QMUxXQsE.js → CreatedOnBrandCrowd-pt-pt-CGUC-ZbP.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-M7OoJt-1.js → CreatedOnDcom-de-de-Dvds-Vmy.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-D7hMM0AI.js → CreatedOnDcom-en-us-fd3C-8Tp.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-CLw-BLp2.js → CreatedOnDcom-es-es-DTXZW35-.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BRu_kmeu.js → CreatedOnDcom-fr-ca-DRCYOKPs.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-IMnEv_yM.js → CreatedOnDcom-fr-fr-DpIB8x2x.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-Bt6v1p27.js → CreatedOnDcom-pt-br-Esnxy8wX.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-BVsFJIJg.js → CreatedOnDcom-pt-pt-CaiVHFXR.js} +1 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +7 -1
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +6 -0
- package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +3 -0
- package/lib/src/brand-page-maker/composables/useEventTracker.js +10 -0
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +2 -4
- package/lib/src/{bundled-translations-KY0aSmMp.js → bundled-translations-BYUfWBT3.js} +9 -9
- package/lib/src/{bundled-translations.de-DE-BIFi4bYj.js → bundled-translations.de-DE-MmzUu1tt.js} +13 -13
- package/lib/src/{bundled-translations.es-ES-CHlKuPLO.js → bundled-translations.es-ES-D-tG33XL.js} +1 -1
- package/lib/src/{bundled-translations.fr-CA-DR1fvm8e.js → bundled-translations.fr-CA-CNc1H1h3.js} +1 -1
- package/lib/src/{bundled-translations.fr-FR-DnpWVIQg.js → bundled-translations.fr-FR-DX0-yepT.js} +1 -1
- package/lib/src/{bundled-translations.pt-BR-p8szTOiT.js → bundled-translations.pt-BR-ih1-LRgy.js} +9 -9
- package/lib/src/{bundled-translations.pt-PT-DEk39NhW.js → bundled-translations.pt-PT-Dq2CmD1p.js} +1 -1
- package/lib/src/bundles/bundled-translations.de-DE.json +3 -1
- package/lib/src/bundles/bundled-translations.es-ES.json +3 -1
- package/lib/src/bundles/bundled-translations.fr-CA.json +3 -1
- package/lib/src/bundles/bundled-translations.fr-FR.json +3 -1
- package/lib/src/bundles/bundled-translations.json +3 -1
- package/lib/src/bundles/bundled-translations.pt-BR.json +3 -1
- package/lib/src/bundles/bundled-translations.pt-PT.json +3 -1
- package/lib/src/composables/useTranslate.d.ts +1 -1
- package/lib/src/composables/useTranslate.js +1 -1
- package/lib/src/css/library.brand-page-brandCrowd.min.css +4 -4
- package/lib/src/css/library.brand-page-brandPage.min.css +4 -4
- package/lib/src/css/library.brand-page-designCom.min.css +4 -4
- package/lib/src/css/library.brand-page-preview.min.css +4 -4
- package/lib/src/index.mjs +2 -2
- package/lib/src/{lib-B38VVCSw.js → lib-V7aqfuiB.js} +18106 -17965
- package/lib/src/metadata-builder/builders/item-id-utils.d.ts +2 -0
- package/lib/src/metadata-builder/builders/item-id-utils.js +8 -2
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +8 -1
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +6 -0
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +5 -0
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +5 -0
- package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.d.ts +9 -1
- package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.js +31 -9
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -0
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +5 -0
- package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -1
- package/lib/src/shared/page-navigation/store/page-navigation-store.js +22 -9
- package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +7 -1
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +10 -1
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.js +12 -35
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +6 -0
- package/lib/src/shared/page-settings/duplicate-page.util.d.ts +4 -0
- package/lib/src/shared/page-settings/duplicate-page.util.js +82 -0
- package/lib/src/shared/page-settings/models.d.ts +1 -0
- package/lib/src/shared/page-settings/store/page-settings-store.d.ts +5 -0
- package/lib/src/shared/page-settings/store/page-settings-store.js +90 -7
- package/lib/src/shared/page-settings/utils.d.ts +3 -0
- package/lib/src/shared/page-settings/utils.js +6 -0
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -0
- package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +1 -0
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -0
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -0
- package/lib/src/tracker.mjs +1 -1
- package/lib/src/{useEventTracker-C5Ql5p7u.js → useEventTracker-DwPTmzpa.js} +13 -3
- package/package.json +1 -1
- package/src/brand-page-maker/composables/useEventTracker.ts +13 -0
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +2 -4
- package/src/bundles/bundled-translations.de-DE.json +3 -1
- package/src/bundles/bundled-translations.es-ES.json +3 -1
- package/src/bundles/bundled-translations.fr-CA.json +3 -1
- package/src/bundles/bundled-translations.fr-FR.json +3 -1
- package/src/bundles/bundled-translations.json +3 -1
- package/src/bundles/bundled-translations.pt-BR.json +3 -1
- package/src/bundles/bundled-translations.pt-PT.json +3 -1
- package/src/composables/useTranslate.ts +2 -1
- package/src/metadata-builder/builders/item-id-utils.ts +9 -2
- package/src/shared/ecommerce-settings/components/product-config-field/ProductConfigOption.vue +0 -2
- package/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.ts +32 -10
- package/src/shared/page-navigation/store/page-navigation-store.ts +41 -13
- package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +3 -0
- package/src/shared/page-settings/components/page-settings/PageElement.vue +2 -0
- package/src/shared/page-settings/components/page-settings/PageSettingMenu.test.ts +105 -0
- package/src/shared/page-settings/components/page-settings/PageSettingMenu.vue +16 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.duplicate.test.ts +108 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.mixin.ts +22 -50
- package/src/shared/page-settings/components/page-settings/PageSettings.vue +1 -0
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.de-DE.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.es-ES.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-CA.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-FR.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-BR.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-PT.json +3 -1
- package/src/shared/page-settings/duplicate-page.util.test.ts +382 -0
- package/src/shared/page-settings/duplicate-page.util.ts +124 -0
- package/src/shared/page-settings/models.ts +1 -0
- package/src/shared/page-settings/store/page-settings-store.test.ts +512 -1
- package/src/shared/page-settings/store/page-settings-store.ts +145 -11
- package/src/shared/page-settings/utils.ts +10 -0
|
@@ -6,6 +6,8 @@ import type { BrandPageModel } from '../../brand-page/models';
|
|
|
6
6
|
export declare const END_ITEM_ANCHOR = "__end__";
|
|
7
7
|
/** Generate a stable item id. Mirrors section-level `uuid.v4()` id generation. */
|
|
8
8
|
export declare const generateItemId: () => string;
|
|
9
|
+
/** Every array field the minifier surfaces to the AI as a list of items. */
|
|
10
|
+
export declare const ITEM_ID_BEARING_FIELDS: readonly ["images", "items", "textItems", "links"];
|
|
9
11
|
/**
|
|
10
12
|
* Walk an entire BrandPageModel and backfill a stable uuid `id` onto every
|
|
11
13
|
* nested array item that lacks one (gallery images, images/shapes/testimonial
|
|
@@ -6,6 +6,13 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
6
6
|
export const END_ITEM_ANCHOR = '__end__';
|
|
7
7
|
/** Generate a stable item id. Mirrors section-level `uuid.v4()` id generation. */
|
|
8
8
|
export const generateItemId = () => uuidv4();
|
|
9
|
+
/** Every array field the minifier surfaces to the AI as a list of items. */
|
|
10
|
+
export const ITEM_ID_BEARING_FIELDS = [
|
|
11
|
+
'images',
|
|
12
|
+
'items',
|
|
13
|
+
'textItems',
|
|
14
|
+
'links',
|
|
15
|
+
];
|
|
9
16
|
/**
|
|
10
17
|
* Walk an entire BrandPageModel and backfill a stable uuid `id` onto every
|
|
11
18
|
* nested array item that lacks one (gallery images, images/shapes/testimonial
|
|
@@ -19,8 +26,7 @@ export const generateItemId = () => uuidv4();
|
|
|
19
26
|
*/
|
|
20
27
|
export const ensureBrandPageItemIds = (model) => {
|
|
21
28
|
const ensureContentItemIds = (content) => {
|
|
22
|
-
|
|
23
|
-
for (const field of ['images', 'items', 'textItems', 'links']) {
|
|
29
|
+
for (const field of ITEM_ID_BEARING_FIELDS) {
|
|
24
30
|
const arr = content[field];
|
|
25
31
|
if (Array.isArray(arr))
|
|
26
32
|
ensureItemIds(arr);
|
package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts
CHANGED
|
@@ -471,6 +471,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
471
471
|
hasVisibleNavigation(): boolean;
|
|
472
472
|
}, {
|
|
473
473
|
getPageSlugsForDepth(parentRouteId?: string): string[];
|
|
474
|
+
getDisplayNamesForDepth(parentRouteId?: string): string[];
|
|
474
475
|
getParentRouteIdForPage(pageId: string): string | undefined;
|
|
475
476
|
getParentRouteIdForRoute(routeId: string): string | undefined;
|
|
476
477
|
getFirstOrDefaultRoute(): import("@brand-page").NavRoute;
|
|
@@ -483,7 +484,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
483
484
|
setNav({ nav }: {
|
|
484
485
|
nav: import("@brand-page").NavModel;
|
|
485
486
|
}): void;
|
|
486
|
-
addNavRoute(newRoute: import("@brand-page").NavRoute, parentRouteId?: string): void;
|
|
487
|
+
addNavRoute(newRoute: import("@brand-page").NavRoute, parentRouteId?: string, insertAfterRouteId?: string): void;
|
|
487
488
|
removeNavRoute(routeId: string): void;
|
|
488
489
|
onDisplayNameChange(routeId: string, newDisplayName: string): void;
|
|
489
490
|
setRouteTitleAndSlugByPageId(pageId: string, displayName: string, slug: string): void;
|
|
@@ -508,6 +509,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
508
509
|
setPages({ pages }: {
|
|
509
510
|
pages: import("../../../page-settings/models").ContentPage[];
|
|
510
511
|
}): void;
|
|
512
|
+
syncPagesWithOrders(): void;
|
|
513
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
511
514
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
512
515
|
isNewPageModalVisible: boolean;
|
|
513
516
|
parentPageId?: string;
|
|
@@ -557,6 +560,9 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
557
560
|
title: string;
|
|
558
561
|
newPageParentId?: string;
|
|
559
562
|
}): import("../../../page-settings/models").ContentPage;
|
|
563
|
+
duplicatePage({ pageId, }: {
|
|
564
|
+
pageId: string;
|
|
565
|
+
}): Promise<import("../../../page-settings/models").ContentPage | undefined>;
|
|
560
566
|
setSelectedSectionName({ sectionName, }: {
|
|
561
567
|
sectionName?: import("../../../page-settings/store").PageSettingsSection;
|
|
562
568
|
}): void;
|
|
@@ -681,6 +687,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
681
687
|
sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
|
|
682
688
|
sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
|
|
683
689
|
sendWebsiteManageReorderPageEvent: (pageName: string) => void;
|
|
690
|
+
sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
|
|
684
691
|
sendPublishOptionsForFreeEvent: () => void;
|
|
685
692
|
sendPublishOptionsForPremiumEvent: () => void;
|
|
686
693
|
sendPublishOptionsForExitEvent: () => void;
|
|
@@ -516,6 +516,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
516
516
|
setPages({ pages }: {
|
|
517
517
|
pages: import("../../../page-settings/models").ContentPage[];
|
|
518
518
|
}): void;
|
|
519
|
+
syncPagesWithOrders(): void;
|
|
520
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
519
521
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
520
522
|
isNewPageModalVisible: boolean;
|
|
521
523
|
parentPageId?: string;
|
|
@@ -565,6 +567,9 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
565
567
|
title: string;
|
|
566
568
|
newPageParentId?: string;
|
|
567
569
|
}): import("../../../page-settings/models").ContentPage;
|
|
570
|
+
duplicatePage({ pageId, }: {
|
|
571
|
+
pageId: string;
|
|
572
|
+
}): Promise<import("../../../page-settings/models").ContentPage | undefined>;
|
|
568
573
|
setSelectedSectionName({ sectionName, }: {
|
|
569
574
|
sectionName?: import("../../../page-settings/store").PageSettingsSection;
|
|
570
575
|
}): void;
|
|
@@ -800,6 +805,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
800
805
|
sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
|
|
801
806
|
sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
|
|
802
807
|
sendWebsiteManageReorderPageEvent: (pageName: string) => void;
|
|
808
|
+
sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
|
|
803
809
|
sendPublishOptionsForFreeEvent: () => void;
|
|
804
810
|
sendPublishOptionsForPremiumEvent: () => void;
|
|
805
811
|
sendPublishOptionsForExitEvent: () => void;
|
|
@@ -532,6 +532,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
532
532
|
sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
|
|
533
533
|
sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
|
|
534
534
|
sendWebsiteManageReorderPageEvent: (pageName: string) => void;
|
|
535
|
+
sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
|
|
535
536
|
sendPublishOptionsForFreeEvent: () => void;
|
|
536
537
|
sendPublishOptionsForPremiumEvent: () => void;
|
|
537
538
|
sendPublishOptionsForExitEvent: () => void;
|
|
@@ -738,6 +738,8 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
738
738
|
setPages({ pages }: {
|
|
739
739
|
pages: import("../../../page-settings/models").ContentPage[];
|
|
740
740
|
}): void;
|
|
741
|
+
syncPagesWithOrders(): void;
|
|
742
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
741
743
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
742
744
|
isNewPageModalVisible: boolean;
|
|
743
745
|
parentPageId?: string;
|
|
@@ -787,6 +789,9 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
787
789
|
title: string;
|
|
788
790
|
newPageParentId?: string;
|
|
789
791
|
}): import("../../../page-settings/models").ContentPage;
|
|
792
|
+
duplicatePage({ pageId, }: {
|
|
793
|
+
pageId: string;
|
|
794
|
+
}): Promise<import("../../../page-settings/models").ContentPage | undefined>;
|
|
790
795
|
setSelectedSectionName({ sectionName, }: {
|
|
791
796
|
sectionName?: import("../../../page-settings/store").PageSettingsSection;
|
|
792
797
|
}): void;
|
|
@@ -540,6 +540,8 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
540
540
|
setPages({ pages }: {
|
|
541
541
|
pages: import("../../../page-settings/models").ContentPage[];
|
|
542
542
|
}): void;
|
|
543
|
+
syncPagesWithOrders(): void;
|
|
544
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
543
545
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
544
546
|
isNewPageModalVisible: boolean;
|
|
545
547
|
parentPageId?: string;
|
|
@@ -589,6 +591,9 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
589
591
|
title: string;
|
|
590
592
|
newPageParentId?: string;
|
|
591
593
|
}): import("../../../page-settings/models").ContentPage;
|
|
594
|
+
duplicatePage({ pageId, }: {
|
|
595
|
+
pageId: string;
|
|
596
|
+
}): Promise<import("../../../page-settings/models").ContentPage | undefined>;
|
|
592
597
|
setSelectedSectionName({ sectionName, }: {
|
|
593
598
|
sectionName?: import("@shared/page-settings/store").PageSettingsSection;
|
|
594
599
|
}): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BrandPageModel } from '@brand-page/models';
|
|
1
|
+
import type { BrandPageModel, Content } from '@brand-page/models';
|
|
2
2
|
/**
|
|
3
3
|
* Refresh a (migrated, current-version) BrandPageModel so it is safe to persist as a *duplicate*.
|
|
4
4
|
* Two things travel with a verbatim copy that must not: client-minted identifiers that the
|
|
@@ -9,3 +9,11 @@ import type { BrandPageModel } from '@brand-page/models';
|
|
|
9
9
|
* See `shop-duplication.md`.
|
|
10
10
|
*/
|
|
11
11
|
export declare function refreshDuplicatedMetadata(model: BrandPageModel): BrandPageModel;
|
|
12
|
+
/**
|
|
13
|
+
* Rotate the client-minted ids the brand-page-api keys a **global** row on — shop product / sku_key
|
|
14
|
+
* and payment-option ids. A verbatim copy collides with its source on those tables.
|
|
15
|
+
*
|
|
16
|
+
* Shared with page duplication, which wants only this half: that copy keeps the same owner, so it
|
|
17
|
+
* keeps its payment connection.
|
|
18
|
+
*/
|
|
19
|
+
export declare function rotateMonetizationIds(content: Content): void;
|
|
@@ -16,10 +16,8 @@ export function refreshDuplicatedMetadata(model) {
|
|
|
16
16
|
// content-level connections below are cleared. Disconnect it too; the new owner reconnects.
|
|
17
17
|
model.paymentConfig = undefined;
|
|
18
18
|
model.pages.forEach((page) => page.contents.forEach((content) => {
|
|
19
|
+
rotateMonetizationIds(content);
|
|
19
20
|
if (isShopContent(content)) {
|
|
20
|
-
// Shop products collide on the global product / sku_key tables. The shop's payment-config
|
|
21
|
-
// link is written with throwaway ids (not the content id), so only the product ids rotate.
|
|
22
|
-
content.products.forEach(rotateProductIds);
|
|
23
21
|
disconnectPayment(content.shopPaymentConfig);
|
|
24
22
|
}
|
|
25
23
|
else if (isPayment(content)) {
|
|
@@ -31,6 +29,26 @@ export function refreshDuplicatedMetadata(model) {
|
|
|
31
29
|
}));
|
|
32
30
|
return model;
|
|
33
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Rotate the client-minted ids the brand-page-api keys a **global** row on — shop product / sku_key
|
|
34
|
+
* and payment-option ids. A verbatim copy collides with its source on those tables.
|
|
35
|
+
*
|
|
36
|
+
* Shared with page duplication, which wants only this half: that copy keeps the same owner, so it
|
|
37
|
+
* keeps its payment connection.
|
|
38
|
+
*/
|
|
39
|
+
export function rotateMonetizationIds(content) {
|
|
40
|
+
if (isShopContent(content)) {
|
|
41
|
+
// The shop's payment-config link is written with throwaway ids (not the content id), so only
|
|
42
|
+
// the product ids rotate here.
|
|
43
|
+
content.products.forEach(rotateProductIds);
|
|
44
|
+
}
|
|
45
|
+
else if (isPayment(content)) {
|
|
46
|
+
rotatePaymentOptionIds(content.requestPaymentConfig);
|
|
47
|
+
}
|
|
48
|
+
else if (isSupportMe(content)) {
|
|
49
|
+
rotatePaymentOptionIds(content.supportMeConfig);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
34
52
|
function rotateProductIds(product) {
|
|
35
53
|
product.id = uuid();
|
|
36
54
|
// The option id is the only value cross-referenced inside a product — SKUs point at product
|
|
@@ -56,17 +74,21 @@ function rotateProductIds(product) {
|
|
|
56
74
|
sku.skuKey = generateSku(product.name, sku.variant[0]?.option);
|
|
57
75
|
});
|
|
58
76
|
}
|
|
77
|
+
// `brand_page_product` is keyed on the payment-option id, so a verbatim copy would adopt the
|
|
78
|
+
// source's product rows on its next payment upsert.
|
|
79
|
+
function rotatePaymentOptionIds(config) {
|
|
80
|
+
config.paymentOptions.forEach((option) => {
|
|
81
|
+
option.id = uuid();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
59
84
|
/**
|
|
60
85
|
* request-payment / support-me content. The api keys `content_block_payment_config` on the content
|
|
61
|
-
* id (PK)
|
|
62
|
-
*
|
|
63
|
-
*
|
|
86
|
+
* id (PK), so a verbatim copy would repoint the source's config link on its next payment upsert -
|
|
87
|
+
* re-mint it, then drop the connection. (The option ids are rotated upstream, for both kinds of
|
|
88
|
+
* duplication.)
|
|
64
89
|
*/
|
|
65
90
|
function resetPaymentBlock(content, config) {
|
|
66
91
|
content.id = uuid();
|
|
67
|
-
config.paymentOptions.forEach((option) => {
|
|
68
|
-
option.id = uuid();
|
|
69
|
-
});
|
|
70
92
|
disconnectPayment(config);
|
|
71
93
|
}
|
|
72
94
|
/**
|
|
@@ -299,6 +299,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
299
299
|
sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
|
|
300
300
|
sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
|
|
301
301
|
sendWebsiteManageReorderPageEvent: (pageName: string) => void;
|
|
302
|
+
sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
|
|
302
303
|
sendPublishOptionsForFreeEvent: () => void;
|
|
303
304
|
sendPublishOptionsForPremiumEvent: () => void;
|
|
304
305
|
sendPublishOptionsForExitEvent: () => void;
|
|
@@ -707,6 +707,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
707
707
|
setPages({ pages }: {
|
|
708
708
|
pages: import("../../page-settings/models").ContentPage[];
|
|
709
709
|
}): void;
|
|
710
|
+
syncPagesWithOrders(): void;
|
|
711
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
710
712
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
711
713
|
isNewPageModalVisible: boolean;
|
|
712
714
|
parentPageId?: string;
|
|
@@ -756,6 +758,9 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
756
758
|
title: string;
|
|
757
759
|
newPageParentId?: string;
|
|
758
760
|
}): import("../../page-settings/models").ContentPage;
|
|
761
|
+
duplicatePage({ pageId, }: {
|
|
762
|
+
pageId: string;
|
|
763
|
+
}): Promise<import("../../page-settings/models").ContentPage | undefined>;
|
|
759
764
|
setSelectedSectionName({ sectionName, }: {
|
|
760
765
|
sectionName?: import("@shared/page-settings/store").PageSettingsSection;
|
|
761
766
|
}): void;
|
|
@@ -9,6 +9,7 @@ export declare const usePageNavigationStore: import("pinia").StoreDefinition<"pa
|
|
|
9
9
|
hasVisibleNavigation(): boolean;
|
|
10
10
|
}, {
|
|
11
11
|
getPageSlugsForDepth(parentRouteId?: string): string[];
|
|
12
|
+
getDisplayNamesForDepth(parentRouteId?: string): string[];
|
|
12
13
|
getParentRouteIdForPage(pageId: string): string | undefined;
|
|
13
14
|
getParentRouteIdForRoute(routeId: string): string | undefined;
|
|
14
15
|
getFirstOrDefaultRoute(): NavRoute;
|
|
@@ -21,7 +22,7 @@ export declare const usePageNavigationStore: import("pinia").StoreDefinition<"pa
|
|
|
21
22
|
setNav({ nav }: {
|
|
22
23
|
nav: NavModel;
|
|
23
24
|
}): void;
|
|
24
|
-
addNavRoute(newRoute: NavRoute, parentRouteId?: string): void;
|
|
25
|
+
addNavRoute(newRoute: NavRoute, parentRouteId?: string, insertAfterRouteId?: string): void;
|
|
25
26
|
removeNavRoute(routeId: string): void;
|
|
26
27
|
onDisplayNameChange(routeId: string, newDisplayName: string): void;
|
|
27
28
|
setRouteTitleAndSlugByPageId(pageId: string, displayName: string, slug: string): void;
|
|
@@ -16,9 +16,9 @@ const findParentRouteIdForRouteId = (orders, targetRouteId, parentRouteId) => {
|
|
|
16
16
|
}
|
|
17
17
|
return undefined;
|
|
18
18
|
};
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
const
|
|
19
|
+
// Routes at one depth. Slugs and titles only need to be unique among siblings,
|
|
20
|
+
// so /home/home-1 and /my-home/home-1 can coexist.
|
|
21
|
+
const getRoutesForCurrentDepth = (navModel, parentRouteId, excludeRouteId) => {
|
|
22
22
|
let ordersAtDepth;
|
|
23
23
|
if (!parentRouteId) {
|
|
24
24
|
ordersAtDepth = navModel.orders;
|
|
@@ -28,10 +28,9 @@ const getPageSlugsForCurrentDepth = (navModel, parentRouteId, excludeRouteId) =>
|
|
|
28
28
|
ordersAtDepth = parentOrder?.subRoutes || [];
|
|
29
29
|
}
|
|
30
30
|
const routeIdsAtDepth = ordersAtDepth.map((order) => order.routeId);
|
|
31
|
-
return navModel.routes
|
|
32
|
-
.filter((route) => routeIdsAtDepth.includes(route.id) && route.id !== excludeRouteId)
|
|
33
|
-
.map((route) => route.pageSlug);
|
|
31
|
+
return navModel.routes.filter((route) => routeIdsAtDepth.includes(route.id) && route.id !== excludeRouteId);
|
|
34
32
|
};
|
|
33
|
+
const getPageSlugsForCurrentDepth = (navModel, parentRouteId, excludeRouteId) => getRoutesForCurrentDepth(navModel, parentRouteId, excludeRouteId).map((route) => route.pageSlug);
|
|
35
34
|
export const usePageNavigationStore = defineStore('pageNavigation', {
|
|
36
35
|
state: () => ({
|
|
37
36
|
routes: [],
|
|
@@ -62,6 +61,9 @@ export const usePageNavigationStore = defineStore('pageNavigation', {
|
|
|
62
61
|
getPageSlugsForDepth(parentRouteId) {
|
|
63
62
|
return getPageSlugsForCurrentDepth(this.nav, parentRouteId);
|
|
64
63
|
},
|
|
64
|
+
getDisplayNamesForDepth(parentRouteId) {
|
|
65
|
+
return getRoutesForCurrentDepth(this.nav, parentRouteId).map((route) => route.displayName);
|
|
66
|
+
},
|
|
65
67
|
getParentRouteIdForPage(pageId) {
|
|
66
68
|
const route = this.routes.find((r) => r.pageId === pageId);
|
|
67
69
|
if (!route) {
|
|
@@ -93,16 +95,27 @@ export const usePageNavigationStore = defineStore('pageNavigation', {
|
|
|
93
95
|
this.routes = nav.routes;
|
|
94
96
|
this.orders = nav.orders;
|
|
95
97
|
},
|
|
96
|
-
addNavRoute(newRoute, parentRouteId) {
|
|
98
|
+
addNavRoute(newRoute, parentRouteId, insertAfterRouteId) {
|
|
97
99
|
const newOrder = {
|
|
98
100
|
routeId: newRoute.id,
|
|
99
101
|
subRoutes: [],
|
|
100
102
|
};
|
|
103
|
+
const addToSiblings = (siblings) => {
|
|
104
|
+
const afterIndex = insertAfterRouteId
|
|
105
|
+
? siblings.findIndex((order) => order.routeId === insertAfterRouteId)
|
|
106
|
+
: -1;
|
|
107
|
+
if (afterIndex === -1) {
|
|
108
|
+
siblings.push(newOrder);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
siblings.splice(afterIndex + 1, 0, newOrder);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
101
114
|
if (parentRouteId) {
|
|
102
115
|
// Find where to insert the new route into order
|
|
103
116
|
const parentOrder = findParentOrderByRouteId(this.orders, parentRouteId);
|
|
104
117
|
if (parentOrder) {
|
|
105
|
-
parentOrder.subRoutes
|
|
118
|
+
addToSiblings(parentOrder.subRoutes);
|
|
106
119
|
}
|
|
107
120
|
else {
|
|
108
121
|
console.error(`Parent route with id ${parentRouteId} not found in nav orders.`);
|
|
@@ -110,7 +123,7 @@ export const usePageNavigationStore = defineStore('pageNavigation', {
|
|
|
110
123
|
}
|
|
111
124
|
else {
|
|
112
125
|
// Push to root of orders object as this is not a nested route
|
|
113
|
-
this.orders
|
|
126
|
+
addToSiblings(this.orders);
|
|
114
127
|
}
|
|
115
128
|
this.routes.push(newRoute);
|
|
116
129
|
this.reorderVisibleAndHiddenRoutes();
|
|
@@ -17,6 +17,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
17
17
|
setPages({ pages }: {
|
|
18
18
|
pages: import("../../models").ContentPage[];
|
|
19
19
|
}): void;
|
|
20
|
+
syncPagesWithOrders(): void;
|
|
21
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
20
22
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
21
23
|
isNewPageModalVisible: boolean;
|
|
22
24
|
parentPageId?: string;
|
|
@@ -66,6 +68,9 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
66
68
|
title: string;
|
|
67
69
|
newPageParentId?: string;
|
|
68
70
|
}): import("../../models").ContentPage;
|
|
71
|
+
duplicatePage({ pageId, }: {
|
|
72
|
+
pageId: string;
|
|
73
|
+
}): Promise<import("../../models").ContentPage | undefined>;
|
|
69
74
|
setSelectedSectionName({ sectionName, }: {
|
|
70
75
|
sectionName?: import("../../store").PageSettingsSection;
|
|
71
76
|
}): void;
|
|
@@ -82,6 +87,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
82
87
|
hasVisibleNavigation(): boolean;
|
|
83
88
|
}, {
|
|
84
89
|
getPageSlugsForDepth(parentRouteId?: string): string[];
|
|
90
|
+
getDisplayNamesForDepth(parentRouteId?: string): string[];
|
|
85
91
|
getParentRouteIdForPage(pageId: string): string | undefined;
|
|
86
92
|
getParentRouteIdForRoute(routeId: string): string | undefined;
|
|
87
93
|
getFirstOrDefaultRoute(): import("../../../../brand-page").NavRoute;
|
|
@@ -94,7 +100,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
94
100
|
setNav({ nav }: {
|
|
95
101
|
nav: import("../../../../brand-page").NavModel;
|
|
96
102
|
}): void;
|
|
97
|
-
addNavRoute(newRoute: import("../../../../brand-page").NavRoute, parentRouteId?: string): void;
|
|
103
|
+
addNavRoute(newRoute: import("../../../../brand-page").NavRoute, parentRouteId?: string, insertAfterRouteId?: string): void;
|
|
98
104
|
removeNavRoute(routeId: string): void;
|
|
99
105
|
onDisplayNameChange(routeId: string, newDisplayName: string): void;
|
|
100
106
|
setRouteTitleAndSlugByPageId(pageId: string, displayName: string, slug: string): void;
|
|
@@ -39,6 +39,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
39
39
|
fields: Record<string, import("../../../content-settings/models").ContentItemFieldSettings>;
|
|
40
40
|
};
|
|
41
41
|
}[];
|
|
42
|
+
isHidden?: boolean | undefined;
|
|
42
43
|
};
|
|
43
44
|
pageTitle: string;
|
|
44
45
|
pageTitleError: string | null;
|
|
@@ -4151,6 +4152,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
4151
4152
|
setPages({ pages }: {
|
|
4152
4153
|
pages: ContentPage[];
|
|
4153
4154
|
}): void;
|
|
4155
|
+
syncPagesWithOrders(): void;
|
|
4156
|
+
showPageLimitUpgradeModalAsync(ctaLocation: string): Promise<void>;
|
|
4154
4157
|
setNewPageModalVisibilityAsync({ isNewPageModalVisible, parentPageId, }: {
|
|
4155
4158
|
isNewPageModalVisible: boolean;
|
|
4156
4159
|
parentPageId?: string;
|
|
@@ -4200,6 +4203,9 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
4200
4203
|
title: string;
|
|
4201
4204
|
newPageParentId?: string;
|
|
4202
4205
|
}): ContentPage;
|
|
4206
|
+
duplicatePage({ pageId, }: {
|
|
4207
|
+
pageId: string;
|
|
4208
|
+
}): Promise<ContentPage | undefined>;
|
|
4203
4209
|
setSelectedSectionName({ sectionName, }: {
|
|
4204
4210
|
sectionName?: PageSettingsSection;
|
|
4205
4211
|
}): void;
|
|
@@ -4216,6 +4222,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
4216
4222
|
hasVisibleNavigation(): boolean;
|
|
4217
4223
|
}, {
|
|
4218
4224
|
getPageSlugsForDepth(parentRouteId?: string): string[];
|
|
4225
|
+
getDisplayNamesForDepth(parentRouteId?: string): string[];
|
|
4219
4226
|
getParentRouteIdForPage(pageId: string): string | undefined;
|
|
4220
4227
|
getParentRouteIdForRoute(routeId: string): string | undefined;
|
|
4221
4228
|
getFirstOrDefaultRoute(): import("@brand-page").NavRoute;
|
|
@@ -4228,7 +4235,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
4228
4235
|
setNav({ nav }: {
|
|
4229
4236
|
nav: import("@brand-page").NavModel;
|
|
4230
4237
|
}): void;
|
|
4231
|
-
addNavRoute(newRoute: import("@brand-page").NavRoute, parentRouteId?: string): void;
|
|
4238
|
+
addNavRoute(newRoute: import("@brand-page").NavRoute, parentRouteId?: string, insertAfterRouteId?: string): void;
|
|
4232
4239
|
removeNavRoute(routeId: string): void;
|
|
4233
4240
|
onDisplayNameChange(routeId: string, newDisplayName: string): void;
|
|
4234
4241
|
setRouteTitleAndSlugByPageId(pageId: string, displayName: string, slug: string): void;
|
|
@@ -5169,6 +5176,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
5169
5176
|
sendWebsiteManageAddPageEvent: (pageName: string, isSubPageMode?: boolean) => void;
|
|
5170
5177
|
sendWebsiteManageRemovePageEvent: (pageName: string, isRouteNested?: boolean) => void;
|
|
5171
5178
|
sendWebsiteManageReorderPageEvent: (pageName: string) => void;
|
|
5179
|
+
sendWebsiteManageDuplicatePageEvent: (sourcePageName: string, isSubPage?: boolean) => void;
|
|
5172
5180
|
sendPublishOptionsForFreeEvent: () => void;
|
|
5173
5181
|
sendPublishOptionsForPremiumEvent: () => void;
|
|
5174
5182
|
sendPublishOptionsForExitEvent: () => void;
|
|
@@ -5211,6 +5219,7 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
5211
5219
|
isRootPage(index: number): boolean;
|
|
5212
5220
|
onAddSubPageSelected(pageId: string): Promise<void>;
|
|
5213
5221
|
onAddNewPageSelected(): Promise<void>;
|
|
5222
|
+
onDuplicatePageSelected(pageId: string): Promise<void>;
|
|
5214
5223
|
onChangePageVisibility(pageId: string): void;
|
|
5215
5224
|
onDeletePageSelected(pageId: string): void;
|
|
5216
5225
|
onConfirmDeletePage(pageId: string): void;
|
|
@@ -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;
|
|
@@ -4790,6 +4795,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
4790
4795
|
fields: Record<string, ContentItemFieldSettings>;
|
|
4791
4796
|
};
|
|
4792
4797
|
}[];
|
|
4798
|
+
isHidden?: boolean | undefined;
|
|
4793
4799
|
}[];
|
|
4794
4800
|
}, {
|
|
4795
4801
|
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 & 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
|
+
});
|