@contractspec/bundle.marketing 3.7.6 → 3.7.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 (163) hide show
  1. package/.turbo/turbo-build.log +84 -84
  2. package/AGENTS.md +29 -21
  3. package/README.md +36 -49
  4. package/dist/browser/components/marketing/ChangelogPage.js +8 -8
  5. package/dist/browser/components/marketing/CofounderPage.js +167 -523
  6. package/dist/browser/components/marketing/ContactClient.js +200 -207
  7. package/dist/browser/components/marketing/ContributePage.js +211 -463
  8. package/dist/browser/components/marketing/DesignPartnerPage.js +165 -218
  9. package/dist/browser/components/marketing/LandingPage.js +464 -568
  10. package/dist/browser/components/marketing/PricingClient.js +213 -839
  11. package/dist/browser/components/marketing/ProductClientPage.js +265 -463
  12. package/dist/browser/components/marketing/index.js +2007 -3338
  13. package/dist/browser/components/marketing/pricing-thinking-modal.js +12 -12
  14. package/dist/browser/components/marketing/sections/AudienceSection.js +2 -2
  15. package/dist/browser/components/marketing/sections/CorePositioningSection.js +2 -2
  16. package/dist/browser/components/marketing/sections/CtaSection.js +3 -3
  17. package/dist/browser/components/marketing/sections/FearsSection.js +3 -3
  18. package/dist/browser/components/marketing/sections/HeroMarketingSection.js +6 -6
  19. package/dist/browser/components/marketing/sections/IconGridSection.js +2 -2
  20. package/dist/browser/components/marketing/sections/OutputsSection.js +2 -2
  21. package/dist/browser/components/marketing/sections/ProblemSection.js +2 -2
  22. package/dist/browser/components/marketing/sections/SolutionSection.js +2 -2
  23. package/dist/browser/components/marketing/sections/StepsSection.js +4 -4
  24. package/dist/browser/components/marketing/studio-signup-section.js +25 -41
  25. package/dist/browser/components/templates/TemplatesClientPage.js +2324 -3578
  26. package/dist/browser/components/templates/TemplatesPage.js +1 -1
  27. package/dist/browser/components/templates/TemplatesPreviewModal.js +3 -3
  28. package/dist/browser/components/templates/index.js +2361 -3615
  29. package/dist/browser/index.js +2363 -3617
  30. package/dist/browser/libs/email/client.js +1 -1
  31. package/dist/browser/libs/email/contact.js +1 -1
  32. package/dist/browser/libs/email/newsletter.js +1 -1
  33. package/dist/browser/libs/email/waitlist-application.js +1 -1
  34. package/dist/browser/libs/email/waitlist.js +1 -1
  35. package/dist/browser/registry/engine.js +2003 -3334
  36. package/dist/browser/registry/index.js +2003 -3334
  37. package/dist/browser/registry/registry-docs.js +2 -2
  38. package/dist/browser/registry/registry-landing.js +2007 -3338
  39. package/dist/browser/registry/registry.js +2003 -3334
  40. package/dist/browser/registry/utils.js +2003 -3334
  41. package/dist/components/marketing/ChangelogPage.js +8 -8
  42. package/dist/components/marketing/CofounderPage.js +167 -523
  43. package/dist/components/marketing/ContactClient.js +200 -207
  44. package/dist/components/marketing/ContributePage.d.ts +0 -2
  45. package/dist/components/marketing/ContributePage.js +211 -463
  46. package/dist/components/marketing/DesignPartnerPage.js +165 -218
  47. package/dist/components/marketing/LandingPage.js +464 -568
  48. package/dist/components/marketing/PricingClient.js +213 -839
  49. package/dist/components/marketing/ProductClientPage.js +265 -463
  50. package/dist/components/marketing/index.d.ts +5 -5
  51. package/dist/components/marketing/index.js +2007 -3338
  52. package/dist/components/marketing/pricing-thinking-modal.js +12 -12
  53. package/dist/components/marketing/sections/AudienceSection.js +2 -2
  54. package/dist/components/marketing/sections/CorePositioningSection.js +2 -2
  55. package/dist/components/marketing/sections/CtaSection.js +3 -3
  56. package/dist/components/marketing/sections/FearsSection.js +3 -3
  57. package/dist/components/marketing/sections/HeroMarketingSection.js +6 -6
  58. package/dist/components/marketing/sections/IconGridSection.d.ts +3 -3
  59. package/dist/components/marketing/sections/IconGridSection.js +2 -2
  60. package/dist/components/marketing/sections/OutputsSection.js +2 -2
  61. package/dist/components/marketing/sections/ProblemSection.js +2 -2
  62. package/dist/components/marketing/sections/SolutionSection.js +2 -2
  63. package/dist/components/marketing/sections/StepsSection.js +4 -4
  64. package/dist/components/marketing/studio-signup-section.js +25 -41
  65. package/dist/components/templates/TemplatesClientPage.js +2324 -3578
  66. package/dist/components/templates/TemplatesPage.js +1 -1
  67. package/dist/components/templates/TemplatesPreviewModal.js +3 -3
  68. package/dist/components/templates/index.js +2361 -3615
  69. package/dist/index.js +2363 -3617
  70. package/dist/libs/email/client.js +1 -1
  71. package/dist/libs/email/contact.js +1 -1
  72. package/dist/libs/email/newsletter.js +1 -1
  73. package/dist/libs/email/waitlist-application.js +1 -1
  74. package/dist/libs/email/waitlist.js +1 -1
  75. package/dist/node/components/marketing/ChangelogPage.js +8 -8
  76. package/dist/node/components/marketing/CofounderPage.js +167 -523
  77. package/dist/node/components/marketing/ContactClient.js +200 -207
  78. package/dist/node/components/marketing/ContributePage.js +211 -463
  79. package/dist/node/components/marketing/DesignPartnerPage.js +165 -218
  80. package/dist/node/components/marketing/LandingPage.js +464 -568
  81. package/dist/node/components/marketing/PricingClient.js +213 -839
  82. package/dist/node/components/marketing/ProductClientPage.js +265 -463
  83. package/dist/node/components/marketing/index.js +2007 -3338
  84. package/dist/node/components/marketing/pricing-thinking-modal.js +12 -12
  85. package/dist/node/components/marketing/sections/AudienceSection.js +2 -2
  86. package/dist/node/components/marketing/sections/CorePositioningSection.js +2 -2
  87. package/dist/node/components/marketing/sections/CtaSection.js +3 -3
  88. package/dist/node/components/marketing/sections/FearsSection.js +3 -3
  89. package/dist/node/components/marketing/sections/HeroMarketingSection.js +6 -6
  90. package/dist/node/components/marketing/sections/IconGridSection.js +2 -2
  91. package/dist/node/components/marketing/sections/OutputsSection.js +2 -2
  92. package/dist/node/components/marketing/sections/ProblemSection.js +2 -2
  93. package/dist/node/components/marketing/sections/SolutionSection.js +2 -2
  94. package/dist/node/components/marketing/sections/StepsSection.js +4 -4
  95. package/dist/node/components/marketing/studio-signup-section.js +25 -41
  96. package/dist/node/components/templates/TemplatesClientPage.js +2324 -3578
  97. package/dist/node/components/templates/TemplatesPage.js +1 -1
  98. package/dist/node/components/templates/TemplatesPreviewModal.js +3 -3
  99. package/dist/node/components/templates/index.js +2361 -3615
  100. package/dist/node/index.js +2363 -3617
  101. package/dist/node/libs/email/client.js +1 -1
  102. package/dist/node/libs/email/contact.js +1 -1
  103. package/dist/node/libs/email/newsletter.js +1 -1
  104. package/dist/node/libs/email/waitlist-application.js +1 -1
  105. package/dist/node/libs/email/waitlist.js +1 -1
  106. package/dist/node/registry/engine.js +2003 -3334
  107. package/dist/node/registry/index.js +2003 -3334
  108. package/dist/node/registry/registry-docs.js +2 -2
  109. package/dist/node/registry/registry-landing.js +2007 -3338
  110. package/dist/node/registry/registry.js +2003 -3334
  111. package/dist/node/registry/utils.js +2003 -3334
  112. package/dist/registry/engine.js +2003 -3334
  113. package/dist/registry/index.js +2003 -3334
  114. package/dist/registry/registry-docs.js +2 -2
  115. package/dist/registry/registry-landing.js +2007 -3338
  116. package/dist/registry/registry.js +2003 -3334
  117. package/dist/registry/utils.js +2003 -3334
  118. package/package.json +22 -22
  119. package/src/bundles/MarketingBundle.ts +273 -273
  120. package/src/components/marketing/ChangelogPage.tsx +72 -100
  121. package/src/components/marketing/CofounderPage.tsx +120 -384
  122. package/src/components/marketing/ContactClient.tsx +164 -154
  123. package/src/components/marketing/ContributePage.tsx +139 -313
  124. package/src/components/marketing/DesignPartnerPage.tsx +133 -171
  125. package/src/components/marketing/LandingPage.tsx +353 -25
  126. package/src/components/marketing/PricingClient.tsx +192 -437
  127. package/src/components/marketing/ProductClientPage.tsx +255 -377
  128. package/src/components/marketing/index.ts +5 -5
  129. package/src/components/marketing/pricing-thinking-modal.tsx +197 -197
  130. package/src/components/marketing/sections/AudienceSection.tsx +55 -56
  131. package/src/components/marketing/sections/CorePositioningSection.tsx +37 -37
  132. package/src/components/marketing/sections/CtaSection.tsx +49 -50
  133. package/src/components/marketing/sections/DevelopersSection.tsx +26 -27
  134. package/src/components/marketing/sections/FearsSection.tsx +36 -37
  135. package/src/components/marketing/sections/HeroMarketingSection.tsx +59 -59
  136. package/src/components/marketing/sections/IconGridSection.tsx +71 -71
  137. package/src/components/marketing/sections/OutputsSection.tsx +51 -52
  138. package/src/components/marketing/sections/ProblemSection.tsx +39 -40
  139. package/src/components/marketing/sections/SolutionSection.tsx +39 -40
  140. package/src/components/marketing/sections/StepsSection.tsx +47 -48
  141. package/src/components/marketing/studio-signup-section.tsx +39 -41
  142. package/src/components/templates/TemplatesClientPage.tsx +727 -685
  143. package/src/components/templates/TemplatesPage.tsx +110 -110
  144. package/src/components/templates/TemplatesPreviewModal.tsx +197 -198
  145. package/src/index.ts +4 -4
  146. package/src/libs/email/client.test.ts +81 -81
  147. package/src/libs/email/client.ts +111 -111
  148. package/src/libs/email/contact.ts +35 -35
  149. package/src/libs/email/newsletter.ts +46 -46
  150. package/src/libs/email/types.ts +29 -29
  151. package/src/libs/email/utils.ts +5 -5
  152. package/src/libs/email/waitlist-application.ts +72 -72
  153. package/src/libs/email/waitlist.ts +46 -46
  154. package/src/libs/pricing-examples.ts +12 -12
  155. package/src/registry/engine.ts +16 -16
  156. package/src/registry/factory.ts +57 -57
  157. package/src/registry/registry-docs.ts +656 -666
  158. package/src/registry/registry-landing.ts +94 -95
  159. package/src/registry/registry.ts +36 -37
  160. package/src/registry/types.ts +2 -2
  161. package/src/registry/utils.ts +56 -56
  162. package/tsconfig.json +11 -11
  163. package/tsdown.config.js +5 -5
