@designcrowd/library.brand-page 5.16.3 → 5.16.4-abtest-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-BVouwZJN.js → CreatedOnBrandCrowd-de-de-DQc3IjpR.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-Dtx-1uWO.js → CreatedOnBrandCrowd-en-us-CIgIJjzf.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CmlnW8oC.js → CreatedOnBrandCrowd-es-es-C4JLISvP.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-C95ioWzr.js → CreatedOnBrandCrowd-fr-ca-DPpWX0aJ.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-C95ioWzr.js → CreatedOnBrandCrowd-fr-fr-DPpWX0aJ.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-C_5SiaiE.js → CreatedOnBrandCrowd-pt-br-DmKD5o2o.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-C_5SiaiE.js → CreatedOnBrandCrowd-pt-pt-DmKD5o2o.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-BP4saQXn.js → CreatedOnDcom-de-de-DhALOqwz.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-B8Taru6f.js → CreatedOnDcom-en-us-LmejZqTn.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-Bo3JhWwd.js → CreatedOnDcom-es-es-D3ZH5JD5.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-CDk3ZAHU.js → CreatedOnDcom-fr-ca-BUOMh8kU.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-CD04W_hw.js → CreatedOnDcom-fr-fr-CNc-56QW.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-B2DGv1qj.js → CreatedOnDcom-pt-br-BGvy2ON7.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-TYqvgQ08.js → CreatedOnDcom-pt-pt-FaVxQHpN.js} +1 -1
  15. package/lib/src/brand-page/models/brand-page-context.model.d.ts +1 -1
  16. package/lib/src/brand-page/models/brand-page-context.model.js +5 -1
  17. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +13 -0
  18. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +30 -1
  19. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +0 -12
  20. package/lib/src/brand-page-maker/stores/index.d.ts +1 -0
  21. package/lib/src/brand-page-maker/stores/index.js +1 -0
  22. package/lib/src/brand-page-maker/stores/snapshot/snapshot-store.d.ts +14 -0
  23. package/lib/src/brand-page-maker/stores/snapshot/snapshot-store.js +21 -0
  24. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +2 -0
  25. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +117 -0
  26. package/lib/src/bundled-translations-Dw3XtNmX.js +163 -0
  27. package/lib/src/bundled-translations.de-DE-B82Ik9M7.js +163 -0
  28. package/lib/src/bundled-translations.es-ES-cf3pdNuN.js +163 -0
  29. package/lib/src/bundled-translations.fr-CA-DP44EcAZ.js +163 -0
  30. package/lib/src/bundled-translations.fr-FR-V0Zt25kg.js +163 -0
  31. package/lib/src/bundled-translations.pt-BR-Brv6rCss.js +163 -0
  32. package/lib/src/bundled-translations.pt-PT-BTgscCTG.js +163 -0
  33. package/lib/src/bundles/bundled-translations.de-DE.json +15 -1
  34. package/lib/src/bundles/bundled-translations.es-ES.json +15 -1
  35. package/lib/src/bundles/bundled-translations.fr-CA.json +15 -1
  36. package/lib/src/bundles/bundled-translations.fr-FR.json +15 -1
  37. package/lib/src/bundles/bundled-translations.json +15 -1
  38. package/lib/src/bundles/bundled-translations.pt-BR.json +15 -1
  39. package/lib/src/bundles/bundled-translations.pt-PT.json +15 -1
  40. package/lib/src/clients/brand-page-client.d.ts +0 -4
  41. package/lib/src/clients/design-template-client.d.ts +2 -0
  42. package/lib/src/clients/design-template-client.js +33 -0
  43. package/lib/src/composables/useSnapshot.d.ts +5 -0
  44. package/lib/src/composables/useSnapshot.js +74 -0
  45. package/lib/src/composables/useTranslate.d.ts +2 -1
  46. package/lib/src/composables/useTranslate.js +2 -1
  47. package/lib/src/css/library.brand-page-brandCrowd.min.css +149 -8
  48. package/lib/src/css/library.brand-page-brandPage.min.css +136 -8
  49. package/lib/src/css/library.brand-page-designCom.min.css +149 -8
  50. package/lib/src/css/library.brand-page-preview.min.css +133 -8
  51. package/lib/src/index.mjs +1 -1
  52. package/lib/src/{lib-3sE17Ltm.js → lib-CqA2Cr9V.js} +20353 -19606
  53. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +1 -0
  54. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +3 -0
  55. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +2 -0
  56. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.js +6 -0
  57. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +8 -0
  58. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +44 -1
  59. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +1 -0
  60. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +3 -0
  61. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +21 -3
  62. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +39 -2
  63. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +20 -2
  64. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +41 -2
  65. package/lib/src/shared/content-settings/utils.d.ts +1 -0
  66. package/lib/src/shared/content-settings/utils.js +6 -0
  67. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +0 -1
  68. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.js +0 -4
  69. package/package.json +2 -2
  70. package/src/brand-page/models/brand-page-context.model.ts +13 -3
  71. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +31 -0
  72. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +2 -1
  73. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +0 -36
  74. package/src/brand-page-maker/stores/index.ts +1 -0
  75. package/src/brand-page-maker/stores/snapshot/snapshot-store.ts +31 -0
  76. package/src/brand-page-maker/utils/rich-text-editor.util.ts +154 -0
  77. package/src/bundles/bundled-translations.de-DE.json +15 -1
  78. package/src/bundles/bundled-translations.es-ES.json +15 -1
  79. package/src/bundles/bundled-translations.fr-CA.json +15 -1
  80. package/src/bundles/bundled-translations.fr-FR.json +15 -1
  81. package/src/bundles/bundled-translations.json +15 -1
  82. package/src/bundles/bundled-translations.pt-BR.json +15 -1
  83. package/src/bundles/bundled-translations.pt-PT.json +15 -1
  84. package/src/clients/brand-page-client.ts +0 -4
  85. package/src/clients/design-template-client.ts +57 -0
  86. package/src/composables/useSnapshot.ts +97 -0
  87. package/src/composables/useTranslate.ts +3 -0
  88. package/src/shared/common/i18n/common.de-DE.json +5 -1
  89. package/src/shared/common/i18n/common.es-ES.json +5 -1
  90. package/src/shared/common/i18n/common.fr-CA.json +5 -1
  91. package/src/shared/common/i18n/common.fr-FR.json +5 -1
  92. package/src/shared/common/i18n/common.json +5 -1
  93. package/src/shared/common/i18n/common.pt-BR.json +5 -1
  94. package/src/shared/common/i18n/common.pt-PT.json +5 -1
  95. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +3 -0
  96. package/src/shared/content-settings/components/content-settings/ContentSettings.vue +2 -0
  97. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.ts +8 -0
  98. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.vue +1 -0
  99. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +54 -0
  100. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.vue +20 -2
  101. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +3 -1
  102. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.vue +1 -0
  103. package/src/shared/content-settings/components/generate-ai-button/GenerateAiButton.vue +95 -0
  104. package/src/shared/content-settings/components/generate-ai-modal/GenerateAiModal.vue +500 -0
  105. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.de-DE.json +12 -0
  106. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.es-ES.json +12 -0
  107. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.fr-CA.json +12 -0
  108. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.fr-FR.json +12 -0
  109. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.json +12 -0
  110. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.pt-BR.json +12 -0
  111. package/src/shared/content-settings/components/generate-ai-modal/i18n/generate-ai.pt-PT.json +12 -0
  112. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +44 -2
  113. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +15 -3
  114. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +52 -2
  115. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +16 -3
  116. package/src/shared/content-settings/utils.ts +9 -0
  117. package/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.ts +0 -6
  118. package/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.vue +1 -1
  119. package/lib/src/bundled-translations-D9iHE5G9.js +0 -161
  120. package/lib/src/bundled-translations.de-DE-DW3iDx_G.js +0 -161
  121. package/lib/src/bundled-translations.es-ES-C9I8wDyq.js +0 -161
  122. package/lib/src/bundled-translations.fr-CA-C0NN8DsO.js +0 -161
  123. package/lib/src/bundled-translations.fr-FR-oKV5NyE1.js +0 -161
  124. package/lib/src/bundled-translations.pt-BR-CTvCfv1X.js +0 -161
  125. package/lib/src/bundled-translations.pt-PT-CbwxwRY1.js +0 -161
