@designcrowd/library.brand-page 5.19.1-error-html2canvas-2 → 5.19.1-id-based-items-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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-DmKe7W8L.js → CreatedOnBrandCrowd-de-de-0PcGqy0N.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-en-us-D2EeiNep.js → CreatedOnBrandCrowd-en-us-DB8wcFMI.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-es-es-BqVwNY1D.js → CreatedOnBrandCrowd-es-es-DVWQ243C.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-C756a27-.js → CreatedOnBrandCrowd-fr-ca-D-M_L0Du.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-C756a27-.js → CreatedOnBrandCrowd-fr-fr-D-M_L0Du.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-FNHAnZUw.js → CreatedOnBrandCrowd-pt-br-CNpzknxU.js} +3 -3
- package/lib/src/{CreatedOnBrandCrowd-pt-br-FNHAnZUw.js → CreatedOnBrandCrowd-pt-pt-CNpzknxU.js} +3 -3
- package/lib/src/{CreatedOnDcom-de-de-8AZZFmDt.js → CreatedOnDcom-de-de-DMJoD5wE.js} +3 -3
- package/lib/src/{CreatedOnDcom-en-us-DizTP32i.js → CreatedOnDcom-en-us-B-F3Pmjv.js} +3 -3
- package/lib/src/{CreatedOnDcom-es-es-qnp9_n_A.js → CreatedOnDcom-es-es-DmLk-cA2.js} +3 -3
- package/lib/src/{CreatedOnDcom-fr-ca-jUs352uK.js → CreatedOnDcom-fr-ca-ntnsCzvA.js} +3 -3
- package/lib/src/{CreatedOnDcom-fr-fr-CTxXtHyk.js → CreatedOnDcom-fr-fr-DFcO0gHL.js} +3 -3
- package/lib/src/{CreatedOnDcom-pt-br-nQHj8P7d.js → CreatedOnDcom-pt-br-CtuJoVXW.js} +3 -3
- package/lib/src/{CreatedOnDcom-pt-pt-DJmVYQuL.js → CreatedOnDcom-pt-pt-B7shZVVn.js} +3 -3
- package/lib/src/brand-page/clients/brand-page.client.d.ts +9 -9
- package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +2 -1
- package/lib/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.d.ts +2 -4
- package/lib/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.js +0 -1
- package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.d.ts +2 -4
- package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.js +4 -5
- package/lib/src/brand-page/components/page/Page.mixin.d.ts +1 -1
- package/lib/src/brand-page/components/page/PageComponent.mixin.d.ts +1 -1
- package/lib/src/brand-page/components/payment/Payment.mixin.d.ts +3 -3
- package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +13 -13
- package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +13 -13
- package/lib/src/brand-page/constants/sections/footer-display.constants.js +1 -1
- package/lib/src/brand-page/models/index.d.ts +13 -2
- package/lib/src/brand-page/models/index.js +0 -3
- package/lib/src/brand-page/models/payment.model.d.ts +1 -1
- package/lib/src/brand-page/utils/personalisation.util.js +14 -72
- package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +19 -21
- package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/admin-background-settings/AdminBackgroundSettings.mixin.d.ts +10 -16
- package/lib/src/brand-page-maker/components/admin-logo-settings/AdminLogoSettings.mixin.d.ts +3 -3
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +170 -181
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +0 -3
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +0 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +12 -12
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.js +2 -2
- package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.js +7 -0
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +149 -148
- package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +21 -21
- package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.js +1 -1
- package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.d.ts +2 -4
- package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.d.ts +2 -4
- package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.d.ts +2 -5
- package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.d.ts +2 -4
- package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/device/Device.mixin.d.ts +2 -4
- package/lib/src/brand-page-maker/components/device/Device.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.d.ts +2 -4
- package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +3 -3
- package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +11 -11
- package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +25 -27
- package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.js +0 -1
- package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +27 -27
- package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.js +1 -1
- package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +12 -17
- package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +12 -12
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +32 -30
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +5 -16
- package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +11 -11
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +1 -1
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +6 -6
- package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +2 -2
- package/lib/src/brand-page-maker/constants/config-settings-constant.d.ts +6 -6
- package/lib/src/brand-page-maker/constants/config-settings-constant.js +1 -1
- package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.d.ts +1 -1
- package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.js +1 -1
- package/lib/src/brand-page-maker/models/content-settings.d.ts +1 -1
- package/lib/src/brand-page-maker/models/navigation-style-configs.js +1 -1
- package/lib/src/brand-page-maker/routing/brand-page-maker-router.d.ts +2 -1
- package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +378 -366
- package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +4 -5
- package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +5 -8
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +1 -3
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +3 -7
- package/lib/src/brand-page-maker/stores/history/history-store.d.ts +15 -15
- package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +0 -6
- package/lib/src/brand-page-maker/stores/ui/ui-store.js +0 -8
- package/lib/src/brand-page-maker/utils/content-settings.util.js +1 -1
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +1 -1
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +4 -4
- package/lib/src/clients/brand-page-client.d.ts +15 -16
- package/lib/src/clients/design-template-client.d.ts +0 -3
- package/lib/src/clients/media-client.d.ts +2 -2
- package/lib/src/clients/models.d.ts +0 -2
- package/lib/src/composables/useSnapshot.js +18 -18
- package/lib/src/css/library.brand-page-brandCrowd.min.css +324 -342
- package/lib/src/css/library.brand-page-brandPage.min.css +324 -620
- package/lib/src/css/library.brand-page-designCom.min.css +324 -342
- package/lib/src/css/library.brand-page-preview.min.css +324 -598
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.mjs +15 -16
- package/lib/src/{lib-DityivZa.js → lib-BQmJBVbS.js} +33238 -36458
- package/lib/src/lib.js +1 -1
- package/lib/src/metadata-builder/builders/footer-builder.js +56 -91
- package/lib/src/metadata-builder/builders/item-id-utils.d.ts +41 -0
- package/lib/src/metadata-builder/builders/item-id-utils.js +68 -0
- package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +57 -68
- package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +58 -93
- package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +66 -99
- package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +57 -17
- package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +51 -85
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +49 -74
- package/lib/src/metadata-builder/builders/shape-color-utils.js +1 -1
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +13 -4
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +66 -48
- package/lib/src/plugins/google-maps-plugin/google-maps-plugin.d.ts +1 -1
- package/lib/src/plugins/google-maps-plugin/google-maps-plugin.js +8 -8
- package/lib/src/shared/background-settings/components/background-shapes/BackgroundShapeSettings.mixin.d.ts +13 -13
- package/lib/src/shared/background-settings/store/background-settings-store.d.ts +3 -3
- package/lib/src/shared/color-picker/components/ColorPicker.mixin.d.ts +2 -4
- package/lib/src/shared/color-picker/components/ColorPicker.mixin.js +2 -2
- package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.d.ts +2 -4
- package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.js +0 -1
- package/lib/src/shared/confirm-modal/ConfirmModal.mixin.d.ts +2 -5
- package/lib/src/shared/confirm-modal/ConfirmModal.mixin.js +0 -1
- package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +129 -131
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +1 -2
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +110 -111
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +81 -84
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.js +0 -5
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +109 -117
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +14 -1
- package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +70 -72
- package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +3 -4
- package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.d.ts +4 -4
- package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.d.ts +1 -3
- package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.js +0 -1
- package/lib/src/shared/content-settings/components/images-field/ImagesField.mixin.d.ts +2 -2
- package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.d.ts +3 -4
- package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.js +1 -1
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +40 -40
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigOptionField.mixin.d.ts +6 -5
- package/lib/src/shared/content-settings/components/shape-field/ShapeField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/slider-field/SliderField.mixin.d.ts +2 -2
- package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -1
- package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +2 -1
- package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -1
- package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.d.ts +28 -28
- package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/toggle-field/ToggleField.mixin.d.ts +2 -2
- package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/store/content-settings-store.d.ts +39 -39
- package/lib/src/shared/content-settings/utils.js +1 -0
- package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.d.ts +4 -6
- package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.js +0 -1
- package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.d.ts +2 -4
- package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.js +0 -1
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +21 -29
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +1 -1
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +51 -50
- package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.d.ts +4 -6
- package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.js +0 -1
- package/lib/src/shared/logo-settings/store/editable-logo-store.js +1 -1
- package/lib/src/shared/logo-settings/store/logo-settings-store.d.ts +1 -1
- package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +5 -13
- package/lib/src/shared/logo-versions/LogoVersions.mixin.js +0 -1
- package/lib/src/shared/models.d.ts +0 -2
- package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +6 -14
- package/lib/src/shared/my-designs/MyDesigns.mixin.js +0 -1
- package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +5 -14
- package/lib/src/shared/my-uploads/MyMedia.mixin.js +0 -1
- package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +5 -14
- package/lib/src/shared/my-uploads/MyUploads.mixin.js +0 -1
- package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.d.ts +3 -5
- package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.js +0 -1
- package/lib/src/shared/navigation-style-settings/store/index.d.ts +2 -2
- package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -2
- package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +28 -27
- package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.js +2 -0
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +179 -188
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +177 -181
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +1 -1
- package/lib/src/shared/page-settings/store/page-settings-store.d.ts +9 -9
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +39 -39
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.js +1 -0
- package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +18 -18
- package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +6 -6
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +60 -66
- package/lib/src/shared/style-settings/store/style-settings-store.d.ts +1 -1
- package/lib/src/shared/style-settings/store/style-settings-store.js +1 -1
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +450 -20
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.js +2 -2
- package/lib/src/shared/template-settings/models.d.ts +0 -1
- package/lib/src/shared/template-settings/store/template-settings-store.d.ts +0 -4
- package/lib/src/shared/template-settings/store/template-settings-store.js +11 -79
- package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +4 -9
- package/package.json +57 -61
- package/src/brand-page/components/about-section/AboutSection.vue.test.ts +5 -7
- package/src/brand-page/components/brand-page-footer/BrandPageFooter.vue +1 -1
- package/src/brand-page/components/image/Image.mixin.ts +1 -1
- package/src/brand-page/components/image/Image.test.ts +1 -1
- package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +2 -1
- package/src/brand-page/components/images-section/ImagesSection.vue +1 -0
- package/src/brand-page/components/logo/LogoRender.vue +188 -188
- package/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.ts +0 -1
- package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +6 -15
- package/src/brand-page/components/page-footer/PageFooter.vue +0 -1
- package/src/brand-page/components/page-header/HeaderLayout.vue +6 -12
- package/src/brand-page/components/page-header/PageHeader.vue +0 -1
- package/src/brand-page/components/shapes-section/ShapesSection.vue +1 -1
- package/src/brand-page/components/shop/CartModal.vue +3 -1
- package/src/brand-page/components/shop/FeaturedDisplay.vue +2 -2
- package/src/brand-page/components/shop/PaymentModal.vue +5 -1
- package/src/brand-page/components/shop/ProductDetailModal.vue +1 -1
- package/src/brand-page/components/shop/Shop.vue +1 -1
- package/src/brand-page/components/shop/TextDisplay.vue +1 -1
- package/src/brand-page/components/shop/ThumbnailDisplay.vue +2 -2
- package/src/brand-page/components/testimonial-section/TestimonialSection.vue +3 -3
- package/src/brand-page/components/text-wrapper/TextWrapper.vue +1 -3
- package/src/brand-page/constants/sections/footer-display.constants.ts +1 -1
- package/src/brand-page/models/index.ts +12 -4
- package/src/brand-page/models/payment.model.ts +1 -2
- package/src/brand-page/models/shop.model.ts +2 -0
- package/src/brand-page/models/user-personalisation.model.ts +2 -1
- package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +0 -3
- package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +0 -1
- package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +0 -2
- package/src/brand-page/utils/migration/migration-01_1.ts +1 -1
- package/src/brand-page/utils/migration/migration-02.ts +1 -0
- package/src/brand-page/utils/migration/migration-03.ts +1 -0
- package/src/brand-page/utils/migration/migration-04.ts +1 -0
- package/src/brand-page/utils/migration/migration-05.ts +5 -1
- package/src/brand-page/utils/migration/migration-05_2.ts +1 -1
- package/src/brand-page/utils/migration/migration-08.test.ts +1 -1
- package/src/brand-page/utils/personalisation.util.test.ts +0 -35
- package/src/brand-page/utils/personalisation.util.ts +23 -119
- package/src/brand-page/view-models.ts +56 -38
- package/src/brand-page-maker/assessors/banner-section-description-visibility.assessor.ts +6 -6
- package/src/brand-page-maker/assessors/color-replacement-icon-visibility.assessor.ts +2 -2
- package/src/brand-page-maker/assessors/color-replacement-profile-visibility.assessor.ts +2 -2
- package/src/brand-page-maker/assessors/color-replacement-visibility.assessor.ts +2 -2
- package/src/brand-page-maker/assessors/contact-form-visibility.assessor.ts +14 -14
- package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -1
- package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +0 -1
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +1 -5
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +0 -1
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.ts +5 -2
- package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +5 -0
- package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.ts +8 -0
- package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-site-template.examples.ts +2 -0
- package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.vue +0 -1
- package/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.ts +1 -1
- package/src/brand-page-maker/components/color-palette/BpColorPalette.vue +2 -1
- package/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.ts +0 -1
- package/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.ts +0 -1
- package/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.ts +0 -1
- package/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.ts +0 -1
- package/src/brand-page-maker/components/device/Device.mixin.ts +0 -1
- package/src/brand-page-maker/components/navigation/Navigation.mixin.ts +0 -1
- package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +1 -3
- package/src/brand-page-maker/components/sensitive-content/SensitiveContent.vue +4 -1
- package/src/brand-page-maker/components/seo-settings/DeleteDomainModal.vue +1 -1
- package/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.ts +0 -1
- package/src/brand-page-maker/components/seo-settings/SeoPageSettings.vue +1 -1
- package/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.ts +1 -0
- package/src/brand-page-maker/components/seo-settings/UrlSettings.vue +1 -0
- package/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.vue +5 -1
- package/src/brand-page-maker/components/shop/AddOrEditProductContainer.vue +1 -1
- package/src/brand-page-maker/components/shop/ReplaceImageSettingsForShop.vue +1 -1
- package/src/brand-page-maker/components/social-share/SocialShare.vue +1 -1
- package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +8 -22
- package/src/brand-page-maker/components/template-preview/TemplatePreview.vue +10 -5
- package/src/brand-page-maker/components/video-play/VideoPlayButton.vue +1 -1
- package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +1 -1
- package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +6 -6
- package/src/brand-page-maker/constants/config-settings-constant.ts +3 -3
- package/src/brand-page-maker/convertors/text-image-display-style.convertor.ts +1 -1
- package/src/brand-page-maker/models/button-styles-config.ts +2 -4
- package/src/brand-page-maker/models/content-settings-configs.ts +2 -4
- package/src/brand-page-maker/models/navigation-style-configs.ts +6 -5
- package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.ts +1 -0
- package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +5 -8
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +3 -1
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +4 -12
- package/src/brand-page-maker/stores/ui/ui-store.ts +0 -16
- package/src/brand-page-maker/utils/content-settings.util.ts +1 -1
- package/src/brand-page-maker/utils/rich-text-editor.util.ts +4 -4
- package/src/clients/design-template-client.ts +0 -3
- package/src/clients/models.ts +0 -2
- package/src/composables/useSnapshot.ts +22 -19
- package/src/lib.ts +0 -1
- package/src/metadata-builder/builders/footer-builder.test.ts +245 -84
- package/src/metadata-builder/builders/footer-builder.ts +81 -130
- package/src/metadata-builder/builders/item-id-utils.test.ts +118 -0
- package/src/metadata-builder/builders/item-id-utils.ts +85 -0
- package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +62 -88
- package/src/metadata-builder/builders/section-builders/images-section-builder.ts +66 -129
- package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +105 -160
- package/src/metadata-builder/builders/section-builders/social-links-builder.ts +65 -20
- package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +83 -146
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +70 -122
- package/src/metadata-builder/builders/shape-color-utils.ts +2 -2
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +13 -4
- package/src/metadata-builder/parsers/section-tool-arguments.ts +66 -48
- package/src/plugins/google-maps-plugin/google-maps-plugin.ts +8 -8
- package/src/shared/background-settings/components/BackgroundSettings.vue +1 -1
- package/src/shared/background-settings/components/background-color/BackgroundColorSettings.vue +1 -1
- package/src/shared/color-picker/components/ColorPicker.mixin.ts +7 -2
- package/src/shared/color-picker/components/ColorPickerSwatch.mixin.ts +0 -1
- package/src/shared/common/NumberInput.vue +0 -1
- package/src/shared/common/SideBarSettingsHeader.vue +2 -2
- package/src/shared/confirm-modal/ConfirmModal.mixin.ts +0 -1
- package/src/shared/content-settings/components/booking-url-field/BookingUrlField.vue +0 -1
- package/src/shared/content-settings/components/button-configuration-field/ButtonConfigurationField.vue +0 -1
- package/src/shared/content-settings/components/color-field/ColorField.vue +10 -3
- package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +1 -1
- package/src/shared/content-settings/components/content-settings/ContentSettingsLockedButton.vue +0 -1
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.ts +0 -5
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +14 -1
- package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +6 -4
- package/src/shared/content-settings/components/icon-library/IconLibrary.mixin.ts +0 -1
- package/src/shared/content-settings/components/items-field/ItemsField.mixin.ts +1 -1
- package/src/shared/content-settings/components/logo-selection-field/LogoSelectionField.vue +1 -1
- package/src/shared/content-settings/components/logo-size-field/LogoSizeSliderField.vue +1 -1
- package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +1 -1
- package/src/shared/content-settings/components/personalisation-role-field/PersonalisationRoleField.vue +4 -4
- package/src/shared/content-settings/components/text-image-field/TextImageField.vue +8 -2
- package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -1
- package/src/shared/content-settings/models.ts +2 -3
- package/src/shared/content-settings/utils.ts +1 -0
- package/src/shared/dropzone-image-uploader/components/DropzoneImageUploader.vue +0 -1
- package/src/shared/ecommerce-settings/store/cart-store.test.ts +1 -1
- package/src/shared/font-family-selector/FontFamilySelector.mixin.ts +0 -1
- package/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.ts +0 -1
- package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +1 -1
- package/src/shared/logo-settings/components/LogoSettings.mixin.ts +1 -0
- package/src/shared/logo-settings/components/LogoSettingsHeader.mixin.ts +0 -1
- package/src/shared/logo-settings/components/LogoStyleSettingsWithState.vue +2 -2
- package/src/shared/logo-settings/components/LogoTemplateSearch.vue +1 -1
- package/src/shared/logo-settings/store/editable-logo-store.ts +1 -1
- package/src/shared/logo-settings/store/logo-settings-store.test.ts +4 -4
- package/src/shared/logo-versions/LogoVersions.mixin.ts +0 -1
- package/src/shared/models.ts +0 -6
- package/src/shared/my-designs/MyDesigns.mixin.ts +0 -1
- package/src/shared/my-uploads/MyMedia.mixin.ts +0 -1
- package/src/shared/my-uploads/MyMediaSelectionItem.vue +1 -2
- package/src/shared/my-uploads/MyUploads.mixin.ts +0 -1
- package/src/shared/my-uploads/models.ts +2 -1
- package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.ts +0 -1
- package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +1 -1
- package/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.ts +3 -0
- package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +1 -1
- package/src/shared/page-settings/components/page-settings/PageSettings.vue +2 -1
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderDisplayStyle.vue +1 -1
- package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +1 -1
- package/src/shared/page-settings/store/page-settings-store.test.ts +8 -4
- package/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.ts +1 -0
- package/src/shared/shape-library/ShapeLibrary.vue +2 -2
- package/src/shared/style-settings/store/style-settings-store.ts +1 -1
- package/src/shared/template-settings/components/TemplatesSettings.mixin.ts +2 -2
- package/src/shared/template-settings/models.ts +0 -1
- package/src/shared/template-settings/store/template-settings-store.ts +18 -105
- package/src/shared/unsplash-library/UnsplashLibrary.vue +6 -2
- package/src/shared/video-library/VideoLibrary.vue +1 -1
- package/lib/src/brand-page/components/banner-section/BannerSection.mixin.d.ts +0 -103
- package/lib/src/brand-page/components/banner-section/BannerSection.mixin.js +0 -178
- package/src/brand-page/components/banner-section/BannerSection.mixin.ts +0 -210
- package/src/brand-page-maker/components/template-preview/TemplatePreviewAiBadge.vue +0 -12
|
@@ -14,6 +14,10 @@ import { FooterBuilder } from './footer-builder';
|
|
|
14
14
|
* The fix routes copyright style writes through `applyDetachedStyleOverride`,
|
|
15
15
|
* which resolves the preset in and clears `textStyleType` so the per-element
|
|
16
16
|
* override sticks.
|
|
17
|
+
*
|
|
18
|
+
* BP-5603: social link mutations converted from index-based to id-based
|
|
19
|
+
* (item_id / move_before_item_id / _remove_social_link_ids), mirroring the
|
|
20
|
+
* section-level move_before_content_id contract one level deeper.
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
23
|
const textStyleOptions = {
|
|
@@ -42,6 +46,37 @@ const makeModel = (copyrightStyle: Record<string, unknown>): BrandPageModel =>
|
|
|
42
46
|
pageFooterMobile: { copyrightStyle: { ...copyrightStyle } },
|
|
43
47
|
}) as unknown as BrandPageModel;
|
|
44
48
|
|
|
49
|
+
const makeModelWithLinks = (
|
|
50
|
+
links: { id?: string; imageUrl: string; url: string }[],
|
|
51
|
+
): BrandPageModel =>
|
|
52
|
+
({
|
|
53
|
+
styles: { textStyles: { textStyleOptions } },
|
|
54
|
+
pageFooterDesktop: {
|
|
55
|
+
copyrightStyle: {},
|
|
56
|
+
socialLinks: links.map((l) => ({
|
|
57
|
+
id: l.id,
|
|
58
|
+
imageUrl: l.imageUrl,
|
|
59
|
+
url: l.url,
|
|
60
|
+
imageTitle: '',
|
|
61
|
+
width: 0,
|
|
62
|
+
height: 0,
|
|
63
|
+
imageLink: '',
|
|
64
|
+
})),
|
|
65
|
+
},
|
|
66
|
+
pageFooterMobile: {
|
|
67
|
+
copyrightStyle: {},
|
|
68
|
+
socialLinks: links.map((l) => ({
|
|
69
|
+
id: l.id,
|
|
70
|
+
imageUrl: l.imageUrl,
|
|
71
|
+
url: l.url,
|
|
72
|
+
imageTitle: '',
|
|
73
|
+
width: 0,
|
|
74
|
+
height: 0,
|
|
75
|
+
imageLink: '',
|
|
76
|
+
})),
|
|
77
|
+
},
|
|
78
|
+
}) as unknown as BrandPageModel;
|
|
79
|
+
|
|
45
80
|
const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
|
|
46
81
|
new FooterBuilder({
|
|
47
82
|
id: 'f1',
|
|
@@ -49,6 +84,10 @@ const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
|
|
|
49
84
|
payload: args,
|
|
50
85
|
}).buildAsync(model);
|
|
51
86
|
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Copyright font (BP-5420)
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
52
91
|
describe('FooterBuilder — copyright font vs global style', () => {
|
|
53
92
|
it('detaches from the global preset: applies the font, clears textStyleType, retains preset values', async () => {
|
|
54
93
|
const model = makeModel({
|
|
@@ -75,139 +114,261 @@ describe('FooterBuilder — copyright font vs global style', () => {
|
|
|
75
114
|
});
|
|
76
115
|
});
|
|
77
116
|
|
|
78
|
-
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Social links — id-based mutations (BP-5603)
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
79
120
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
121
|
+
describe('FooterBuilder — social links id-based mutations', () => {
|
|
122
|
+
it('backfills ids on legacy links that have none', async () => {
|
|
123
|
+
// Links without ids (legacy persisted data)
|
|
124
|
+
const model = makeModelWithLinks([
|
|
125
|
+
{ imageUrl: 'facebook', url: 'https://fb.com' },
|
|
126
|
+
{ imageUrl: 'instagram', url: 'https://ig.com' },
|
|
127
|
+
]);
|
|
84
128
|
|
|
85
|
-
|
|
86
|
-
* Footers migrated from the legacy single `footerStyles` model
|
|
87
|
-
* (migration-01) split into desktop + mobile via a shallow spread, leaving
|
|
88
|
-
* both viewports' `socialLinks` pointing at ONE shared array. `cloneDeep`
|
|
89
|
-
* preserves that shared reference. This factory reproduces that shape.
|
|
90
|
-
*/
|
|
91
|
-
const makeSharedSocialModel = (links: Link[]): BrandPageModel => {
|
|
92
|
-
const shared = links.map((l) => ({ ...l }));
|
|
129
|
+
const { model: out } = await run({}, model);
|
|
93
130
|
|
|
94
|
-
|
|
95
|
-
styles: { textStyles: { textStyleOptions } },
|
|
96
|
-
pageFooterDesktop: { socialLinks: shared },
|
|
97
|
-
pageFooterMobile: { socialLinks: shared },
|
|
98
|
-
} as unknown as BrandPageModel;
|
|
99
|
-
};
|
|
131
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
100
132
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
pageFooterMobile: { socialLinks: links.map((l) => ({ ...l })) },
|
|
106
|
-
}) as unknown as BrandPageModel;
|
|
133
|
+
expect(links[0].id).toBeTruthy();
|
|
134
|
+
expect(links[1].id).toBeTruthy();
|
|
135
|
+
expect(links[0].id).not.toBe(links[1].id);
|
|
136
|
+
});
|
|
107
137
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// Reproduces the bug: a single requested removal index dropped TWO links
|
|
113
|
-
// because both viewports shared one array and the builder spliced it twice.
|
|
114
|
-
it('removes exactly one link per requested index even when viewports share the array', async () => {
|
|
115
|
-
const model = makeSharedSocialModel([
|
|
116
|
-
link('facebook'),
|
|
117
|
-
link('instagram'),
|
|
118
|
-
link('x'),
|
|
119
|
-
link('youtube'),
|
|
138
|
+
it('updates an existing link in place by item_id', async () => {
|
|
139
|
+
const model = makeModelWithLinks([
|
|
140
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
141
|
+
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
120
142
|
]);
|
|
121
143
|
|
|
122
144
|
const { model: out } = await run(
|
|
123
|
-
{
|
|
145
|
+
{
|
|
146
|
+
social_links: [
|
|
147
|
+
{ item_id: 'link-1', url: 'https://facebook.com/mybrand' },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
124
150
|
model,
|
|
125
151
|
);
|
|
126
152
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
153
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
154
|
+
|
|
155
|
+
expect(links).toHaveLength(2);
|
|
156
|
+
expect(links[0].url).toBe('https://facebook.com/mybrand');
|
|
157
|
+
expect(links[0].id).toBe('link-1');
|
|
158
|
+
// untouched link unchanged
|
|
159
|
+
expect(links[1].id).toBe('link-2');
|
|
160
|
+
expect(links[1].url).toBe('https://ig.com');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('updates social_link_icon on an existing link', async () => {
|
|
164
|
+
const model = makeModelWithLinks([
|
|
165
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
131
166
|
]);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
167
|
+
|
|
168
|
+
const { model: out } = await run(
|
|
169
|
+
{
|
|
170
|
+
social_links: [{ item_id: 'link-1', social_link_icon: 'facebook-v2' }],
|
|
171
|
+
},
|
|
172
|
+
model,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(out.pageFooterDesktop!.socialLinks[0].imageUrl).toBe('facebook-v2');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('skips unknown item_id silently', async () => {
|
|
179
|
+
const model = makeModelWithLinks([
|
|
180
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
136
181
|
]);
|
|
182
|
+
|
|
183
|
+
const { model: out } = await run(
|
|
184
|
+
{
|
|
185
|
+
social_links: [{ item_id: 'no-such-id', url: 'https://nowhere.com' }],
|
|
186
|
+
},
|
|
187
|
+
model,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
191
|
+
|
|
192
|
+
expect(links).toHaveLength(1);
|
|
193
|
+
expect(links[0].url).toBe('https://fb.com');
|
|
137
194
|
});
|
|
138
195
|
|
|
139
|
-
it('
|
|
140
|
-
const model =
|
|
141
|
-
link
|
|
142
|
-
link('instagram'),
|
|
143
|
-
link('x'),
|
|
196
|
+
it('inserts a new link (no item_id) appended by default', async () => {
|
|
197
|
+
const model = makeModelWithLinks([
|
|
198
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
144
199
|
]);
|
|
145
200
|
|
|
146
201
|
const { model: out } = await run(
|
|
147
|
-
{
|
|
202
|
+
{
|
|
203
|
+
social_links: [
|
|
204
|
+
{ social_link_icon: 'instagram', url: 'https://ig.com' },
|
|
205
|
+
],
|
|
206
|
+
},
|
|
148
207
|
model,
|
|
149
208
|
);
|
|
150
209
|
|
|
151
|
-
|
|
152
|
-
|
|
210
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
211
|
+
|
|
212
|
+
expect(links).toHaveLength(2);
|
|
213
|
+
expect(links[1].imageUrl).toBe('instagram');
|
|
214
|
+
expect(links[1].id).toBeTruthy();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('inserts a new link before an anchor via move_before_item_id', async () => {
|
|
218
|
+
const model = makeModelWithLinks([
|
|
219
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
220
|
+
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const { model: out } = await run(
|
|
224
|
+
{
|
|
225
|
+
social_links: [
|
|
226
|
+
{
|
|
227
|
+
social_link_icon: 'x',
|
|
228
|
+
url: 'https://x.com',
|
|
229
|
+
move_before_item_id: 'link-2',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
model,
|
|
153
234
|
);
|
|
235
|
+
|
|
236
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
237
|
+
|
|
238
|
+
expect(links).toHaveLength(3);
|
|
239
|
+
expect(links[0].id).toBe('link-1');
|
|
240
|
+
expect(links[1].imageUrl).toBe('x');
|
|
241
|
+
expect(links[2].id).toBe('link-2');
|
|
154
242
|
});
|
|
155
243
|
|
|
156
|
-
it('
|
|
157
|
-
const model =
|
|
158
|
-
link
|
|
159
|
-
link
|
|
160
|
-
link('x'),
|
|
161
|
-
link('youtube'),
|
|
244
|
+
it('inserts a new link at end via move_before_item_id "__end__"', async () => {
|
|
245
|
+
const model = makeModelWithLinks([
|
|
246
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
247
|
+
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
162
248
|
]);
|
|
163
249
|
|
|
164
250
|
const { model: out } = await run(
|
|
165
|
-
{
|
|
251
|
+
{
|
|
252
|
+
social_links: [
|
|
253
|
+
{
|
|
254
|
+
social_link_icon: 'x',
|
|
255
|
+
url: 'https://x.com',
|
|
256
|
+
move_before_item_id: '__end__',
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
},
|
|
166
260
|
model,
|
|
167
261
|
);
|
|
168
262
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
263
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
264
|
+
|
|
265
|
+
expect(links).toHaveLength(3);
|
|
266
|
+
expect(links[2].imageUrl).toBe('x');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('moves an existing link before an anchor via move_before_item_id', async () => {
|
|
270
|
+
const model = makeModelWithLinks([
|
|
271
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
272
|
+
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
273
|
+
{ id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
|
|
172
274
|
]);
|
|
173
|
-
|
|
275
|
+
|
|
276
|
+
const { model: out } = await run(
|
|
277
|
+
{
|
|
278
|
+
social_links: [{ item_id: 'link-3', move_before_item_id: 'link-1' }],
|
|
279
|
+
},
|
|
280
|
+
model,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
284
|
+
|
|
285
|
+
expect(links.map((l) => l.id)).toEqual(['link-3', 'link-1', 'link-2']);
|
|
174
286
|
});
|
|
175
287
|
|
|
176
|
-
it('removes
|
|
177
|
-
const model =
|
|
178
|
-
link
|
|
179
|
-
link
|
|
180
|
-
link
|
|
288
|
+
it('removes links by id', async () => {
|
|
289
|
+
const model = makeModelWithLinks([
|
|
290
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
291
|
+
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
292
|
+
{ id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
|
|
181
293
|
]);
|
|
182
294
|
|
|
183
295
|
const { model: out } = await run(
|
|
184
|
-
{
|
|
296
|
+
{ _remove_social_link_ids: ['link-1', 'link-3'] },
|
|
185
297
|
model,
|
|
186
298
|
);
|
|
187
299
|
|
|
188
|
-
|
|
189
|
-
|
|
300
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
301
|
+
|
|
302
|
+
expect(links).toHaveLength(1);
|
|
303
|
+
expect(links[0].id).toBe('link-2');
|
|
190
304
|
});
|
|
191
305
|
|
|
192
|
-
it('
|
|
193
|
-
const model =
|
|
306
|
+
it('_replace_social_links rebuilds the entire list and assigns fresh ids', async () => {
|
|
307
|
+
const model = makeModelWithLinks([
|
|
308
|
+
{ id: 'old-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
309
|
+
]);
|
|
194
310
|
|
|
195
311
|
const { model: out } = await run(
|
|
196
312
|
{
|
|
197
|
-
|
|
313
|
+
_replace_social_links: true,
|
|
314
|
+
social_links: [
|
|
315
|
+
{ social_link_icon: 'instagram', url: 'https://ig.com' },
|
|
316
|
+
{ social_link_icon: 'x', url: 'https://x.com' },
|
|
317
|
+
],
|
|
198
318
|
},
|
|
199
319
|
model,
|
|
200
320
|
);
|
|
201
321
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
322
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
323
|
+
|
|
324
|
+
expect(links).toHaveLength(2);
|
|
325
|
+
expect(links[0].imageUrl).toBe('instagram');
|
|
326
|
+
expect(links[1].imageUrl).toBe('x');
|
|
327
|
+
// fresh ids assigned, not the old one
|
|
328
|
+
expect(links[0].id).toBeTruthy();
|
|
329
|
+
expect(links[0].id).not.toBe('old-1');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('_replace_social_links with empty social_links clears all', async () => {
|
|
333
|
+
const model = makeModelWithLinks([
|
|
334
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
206
335
|
]);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
336
|
+
|
|
337
|
+
const { model: out } = await run({ _replace_social_links: true }, model);
|
|
338
|
+
|
|
339
|
+
expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('normalises mailto: and tel: prefixes on new links', async () => {
|
|
343
|
+
const model = makeModelWithLinks([]);
|
|
344
|
+
|
|
345
|
+
const { model: out } = await run(
|
|
346
|
+
{
|
|
347
|
+
social_links: [
|
|
348
|
+
{ social_link_icon: 'email', url: 'mailto:me@example.com' },
|
|
349
|
+
{ social_link_icon: 'phone', url: 'tel:+1234567890' },
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
model,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
const links = out.pageFooterDesktop!.socialLinks;
|
|
356
|
+
|
|
357
|
+
expect(links[0].url).toBe('me@example.com');
|
|
358
|
+
expect(links[1].url).toBe('+1234567890');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('applies mutations to both desktop and mobile footers', async () => {
|
|
362
|
+
const model = makeModelWithLinks([
|
|
363
|
+
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
211
364
|
]);
|
|
365
|
+
|
|
366
|
+
const { model: out } = await run(
|
|
367
|
+
{ _remove_social_link_ids: ['link-1'] },
|
|
368
|
+
model,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
|
|
372
|
+
expect(out.pageFooterMobile!.socialLinks).toHaveLength(0);
|
|
212
373
|
});
|
|
213
374
|
});
|
|
@@ -10,16 +10,17 @@ import {
|
|
|
10
10
|
import type {
|
|
11
11
|
UpdateFooterArguments,
|
|
12
12
|
ViewportFooterOverrides,
|
|
13
|
-
FooterSocialLinkItem,
|
|
14
13
|
} from '../parsers/ai-toolcall-parser';
|
|
15
14
|
import type { Logger } from '../logger';
|
|
16
15
|
import type { TextStyleOptions } from '../../brand-page/models/styles.model';
|
|
17
16
|
import { FOOTER_CHECKS } from './section-rules/footer.rules';
|
|
18
17
|
import { applyDetachedStyleOverride } from './section-field-utils';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
import {
|
|
19
|
+
ensureItemIds,
|
|
20
|
+
generateItemId,
|
|
21
|
+
removeItemsByIds,
|
|
22
|
+
repositionItemByAnchor,
|
|
23
|
+
} from './item-id-utils';
|
|
23
24
|
|
|
24
25
|
const normaliseSocialUrl = (url: string): string => {
|
|
25
26
|
for (const prefix of ['mailto:', 'tel:']) {
|
|
@@ -29,113 +30,6 @@ const normaliseSocialUrl = (url: string): string => {
|
|
|
29
30
|
return url;
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
const makeSocialLink = (
|
|
33
|
-
icon: string | undefined,
|
|
34
|
-
url: string | undefined,
|
|
35
|
-
): FooterSocialLink =>
|
|
36
|
-
({
|
|
37
|
-
imageUrl: icon || '',
|
|
38
|
-
url: normaliseSocialUrl(url || ''),
|
|
39
|
-
imageTitle: '',
|
|
40
|
-
width: 0,
|
|
41
|
-
height: 0,
|
|
42
|
-
imageLink: '',
|
|
43
|
-
}) as FooterSocialLink;
|
|
44
|
-
|
|
45
|
-
const hasSocialLinkMutation = (args: UpdateFooterArguments): boolean =>
|
|
46
|
-
args._replace_social_links === true ||
|
|
47
|
-
(args._remove_social_link_indices?.length ?? 0) > 0 ||
|
|
48
|
-
(args._reorder_social_links?.length ?? 0) > 0 ||
|
|
49
|
-
(args.social_links?.length ?? 0) > 0;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Social links are a GLOBAL footer concern — one logical list that both
|
|
53
|
-
* viewports render identically. Derive the new list ONCE from the current one
|
|
54
|
-
* WITHOUT mutating the input, so the caller can assign an independent copy to
|
|
55
|
-
* each viewport.
|
|
56
|
-
*
|
|
57
|
-
* Why this can't mutate per-viewport in place: footers migrated from the legacy
|
|
58
|
-
* single `footerStyles` model (migration-01) share ONE `socialLinks` array
|
|
59
|
-
* reference across pageFooterDesktop and pageFooterMobile, and `cloneDeep`
|
|
60
|
-
* preserves that shared reference. Running an in-place `splice` once per
|
|
61
|
-
* viewport therefore applies it TWICE to the same array — e.g. one requested
|
|
62
|
-
* removal index drops two links. Compute-once + assign-fresh is correct for
|
|
63
|
-
* both shared (legacy) and independent (new-model) footers.
|
|
64
|
-
*/
|
|
65
|
-
const computeSocialLinks = (
|
|
66
|
-
current: FooterSocialLink[] | undefined,
|
|
67
|
-
args: UpdateFooterArguments,
|
|
68
|
-
): FooterSocialLink[] => {
|
|
69
|
-
// _replace_social_links: replace entire list (empty/absent social_links = clear all).
|
|
70
|
-
// A replacement supersedes every other social link mutation.
|
|
71
|
-
if (args._replace_social_links) {
|
|
72
|
-
return (args.social_links ?? []).map((l) =>
|
|
73
|
-
makeSocialLink(l.social_link_icon, l.url),
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const links: FooterSocialLink[] = [...(current ?? [])];
|
|
78
|
-
|
|
79
|
-
// item_index-based updates for existing links — replace the item reference
|
|
80
|
-
// (never mutate the original object, which may be shared across viewports).
|
|
81
|
-
for (const link of args.social_links ?? []) {
|
|
82
|
-
if (link.item_index === undefined) continue;
|
|
83
|
-
|
|
84
|
-
const existing = links[link.item_index];
|
|
85
|
-
|
|
86
|
-
if (!existing) continue;
|
|
87
|
-
|
|
88
|
-
links[link.item_index] = {
|
|
89
|
-
...existing,
|
|
90
|
-
...(link.social_link_icon !== undefined
|
|
91
|
-
? { imageUrl: link.social_link_icon }
|
|
92
|
-
: {}),
|
|
93
|
-
...(link.url !== undefined ? { url: normaliseSocialUrl(link.url) } : {}),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Remove by index (descending order to preserve indices during splice).
|
|
98
|
-
if (args._remove_social_link_indices?.length) {
|
|
99
|
-
const sorted = [...args._remove_social_link_indices].sort((a, b) => b - a);
|
|
100
|
-
|
|
101
|
-
for (const idx of sorted) {
|
|
102
|
-
if (idx >= 0 && idx < links.length) links.splice(idx, 1);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Insert new links (descending insert_at order to preserve positions).
|
|
107
|
-
const insertLinks = (args.social_links ?? []).filter(
|
|
108
|
-
(l): l is FooterSocialLinkItem & { insert_at: number } =>
|
|
109
|
-
l.insert_at !== undefined && l.item_index === undefined,
|
|
110
|
-
);
|
|
111
|
-
const sortedInserts = [...insertLinks].sort(
|
|
112
|
-
(a, b) => b.insert_at - a.insert_at,
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
for (const link of sortedInserts) {
|
|
116
|
-
const pos = Math.max(0, Math.min(link.insert_at, links.length));
|
|
117
|
-
|
|
118
|
-
links.splice(pos, 0, makeSocialLink(link.social_link_icon, link.url));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Reorder (requires ALL indices — partial reorder is not supported).
|
|
122
|
-
if (args._reorder_social_links?.length) {
|
|
123
|
-
const newOrder = args._reorder_social_links;
|
|
124
|
-
const unique = new Set(newOrder);
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
unique.size === newOrder.length &&
|
|
128
|
-
newOrder.every((idx) => idx >= 0 && idx < links.length)
|
|
129
|
-
) {
|
|
130
|
-
const reordered = newOrder.map((idx) => links[idx]);
|
|
131
|
-
|
|
132
|
-
if (reordered.length === links.length) return reordered;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return links;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
33
|
const applyGlobalFooterChanges = (
|
|
140
34
|
footer: AbstractPageFooterStyles,
|
|
141
35
|
args: UpdateFooterArguments,
|
|
@@ -150,6 +44,80 @@ const applyGlobalFooterChanges = (
|
|
|
150
44
|
{ fontFamily: args.copyright_text_font_family },
|
|
151
45
|
textStyleOptions,
|
|
152
46
|
);
|
|
47
|
+
|
|
48
|
+
const links = args.social_links;
|
|
49
|
+
|
|
50
|
+
// _replace_social_links: replace entire list (empty/absent social_links = clear all).
|
|
51
|
+
// When replacing, all other social link mutations (remove, insert, reorder) are skipped —
|
|
52
|
+
// they are superseded by the replacement and running them would cause duplicates.
|
|
53
|
+
if (args._replace_social_links) {
|
|
54
|
+
footer.socialLinks = (links ?? []).map((l) => ({
|
|
55
|
+
id: generateItemId(),
|
|
56
|
+
imageUrl: l.social_link_icon || '',
|
|
57
|
+
url: normaliseSocialUrl(l.url || ''),
|
|
58
|
+
imageTitle: '',
|
|
59
|
+
width: 0,
|
|
60
|
+
height: 0,
|
|
61
|
+
imageLink: '',
|
|
62
|
+
}));
|
|
63
|
+
} else {
|
|
64
|
+
// Targeted mutations — only run when NOT replacing the entire list.
|
|
65
|
+
// BP-5603: id-based contract (item_id / move_before_item_id / _remove_social_link_ids).
|
|
66
|
+
// Backfill ids on legacy links so they become referenceable after this call.
|
|
67
|
+
ensureItemIds(footer.socialLinks);
|
|
68
|
+
|
|
69
|
+
// Remove by id (order-independent — no index shifting needed).
|
|
70
|
+
footer.socialLinks = removeItemsByIds(
|
|
71
|
+
footer.socialLinks,
|
|
72
|
+
args._remove_social_link_ids,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Update existing links (item_id present) or insert new links (no item_id).
|
|
76
|
+
if (links?.length) {
|
|
77
|
+
footer.socialLinks = footer.socialLinks ?? [];
|
|
78
|
+
|
|
79
|
+
for (const link of links) {
|
|
80
|
+
if (link.item_id) {
|
|
81
|
+
// Update existing link in place; reposition if move_before_item_id set.
|
|
82
|
+
const existing = footer.socialLinks.find(
|
|
83
|
+
(l) => l.id === link.item_id,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Unknown id — skip rather than guess at a position.
|
|
87
|
+
if (!existing) continue;
|
|
88
|
+
|
|
89
|
+
if (link.social_link_icon !== undefined)
|
|
90
|
+
existing.imageUrl = link.social_link_icon;
|
|
91
|
+
|
|
92
|
+
if (link.url !== undefined)
|
|
93
|
+
existing.url = normaliseSocialUrl(link.url);
|
|
94
|
+
|
|
95
|
+
repositionItemByAnchor(
|
|
96
|
+
footer.socialLinks,
|
|
97
|
+
existing,
|
|
98
|
+
link.move_before_item_id,
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
// New link — assign a stable id and position via move_before_item_id.
|
|
102
|
+
const newLink = {
|
|
103
|
+
id: generateItemId(),
|
|
104
|
+
imageUrl: link.social_link_icon || '',
|
|
105
|
+
url: normaliseSocialUrl(link.url || ''),
|
|
106
|
+
imageTitle: '',
|
|
107
|
+
width: 0,
|
|
108
|
+
height: 0,
|
|
109
|
+
imageLink: '' as string | false,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
repositionItemByAnchor(
|
|
113
|
+
footer.socialLinks,
|
|
114
|
+
newLink,
|
|
115
|
+
link.move_before_item_id,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
153
121
|
};
|
|
154
122
|
|
|
155
123
|
const applyViewportFooterChanges = (
|
|
@@ -230,30 +198,13 @@ export class FooterBuilder extends BaseMetadataBuilder<UpdateFooterArguments> {
|
|
|
230
198
|
const args = this.request.payload ?? {};
|
|
231
199
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
232
200
|
|
|
233
|
-
// Global
|
|
234
|
-
// viewports. These are idempotent assignments, safe to run per-viewport.
|
|
201
|
+
// Global fields (copyright_text, social links) always apply to both viewports.
|
|
235
202
|
if (model.pageFooterDesktop)
|
|
236
203
|
applyGlobalFooterChanges(model.pageFooterDesktop, args, textStyleOptions);
|
|
237
204
|
|
|
238
205
|
if (model.pageFooterMobile)
|
|
239
206
|
applyGlobalFooterChanges(model.pageFooterMobile, args, textStyleOptions);
|
|
240
207
|
|
|
241
|
-
// Social links are a single global list. Derive the new list ONCE, then
|
|
242
|
-
// assign an INDEPENDENT copy to each viewport — never mutate a (possibly
|
|
243
|
-
// shared) viewport array in place. See computeSocialLinks.
|
|
244
|
-
if (hasSocialLinkMutation(args)) {
|
|
245
|
-
const current =
|
|
246
|
-
model.pageFooterDesktop?.socialLinks ??
|
|
247
|
-
model.pageFooterMobile?.socialLinks;
|
|
248
|
-
const updated = computeSocialLinks(current, args);
|
|
249
|
-
|
|
250
|
-
if (model.pageFooterDesktop)
|
|
251
|
-
model.pageFooterDesktop.socialLinks = updated.map((l) => ({ ...l }));
|
|
252
|
-
|
|
253
|
-
if (model.pageFooterMobile)
|
|
254
|
-
model.pageFooterMobile.socialLinks = updated.map((l) => ({ ...l }));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
208
|
// Viewport-specific styling via explicit sub-objects.
|
|
258
209
|
if (args.desktop && model.pageFooterDesktop)
|
|
259
210
|
applyViewportFooterChanges(model.pageFooterDesktop, args.desktop);
|