@@ -1,22 +1,22 @@
1
+ import { Logger } from '@contractspec/lib.logger';
1
2
  import { createClient, Temv1alpha1 } from '@scaleway/sdk';
2
3
  import type { Region } from '@scaleway/sdk-client';
3
- import { Logger } from '@contractspec/lib.logger';
4
4
  import type {
5
- EmailAddress,
6
- EmailConfigResult,
7
- EmailSendOutcome,
8
- EmailServiceConfig,
9
- SendEmailRequest,
5
+ EmailAddress,
6
+ EmailConfigResult,
7
+ EmailSendOutcome,
8
+ EmailServiceConfig,
9
+ SendEmailRequest,
10
10
  } from './types';
11
11
 
12
12
  const DEFAULT_FROM: EmailAddress = {
13
- email: 'noreply@transactional.contractspec.io',
14
- name: 'ContractSpec',
13
+ email: 'noreply@transactional.contractspec.io',
14
+ name: 'ContractSpec',
15
15
  };
16
16
 
17
17
  const DEFAULT_TEAM_INBOX: EmailAddress = {
18
- email: 'contact@contractspec.io',
19
- name: 'ContractSpec Team',
18
+ email: 'contact@contractspec.io',
19
+ name: 'ContractSpec Team',
20
20
  };