@@ -0,0 +1,97 @@
1
+ import { ref } from 'vue';
2
+
3
+ const HTML2CANVAS_CDN_URL =
4
+ 'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js';
5
+
6
+ let html2canvasLoadPromise: Promise<void> | null = null;
7
+
8
+ /**
9
+ * Dynamically loads html2canvas from CDN if not already loaded
10
+ */
11
+ function loadHtml2Canvas(): Promise<void> {
12
+ // If already loaded, return immediately
13
+ if (typeof window.html2canvas !== 'undefined') {
14
+ return Promise.resolve();
15
+ }
16
+
17
+ // If already loading, return the existing promise
18
+ if (html2canvasLoadPromise) {
19
+ return html2canvasLoadPromise;
20
+ }
21
+
22
+ // Create new load promise
23
+ html2canvasLoadPromise = new Promise((resolve, reject) => {
24
+ const script = document.createElement('script');
25
+
26
+ script.src = HTML2CANVAS_CDN_URL;
27
+ script.async = true;
28
+
29
+ script.onload = () => {
30
+ console.log('✅ html2canvas loaded successfully from CDN');
31
+ resolve();
32
+ };
33
+
34
+ script.onerror = () => {
35
+ console.error('❌ Failed to load html2canvas from CDN');
36
+ html2canvasLoadPromise = null; // Reset so we can retry
37
+ reject(new Error('Failed to load html2canvas'));
38
+ };
39
+
40
+ document.head.appendChild(script);
41
+ });
42
+
43
+ return html2canvasLoadPromise;
44
+ }
45
+
46
+ export function useSnapshot() {
47
+ const isCapturing = ref(false);
48
+ const lastSnapshot = ref<string | null>(null);
49
+
50
+ const captureSnapshot = async (
51
+ element: HTMLElement | null,
52
+ ): Promise<string | null> => {
53
+ if (!element) {
54
+ console.error('No element provided for snapshot');
55
+
56
+ return null;
57
+ }
58
+
59
+ isCapturing.value = true;
60
+
61
+ try {
62
+ // Dynamically load html2canvas if not already loaded
63
+ await loadHtml2Canvas();
64
+
65
+ // Double-check it's loaded
66
+ if (typeof window.html2canvas === 'undefined') {
67
+ throw new Error('html2canvas failed to load');
68
+ }
69
+
70
+ const canvas = await window.html2canvas(element, {
71
+ allowTaint: true,
72
+ useCORS: true,
73
+ scale: 0.5, // Reduce quality to decrease file size
74
+ logging: false,
75
+ backgroundColor: '#ffffff',
76
+ });
77
+
78
+ const base64Image = canvas.toDataURL('image/jpeg', 0.8); // Use JPEG with 80% quality for smaller size
79
+
80
+ lastSnapshot.value = base64Image;
81
+
82
+ return base64Image;
83
+ } catch (error) {
84
+ console.error('Error capturing snapshot:', error);
85
+
86
+ return null;
87
+ } finally {
88
+ isCapturing.value = false;
89
+ }
90
+ };
91
+
92
+ return {
93
+ captureSnapshot,
94
+ isCapturing,
95
+ lastSnapshot,
96
+ };
97
+ }
@@ -206,6 +206,8 @@ const logoSwitchConfirmationModalTr = (key, valuesToInterpolate = {}) =>
206
206
  tr(`logoSwitchConfirmationModal.${key}`, valuesToInterpolate);
