@designcrowd/library.brand-page 5.17.6 → 5.17.7

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 (172) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-BI2KuOEF.js → CreatedOnBrandCrowd-de-de-BUQFMhZb.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BYLJCjLJ.js → CreatedOnBrandCrowd-en-us-QS-5r4XI.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Y_D6cuGE.js → CreatedOnBrandCrowd-es-es-CP2iH-n0.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CqCBRjYh.js → CreatedOnBrandCrowd-fr-ca-qfD4NmQZ.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CqCBRjYh.js → CreatedOnBrandCrowd-fr-fr-qfD4NmQZ.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-Bt0YF6DA.js → CreatedOnBrandCrowd-pt-br-CfmaB7zE.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-Bt0YF6DA.js → CreatedOnBrandCrowd-pt-pt-CfmaB7zE.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CEk8FBEG.js → CreatedOnDcom-de-de-BxSUWfGN.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-DWw4Tm2L.js → CreatedOnDcom-en-us-CSeUdR6v.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DpG-8o98.js → CreatedOnDcom-es-es-g7rCfAtP.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-CEla7RmR.js → CreatedOnDcom-fr-ca-Bh_BTb-O.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DRXeVTlV.js → CreatedOnDcom-fr-fr-Clgd_Ujm.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-Bzu_phZc.js → CreatedOnDcom-pt-br-Dlgk0myX.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-DWax8Er7.js → CreatedOnDcom-pt-pt-B14APfrn.js} +1 -1
  15. package/lib/src/brand-page/models/brand-page-context.model.d.ts +2 -1
  16. package/lib/src/brand-page/models/brand-page-context.model.js +5 -0
  17. package/lib/src/brand-page/models/index.d.ts +1 -0
  18. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +62 -2
  19. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +1 -0
  20. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +60 -2
  21. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +30 -1
  22. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +60 -2
  23. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +60 -2
  24. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +60 -2
  25. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +60 -2
  26. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +60 -2
  27. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +60 -2
  28. package/lib/src/brand-page-maker/composables/ai-chat/useAiChat.js +3 -1
  29. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -1
  30. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.js +6 -6
  31. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.d.ts +2 -0
  32. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +258 -0
  33. package/lib/src/brand-page-maker/constants/sidebar-constant.d.ts +1 -0
  34. package/lib/src/brand-page-maker/constants/sidebar-constant.js +8 -0
  35. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.d.ts +16 -0
  36. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.js +14 -0
  37. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +62 -3
  38. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +2 -0
  39. package/lib/src/brand-page-maker/stores/index.d.ts +1 -0
  40. package/lib/src/brand-page-maker/stores/index.js +1 -0
  41. package/lib/src/brand-page-maker/stores/snapshot/snapshot-store.d.ts +2 -0
  42. package/lib/src/brand-page-maker/stores/snapshot/snapshot-store.js +9 -0
  43. package/lib/src/clients/ai-assistant-client.d.ts +8 -0
  44. package/lib/src/clients/ai-assistant-client.js +11 -1
  45. package/lib/src/composables/useSnapshot.js +33 -0
  46. package/lib/src/css/library.brand-page-brandCrowd.min.css +19 -6
  47. package/lib/src/css/library.brand-page-brandPage.min.css +19 -6
  48. package/lib/src/css/library.brand-page-designCom.min.css +19 -6
  49. package/lib/src/css/library.brand-page-preview.min.css +19 -6
  50. package/lib/src/index.d.ts +1 -0
  51. package/lib/src/index.mjs +42 -41
  52. package/lib/src/{lib-DODSa6QK.js → lib-pOkCQP0J.js} +28820 -26841
  53. package/lib/src/lib.js +1 -0
  54. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +1 -1
  55. package/lib/src/metadata-builder/ai-metadata-executor.js +68 -7
  56. package/lib/src/metadata-builder/builders/add-page-builder.d.ts +14 -0
  57. package/lib/src/metadata-builder/builders/add-page-builder.js +66 -0
  58. package/lib/src/metadata-builder/builders/remove-page-builder.d.ts +13 -0
  59. package/lib/src/metadata-builder/builders/remove-page-builder.js +50 -0
  60. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.d.ts +6 -0
  61. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +17 -0
  62. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.d.ts +6 -0
  63. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +25 -0
  64. package/lib/src/metadata-builder/builders/section-builders/base-section-builder.d.ts +40 -0
  65. package/lib/src/metadata-builder/builders/section-builders/base-section-builder.js +108 -0
  66. package/lib/src/metadata-builder/builders/section-builders/button-section-builder.d.ts +6 -0
  67. package/lib/src/metadata-builder/builders/section-builders/button-section-builder.js +16 -0
  68. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.d.ts +6 -0
  69. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +27 -0
  70. package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.d.ts +13 -0
  71. package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.js +37 -0
  72. package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.d.ts +6 -0
  73. package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.js +16 -0
  74. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.d.ts +6 -0
  75. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +76 -0
  76. package/lib/src/metadata-builder/builders/section-builders/image-section-builder.d.ts +6 -0
  77. package/lib/src/metadata-builder/builders/section-builders/image-section-builder.js +16 -0
  78. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.d.ts +6 -0
  79. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +158 -0
  80. package/lib/src/metadata-builder/builders/section-builders/index.d.ts +17 -0
  81. package/lib/src/metadata-builder/builders/section-builders/index.js +17 -0
  82. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.d.ts +6 -0
  83. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +17 -0
  84. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.d.ts +6 -0
  85. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +16 -0
  86. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.d.ts +15 -0
  87. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.js +62 -0
  88. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.d.ts +6 -0
  89. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +16 -0
  90. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.d.ts +6 -0
  91. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +162 -0
  92. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.d.ts +6 -0
  93. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +50 -0
  94. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.d.ts +6 -0
  95. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +133 -0
  96. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.d.ts +6 -0
  97. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +154 -0
  98. package/lib/src/metadata-builder/builders/section-content-factory.d.ts +3 -0
  99. package/lib/src/metadata-builder/builders/section-content-factory.js +13 -0
  100. package/lib/src/metadata-builder/builders/section-field-maps.d.ts +12 -0
  101. package/lib/src/metadata-builder/builders/section-field-maps.js +180 -0
  102. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +11 -0
  103. package/lib/src/metadata-builder/builders/section-field-utils.js +109 -0
  104. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +6 -0
  105. package/lib/src/metadata-builder/helpers/rte.helpers.js +16 -1
  106. package/lib/src/metadata-builder/metadata-executor.js +71 -1
  107. package/lib/src/metadata-builder/models.d.ts +54 -0
  108. package/lib/src/metadata-builder/models.js +58 -0
  109. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +51 -3
  110. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +110 -13
  111. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +285 -0
  112. package/lib/src/metadata-builder/parsers/section-tool-arguments.js +7 -0
  113. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +60 -2
  114. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +60 -2
  115. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +60 -2
  116. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +60 -2
  117. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +90 -3
  118. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +61 -2
  119. package/lib/src/shared/side-bar/components/SideBar.mixin.js +6 -0
  120. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +60 -2
  121. package/lib/src/utils/minify-brand-page-metadata.d.ts +22 -0
  122. package/lib/src/utils/minify-brand-page-metadata.js +344 -0
  123. package/package.json +1 -1
  124. package/src/brand-page/models/brand-page-context.model.ts +12 -3
  125. package/src/brand-page/models/index.ts +1 -0
  126. package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +80 -0
  127. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +4 -0
  128. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +9 -2
  129. package/src/brand-page-maker/composables/ai-chat/useAiChat.ts +4 -1
  130. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +6 -6
  131. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +363 -0
  132. package/src/brand-page-maker/constants/sidebar-constant.ts +8 -0
  133. package/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.ts +19 -0
  134. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +3 -1
  135. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +4 -0
  136. package/src/brand-page-maker/stores/index.ts +1 -0
  137. package/src/brand-page-maker/stores/snapshot/snapshot-store.ts +12 -0
  138. package/src/clients/ai-assistant-client.ts +17 -1
  139. package/src/composables/useSnapshot.ts +47 -0
  140. package/src/lib.ts +4 -0
  141. package/src/metadata-builder/ai-metadata-executor.ts +116 -26
  142. package/src/metadata-builder/builders/add-page-builder.ts +98 -0
  143. package/src/metadata-builder/builders/remove-page-builder.ts +83 -0
  144. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +28 -0
  145. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +45 -0
  146. package/src/metadata-builder/builders/section-builders/base-section-builder.ts +160 -0
  147. package/src/metadata-builder/builders/section-builders/button-section-builder.ts +24 -0
  148. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +43 -0
  149. package/src/metadata-builder/builders/section-builders/delete-section-builder.ts +63 -0
  150. package/src/metadata-builder/builders/section-builders/divider-section-builder.ts +27 -0
  151. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +111 -0
  152. package/src/metadata-builder/builders/section-builders/image-section-builder.ts +24 -0
  153. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +223 -0
  154. package/src/metadata-builder/builders/section-builders/index.ts +21 -0
  155. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +29 -0
  156. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +27 -0
  157. package/src/metadata-builder/builders/section-builders/move-section-builder.ts +100 -0
  158. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +27 -0
  159. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +231 -0
  160. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +79 -0
  161. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +194 -0
  162. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +234 -0
  163. package/src/metadata-builder/builders/section-content-factory.ts +20 -0
  164. package/src/metadata-builder/builders/section-field-maps.ts +235 -0
  165. package/src/metadata-builder/builders/section-field-utils.ts +157 -0
  166. package/src/metadata-builder/helpers/rte.helpers.ts +23 -1
  167. package/src/metadata-builder/metadata-executor.ts +96 -4
  168. package/src/metadata-builder/models.ts +62 -0
  169. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +141 -17
  170. package/src/metadata-builder/parsers/section-tool-arguments.ts +417 -0
  171. package/src/shared/side-bar/components/SideBar.mixin.ts +10 -0
  172. package/src/utils/minify-brand-page-metadata.ts +407 -0