21
21
 
22
22
  const DEFAULT_REGION: Region = 'fr-par';
@@ -26,121 +26,121 @@ type EmailApi = Pick<Temv1alpha1.API, 'createEmail'>;
26
26
  let cachedConfig: EmailServiceConfig | null = null;
27
27
  let cachedClient: EmailApi | null = null;
28
28
  let apiFactory: (client: ReturnType<typeof createClient>) => EmailApi = (
29
- client
29
+ client
30
30
  ) => new Temv1alpha1.API(client);
31
31
 
32
32
  const mapRegion = (value?: string | null): Region => {
33
- const normalized = value?.trim().toLowerCase();
34
- if (normalized === 'par' || normalized === 'fr-par') return 'fr-par';
35
- if (normalized === 'ams' || normalized === 'nl-ams') return 'nl-ams';
36
- if (normalized === 'waw' || normalized === 'pl-waw') return 'pl-waw';
37
- return DEFAULT_REGION;
33
+ const normalized = value?.trim().toLowerCase();
34
+ if (normalized === 'par' || normalized === 'fr-par') return 'fr-par';
35
+ if (normalized === 'ams' || normalized === 'nl-ams') return 'nl-ams';
36
+ if (normalized === 'waw' || normalized === 'pl-waw') return 'pl-waw';
37
+ return DEFAULT_REGION;
38
38
  };