207
207
  const logoTemplateSearchModalTr = (key, valuesToInterpolate = {}) =>
208
208
  tr(`logoTemplateSearchModal.${key}`, valuesToInterpolate);
209
+ const generateAiModalTr = (key, valuesToInterpolate = {}) =>
210
+ tr(`aiModal.${key}`, valuesToInterpolate);
209
211
 
210
212
  const currentLocale = (): LocaleTypes => {
211
213
  const i18 = getI18NextInstance();
@@ -303,4 +305,5 @@ export {
303
305
  videoLibraryTr,
304
306
  logoSwitchConfirmationModalTr,
305
307
  logoTemplateSearchModalTr,
308
+ generateAiModalTr,
306
309
  };
@@ -37,6 +37,9 @@
37
37
  "replace" : "Ersetzen",
38
38
  "save" : "Speichern",
39
39
  "remove" : "Entfernen",
40
+ "copy" : "Kopieren",
41
+ "sendButtonText" : "Senden",
42
+ "regenerateText" : "Regenerieren",
40
43
  "addShapeLabel" : "Form hinzufügen",
41
44
  "addImageLabel" : "Bild hinzufügen",
42
45
  "addFaviconLabel" : "Favicon hinzufügen",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Weiter",
53
56
  "defaultSaveDescription" : "Vergessen Sie nicht, Ihre Änderungen zu veröffentlichen, sobald Sie mit Ihrem Design zufrieden sind.",
54
57
  "saveSuccess" : "Gespeichert!",
55
- "changeTemplateConfirmationMessage" : "Wenn Sie die Vorlagen ändern, gehen Ihre Änderungen verloren. Alle Ihre Inhalte werden gelöscht. Wir empfehlen Ihnen, Ihre Website vorher zu duplizieren, wenn Sie Ihre Änderungen beibehalten möchten."
58
+ "changeTemplateConfirmationMessage" : "Wenn Sie die Vorlagen ändern, gehen Ihre Änderungen verloren. Alle Ihre Inhalte werden gelöscht. Wir empfehlen Ihnen, Ihre Website vorher zu duplizieren, wenn Sie Ihre Änderungen beibehalten möchten.",
59
+ "generateText" : "Text generieren"
56
60
  }
