@designcrowd/library.brand-page 5.16.43 → 5.16.44-ast-keywords-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-C5cH1T-1.js → CreatedOnBrandCrowd-de-de-CiHuBBL8.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CYMnCJLK.js → CreatedOnBrandCrowd-en-us-Dz9i57PK.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-lj9In06n.js → CreatedOnBrandCrowd-es-es-BA8ItJ95.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-d2eXSGxp.js → CreatedOnBrandCrowd-fr-ca-Bh5dIlNr.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-d2eXSGxp.js → CreatedOnBrandCrowd-fr-fr-Bh5dIlNr.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-B0KA6Yz2.js → CreatedOnBrandCrowd-pt-br-BxwPcnsE.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-B0KA6Yz2.js → CreatedOnBrandCrowd-pt-pt-BxwPcnsE.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-BVVrQTkJ.js → CreatedOnDcom-de-de-CY3Nae7j.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-DFt3dW1K.js → CreatedOnDcom-en-us-H6Rd_A3u.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DwdbeJO4.js → CreatedOnDcom-es-es-BwvKSqMp.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-jlQTAkBO.js → CreatedOnDcom-fr-ca-VsnNmsta.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-OFLmEho0.js → CreatedOnDcom-fr-fr-CqIzHOhw.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-BHqtceac.js → CreatedOnDcom-pt-br-eOza0P1S.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-DIrlJ6nF.js → CreatedOnDcom-pt-pt-mim8mTuS.js} +1 -1
  15. package/lib/src/brand-page/__fixtures__/brand-page-sample-data.js +2 -0
  16. package/lib/src/brand-page/clients/brand-page.client.d.ts +2 -2
  17. package/lib/src/brand-page/clients/brand-page.client.js +4 -2
  18. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.d.ts +10 -1
  19. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +18 -2
  20. package/lib/src/brand-page/components/image-gallery/ImageGallery.stories.js +2 -1
  21. package/lib/src/brand-page/components/images-section/ImagesSection.stories.js +2 -1
  22. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.d.ts +8 -0
  23. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +22 -7
  24. package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +1 -0
  25. package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +1 -0
  26. package/lib/src/brand-page/components/profile/Profile.stories.js +2 -1
  27. package/lib/src/brand-page/composables/recaptcha.composable.d.ts +23 -0
  28. package/lib/src/brand-page/composables/recaptcha.composable.js +109 -0
  29. package/lib/src/brand-page/constants/misc.constants.d.ts +3 -0
  30. package/lib/src/brand-page/constants/misc.constants.js +4 -0
  31. package/lib/src/brand-page/models/brand-page-context.model.d.ts +2 -0
  32. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +2 -0
  33. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +2 -0
  34. package/lib/src/brand-page-maker/components/preview/Preview.mixin.js +1 -0
  35. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +2 -0
  36. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +2 -0
  37. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +2 -0
  38. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +2 -0
  39. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +2 -0
  40. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +1 -0
  41. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +2 -0
  42. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +3 -0
  43. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +2 -0
  44. package/lib/src/bundled-translations-BGBFf8J6.js +169 -0
  45. package/lib/src/bundled-translations.de-DE-CMMRRwga.js +169 -0
  46. package/lib/src/bundled-translations.es-ES-XRLPG1g-.js +169 -0
  47. package/lib/src/{bundled-translations.fr-CA-D4lvna7W.js → bundled-translations.fr-CA-JVxHjJw5.js} +5 -5
  48. package/lib/src/bundled-translations.fr-FR-B9v33v0w.js +169 -0
  49. package/lib/src/bundled-translations.pt-BR-B_hexxsq.js +169 -0
  50. package/lib/src/bundled-translations.pt-PT-B9aY7rS8.js +169 -0
  51. package/lib/src/bundles/bundled-translations.de-DE.json +1 -1
  52. package/lib/src/bundles/bundled-translations.es-ES.json +1 -1
  53. package/lib/src/bundles/bundled-translations.fr-CA.json +1 -1
  54. package/lib/src/bundles/bundled-translations.fr-FR.json +1 -1
  55. package/lib/src/bundles/bundled-translations.json +1 -1
  56. package/lib/src/bundles/bundled-translations.pt-BR.json +1 -1
  57. package/lib/src/bundles/bundled-translations.pt-PT.json +1 -1
  58. package/lib/src/clients/fabric-render-client.d.ts +1 -1
  59. package/lib/src/clients/fabric-render-client.js +6 -2
  60. package/lib/src/css/library.brand-page-brandCrowd.min.css +16 -16
  61. package/lib/src/css/library.brand-page-brandPage.min.css +16 -16
  62. package/lib/src/css/library.brand-page-designCom.min.css +16 -16
  63. package/lib/src/css/library.brand-page-preview.min.css +16 -16
  64. package/lib/src/index.d.ts +3 -1
  65. package/lib/src/index.mjs +127 -123
  66. package/lib/src/{lib-CnJZ6ibD.js → lib-C7kurqjw.js} +14511 -14404
  67. package/lib/src/lib.js +2 -1
  68. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.js +1 -0
  69. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +0 -1
  70. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +0 -3
  71. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +1 -0
  72. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +2 -0
  73. package/lib/src/shared/content-settings/components/radio-field/RadioField.mixin.js +1 -0
  74. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +292 -0
  75. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +16 -1
  76. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +2 -0
  77. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -0
  78. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -0
  79. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +2 -0
  80. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +2 -0
  81. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +2 -0
  82. package/package.json +3 -3
  83. package/src/brand-page/__fixtures__/brand-page-sample-data.ts +2 -0
  84. package/src/brand-page/clients/brand-page.client.ts +5 -0
  85. package/src/brand-page/components/about-section/AboutSection.vue.test.ts +1 -0
  86. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +23 -0
  87. package/src/brand-page/components/contact-form/ContactForm.vue +5 -0
  88. package/src/brand-page/components/image/Image.test.ts +1 -0
  89. package/src/brand-page/components/image-gallery/ImageGallery.stories.ts +2 -1
  90. package/src/brand-page/components/image-gallery/ImageGallery.test.ts +1 -0
  91. package/src/brand-page/components/images-section/ImagesSection.stories.ts +2 -1
  92. package/src/brand-page/components/mailing-list/MailingList.mixin.ts +32 -12
  93. package/src/brand-page/components/profile/Profile.stories.ts +2 -1
  94. package/src/brand-page/components/social-links/SocialLinks.vue.test.ts +1 -0
  95. package/src/brand-page/composables/recaptcha.composable.ts +159 -0
  96. package/src/brand-page/constants/misc.constants.ts +5 -0
  97. package/src/brand-page/models/brand-page-context.model.ts +2 -0
  98. package/src/brand-page-maker/components/preview/Preview.mixin.ts +1 -0
  99. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +1 -0
  100. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +3 -0
  101. package/src/bundles/bundled-translations.de-DE.json +1 -1
  102. package/src/bundles/bundled-translations.es-ES.json +1 -1
  103. package/src/bundles/bundled-translations.fr-CA.json +1 -1
  104. package/src/bundles/bundled-translations.fr-FR.json +1 -1
  105. package/src/bundles/bundled-translations.json +1 -1
  106. package/src/bundles/bundled-translations.pt-BR.json +1 -1
  107. package/src/bundles/bundled-translations.pt-PT.json +1 -1
  108. package/src/clients/fabric-render-client.ts +8 -1
  109. package/src/lib.ts +5 -0
  110. package/src/shared/common/i18n/common.de-DE.json +66 -56
  111. package/src/shared/common/i18n/common.es-ES.json +66 -56
  112. package/src/shared/common/i18n/common.fr-CA.json +66 -56
  113. package/src/shared/common/i18n/common.fr-FR.json +66 -56
  114. package/src/shared/common/i18n/common.json +1 -1
  115. package/src/shared/common/i18n/common.pt-BR.json +66 -56
  116. package/src/shared/common/i18n/common.pt-PT.json +66 -56
  117. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.ts +1 -0
  118. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +0 -3
  119. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +1 -0
  120. package/src/shared/content-settings/components/generate-ai-button/GenerateAiButton.vue +1 -1
  121. package/src/shared/content-settings/components/radio-field/RadioField.mixin.ts +1 -0
  122. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +22 -1
  123. package/src/shared/logo-settings/components/EditLogoSettings.vue +1 -0
  124. package/src/shared/logo-settings/components/LogoTemplateSearch.vue +9 -12
  125. package/lib/src/bundled-translations-m0LLDrVj.js +0 -169
  126. package/lib/src/bundled-translations.de-DE-1O8nqesB.js +0 -169
  127. package/lib/src/bundled-translations.es-ES-DpzOLKxV.js +0 -169
  128. package/lib/src/bundled-translations.fr-FR-rn7Qlrcy.js +0 -169
  129. package/lib/src/bundled-translations.pt-BR-Cuinfm5y.js +0 -169
  130. package/lib/src/bundled-translations.pt-PT-BWcxkaQn.js +0 -169
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "275",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "251",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "250",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as e, openBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CnJZ6ibD.js";
2
+ import { _ as t } from "./lib-C7kurqjw.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CnJZ6ibD.js";
2
+ import { _ as e } from "./lib-C7kurqjw.js";
3
3
  const M = {}, Z = {
4
4
  width: "242",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CnJZ6ibD.js";
2
+ import { _ as e } from "./lib-C7kurqjw.js";
3
3
  const i = {}, M = {
4
4
  width: "231",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CnJZ6ibD.js";
2
+ import { _ as e } from "./lib-C7kurqjw.js";
3
3
  const M = {}, Z = {
4
4
  width: "230",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CnJZ6ibD.js";
2
+ import { _ as e } from "./lib-C7kurqjw.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CnJZ6ibD.js";
2
+ import { _ as e } from "./lib-C7kurqjw.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-CnJZ6ibD.js";
2
+ import { _ as M } from "./lib-C7kurqjw.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { createElementBlock as H, openBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-CnJZ6ibD.js";
2
+ import { _ as M } from "./lib-C7kurqjw.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -97,6 +97,7 @@ export const context = {
97
97
  mediaSizes: MEDIA_SIZES.sizeThumb,
98
98
  },
99
99
  isCapturingPreview: false,
100
+ recaptchaSiteKey: undefined,
100
101
  };
101
102
  export const BScontext = {
102
103
  isTemplate: false,
@@ -119,6 +120,7 @@ export const BScontext = {
119
120
  mediaSizes: MEDIA_SIZES.sizeThumb,
120
121
  },
121
122
  isCapturingPreview: false,
123
+ recaptchaSiteKey: undefined,
122
124
  };
123
125
  export const logoTheme = {
124
126
  logoTextColor: {
@@ -16,8 +16,8 @@ export interface CartForShopRequest {
16
16
  unitPrice: number;
17
17
  }
18
18
  declare const _default: {
19
- addMailSubscriber: (brandPageToken: string, email: string, name?: string | undefined) => Promise<FormResult>;
20
- submitContactForm: (brandPageToken: string, contentBlockId: string, values: ContactFormField[]) => Promise<FormResult>;
19
+ addMailSubscriber: (brandPageToken: string, email: string, name?: string | undefined, recaptchaToken?: string | undefined) => Promise<FormResult>;
20
+ submitContactForm: (brandPageToken: string, contentBlockId: string, values: ContactFormField[], recaptchaToken?: string | undefined) => Promise<FormResult>;
21
21
  getPaymentConfigStatusByPaymentConfigId: (paymentConfigId: string) => Promise<string>;
22
22
  getStoreProductsByBrandPageToken: (brandPageToken: string) => Promise<string>;
23
23
  createBrandPageCart: (request: {
@@ -1,12 +1,13 @@
1
1
  import axios from 'axios';
2
2
  import * as constants from '../constants';
3
3
  import * as csrfUtil from '../utils/csrf.util';
4
- const addMailSubscriber = async (brandPageToken, email, name) => {
4
+ const addMailSubscriber = async (brandPageToken, email, name, recaptchaToken) => {
5
5
  try {
6
6
  const csrfToken = csrfUtil.getCsrfTokenFromMetaHeader();
7
7
  await axios.post(`/api/mailing-lists/${brandPageToken}`, {
8
8
  email,
9
9
  name,
10
+ recaptchaToken,
10
11
  }, {
11
12
  headers: {
12
13
  'Content-Type': 'application/json',
@@ -28,12 +29,13 @@ const addMailSubscriber = async (brandPageToken, email, name) => {
28
29
  };
29
30
  }
30
31
  };
31
- const submitContactForm = async (brandPageToken, contentBlockId, values) => {
32
+ const submitContactForm = async (brandPageToken, contentBlockId, values, recaptchaToken) => {
32
33
  try {
33
34
  const csrfToken = csrfUtil.getCsrfTokenFromMetaHeader();
34
35
  const formBody = {
35
36
  contentBlockId,
36
37
  fields: values,
38
+ recaptchaToken,
37
39
  };
38
40
  await axios.post(`/api/contact/${brandPageToken}`, formBody, {
39
41
  headers: {
@@ -28,7 +28,16 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
28
28
  required: false;
29
29
  default: undefined;
30
30
  };
31
- }>, {}, {
31
+ }>, {
32
+ recaptcha: {
33
+ isReady: import("vue").ComputedRef<boolean>;
34
+ initialise: (config: import("../../composables/recaptcha.composable").ReCaptchaConfig) => Promise<void>;
35
+ executeAsync: (action: string) => Promise<string | undefined>;
36
+ error: import("vue").ComputedRef<string | undefined>;
37
+ isLoading: import("vue").ComputedRef<boolean>;
38
+ siteKey: import("vue").ComputedRef<string>;
39
+ };
40
+ }, {
32
41
  values: FieldValue[];
33
42
  hasSubmitted: boolean;
34
43
  isSubmitting: boolean;
@@ -3,8 +3,9 @@ import { FORM_FIELD_TYPES } from '../../models';
3
3
  import { getEmailValidationError, getMessageValidationError, getNameValidationError, getPhoneValidationError, } from '../../utils';
4
4
  import brandPageClient from '../../clients/brand-page.client';
5
5
  import { isEmail } from '../../../utils/validator.util';
6
- import { ALIGN_CARD_CENTER, ALIGN_CARD_CENTER_IMAGE, ALIGN_CENTERED, ALIGN_LEFT_FULL_IMAGE, ALIGN_RIGHT_FULL_IMAGE, DISPLAY_STYLE_COLLAPSED, TEXT_ONLY, } from '../../constants';
6
+ import { ALIGN_CARD_CENTER, ALIGN_CARD_CENTER_IMAGE, ALIGN_CENTERED, ALIGN_LEFT_FULL_IMAGE, ALIGN_RIGHT_FULL_IMAGE, DISPLAY_STYLE_COLLAPSED, RECAPTCHA_ACTION_CONTACT_FORM, TEXT_ONLY, } from '../../constants';
7
7
  import { getRenderText } from '../../utils';
8
+ import { useReCaptcha } from '../../composables/recaptcha.composable';
8
9
  export default defineComponent({
9
10
  props: {
10
11
  context: {
@@ -29,6 +30,12 @@ export default defineComponent({
29
30
  default: undefined,
30
31
  },
31
32
  },
33
+ setup() {
34
+ const recaptcha = useReCaptcha();
35
+ return {
36
+ recaptcha,
37
+ };
38
+ },
32
39
  data: () => ({
33
40
  values: [],
34
41
  hasSubmitted: false,
@@ -136,6 +143,11 @@ export default defineComponent({
136
143
  field: f,
137
144
  }));
138
145
  },
146
+ mounted() {
147
+ if (this.context?.recaptchaSiteKey && !this.isPreview && this.recaptcha) {
148
+ this.recaptcha.initialise({ siteKey: this.context.recaptchaSiteKey });
149
+ }
150
+ },
139
151
  methods: {
140
152
  async submitContactForm() {
141
153
  this.validateFormValues();
@@ -152,12 +164,16 @@ export default defineComponent({
152
164
  let result;
153
165
  this.isSubmitting = true;
154
166
  try {
167
+ let recaptchaToken;
168
+ if (this.context?.recaptchaSiteKey && this.recaptcha?.isReady?.value) {
169
+ recaptchaToken = await this.recaptcha.executeAsync(RECAPTCHA_ACTION_CONTACT_FORM);
170
+ }
155
171
  result = await brandPageClient.submitContactForm(this.context.brandPageToken, this.viewModel.id, this.values.map((v) => ({
156
172
  id: v.field.id,
157
173
  name: v.field.name,
158
174
  type: v.field.type,
159
175
  value: v.value,
160
- })));
176
+ })), recaptchaToken);
161
177
  }
162
178
  finally {
163
179
  this.isSubmitting = false;
@@ -29,7 +29,8 @@ const context = {
29
29
  options: {
30
30
  mediaSizes: MEDIA_SIZES.sizeThumb,
31
31
  },
32
- isCapturingPreview: false
32
+ isCapturingPreview: false,
33
+ recaptchaSiteKey: undefined,
33
34
  };
34
35
  export const Carousel = () => {
35
36
  return defineComponent({
@@ -29,7 +29,8 @@ const context = {
29
29
  options: {
30
30
  mediaSizes: MEDIA_SIZES.sizeThumb,
31
31
  },
32
- isCapturingPreview: false
32
+ isCapturingPreview: false,
33
+ recaptchaSiteKey: undefined,
33
34
  };
34
35
  export const Images = () => {
35
36
  return defineComponent({
@@ -21,6 +21,14 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
21
21
  };
22
22
  }>, {
23
23
  mailingListTr: (key: any, valuesToInterpolate?: {}) => any;
24
+ recaptcha: {
25
+ isReady: import("vue").ComputedRef<boolean>;
26
+ initialise: (config: import("../../composables/recaptcha.composable").ReCaptchaConfig) => Promise<void>;
27
+ executeAsync: (action: string) => Promise<string | undefined>;
28
+ error: import("vue").ComputedRef<string | undefined>;
29
+ isLoading: import("vue").ComputedRef<boolean>;
30
+ siteKey: import("vue").ComputedRef<string>;
31
+ };
24
32
  }, {
25
33
  email: string;
26
34
  emailError: string | undefined;
@@ -2,6 +2,8 @@ import { defineComponent } from 'vue';
2
2
  import brandPageClient from '../../clients/brand-page.client';
3
3
  import { getEmailValidationError } from '../../utils';
4
4
  import { contentSettingsTr, mailingListTr, } from '../../../composables/useTranslate';
5
+ import { useReCaptcha } from '../../composables/recaptcha.composable';
6
+ import { RECAPTCHA_ACTION_MAILING_LIST } from '../../constants';
5
7
  const MAX_NAME_LENGTH = 200;
6
8
  export default defineComponent({
7
9
  props: {
@@ -24,8 +26,10 @@ export default defineComponent({
24
26
  },
25
27
  },
26
28
  setup() {
29
+ const recaptcha = useReCaptcha();
27
30
  return {
28
31
  mailingListTr,
32
+ recaptcha,
29
33
  };
30
34
  },
31
35
  data() {
@@ -42,6 +46,9 @@ export default defineComponent({
42
46
  },
43
47
  mounted() {
44
48
  this.show = true;
49
+ if (this.context?.recaptchaSiteKey && !this.isPreview && this.recaptcha) {
50
+ this.recaptcha.initialise({ siteKey: this.context.recaptchaSiteKey });
51
+ }
45
52
  },
46
53
  computed: {
47
54
  hasNameError() {
@@ -107,14 +114,22 @@ export default defineComponent({
107
114
  return;
108
115
  }
109
116
  this.isSubmitting = true;
110
- const result = await brandPageClient.addMailSubscriber(this.brandPageToken, this.email, this.name === '' ? undefined : this.name);
111
- this.isSubmitting = false;
112
- if (!result.success) {
113
- this.submitError = result.errMessage;
114
- return;
117
+ try {
118
+ let recaptchaToken;
119
+ if (this.context?.recaptchaSiteKey && this.recaptcha?.isReady?.value) {
120
+ recaptchaToken = await this.recaptcha.executeAsync(RECAPTCHA_ACTION_MAILING_LIST);
121
+ }
122
+ const result = await brandPageClient.addMailSubscriber(this.brandPageToken, this.email, this.name === '' ? undefined : this.name, recaptchaToken);
123
+ if (!result.success) {
124
+ this.submitError = result.errMessage;
125
+ return;
126
+ }
127
+ this.submitError = undefined;
128
+ this.hasSubscribed = result.success;
129
+ }
130
+ finally {
131
+ this.isSubmitting = false;
115
132
  }
116
- this.submitError = undefined;
117
- this.hasSubscribed = result.success;
118
133
  },
119
134
  validateName() {
120
135
  if (this.name.trim() === '') {
@@ -12,6 +12,7 @@ export declare const context: {
12
12
  pages?: import("../../../view-models").PageViewModel[] | undefined;
13
13
  currentPageSlug?: string | undefined;
14
14
  runtimeEnvironment: string;
15
+ recaptchaSiteKey: string | undefined;
15
16
  pageTitle: string;
16
17
  customDomain: string | undefined;
17
18
  brandPageType: "default" | "brandSite" | "brandContact";
@@ -12,6 +12,7 @@ export declare const context: {
12
12
  pages?: import("../../../view-models").PageViewModel[] | undefined;
13
13
  currentPageSlug?: string | undefined;
14
14
  runtimeEnvironment: string;
15
+ recaptchaSiteKey: string | undefined;
15
16
  pageTitle: string;
16
17
  customDomain: string | undefined;
17
18
  brandPageType: "default" | "brandSite" | "brandContact";
@@ -51,7 +51,8 @@ export const Sample = () => {
51
51
  options: {
52
52
  mediaSizes: MEDIA_SIZES.sizeThumb,
53
53
  },
54
- isCapturingPreview: false
54
+ isCapturingPreview: false,
55
+ recaptchaSiteKey: undefined,
55
56
  };
56
57
  return { viewModel, context };
57
58
  },
@@ -0,0 +1,23 @@
1
+ declare global {
2
+ interface Window {
3
+ grecaptcha: {
4
+ enterprise: {
5
+ ready: (callback: () => void) => void;
6
+ execute: (siteKey: string, options: {
7
+ action: string;
8
+ }) => Promise<string>;
9
+ };
10
+ };
11
+ }
12
+ }
13
+ export interface ReCaptchaConfig {
14
+ siteKey: string;
15
+ }
16
+ export declare const useReCaptcha: () => {
17
+ isReady: import("vue").ComputedRef<boolean>;
18
+ initialise: (config: ReCaptchaConfig) => Promise<void>;
19
+ executeAsync: (action: string) => Promise<string | undefined>;
20
+ error: import("vue").ComputedRef<string | undefined>;
21
+ isLoading: import("vue").ComputedRef<boolean>;
22
+ siteKey: import("vue").ComputedRef<string>;
23
+ };
@@ -0,0 +1,109 @@
1
+ import { reactive, computed } from 'vue';
2
+ const store = reactive({
3
+ isLoaded: false,
4
+ isLoading: false,
5
+ siteKey: '',
6
+ error: undefined,
7
+ });
8
+ const SCRIPT_ID = 'recaptcha-enterprise-script';
9
+ const isBrowser = () => {
10
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
11
+ };
12
+ const loadScript = (siteKey) => {
13
+ return new Promise((resolve, reject) => {
14
+ if (!isBrowser()) {
15
+ reject(new Error('reCAPTCHA can only be loaded in browser environment'));
16
+ return;
17
+ }
18
+ // Check if script already exists
19
+ const existingScript = document.getElementById(SCRIPT_ID);
20
+ if (existingScript) {
21
+ resolve();
22
+ return;
23
+ }
24
+ const script = document.createElement('script');
25
+ script.id = SCRIPT_ID;
26
+ script.src = `https://www.google.com/recaptcha/enterprise.js?render=${siteKey}`;
27
+ script.async = true;
28
+ script.defer = true;
29
+ script.onload = () => {
30
+ resolve();
31
+ };
32
+ script.onerror = () => {
33
+ reject(new Error('Failed to load reCAPTCHA script'));
34
+ };
35
+ document.head.appendChild(script);
36
+ });
37
+ };
38
+ export const useReCaptcha = () => {
39
+ const isReady = computed(() => store.isLoaded && !store.isLoading);
40
+ /**
41
+ * Initialize reCAPTCHA with the site key.
42
+ * Should be called once when the component mounts.
43
+ */
44
+ const initialise = async (config) => {
45
+ if (!isBrowser()) {
46
+ return;
47
+ }
48
+ if (store.isLoaded || store.isLoading) {
49
+ return;
50
+ }
51
+ if (!config.siteKey) {
52
+ store.error = 'reCAPTCHA site key is required';
53
+ return;
54
+ }
55
+ store.siteKey = config.siteKey;
56
+ store.isLoading = true;
57
+ store.error = undefined;
58
+ try {
59
+ await loadScript(config.siteKey);
60
+ store.isLoaded = true;
61
+ }
62
+ catch (err) {
63
+ store.error =
64
+ err instanceof Error ? err.message : 'Failed to load reCAPTCHA';
65
+ }
66
+ finally {
67
+ store.isLoading = false;
68
+ }
69
+ };
70
+ /**
71
+ * Execute reCAPTCHA and get a token for the specified action.
72
+ * @param action - The action name for reCAPTCHA (e.g., 'CONTACT_FORM_SUBMIT')
73
+ * @returns Promise<string | undefined> - The reCAPTCHA token or undefined if failed
74
+ */
75
+ const executeAsync = async (action) => {
76
+ if (!isBrowser()) {
77
+ return undefined;
78
+ }
79
+ if (!store.isLoaded || !store.siteKey) {
80
+ return undefined;
81
+ }
82
+ try {
83
+ return await new Promise((resolve, reject) => {
84
+ window.grecaptcha.enterprise.ready(async () => {
85
+ try {
86
+ const token = await window.grecaptcha.enterprise.execute(store.siteKey, { action });
87
+ resolve(token);
88
+ }
89
+ catch (err) {
90
+ reject(err);
91
+ }
92
+ });
93
+ });
94
+ }
95
+ catch (err) {
96
+ store.error =
97
+ err instanceof Error ? err.message : 'Failed to execute reCAPTCHA';
98
+ return undefined;
99
+ }
100
+ };
101
+ return {
102
+ isReady,
103
+ initialise,
104
+ executeAsync,
105
+ error: computed(() => store.error),
106
+ isLoading: computed(() => store.isLoading),
107
+ siteKey: computed(() => store.siteKey),
108
+ };
109
+ };
@@ -51,6 +51,9 @@ export declare const STATUS_CODE_CONFLICT = 409;
51
51
  export declare const MESSAGE_ADD_MAIL_SUBSCRIBER_CONFLICT: () => any;
52
52
  export declare const MESSAGE_ADD_MAIL_SUBSCRIBER_FAILED: () => any;
53
53
  export declare const MESSAGE_CONTACT_MESSAGE_FAILED: () => any;
54
+ export declare const RECAPTCHA_ACTION_MAILING_LIST = "MAILING_LIST_SUBSCRIBE";
55
+ export declare const RECAPTCHA_ACTION_CONTACT_FORM = "CONTACT_FORM_SUBMIT";
56
+ export declare const RECAPTCHA_MIN_SCORE = 0.5;
54
57
  export declare const stripeDefaultStyle: {
55
58
  readonly base: {
56
59
  readonly color: "#7d8284";
@@ -61,6 +61,10 @@ export const STATUS_CODE_CONFLICT = 409;
61
61
  export const MESSAGE_ADD_MAIL_SUBSCRIBER_CONFLICT = () => errorTr('alreadySubscribedError');
62
62
  export const MESSAGE_ADD_MAIL_SUBSCRIBER_FAILED = () => errorTr('mailingListError');
63
63
  export const MESSAGE_CONTACT_MESSAGE_FAILED = () => errorTr('contactMessageError');
64
+ // reCAPTCHA constants
65
+ export const RECAPTCHA_ACTION_MAILING_LIST = 'MAILING_LIST_SUBSCRIBE';
66
+ export const RECAPTCHA_ACTION_CONTACT_FORM = 'CONTACT_FORM_SUBMIT';
67
+ export const RECAPTCHA_MIN_SCORE = 0.5;
64
68
  export const stripeDefaultStyle = {
65
69
  base: {
66
70
  color: '#7d8284',
@@ -59,6 +59,8 @@ export interface BrandPageContext {
59
59
  currentPageSlug?: string;
60
60
  runtimeEnvironment: string;
61
61
  stripePublishableKey: string | undefined;
62
+ /** reCAPTCHA Enterprise site key for spam protection */
63
+ recaptchaSiteKey: string | undefined;
62
64
  pageTitle: string;
63
65
  customDomain: string | undefined;
64
66
  brandPageType: BrandPageType;
@@ -273,6 +273,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
273
273
  isSubsequentPublish: boolean;
274
274
  isContextualUpgradeModalVisible: boolean;
275
275
  contextualUpgradeType: string | number | symbol | undefined;
276
+ templateSearchTerm: string | undefined;
276
277
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
277
278
  isUserDraft: (state: {
278
279
  availableTaxes: any[];
@@ -334,6 +335,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
334
335
  isSubsequentPublish: boolean;
335
336
  isContextualUpgradeModalVisible: boolean;
336
337
  contextualUpgradeType: string | number | symbol | undefined;
338
+ templateSearchTerm: string | undefined;
337
339
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
338
340
  }, {
339
341
  initialise(payload: import("../../stores/brandpage-container").InitialisePayload): void;
@@ -705,6 +705,7 @@ declare const _default: import("vue").DefineComponent<{}, {
705
705
  isSubsequentPublish: boolean;
706
706
  isContextualUpgradeModalVisible: boolean;
707
707
  contextualUpgradeType: string | number | symbol | undefined;
708
+ templateSearchTerm: string | undefined;
708
709
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
709
710
  isUserDraft: (state: {
710
711
  availableTaxes: any[];
@@ -766,6 +767,7 @@ declare const _default: import("vue").DefineComponent<{}, {
766
767
  isSubsequentPublish: boolean;
767
768
  isContextualUpgradeModalVisible: boolean;
768
769
  contextualUpgradeType: string | number | symbol | undefined;
770
+ templateSearchTerm: string | undefined;
769
771
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
770
772
  }, {
771
773
  initialise(payload: import("../../stores/brandpage-container").InitialisePayload): void;
@@ -97,6 +97,7 @@ export default defineComponent({
97
97
  isBrandContact: this.viewModel.brandPageType === BRAND_PAGE_TYPES.brandContact,
98
98
  isCapturingPreview: false,
99
99
  abTests: this.abTests,
100
+ recaptchaSiteKey: undefined,
100
101
  };
101
102
  },
102
103
  hasContents() {
@@ -71,6 +71,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
71
71
  isSubsequentPublish: boolean;
72
72
  isContextualUpgradeModalVisible: boolean;
73
73
  contextualUpgradeType: string | number | symbol | undefined;
74
+ templateSearchTerm: string | undefined;
74
75
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
75
76
  isUserDraft: (state: {
76
77
  availableTaxes: any[];
@@ -132,6 +133,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
132
133
  isSubsequentPublish: boolean;
133
134
  isContextualUpgradeModalVisible: boolean;
134
135
  contextualUpgradeType: string | number | symbol | undefined;
136
+ templateSearchTerm: string | undefined;
135
137
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
136
138
  }, {
137
139
  initialise(payload: import("../../stores/brandpage-container").InitialisePayload): void;
@@ -87,6 +87,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
87
87
  isSubsequentPublish: boolean;
88
88
  isContextualUpgradeModalVisible: boolean;
89
89
  contextualUpgradeType: string | number | symbol | undefined;
90
+ templateSearchTerm: string | undefined;
90
91
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
91
92
  isUserDraft: (state: {
92
93
  availableTaxes: any[];
@@ -148,6 +149,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
148
149
  isSubsequentPublish: boolean;
149
150
  isContextualUpgradeModalVisible: boolean;
150
151
  contextualUpgradeType: string | number | symbol | undefined;
152
+ templateSearchTerm: string | undefined;
151
153
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
152
154
  }, {
153
155
  initialise(payload: import("../../stores/brandpage-container").InitialisePayload): void;
@@ -92,6 +92,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
92
92
  isSubsequentPublish: boolean;
93
93
  isContextualUpgradeModalVisible: boolean;
94
94
  contextualUpgradeType: string | number | symbol | undefined;
95
+ templateSearchTerm: string | undefined;
95
96
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
96
97
  isUserDraft: (state: {
97
98
  availableTaxes: any[];
@@ -153,6 +154,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
153
154
  isSubsequentPublish: boolean;
154
155
  isContextualUpgradeModalVisible: boolean;
155
156
  contextualUpgradeType: string | number | symbol | undefined;
157
+ templateSearchTerm: string | undefined;
156
158
  } & import("pinia").PiniaCustomStateProperties<import("../../stores").BrandPageContainerState>) => boolean;
157
159
  }, {
158
160
  initialise(payload: import("../../stores/brandpage-container").InitialisePayload): void;