39
39
 
40
40
  export const getEmailConfig = (): EmailConfigResult => {
41
- if (cachedConfig) {
42
- return { ok: true, config: cachedConfig };
43
- }
44
-
45
- const accessKey =
46
- process.env.SCALEWAY_ACCESS_KEY || process.env.SCALEWAY_ACCESS_KEY_QUEUE;
47
- const secretKey =
48
- process.env.SCALEWAY_SECRET_KEY || process.env.SCALEWAY_SECRET_KEY_QUEUE;
49
- const projectId = process.env.SCALEWAY_PROJECT_ID;
50
-
51
- if (!accessKey || !secretKey || !projectId) {
52
- return {
53
- ok: false,
54
- errorMessage:
55
- 'Email service is not configured. Please contact us directly at contact@contractspec.io.',
56
- };
57
- }
58
-
59
- const region = mapRegion(process.env.SCALEWAY_REGION);
60
-
61
- cachedConfig = {
62
- accessKey,
63
- secretKey,
64
- projectId,
65
- region,
66
- defaultZone: `${region}-1`,
67
- from: {
68
- email: process.env.SCALEWAY_EMAIL_FROM_EMAIL ?? DEFAULT_FROM.email,
69
- name: process.env.SCALEWAY_EMAIL_FROM_NAME ?? DEFAULT_FROM.name,
70
- },
71
- teamInbox: {
72
- email: process.env.SCALEWAY_EMAIL_TEAM_EMAIL ?? DEFAULT_TEAM_INBOX.email,
73
- name: process.env.SCALEWAY_EMAIL_TEAM_NAME ?? DEFAULT_TEAM_INBOX.name,
74
- },
75
- };
76
-
77
- return { ok: true, config: cachedConfig };
41
+ if (cachedConfig) {
42
+ return { ok: true, config: cachedConfig };
43
+ }
44
+
45
+ const accessKey =
46
+ process.env.SCALEWAY_ACCESS_KEY || process.env.SCALEWAY_ACCESS_KEY_QUEUE;
47
+ const secretKey =
48
+ process.env.SCALEWAY_SECRET_KEY || process.env.SCALEWAY_SECRET_KEY_QUEUE;
49
+ const projectId = process.env.SCALEWAY_PROJECT_ID;
50
+
51
+ if (!accessKey || !secretKey || !projectId) {
52
+ return {
53
+ ok: false,
54
+ errorMessage:
55
+ 'Email service is not configured. Please contact us directly at contact@contractspec.io.',
56
+ };
57
+ }
58
+
59
+ const region = mapRegion(process.env.SCALEWAY_REGION);
60
+
61
+ cachedConfig = {
62
+ accessKey,
63
+ secretKey,
64
+ projectId,
65
+ region,
66
+ defaultZone: `${region}-1`,
67
+ from: {
68
+ email: process.env.SCALEWAY_EMAIL_FROM_EMAIL ?? DEFAULT_FROM.email,
69
+ name: process.env.SCALEWAY_EMAIL_FROM_NAME ?? DEFAULT_FROM.name,
70
+ },
71
+ teamInbox: {
72
+ email: process.env.SCALEWAY_EMAIL_TEAM_EMAIL ?? DEFAULT_TEAM_INBOX.email,
73
+ name: process.env.SCALEWAY_EMAIL_TEAM_NAME ?? DEFAULT_TEAM_INBOX.name,
74
+ },
75
+ };
76
+
77
+ return { ok: true, config: cachedConfig };
78
78
  };