57
61
  }
@@ -37,6 +37,9 @@
37
37
  "replace" : "Sustituir",
38
38
  "save" : "Guardar",
39
39
  "remove" : "Eliminar",
40
+ "copy" : "Copiar",
41
+ "sendButtonText" : "Enviar",
42
+ "regenerateText" : "Volver a generar",
40
43
  "addShapeLabel" : "Añadir forma",
41
44
  "addImageLabel" : "Añadir imagen",
42
45
  "addFaviconLabel" : "Añadir favicon",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Continuar",
53
56
  "defaultSaveDescription" : "No olvides publicar los cambios una vez que tengas un diseño que te guste.",
54
57
  "saveSuccess" : "¡Se ha guardado correctamente!",
55
- "changeTemplateConfirmationMessage" : "Cambiar las plantillas hará que pierdas los cambios. Se borrará todo el contenido. Te recomendamos que hagas un duplicado de tu sitio web de antemano si quieres conservar los cambios."
58
+ "changeTemplateConfirmationMessage" : "Cambiar las plantillas hará que pierdas los cambios. Se borrará todo el contenido. Te recomendamos que hagas un duplicado de tu sitio web de antemano si quieres conservar los cambios.",
59
+ "generateText" : "Generar texto"
56
60
  }
57
61
  }
@@ -37,6 +37,9 @@
37
37
  "replace" : "Remplacer",
38
38
  "save" : "Enregistrer",
39
39
  "remove" : "Supprimer",
40
+ "copy" : "Copier",
41
+ "sendButtonText" : "Envoyer",
42
+ "regenerateText" : "Régénérer",
40
43
  "addShapeLabel" : "Ajouter une forme",
41
44
  "addImageLabel" : "Ajouter une image",
42
45
  "addFaviconLabel" : "Ajouter un favicon",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Continuer",
53
56
  "defaultSaveDescription" : "N’oubliez pas de publier vos modifications une fois que votre design vous convient.",
54
57
  "saveSuccess" : "Enregistrement réussi!",
55
- "changeTemplateConfirmationMessage" : "Si vous changez de modèle, vous perdrez vos modifications et tout votre contenu sera effacé. Nous vous recommandons de dupliquer votre site au préalable si vous souhaitez conserver vos modifications."
58
+ "changeTemplateConfirmationMessage" : "Si vous changez de modèle, vous perdrez vos modifications et tout votre contenu sera effacé. Nous vous recommandons de dupliquer votre site au préalable si vous souhaitez conserver vos modifications.",
59
+ "generateText" : "Générer du texte"
56
60
  }
57
61
  }
@@ -37,6 +37,9 @@
37
37
  "replace" : "Remplacer",
38
38
  "save" : "Enregistrer",
39
39
  "remove" : "Supprimer",
40
+ "copy" : "Copier",
41
+ "sendButtonText" : "Envoyer",
42
+ "regenerateText" : "Régénérer",
40
43
  "addShapeLabel" : "Ajouter une forme",
41
44
  "addImageLabel" : "Ajouter une image",
42
45
  "addFaviconLabel" : "Ajouter un favicon",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Continuer",
53
56
  "defaultSaveDescription" : "N’oubliez pas de publier vos modifications une fois que votre design vous convient.",
54
57
  "saveSuccess" : "Enregistrement réussi !",
55
- "changeTemplateConfirmationMessage" : "Si vous changez de modèle, vous perdrez vos modifications et tout votre contenu sera effacé. Nous vous recommandons de dupliquer votre site au préalable si vous souhaitez conserver vos modifications."
58
+ "changeTemplateConfirmationMessage" : "Si vous changez de modèle, vous perdrez vos modifications et tout votre contenu sera effacé. Nous vous recommandons de dupliquer votre site au préalable si vous souhaitez conserver vos modifications.",
59
+ "generateText" : "Générer du texte"
56
60
  }
57
61
  }
@@ -42,6 +42,9 @@
42
42
  "replace": "Replace",
43
43
  "save": "Save",
44
44
  "remove": "Remove",
45
+ "copy": "Copy",
46
+ "sendButtonText": "Send",
47
+ "regenerateText": "Regenerate",
45
48
  "addShapeLabel": "Add Shape",
46
49
  "addImageLabel": "Add Image",
47
50
  "addFaviconLabel": "Add Favicon",