@@ -0,0 +1,160 @@
1
+ import { type BrandPageModel, type Content } from '../../../brand-page/models';
2
+ import type { Logger } from '../../logger';
3
+ import {
4
+ BaseMetadataBuilder,
5
+ type BuilderResult,
6
+ type MetadataBuilderRequest,
7
+ } from '../../models';
8
+ import type {
9
+ SectionToolArguments,
10
+ SectionType,
11
+ } from '../../parsers/section-tool-arguments';
12
+ import { createDefaultSectionContent } from '../section-content-factory';
13
+
14
+ export interface SectionBuilderPayload {
15
+ sectionType: SectionType;
16
+ arguments: SectionToolArguments;
17
+ /** Only required for update operations. */
18
+ contentId?: string;
19
+ /** Target page ID — used for create operations to place the new section on the correct page. */
20
+ pageId?: string;
21
+ }
22
+
23
+ /**
24
+ * Abstract base for per-section builders.
25
+ *
26
+ * Concrete builders implement `buildAsync` directly, using
27
+ * `resolveContent()` to obtain the content object to mutate.
28
+ */
29
+ export abstract class BaseSectionBuilder<
30
+ C extends Content = Content,
31
+ > extends BaseMetadataBuilder<SectionBuilderPayload> {
32
+ constructor(
33
+ request: MetadataBuilderRequest<SectionBuilderPayload>,
34
+ logger?: Logger,
35
+ ) {
36
+ super(request, logger);
37
+ }
38
+
39
+ protected get sectionType(): SectionType {
40
+ return this.request.payload!.sectionType;
41
+ }
42
+
43
+ protected get mode(): 'create' | 'update' {
44
+ return this.request.type.startsWith('create-') ? 'create' : 'update';
45
+ }
46
+
47
+ protected get args(): SectionToolArguments {
48
+ return this.request.payload!.arguments;
49
+ }
50
+
51
+ /** Override in subclasses for per-section validation. */
52
+ protected validateSection(): string[] {
53
+ return [];
54
+ }
55
+
56
+ async validateAsync(): Promise<{ errors: string[] }> {
57
+ const errors: string[] = [];
58
+ const payload = this.request.payload!;
59
+
60
+ if (this.mode === 'create') {
61
+ if (!createDefaultSectionContent(payload.sectionType)) {
62
+ errors.push(`Unsupported section type: "${payload.sectionType}"`);
63
+ }
64
+ }
65
+
66
+ if (this.mode === 'update') {
67
+ if (!payload.contentId) {
68
+ errors.push('contentId is required for update operations');
69
+ }
70
+ }
71
+
72
+ errors.push(...this.validateSection());
73
+
74
+ return { errors };
75
+ }
76
+
77
+ /**
78
+ * Validates the request, then creates or finds the target content.
79
+ * Returns `{ content }` on success, or `{ error }` with a BuilderResult on failure.
80
+ */
81
+ protected resolveContent(
82
+ model: BrandPageModel,
83
+ ): { content: C } | { error: BuilderResult } {
84
+ if (this.mode === 'create') {
85
+ return this.createContent(model);
86
+ }
87
+
88
+ return this.findContent(model);
89
+ }
90
+
91
+ private createContent(
92
+ model: BrandPageModel,
93
+ ): { content: C } | { error: BuilderResult } {
94
+ const payload = this.request.payload!;
95
+ const { pageId } = payload;
96
+
97
+ const content = createDefaultSectionContent(this.sectionType);
98
+
99
+ if (!content) {
100
+ return {
101
+ error: {
102
+ request: this.request,
103
+ model,
104
+ errors: [
105
+ `Failed to create default content for section type: "${this.sectionType}"`,
106
+ ],
107
+ },
108
+ };
109
+ }
110
+
111
+ const targetPage = pageId
112
+ ? model.pages.find((p) => p.id === pageId)
113
+ : model.pages[0];
114
+
115
+ if (!targetPage) {
116
+ return {
117
+ error: {
118
+ request: this.request,
119
+ model,
120
+ errors: [
121
+ pageId
122
+ ? `Page with ID "${pageId}" not found`
123
+ : 'No pages found in model',
124
+ ],
125
+ },
126
+ };
127
+ }
128
+
129
+ targetPage.contents.push(content);
130
+
131
+ this.log({
132
+ message: `Created ${this.sectionType} section on page ${targetPage.id}`,
133
+ contentId: content.id,
134
+ });
135
+
136
+ return { content: content as C };
137
+ }
138
+
139
+ private findContent(
140
+ model: BrandPageModel,
141
+ ): { content: C } | { error: BuilderResult } {
142
+ const { contentId } = this.request.payload!;
143
+
144
+ for (const page of model.pages) {
145
+ for (const content of page.contents) {
146
+ if (content.id === contentId) {
147
+ return { content: content as C };
148
+ }
149
+ }
150
+ }
151
+
152
+ return {
153
+ error: {
154
+ request: this.request,
155
+ model,
156
+ errors: [`Content with ID "${contentId}" not found`],
157
+ },
158
+ };
159
+ }
160
+ }
@@ -0,0 +1,24 @@
1
+ import type { ButtonContent, BrandPageModel } from '../../../brand-page/models';
2
+ import type { BuilderResult } from '../../models';
3
+ import { applyScalarFields } from '../section-field-utils';
4
+ import { BaseSectionBuilder } from './base-section-builder';
5
+
6
+ export class ButtonSectionBuilder extends BaseSectionBuilder<ButtonContent> {
7
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
8
+ this.log('Build called');
9
+
10
+ const validation = await this.validateAsync();
11
+
12
+ if (validation.errors.length) {
13
+ return { request: this.request, model, errors: validation.errors };
14
+ }
15
+
16
+ const resolved = this.resolveContent(model);
17
+
18
+ if ('error' in resolved) return resolved.error;
19
+
20
+ applyScalarFields(resolved.content, this.args, this.sectionType);
21
+
22
+ return { request: this.request, model };
23
+ }
24
+ }
@@ -0,0 +1,43 @@
1
+ import type {
2
+ ContactFormContent,
3
+ BrandPageModel,
4
+ } from '../../../brand-page/models';
5
+ import type { ContactFormSectionArguments } from '../../parsers/section-tool-arguments';
6
+ import type { BuilderResult } from '../../models';
7
+ import { applyScalarFields, applyStyleFields } from '../section-field-utils';
8
+ import { BaseSectionBuilder } from './base-section-builder';
9
+
10
+ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
11
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
12
+ this.log('Build called');
13
+
14
+ const validation = await this.validateAsync();
15
+
16
+ if (validation.errors.length) {
17
+ return { request: this.request, model, errors: validation.errors };
18
+ }
19
+
20
+ const resolved = this.resolveContent(model);
21
+
22
+ if ('error' in resolved) return resolved.error;
23
+
24
+ const content = resolved.content;
25
+
26
+ applyScalarFields(content, this.args, this.sectionType);
27
+ applyStyleFields(content, this.args, this.sectionType);
28
+
29
+ const args = this.args as ContactFormSectionArguments;
30
+
31
+ if (args.fields && Array.isArray(args.fields) && content.fields) {
32
+ for (const toggle of args.fields) {
33
+ const field = content.fields.find((f) => f.id === toggle.field_id);
34
+
35
+ if (field) {
36
+ field.enabled = toggle.enabled;
37
+ }
38
+ }
39
+ }
40
+
41
+ return { request: this.request, model };
42
+ }
43
+ }
@@ -0,0 +1,63 @@
1
+ import type { BrandPageModel } from '../../../brand-page/models';
2
+ import {
3
+ BaseMetadataBuilder,
4
+ type BuilderResult,
5
+ type MetadataBuilderRequest,
6
+ } from '../../models';
7
+ import type { Logger } from '../../logger';
8
+
9
+ export interface DeleteSectionPayload {
10
+ contentId: string;
11
+ }
12
+
13
+ export class DeleteSectionBuilder extends BaseMetadataBuilder<DeleteSectionPayload> {
14
+ constructor(
15
+ request: MetadataBuilderRequest<DeleteSectionPayload>,
16
+ logger?: Logger,
17
+ ) {
18
+ super(request, logger);
19
+ }
20
+
21
+ async validateAsync(): Promise<{ errors: string[] }> {
22
+ const errors: string[] = [];
23
+ const payload = this.request.payload;
24
+
25
+ if (!payload?.contentId) {
26
+ errors.push('contentId is required for delete_section');
27
+ }
28
+
29
+ return { errors };
30
+ }
31
+
32
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
33
+ this.log('Build called');
34
+
35
+ const validation = await this.validateAsync();
36
+
37
+ if (validation.errors.length) {
38
+ return { request: this.request, model, errors: validation.errors };
39
+ }
40
+
41
+ const { contentId } = this.request.payload!;
42
+
43
+ for (const page of model.pages) {
44
+ const index = page.contents.findIndex((c) => c.id === contentId);
45
+
46
+ if (index !== -1) {
47
+ page.contents.splice(index, 1);
48
+
49
+ this.log({
50
+ message: `Deleted section ${contentId} from page ${page.id}`,
51
+ });
52
+
53
+ return { request: this.request, model };
54
+ }
55
+ }
56
+
57
+ return {
58
+ request: this.request,
59
+ model,
60
+ errors: [`Section with ID "${contentId}" not found`],
61
+ };
62
+ }
63
+ }
@@ -0,0 +1,27 @@
1
+ import type {
2
+ DividerSectionContent,
3
+ BrandPageModel,
4
+ } from '../../../brand-page/models';
5
+ import type { BuilderResult } from '../../models';
6
+ import { applyScalarFields } from '../section-field-utils';
7
+ import { BaseSectionBuilder } from './base-section-builder';
8
+
9
+ export class DividerSectionBuilder extends BaseSectionBuilder<DividerSectionContent> {
10
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
11
+ this.log('Build called');
12
+
13
+ const validation = await this.validateAsync();
14
+
15
+ if (validation.errors.length) {
16
+ return { request: this.request, model, errors: validation.errors };
17
+ }
18
+
19
+ const resolved = this.resolveContent(model);
20
+
21
+ if ('error' in resolved) return resolved.error;
22
+
23
+ applyScalarFields(resolved.content, this.args, this.sectionType);
24
+
25
+ return { request: this.request, model };
26
+ }
27
+ }
@@ -0,0 +1,111 @@
1
+ import type {
2
+ ImageGalleryContent,
3
+ BrandPageModel,
4
+ } from '../../../brand-page/models';
5
+ import type {
6
+ ImageGalleryItem,
7
+ ImageGallerySectionArguments,
8
+ } from '../../parsers/section-tool-arguments';
9
+ import type { BuilderResult } from '../../models';
10
+ import { applyScalarFields } from '../section-field-utils';
11
+ import { BaseSectionBuilder } from './base-section-builder';
12
+
13
+ const applyGalleryType = (
14
+ content: ImageGalleryContent,
15
+ galleryType: string,
16
+ ): void => {
17
+ content.galleryStyles = {
18
+ ...(content.galleryStyles || {}),
19
+ galleryType: galleryType as 'grid' | 'carousel' | 'masonry',
20
+ };
21
+ };
22
+
23
+ const applyGalleryImages = (
24
+ content: ImageGalleryContent,
25
+ images: ImageGalleryItem[],
26
+ ): void => {
27
+ content.images = images
28
+ .filter((img) => !!img.image_url)
29
+ .map((img) => ({
30
+ imageUrl: img.image_url || '',
31
+ imageTitle: img.caption || '',
32
+ width: 0,
33
+ height: 0,
34
+ mediaType: 'image' as const,
35
+ imageLink: '',
36
+ }));
37
+ };
38
+
39
+ const mergeGalleryImages = (
40
+ content: ImageGalleryContent,
41
+ images: ImageGalleryItem[],
42
+ replaceAll: boolean,
43
+ ): void => {
44
+ const updatedImages = images
45
+ .map((img, index) => {
46
+ const existing = content.images?.[index];
47
+ const imageUrl = img.image_url ?? existing?.imageUrl ?? '';
48
+
49
+ return {
50
+ ...(existing || {}),
51
+ imageUrl,
52
+ imageTitle: img.caption ?? existing?.imageTitle ?? '',
53
+ width: existing?.width ?? 0,
54
+ height: existing?.height ?? 0,
55
+ mediaType: existing?.mediaType ?? 'image',
56
+ imageLink: existing?.imageLink ?? '',
57
+ };
58
+ })
59
+ .filter((item) => !!item.imageUrl);
60
+
61
+ if (!replaceAll && content.images && content.images.length > images.length) {
62
+ const extras = content.images.slice(images.length).map((img) => ({
63
+ imageUrl: img.imageUrl ?? '',
64
+ imageTitle: img.imageTitle ?? '',
65
+ width: img.width ?? 0,
66
+ height: img.height ?? 0,
67
+ mediaType: img.mediaType ?? ('image' as const),
68
+ imageLink: img.imageLink ?? '',
69
+ }));
70
+
71
+ updatedImages.push(...extras);
72
+ }
73
+
74
+ content.images = updatedImages;
75
+ };
76
+
77
+ export class ImageGalleryBuilder extends BaseSectionBuilder<ImageGalleryContent> {
78
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
79
+ this.log('Build called');
80
+
81
+ const validation = await this.validateAsync();
82
+
83
+ if (validation.errors.length) {
84
+ return { request: this.request, model, errors: validation.errors };
85
+ }
86
+
87
+ const resolved = this.resolveContent(model);
88
+
89
+ if ('error' in resolved) return resolved.error;
90
+
91
+ const content = resolved.content;
92
+
93
+ applyScalarFields(content, this.args, this.sectionType);
94
+
95
+ const args = this.args as ImageGallerySectionArguments;
96
+
97
+ if (args.gallery_type) {
98
+ applyGalleryType(content, args.gallery_type);
99
+ }
100
+
101
+ if (args.images && Array.isArray(args.images)) {
102
+ if (this.mode === 'create') {
103
+ applyGalleryImages(content, args.images);
104
+ } else {
105
+ mergeGalleryImages(content, args.images, !!args._replace_items);
106
+ }
107
+ }
108
+
109
+ return { request: this.request, model };
110
+ }
111
+ }
@@ -0,0 +1,24 @@
1
+ import type { ImageContent, BrandPageModel } from '../../../brand-page/models';
2
+ import type { BuilderResult } from '../../models';
3
+ import { applyScalarFields } from '../section-field-utils';
4
+ import { BaseSectionBuilder } from './base-section-builder';
5
+
6
+ export class ImageSectionBuilder extends BaseSectionBuilder<ImageContent> {
7
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
8
+ this.log('Build called');
9
+
10
+ const validation = await this.validateAsync();
11
+
12
+ if (validation.errors.length) {
13
+ return { request: this.request, model, errors: validation.errors };
14
+ }
15
+
16
+ const resolved = this.resolveContent(model);
17
+
18
+ if ('error' in resolved) return resolved.error;
19
+
20
+ applyScalarFields(resolved.content, this.args, this.sectionType);
21
+
22
+ return { request: this.request, model };
23
+ }
24
+ }
@@ -0,0 +1,223 @@
1
+ import type {
2
+ ImagesSectionContent,
3
+ BrandPageModel,
4
+ } from '../../../brand-page/models';
5
+ import type {
6
+ ImagesSectionArguments,
7
+ ImagesSectionItem,
8
+ } from '../../parsers/section-tool-arguments';
9
+ import type { BuilderResult } from '../../models';
10
+ import {
11
+ applyScalarFields,
12
+ applyStyleFields,
13
+ applyRTEStyles,
14
+ } from '../section-field-utils';
15
+ import { BaseSectionBuilder } from './base-section-builder';
16
+
17
+ const applyImageItems = (
18
+ content: ImagesSectionContent,
19
+ items: ImagesSectionItem[],
20
+ ): void => {
21
+ content.items = items
22
+ .filter((item) => !!item.image_url)
23
+ .map((item, index) => ({
24
+ imageUrl: item.image_url || '',
25
+ imageTitle: '',
26
+ width: 0,
27
+ height: 0,
28
+ mediaType: 'image',
29
+ imageLink: '',
30
+ title: item.heading || '',
31
+ paragraph: item.text || '',
32
+ itemsId: index,
33
+ colorReplacements: [],
34
+ }));
35
+ };
36
+
37
+ const mergeImageItems = (
38
+ content: ImagesSectionContent,
39
+ items: ImagesSectionItem[],
40
+ replaceAll: boolean,
41
+ ): void => {
42
+ const hasIndexedItems = items.some((item) => item.item_index !== undefined);
43
+
44
+ if (hasIndexedItems) {
45
+ for (const item of items) {
46
+ const targetIndex = item.item_index;
47
+
48
+ if (
49
+ targetIndex === undefined ||
50
+ targetIndex < 0 ||
51
+ !content.items?.[targetIndex]
52
+ )
53
+ continue;
54
+
55
+ const existing = content.items[targetIndex];
56
+
57
+ if (item.image_url !== undefined) existing.imageUrl = item.image_url;
58
+
59
+ if (item.heading !== undefined) existing.title = item.heading;
60
+
61
+ if (item.text !== undefined) existing.paragraph = item.text;
62
+ }
63
+
64
+ return;
65
+ }
66
+
67
+ const updatedItems = items
68
+ .map((item, index) => {
69
+ const existing = content.items?.[index];
70
+
71
+ return {
72
+ ...(existing || {}),
73
+ imageUrl: item.image_url ?? existing?.imageUrl ?? '',
74
+ imageTitle: existing?.imageTitle ?? '',
75
+ width: existing?.width ?? 0,
76
+ height: existing?.height ?? 0,
77
+ mediaType: existing?.mediaType ?? 'image',
78
+ imageLink: existing?.imageLink ?? '',
79
+ title: item.heading ?? existing?.title ?? '',
80
+ paragraph: item.text ?? existing?.paragraph ?? '',
81
+ itemsId: existing?.itemsId ?? index,
82
+ colorReplacements: existing?.colorReplacements ?? [],
83
+ };
84
+ })
85
+ .filter((item) => !!item.imageUrl);
86
+
87
+ if (!replaceAll && content.items && content.items.length > items.length) {
88
+ const remaining = content.items.slice(items.length) as typeof updatedItems;
89
+
90
+ updatedItems.push(...remaining);
91
+ }
92
+
93
+ content.items = updatedItems;
94
+ };
95
+
96
+ export class ImagesSectionBuilder extends BaseSectionBuilder<ImagesSectionContent> {
97
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
98
+ this.log('Build called');
99
+
100
+ const validation = await this.validateAsync();
101
+
102
+ if (validation.errors.length) {
103
+ return { request: this.request, model, errors: validation.errors };
104
+ }
105
+
106
+ const resolved = this.resolveContent(model);
107
+
108
+ if ('error' in resolved) return resolved.error;
109
+
110
+ const content = resolved.content;
111
+
112
+ applyScalarFields(content, this.args, this.sectionType);
113
+ applyStyleFields(content, this.args, this.sectionType);
114
+ applyRTEStyles(content, this.args, this.sectionType);
115
+
116
+ const args = this.args as ImagesSectionArguments;
117
+
118
+ if (args.background_style && typeof args.background_style === 'object') {
119
+ const existing = content.backgroundStyle || {};
120
+
121
+ content.backgroundStyle = {
122
+ ...existing,
123
+ ...Object.fromEntries(
124
+ Object.entries(args.background_style).map(([k, v]) => [
125
+ k,
126
+ v === null ? '' : v,
127
+ ]),
128
+ ),
129
+ };
130
+ }
131
+
132
+ if (args.card_style && typeof args.card_style === 'object') {
133
+ const existing = content.cardStyle || {};
134
+
135
+ content.cardStyle = {
136
+ ...existing,
137
+ ...Object.fromEntries(
138
+ Object.entries(args.card_style).map(([k, v]) => [
139
+ k,
140
+ v === null ? '' : v,
141
+ ]),
142
+ ),
143
+ };
144
+ }
145
+
146
+ if (args.items && Array.isArray(args.items)) {
147
+ if (this.mode === 'create') {
148
+ applyImageItems(content, args.items);
149
+ } else {
150
+ mergeImageItems(content, args.items, !!args._replace_items);
151
+ }
152
+ }
153
+
154
+ // Phase 2: Item operations (remove, insert, reorder)
155
+ if (args._remove_item_indices?.length && content.items) {
156
+ const sorted = [...args._remove_item_indices].sort((a, b) => b - a);
157
+
158
+ for (const idx of sorted) {
159
+ if (idx >= 0 && idx < content.items.length) {
160
+ content.items.splice(idx, 1);
161
+ }
162
+ }
163
+ }
164
+
165
+ if (args.items && Array.isArray(args.items)) {
166
+ const insertItems = args.items.filter(
167
+ (item) => item.insert_at !== undefined && item.item_index === undefined,
168
+ );
169
+ // Process inserts in reverse order of insert_at to preserve indices
170
+ const sortedInserts = [...insertItems].sort(
171
+ (a, b) => (b.insert_at ?? 0) - (a.insert_at ?? 0),
172
+ );
173
+
174
+ for (const item of sortedInserts) {
175
+ if (!item.image_url) continue;
176
+
177
+ const pos = Math.max(
178
+ 0,
179
+ Math.min(item.insert_at ?? 0, content.items?.length ?? 0),
180
+ );
181
+ const newItem = {
182
+ imageUrl: item.image_url,
183
+ imageTitle: '',
184
+ width: 0,
185
+ height: 0,
186
+ mediaType: 'image',
187
+ imageLink: '',
188
+ title: item.heading ?? '',
189
+ paragraph: item.text ?? '',
190
+ itemsId: pos,
191
+ colorReplacements: [],
192
+ };
193
+
194
+ content.items = content.items ?? [];
195
+ content.items.splice(pos, 0, newItem as any);
196
+ }
197
+ }
198
+
199
+ if (args._reorder_items?.length && content.items) {
200
+ const newOrder = args._reorder_items;
201
+ const unique = new Set(newOrder);
202
+
203
+ if (unique.size === newOrder.length) {
204
+ const reordered = newOrder
205
+ .filter((idx) => idx >= 0 && idx < content.items!.length)
206
+ .map((idx) => content.items![idx]);
207
+
208
+ if (reordered.length === content.items.length) {
209
+ content.items = reordered;
210
+ }
211
+ }
212
+ }
213
+
214
+ // Re-index itemsId after any item operations
215
+ if (content.items) {
216
+ content.items.forEach((item, i) => {
217
+ item.itemsId = i;
218
+ });
219
+ }
220
+
221
+ return { request: this.request, model };
222
+ }
223
+ }
@@ -0,0 +1,21 @@
1
+ export {
2
+ BaseSectionBuilder,
3
+ type SectionBuilderPayload,
4
+ } from './base-section-builder';
5
+
6
+ export { BannerSectionBuilder } from './banner-section-builder';
7
+ export { AboutSectionBuilder } from './about-section-builder';
8
+ export { ContactFormBuilder } from './contact-form-builder';
9
+ export { DividerSectionBuilder } from './divider-section-builder';
10
+ export { TextSectionBuilder } from './text-section-builder';
11
+ export { TestimonialSectionBuilder } from './testimonial-section-builder';
12
+ export { ImagesSectionBuilder } from './images-section-builder';
13
+ export { ShapesSectionBuilder } from './shapes-section-builder';
14
+ export { SocialLinksBuilder } from './social-links-builder';
15
+ export { ImageGalleryBuilder } from './image-gallery-builder';
16
+ export { ProfileSectionBuilder } from './profile-section-builder';
17
+ export { MailingListSectionBuilder } from './mailing-list-section-builder';
18
+ export { ImageSectionBuilder } from './image-section-builder';
19
+ export { ButtonSectionBuilder } from './button-section-builder';
20
+ export { DeleteSectionBuilder } from './delete-section-builder';
21
+ export { MoveSectionBuilder } from './move-section-builder';