79
79
 
80
80
  const getTemClient = (config: EmailServiceConfig): EmailApi => {
81
- if (cachedClient) {
82
- return cachedClient;
83
- }
84
-
85
- const client = createClient({
86
- accessKey: config.accessKey,
87
- secretKey: config.secretKey,
88
- defaultProjectId: config.projectId,
89
- defaultRegion: config.region,
90
- defaultZone: config.defaultZone,
91
- });
92
-
93
- cachedClient = apiFactory(client);
94
- return cachedClient;
81
+ if (cachedClient) {
82
+ return cachedClient;
83
+ }
84
+
85
+ const client = createClient({
86
+ accessKey: config.accessKey,
87
+ secretKey: config.secretKey,
88
+ defaultProjectId: config.projectId,
89
+ defaultRegion: config.region,
90
+ defaultZone: config.defaultZone,
91
+ });
92
+
93
+ cachedClient = apiFactory(client);
94
+ return cachedClient;
95
95
  };
96
96
 
97
97
  export const sendEmail = async (
98
- config: EmailServiceConfig,
99
- request: SendEmailRequest
98
+ config: EmailServiceConfig,
99
+ request: SendEmailRequest
100
100
  ): Promise<EmailSendOutcome> => {
101
- try {
102
- const client = getTemClient(config);
103
-
104
- await client.createEmail({
105
- region: config.region,
106
- projectId: config.projectId,
107
- from: config.from,
108
- to: request.to,
109
- subject: request.subject,
110
- text: request.text,
111
- html: request.html || request.text,
112
- additionalHeaders: request.replyTo
113
- ? [{ key: 'Reply-To', value: request.replyTo }]
114
- : undefined,
115
- });
116
-
117
- return { success: true };
118
- } catch (error) {
119
- new Logger().error('scaleway_tem_email_send_failed', {
120
- context: request.context ?? 'email',
121
- error: error instanceof Error ? error.message : error,
122
- });
123
- return {
124
- success: false,
125
- error,
126
- errorMessage: 'Failed to send email via Scaleway.',
127
- };
128
- }
101
+ try {
102
+ const client = getTemClient(config);
103
+
104
+ await client.createEmail({
105
+ region: config.region,
106
+ projectId: config.projectId,
107
+ from: config.from,
108
+ to: request.to,
109
+ subject: request.subject,
110
+ text: request.text,
111
+ html: request.html || request.text,
112
+ additionalHeaders: request.replyTo
113
+ ? [{ key: 'Reply-To', value: request.replyTo }]
114
+ : undefined,
115
+ });
116
+
117
+ return { success: true };
118
+ } catch (error) {
119
+ new Logger().error('scaleway_tem_email_send_failed', {
120
+ context: request.context ?? 'email',
121
+ error: error instanceof Error ? error.message : error,
122
+ });
123
+ return {
124
+ success: false,
125
+ error,
126
+ errorMessage: 'Failed to send email via Scaleway.',
127
+ };
128
+ }
129
129
  };
130
130
 
131
131
  export const __internal = {
132
- resetCaches() {
133
- cachedClient = null;
134
- cachedConfig = null;
135
- apiFactory = (client: ReturnType<typeof createClient>) =>
136
- new Temv1alpha1.API(client);
137
- },
138
- setApiFactory(
139
- factory: (client: ReturnType<typeof createClient>) => EmailApi
140
- ) {
141
- apiFactory = factory;
142
- },
143
- setClient(client: EmailApi) {
144
- cachedClient = client;
145
- },
132
+ resetCaches() {
133
+ cachedClient = null;
134
+ cachedConfig = null;
135
+ apiFactory = (client: ReturnType<typeof createClient>) =>
136
+ new Temv1alpha1.API(client);
137
+ },
138
+ setApiFactory(
139
+ factory: (client: ReturnType<typeof createClient>) => EmailApi
140
+ ) {
141
+ apiFactory = factory;
142
+ },
143
+ setClient(client: EmailApi) {
144
+ cachedClient = client;
145
+ },
146
146
  };