@@ -62,6 +65,7 @@
62
65
  "defaultSaveDescription": "Don’t forget to publish your changes once you are happy with your design.",
63
66
  "saveSuccess": "Save successful!",
64
67
 
65
- "changeTemplateConfirmationMessage": "Changing templates will cause you to lose your changes. All your content will be erased. We recommend you duplicate your site beforehand if you want to retain your changes."
68
+ "changeTemplateConfirmationMessage": "Changing templates will cause you to lose your changes. All your content will be erased. We recommend you duplicate your site beforehand if you want to retain your changes.",
69
+ "generateText": "Generate text"
66
70
  }
67
71
  }
@@ -37,6 +37,9 @@
37
37
  "replace" : "Substituir",
38
38
  "save" : "Salvar",
39
39
  "remove" : "Remover",
40
+ "copy" : "Copiar",
41
+ "sendButtonText" : "Enviar",
42
+ "regenerateText" : "Regenerar",
40
43
  "addShapeLabel" : "Adicionar forma",
41
44
  "addImageLabel" : "Adicionar imagem",
42
45
  "addFaviconLabel" : "Adicionar favicon",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Continuar",
53
56
  "defaultSaveDescription" : "Não se esqueça de publicar as suas alterações quando estiver satisfeito com o seu design.",
54
57
  "saveSuccess" : "Salvo com sucesso!",
55
- "changeTemplateConfirmationMessage" : "Se alterar os modelos, você perderá suas alterações. Todo o seu conteúdo será apagado. Recomendamos que duplique seu site previamente caso queira manter suas alterações."
58
+ "changeTemplateConfirmationMessage" : "Se alterar os modelos, você perderá suas alterações. Todo o seu conteúdo será apagado. Recomendamos que duplique seu site previamente caso queira manter suas alterações.",
59
+ "generateText" : "Gerar texto"
56
60
  }
57
61
  }
@@ -37,6 +37,9 @@
37
37
  "replace" : "Substituir",
38
38
  "save" : "Guardar",
39
39
  "remove" : "Remover",
40
+ "copy" : "Copiar",
41
+ "sendButtonText" : "Enviar",
42
+ "regenerateText" : "Regenerar",
40
43
  "addShapeLabel" : "Adicionar forma",
41
44
  "addImageLabel" : "Adicionar imagem",
42
45
  "addFaviconLabel" : "Adicionar Favicon",
@@ -52,6 +55,7 @@
52
55
  "continue" : "Continuar",
53
56
  "defaultSaveDescription" : "Não se esqueça de publicar as suas alterações quando estiver satisfeito com o seu design.",
54
57
  "saveSuccess" : "Gravação bem-sucedida!",
55
- "changeTemplateConfirmationMessage" : "Se alterar os modelos vai perder as suas alterações. Todo o seu conteúdo será apagado. Recomendamos que duplique o seu site previamente caso queira manter as suas alterações."
58
+ "changeTemplateConfirmationMessage" : "Se alterar os modelos vai perder as suas alterações. Todo o seu conteúdo será apagado. Recomendamos que duplique o seu site previamente caso queira manter as suas alterações.",
59
+ "generateText" : "Gerar texto"
56
60
  }
57
61
  }
@@ -120,6 +120,9 @@ export default defineComponent({
120
120
  },
121
121
  },