@@ -1,38 +1,38 @@
1
1
  'use server';
2
2
 
3
3
  import { getEmailConfig, sendEmail } from './client';
4
- import { escapeHtml, formatMultilineHtml } from './utils';
5
4
  import type { SubmitContactFormResult } from './types';
5
+ import { escapeHtml, formatMultilineHtml } from './utils';
6
6
 
7
7
  const CONTACT_MISSING_CONFIG =
8
- 'Email service is not configured. Please contact us directly at contact@contractspec.io.';
8
+ 'Email service is not configured. Please contact us directly at contact@contractspec.io.';
9
9
  const CONTACT_SEND_ERROR =
10
- 'Failed to send message. Please contact us directly at contact@contractspec.io.';
10
+ 'Failed to send message. Please contact us directly at contact@contractspec.io.';
11
11
 
12
12
  export const submitContactForm = async (
13
- formData: FormData
13
+ formData: FormData
14
14
  ): Promise<SubmitContactFormResult> => {
15
- const name = (formData.get('name') ?? '').toString().trim();
16
- const email = (formData.get('email') ?? '').toString().trim();
17
- const message = (formData.get('message') ?? '').toString().trim();
15
+ const name = (formData.get('name') ?? '').toString().trim();
16
+ const email = (formData.get('email') ?? '').toString().trim();
17
+ const message = (formData.get('message') ?? '').toString().trim();
18
18
 
19
- if (!email) {
20
- return {
21
- success: false,
22
- text: 'Please fill in all required fields.',
23
- };
24
- }
19
+ if (!email) {
20
+ return {
21
+ success: false,
22
+ text: 'Please fill in all required fields.',
23
+ };
24
+ }
25
25
 
26
- const configResult = getEmailConfig();
27
- if (!configResult.ok || !configResult.config) {
28
- return {
29
- success: false,
30
- text: configResult.errorMessage ?? CONTACT_MISSING_CONFIG,
31
- };
32
- }
26
+ const configResult = getEmailConfig();
27
+ if (!configResult.ok || !configResult.config) {
28
+ return {
29
+ success: false,
30
+ text: configResult.errorMessage ?? CONTACT_MISSING_CONFIG,
31
+ };
32
+ }
33
33
 
34
- const senderName = name || email;
35
- const emailContentText = `
34
+ const senderName = name || email;
35
+ const emailContentText = `
36
36
  New contact form submission from ${senderName}
37
37
 
38
38
  Contact Information:
@@ -46,7 +46,7 @@ ${message || 'No message provided'}
46
46
  Submitted via ContractSpec contact form
47
47
  `.trim();
48
48
 
49
- const emailContentHtml = `
49
+ const emailContentHtml = `
50
50
  <div style="font-family: sans-serif; max-width: 640px; margin: 0 auto;">
51
51
  <h1 style="color: #8b5cf6; margin-bottom: 12px;">New contact form submission</h1>
52
52
  <p style="margin: 0 0 12px;">From ${escapeHtml(senderName)}</p>
@@ -63,18 +63,18 @@ Submitted via ContractSpec contact form
63
63
  </div>
64
64
  `;
65
65
 
66
- const sendResult = await sendEmail(configResult.config, {
67
- to: [configResult.config.teamInbox],
68
- subject: `New Contact Form Message from ${senderName}`,
69
- text: emailContentText,
70
- html: emailContentHtml,
71
- replyTo: email,
72
- context: 'contact-form',
73
- });
66
+ const sendResult = await sendEmail(configResult.config, {
67
+ to: [configResult.config.teamInbox],
68
+ subject: `New Contact Form Message from ${senderName}`,
69
+ text: emailContentText,
70
+ html: emailContentHtml,
71
+ replyTo: email,
72
+ context: 'contact-form',
73
+ });
74
74
 
75
- if (!sendResult.success) {
76
- return { success: false, text: CONTACT_SEND_ERROR };
77
- }
75
+ if (!sendResult.success) {
76
+ return { success: false, text: CONTACT_SEND_ERROR };
77
+ }
78
78
 
79
- return { success: true, text: 'Message sent successfully!' };
79
+ return { success: true, text: 'Message sent successfully!' };
80
80
  };
@@ -1,35 +1,35 @@
1
1
  'use server';
2
2
 
3
3
  import { getEmailConfig, sendEmail } from './client';
4
- import { formatMultilineHtml } from './utils';
5
4
  import type { SubmitNewsletterResult } from './types';
5
+ import { formatMultilineHtml } from './utils';
6
6
 
7
7
  const NEWSLETTER_MISSING_CONFIG =
8
- 'Newsletter service is not configured. Please try again later.';
8
+ 'Newsletter service is not configured. Please try again later.';
9
9
  const NEWSLETTER_SEND_ERROR =
10
- 'Failed to subscribe. Please try again later or contact us directly.';
10
+ 'Failed to subscribe. Please try again later or contact us directly.';
11
11
 
12
12
  export const subscribeToNewsletter = async (
13
- formData: FormData
13
+ formData: FormData
14
14
  ): Promise<SubmitNewsletterResult> => {
15
- const email = (formData.get('email') ?? '').toString().trim();
16
-
17
- if (!email || !email.includes('@')) {
18
- return {
19
- success: false,
20
- text: 'Please enter a valid email address.',
21
- };
22
- }
23
-
24
- const configResult = getEmailConfig();
25
- if (!configResult.ok || !configResult.config) {
26
- return {
27
- success: false,
28
- text: configResult.errorMessage ?? NEWSLETTER_MISSING_CONFIG,
29
- };
30
- }
31
-
32
- const welcomeText = `
15
+ const email = (formData.get('email') ?? '').toString().trim();
16
+
17
+ if (!email || !email.includes('@')) {
18
+ return {
19
+ success: false,
20
+ text: 'Please enter a valid email address.',
21
+ };
22
+ }
23
+
24
+ const configResult = getEmailConfig();
25
+ if (!configResult.ok || !configResult.config) {
26
+ return {
27
+ success: false,
28
+ text: configResult.errorMessage ?? NEWSLETTER_MISSING_CONFIG,
29
+ };
30
+ }
31
+
32
+ const welcomeText = `
33
33
  Welcome to ContractSpec!
34
34
 
35
35
  Thanks for subscribing to our newsletter. You'll receive updates on:
@@ -49,7 +49,7 @@ https://contractspec.io
49
49
  Unsubscribe: Reply to this email with "unsubscribe"
50
50
  `.trim();
51
51
 
52
- const welcomeHtml = `
52
+ const welcomeHtml = `
53
53
  <div style="font-family: sans-serif; max-width: 640px; margin: 0 auto;">
54
54
  <h1 style="color: #8b5cf6;">Welcome to ContractSpec!</h1>
55
55
  <p>Thanks for subscribing to our newsletter. You'll receive updates on:</p>
@@ -72,37 +72,37 @@ Unsubscribe: Reply to this email with "unsubscribe"
72
72
  </div>
73
73
  `;
74
74
 
75
- const userSend = await sendEmail(configResult.config, {
76
- to: [{ email }],
77
- subject: 'Welcome to ContractSpec Newsletter',
78
- text: welcomeText,
79
- html: welcomeHtml,
80
- context: 'newsletter-welcome',
81
- });
75
+ const userSend = await sendEmail(configResult.config, {
76
+ to: [{ email }],
77
+ subject: 'Welcome to ContractSpec Newsletter',
78
+ text: welcomeText,
79
+ html: welcomeHtml,
80
+ context: 'newsletter-welcome',
81
+ });
82
82
 
83
- if (!userSend.success) {
84
- return { success: false, text: NEWSLETTER_SEND_ERROR };
85
- }
83
+ if (!userSend.success) {
84
+ return { success: false, text: NEWSLETTER_SEND_ERROR };
85
+ }
86
86
 
87
- const teamNotificationText = `New newsletter subscription from: ${email}`;
88
- const teamNotificationHtml = `
87
+ const teamNotificationText = `New newsletter subscription from: ${email}`;
88
+ const teamNotificationHtml = `
89
89
  <div style="font-family: sans-serif; max-width: 640px; margin: 0 auto;">
90
90
  <p style="margin: 0 0 8px;">New newsletter subscription</p>
91
91
  <p style="margin: 0;"><strong>Email:</strong> ${formatMultilineHtml(email)}</p>
92
92
  </div>
93
93
  `;
94
94
 
95
- const teamSend = await sendEmail(configResult.config, {
96
- to: [configResult.config.teamInbox],
97
- subject: `New Newsletter Subscription: ${email}`,
98
- text: teamNotificationText,
99
- html: teamNotificationHtml,
100
- context: 'newsletter-team-notification',
101
- });
95
+ const teamSend = await sendEmail(configResult.config, {
96
+ to: [configResult.config.teamInbox],
97
+ subject: `New Newsletter Subscription: ${email}`,
98
+ text: teamNotificationText,
99
+ html: teamNotificationHtml,
100
+ context: 'newsletter-team-notification',
101
+ });
102
102
 
103
- if (!teamSend.success) {
104
- return { success: false, text: NEWSLETTER_SEND_ERROR };
105
- }
103
+ if (!teamSend.success) {
104
+ return { success: false, text: NEWSLETTER_SEND_ERROR };
105
+ }
106
106
 
107
- return { success: true, text: 'Successfully subscribed!' };
107
+ return { success: true, text: 'Successfully subscribed!' };
108
108
  };
@@ -3,57 +3,57 @@
3
3
  import type { Region } from '@scaleway/sdk-client';
4
4
 
5
5
  export interface SubmitContactFormResult {
6
- success: boolean;
7
- text: string;
6
+ success: boolean;
7
+ text: string;
8
8
  }
9
9
 
10
10
  export interface SubmitNewsletterResult {
11
- success: boolean;
12
- text: string;
11
+ success: boolean;
12
+ text: string;
13
13
  }
14
14
 
15
15
  export interface SubmitWaitlistResult {
16
- success: boolean;
17
- text: string;
16
+ success: boolean;
17
+ text: string;
18
18
  }
19
19
 
20
20
  export interface SubmitWaitlistApplicationResult {
21
- success: boolean;
22
- text: string;
21
+ success: boolean;
22
+ text: string;
23
23
  }
24
24
 
25
25
  export interface EmailAddress {
26
- email: string;
27
- name?: string;
26
+ email: string;
27
+ name?: string;
28
28
  }
29
29
 
30
30
  export interface EmailServiceConfig {
31
- accessKey: string;
32
- secretKey: string;
33
- projectId: string;
34
- region: Region;
35
- defaultZone: string;
36
- from: EmailAddress;
37
- teamInbox: EmailAddress;
31
+ accessKey: string;
32
+ secretKey: string;
33
+ projectId: string;
34
+ region: Region;
35
+ defaultZone: string;
36
+ from: EmailAddress;
37
+ teamInbox: EmailAddress;
38
38
  }
39
39
 
40
40
  export interface EmailConfigResult {
41
- ok: boolean;
42
- config?: EmailServiceConfig;
43
- errorMessage?: string;
41
+ ok: boolean;
42
+ config?: EmailServiceConfig;
43
+ errorMessage?: string;
44
44
  }
45
45
 
46
46
  export interface SendEmailRequest {
47
- to: EmailAddress[];
48
- subject: string;
49
- text: string;
50
- html: string;
51
- replyTo?: string;
52
- context?: string;
47
+ to: EmailAddress[];
48
+ subject: string;
49
+ text: string;
50
+ html: string;
51
+ replyTo?: string;
52
+ context?: string;
53
53
  }
54
54
 
55
55
  export interface EmailSendOutcome {
56
- success: boolean;
57
- error?: unknown;
58
- errorMessage?: string;
56
+ success: boolean;
57
+ error?: unknown;
58
+ errorMessage?: string;
59
59
  }
@@ -1,8 +1,8 @@
1
1
  export const escapeHtml = (value: string): string =>
2
- value
3
- .replaceAll('&', '&amp;')
4
- .replaceAll('<', '&lt;')
5
- .replaceAll('>', '&gt;');
2
+ value
3
+ .replaceAll('&', '&amp;')
4
+ .replaceAll('<', '&lt;')
5
+ .replaceAll('>', '&gt;');
6
6
 
7
7
  export const formatMultilineHtml = (value: string): string =>
8
- escapeHtml(value).replaceAll('\n', '<br />');
8
+ escapeHtml(value).replaceAll('\n', '<br />');