122
122
  methods: {
123
+ onCapturePreviewSnapshot() {
124
+ this.$emit('capture-preview-snapshot');
125
+ },
123
126
  async onAddContentOptionClicked(option: AddContentOption<any>) {
124
127
  const newItem = await this.contentSettingsStore.addItemByType({
125
128
  type: option.type,
@@ -65,6 +65,7 @@
65
65
  :ref="item.id"
66
66
  :type="item.type"
67
67
  @on-delete="onDeleteItem({ id: item.id }, item.type)"
68
+ @capture-snapshot-before-modal="onCapturePreviewSnapshot"
68
69
  />
69
70
  </template>
70
71
  </ReorderableList>
@@ -131,6 +132,7 @@
131
132
  <!-- Start of Single Content Item Settings -->
132
133
  <FocusedContentItemSettings
133
134
  v-if="mode === CONTENT_SETTINGS_MODES.focusedContentItem"
135
+ @capture-preview-snapshot="onCapturePreviewSnapshot"
134
136
  />
135
137
  <!-- End of Single Content Item Settings -->
136
138
 
@@ -193,6 +193,14 @@ export default defineComponent({
193
193
  this.$emit('on-delete');
194
194
  },
195
195
 
196
+ captureSnapshotBeforeModal() {
197
+ this.$emit('capture-snapshot-before-modal');
198
+ },
199
+
200
+ onCaptureSnapshot() {
201
+ this.$emit('capture-preview-snapshot');
202
+ },
203
+
196
204
  toggleButtonClick(): void {
197
205
  const $sideBarContent = document.querySelector(`#${SIDE_BAR_CONTENT_ID}`);
198
206
 
@@ -68,6 +68,7 @@
68
68
  "
69
69
  :id="id"
70
70
  :field="field"
71
+ @capture-preview-snapshot="onCaptureSnapshot"
71
72
  />
72
73
  </li>
73
74
  </ul>
@@ -10,9 +10,11 @@ import { UI_EVENT_SOURCES } from '../../../models';
10
10
  import { useUiContentSettingsStore } from '../../store';
11
11
  import { useUiSideBarStore } from '../../../side-bar/store';
12
12
  import {
13
+ useBrandPageContainerStore,
13
14
  useBrandPageMakerStore,
14
15
  useBrandPageMakerUserStore,
15
16
  useUiStore,
17
+ useSnapshotStore,
16
18
  } from '../../../../brand-page-maker/stores';
17
19
  import { SIDE_BAR_TAB_NAMES } from '../../../../brand-page-maker/constants';
18
20
  import {
@@ -21,6 +23,12 @@ import {
21
23
  } from '../../../../brand-page-maker/models';
22
24
  import { useUiPageSettingsStore } from '../../../page-settings/store';
23
25
  import { PAGE_SECTIONS } from '../../../page-settings/models';
26
+ import { commonTr } from '../../../../composables/useTranslate';
27
+ import {
28
+ getPlainTextFromRTE,
29
+ replaceTextInRTE,
30
+ } from '../../../../brand-page-maker/utils';
31
+ import { isDefaultContentText } from '../../utils';
24
32
  import { useUiLogoSettingsStore } from '../../../logo-settings/store';
25
33
 
26
34
  const RTE_ALLOWED_SECTION_TYPES = [
@@ -30,11 +38,18 @@ const RTE_ALLOWED_SECTION_TYPES = [
30
38
  'images-section',
31
39
  'shapes-section',
32
40
  ];
41
+ const AI_ALLOWED_SECTION_TYPES = [
42
+ 'banner-section',
43
+ 'text',
44
+ 'about-section',
45
+ 'testimonials-section',
46
+ ];
33
47
  const RTE_DISALLOWED_STYLE_FIELD_NAMES = [
34
48
  'descriptionStyle',
35
49
  'titleStyle',
36
50
  'textStyle',
37
51
  ];
52
+ const AI_ALLOWED_FIELD_NAMES = ['description', 'title', 'text', 'paragraph'];
38
53
  const RTE_DISALLOWED_FIELD_NAMES = ['buttonText'];
39
54
 
40
55
  export default defineComponent({
@@ -65,6 +80,8 @@ export default defineComponent({
65
80
  const uiStore = useUiStore();
66
81
  const sideBarStore = useUiSideBarStore();
67
82
  const pageSettingsStore = useUiPageSettingsStore();
83
+ const brandPageContainerStore = useBrandPageContainerStore();
84
+ const snapshotStore = useSnapshotStore();
68
85
  const uiLogoSettingsStore = useUiLogoSettingsStore();
69
86
 
70
87
  return {
@@ -73,6 +90,7 @@ export default defineComponent({
73
90
  getValue: computed(() => contentSettingsStore.getValue),
74
91
  brandPageData: computed(() => brandPageMakerStore.brandPageData),
75
92
  isAdmin: computed(() => userStore.isAdmin),
93
+ isDesignCom: computed(() => brandPageContainerStore.isDesignCom),
76
94
  canUseRichTextEditor: computed(
77
95
  () => userStore.features.canUseRichTextEditor,
78
96
  ),
@@ -81,6 +99,7 @@ export default defineComponent({
81
99
  () => brandPageMakerStore.isBrandPageOrIsBrandContact,
82
100
  ),
83
101
  logoTheme: computed(() => uiStore.logoTheme),
102
+ snapshotBase64: computed(() => snapshotStore.snapshotBase64),
84
103
  contentSettingsStore,
85
104
  brandPageMakerStore,
86
105
  userStore,
@@ -92,6 +111,7 @@ export default defineComponent({
92
111
  },
93
112
  data: () => ({
94
113
  isToggled: true,
114
+ richTextEditorKey: 0,
95
115
  }),
96
116
  computed: {
97
117
  content(): Content {
@@ -216,6 +236,22 @@ export default defineComponent({
216
236
  disableTransparent(): boolean {
217
237
  return this.field.disableTransparent || false;
218
238
  },
239
+ generateText(): string {
240
+ return commonTr('generateText');
241
+ },
242
+ showGenerateText(): boolean {
243
+ return (
244
+ AI_ALLOWED_SECTION_TYPES.includes(this.type) &&
245
+ AI_ALLOWED_FIELD_NAMES.includes(this.fieldName)
246
+ );
247
+ },
248
+ plainTextValue(): string {
249
+ const plainText = getPlainTextFromRTE(this.value);
250
+
251
+ if (isDefaultContentText(plainText)) return '';
252
+
253
+ return plainText;
254
+ },
219
255
  },
220
256
  watch: {
221
257
  fieldType() {
@@ -298,6 +334,21 @@ export default defineComponent({
298
334
  onToggleStateChange(isToggled: boolean) {
299
335
  this.isToggled = isToggled;
300
336
  },
337
+ onUseGeneratedText(generatedText: string) {
338
+ let formattedText = generatedText;
339
+
340
+ if (this.showRichTextEditor && typeof this.value === 'string') {
341
+ formattedText = replaceTextInRTE(this.value, generatedText);
342
+ }
343
+
344
+ this.contentSettingsStore.updateItemValue({
345
+ id: this.id,
346
+ name: this.fieldName,
347
+ value: formattedText,
348
+ });
349
+
350
+ this.richTextEditorKey = this.richTextEditorKey + 1;
351
+ },
301
352
  clearFieldValue() {
302
353
  const fieldType = this.field.defaultValue;
303
354
 
@@ -309,5 +360,8 @@ export default defineComponent({
309
360
  });
310
361
  }
311
362
  },
363
+ onCaptureSnapshot() {
364
+ this.$emit('capture-preview-snapshot');
365
+ },
312
366
  },
313
367
  });
@@ -16,11 +16,22 @@
16
16
  'tw-flex tw-w-full tw-mb-4': showToggle,
17
17
  }"
18
18
  >
19
- <div class="tw-grow">
19
+ <div class="tw-flex tw-flex-row tw-w-full tw-justify-between">
20
20
  <FieldSubHeader
21
- :class="{ 'tw-mb-2': !field.showToggle && !isInline }"
21
+ :class="{
22
+ 'tw-mb-2': !showGenerateText || (!field.showToggle && !isInline),
23
+ }"
22
24
  :title="field.title"
23
25
  />
26
+ <GenerateAiButton
27
+ v-if="showGenerateText"
28
+ :id="`${id}-${field.name}`"
29
+ :existing-text="plainTextValue"
30
+ :is-design-com="isDesignCom"
31
+ :field-type="field.name"
32
+ @capture-snapshot="onCaptureSnapshot"
33
+ @on-use-text="onUseGeneratedText"
34
+ />
24
35
  </div>
25
36
  <Toggle
26
37
  v-if="showToggle"
@@ -33,6 +44,9 @@
33
44
  :is="`${fieldType}-field`"
34
45
  v-show="!showToggle || isToggled"
35
46
  :id="id"
47
+ :key="
48
+ showRichTextEditor ? `rte-${id}-${richTextEditorKey}` : `field-${id}`
49
+ "
36
50
  :attrs="{
37
51
  'data-test-tag': `${displayName.toLowerCase()}-${field.name}`,
38
52
  }"
@@ -63,10 +77,12 @@
63
77
  :show-text-alignment-options="showAlignment"
64
78
  :show-text-background-color-options="showTextBg"
65
79
  :disable-transparent="disableTransparent"
80
+ :is-design-com="isDesignCom"
66
81
  @update:model-value="onFieldChanged"
67
82
  @change-logo="onFieldChangeLogo"
68
83
  @on-intent-image-replace="onIntentImageReplace"
69
84
  @on-toggle-state-change="onToggleStateChange"
85
+ @capture-preview-snapshot="onCaptureSnapshot"
70
86
  />
71
87
  </div>
72
88
  </div>
@@ -109,6 +125,7 @@ import SliderField from '../slider-field/SliderField.vue';
109
125
  import PageDropdownField from '../page-dropdown-field/PageDropdownField.vue';
110
126
  import PersonalisationOverrideField from '../personalisation-override-field/PersonalisationOverrideField.vue';
111
127
  import BookingUrlField from '../booking-url-field/BookingUrlField.vue';
128
+ import GenerateAiButton from '../generate-ai-button/GenerateAiButton.vue';
112
129
 
113
130
  export default {
114
131
  components: {
@@ -147,6 +164,7 @@ export default {
147
164
  'product-config-option-field': ProductConfigOptionField,
148
165
  FieldSubHeader,
149
166
  Toggle,
167
+ GenerateAiButton,
150
168
  'page-dropdown-field': PageDropdownField,
151
169
  'personalisation-override-field': PersonalisationOverrideField,
152
170
  },
@@ -228,7 +228,9 @@ export default defineComponent({
228
228
  eventSource: UI_EVENT_SOURCES.contentSettings,
229
229
  });
230
230
  },
231
-
231
+ onCaptureSnapshotBeforeModal() {
232
+ this.$emit('capture-preview-snapshot');
233
+ },
232
234
  onToggleGroupCollapseClick(groupId: string) {
233
235
  if (!this.id) {
234
236
  return;
@@ -36,6 +36,7 @@
36
36
  :field="field"
37
37
  :role="item.settings.content.fieldRole"
38
38
  :type="item.type"
39
+ @capture-preview-snapshot="onCaptureSnapshotBeforeModal"
39
40
  />
40
41
  </li>
41
42
  </ul>
@@ -0,0 +1,95 @@
1
+ <template>
2
+ <div v-if="showButton">
3
+ <p
4
+ :title="generateText"
5
+ class="tw-text-sm tw-text-primary-500 tw-font-bold tw-cursor-pointer tw-flex tw-items-center"
6
+ :class="{
7
+ 'tw-uppercase': !props.isDesignCom,
8
+ }"
9
+ @click="onShowGenerateModal"
10
+ >
11
+ <Icon name="ai" class="tw-mr-1 tw-flex" />
12
+ {{ generateText }}
13
+ </p>
14
+ <GenerateAiModal
15
+ :is-visible="generateTextModalVisible"
16
+ :existing-text="props.existingText"
17
+ :is-design-com="props.isDesignCom"
18
+ :snapshot-base64="snapshotBase64"
19
+ :field-type="props.fieldType"
20
+ @on-close="onCloseGenerateTextModal"
21
+ @on-use-text="handleUseGeneratedText"
22
+ />
23
+ </div>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ import { ref, computed } from 'vue';
28
+ import { Icon } from '@designcrowd/fe-shared-lib';
29
+ import GenerateAiModal from '../generate-ai-modal/GenerateAiModal.vue';
30
+ import { commonTr } from '../../../../composables/useTranslate';
31
+ import {
32
+ useBrandPageContainerStore,
33
+ useSnapshotStore,
34
+ } from '../../../../brand-page-maker/stores';
35
+ import type { AbTestResult } from '../../../../brand-page/models/brand-page-context.model';
36
+
37
+ interface Props {
38
+ id: string;
39
+ existingText: string;
40
+ isDesignCom?: boolean;
41
+ captureSnapshotDelay?: number;
42
+ fieldType?: string;
43
+ }
44
+
45
+ const props = withDefaults(defineProps<Props>(), {
46
+ isDesignCom: false,
47
+ fieldType: 'text',
48
+ });
49
+
50
+ const emit = defineEmits<{
51
+ 'on-use-text': [text: string];
52
+ 'capture-snapshot': [];
53
+ }>();
54
+
55
+ const snapshotStore = useSnapshotStore();
56
+ const brandPageContainerStore = useBrandPageContainerStore();
57
+
58
+ const snapshotBase64 = computed(() => snapshotStore.snapshotBase64);
59
+ const abTests = computed(() => brandPageContainerStore.abTests);
60
+
61
+ const showButton = computed(() => {
62
+ const abTest = abTests.value
63
+ .bp_4572_ai_text_generation_post_purchase as unknown as
64
+ | AbTestResult
65
+ | undefined;
66
+ return abTest?.isVariation ?? false;
67
+ });
68
+
69
+ const generateTextModalVisible = ref(false);
70
+ const generateText = commonTr('generateText');
71
+
72
+ const onShowGenerateModal = async () => {
73
+ emit('capture-snapshot');
74
+
75
+ const maxWaitTime = 5000; // 5 seconds max
76
+ const pollInterval = 50; // Check every 50ms
77
+ let elapsedTime = 0;
78
+
79
+ while (!snapshotBase64.value && elapsedTime < maxWaitTime) {
80
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
81
+ elapsedTime += pollInterval;
82
+ }
83
+
84
+ generateTextModalVisible.value = true;
85
+ };
86
+
87
+ const onCloseGenerateTextModal = () => {
88
+ generateTextModalVisible.value = false;
89
+ };
90
+
91
+ const handleUseGeneratedText = (generatedText: string) => {
92
+ emit('on-use-text', generatedText);
93
+ onCloseGenerateTextModal();
94
+ };
95
+ </script>