strapi-plugin-brevo-template-sender 1.0.0

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.
@@ -0,0 +1,2697 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const designSystem = require("@strapi/design-system");
5
+ const admin = require("@strapi/strapi/admin");
6
+ const react = require("react");
7
+ const reactIntl = require("react-intl");
8
+ const icons = require("@strapi/icons");
9
+ const styled = require("styled-components");
10
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
+ const bmcButtonImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiEAAACZCAMAAADOzxqEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIRQTFRFw6kJSkAahnUS8NACHBkhpI8N4cME/+ZAhnUROjMcKyYfaFoW0rYHlYIPs5wLWU0Yd2cUHBkg//e/aFoVd2cTKyYe0rYG/98Q/+6AWU0X//vf//3v/+Eg//CP/+Mw//Sv/+pg//nP/+xw//Kf/+hQ/+5///a/DQwiDQwj/////90A////A0MgiAAAACx0Uk5T/////////////////////////////////////////////////////////wDH1gmMAAARpUlEQVR42uydaWPiOAyGfcQpuUMp04N2em8z8P//3xJiJ5btHJA2pY30ZbctE4z8RJZeK4bsTLt7ePq7RZuhXV+8PG4sHgj8cfNwj56atb29dhFy94QeQrt/bSXk/Rrdg1Yy8p+TkGfMPtCUvWxsQh4xgKA19vfOJOQVnYIGCptnSAgCgtaCSEXIIzoEzcpXNw0hz5iDoDlykYYQrGLQXPauCHlHX6A57bki5A7XGDS3XVSEoNSO1mb/lYRs0A9oHUGE7B7QD2itdrcnBLf70drtZUfu0Ato7fZ3R3CRQetcZghWMmhd9khQT0XrsneCPkDrsickBK3TLpAQNCQEDQlBQ0LQfjohnJB0oZsg5A/6FwnZbv10cUuXHy1Gbxepj26eLyHpav3Rb+uV4OjqGRLCF8uPobZcISOzI+TPcD4OjKTo7XkR4h8HyN6wbJoXISsw+wFdLRYJaUzsyxkKM9g1untWhNB6+ViQjhyDJKtAvRKr33kS8kEXoo0RkiZ/ypS2emGC/p4TIQszE6W0XGmkXe1/koXwVb0krdDfcyKEB0MzVH9f91TBBv09q2qX3x5Rw1T/h/6eFSH7LGO1HBhDZNaC+vvMCNlbekX7ACnzkO0VKiIzJaS0P+liRV2glKlrWpW46eEXC3T4LAmpzdcUM0P78LGYQUK67ZCxLHH7bjaE/FksFuSYxLNahW4xV50HIWTdNAiJ/lYyn4grVfWsMReZASHCykkDSq+qzkO4d7egdgob4FLz2wk5ft/fUf6i/WJCVh8jDZORX07IWEAwiPxyQki1lStIchUcw8U+U0mqpCRAv/9qQpLDLMuu030+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";
13
+ const BrevoLogo = (props) => {
14
+ return /* @__PURE__ */ jsxRuntime.jsx(
15
+ "svg",
16
+ {
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ width: "1000",
19
+ height: "295",
20
+ viewBox: "0 0 1000 295",
21
+ fill: "none",
22
+ ...props,
23
+ children: /* @__PURE__ */ jsxRuntime.jsx(
24
+ "path",
25
+ {
26
+ d: "M820.28 182.338C820.28 137.755 848.604 106.4 888.892 106.4C929.18 106.4 957.956 137.737 957.956 182.338C957.956 226.939 929.187 256.5 888.892 256.5C848.598 256.5 820.28 225.588 820.28 182.338ZM778.224 182.338C778.224 248.12 824.262 294.479 888.886 294.479C953.51 294.479 1000 248.12 1000 182.338C1000 116.556 953.962 68.4399 888.886 68.4399C823.81 68.4399 778.224 115.686 778.224 182.338ZM563.521 71.0853L650.292 291.821H691.025L777.791 71.0853H733.966L671.104 241.498H670.214L607.352 71.0853H563.521ZM394.856 174.383C397.508 133.76 424.515 106.4 461.261 106.4C493.128 106.4 517.037 126.712 520.58 157.179H447.089C420.973 157.179 406.801 160.269 396.191 174.402H394.856V174.39V174.383ZM352.805 181.006C352.805 246.788 399.289 294.46 463.468 294.46C506.854 294.46 544.916 272.391 561.295 237.502L525.885 219.835C513.494 242.792 489.585 256.482 463.468 256.482C432.028 256.482 403.704 232.637 403.704 209.679C403.704 197.766 411.673 192.457 423.18 192.457H563.502V180.544C563.502 114.317 521.007 68.4029 459.925 68.4029C398.844 68.4029 352.799 115.649 352.799 180.988M232.399 291.796H272.242V156.285C272.242 127.149 290.382 106.394 315.627 106.394C326.256 106.394 337.311 109.927 342.635 114.774C346.623 104.174 352.818 93.5923 362.111 82.9924C351.482 74.1684 333.342 68.4153 315.627 68.4153C266.937 68.4153 232.399 104.618 232.399 156.267V291.809V291.796ZM39.843 145.698V37.9598H105.358C127.486 37.9598 142.103 50.7611 142.103 70.185C142.103 92.2542 123.072 109.033 84.1191 121.834C57.5571 130.214 45.6116 137.281 41.1785 145.679L39.843 145.692V145.698ZM39.843 253.861V208.835C39.843 188.967 56.6668 169.543 80.1311 162.032C100.943 154.966 118.193 147.899 132.81 140.407C152.286 151.895 164.232 171.744 164.232 192.5C164.232 227.814 130.584 253.861 84.9909 253.861H39.843ZM0 291.821H88.5337C155.829 291.821 206.282 249.884 206.282 194.257C206.282 163.79 190.794 136.43 163.341 118.763C177.513 104.63 184.153 88.2955 184.153 68.4276C184.153 27.3784 154.493 0 109.791 0H0V291.821Z",
27
+ fill: "currentColor"
28
+ }
29
+ )
30
+ }
31
+ );
32
+ };
33
+ function makeId(uid, event) {
34
+ return `${uid}::${event}`;
35
+ }
36
+ function formatContentTypeUid(uid) {
37
+ return uid.replace(/^api::/, "");
38
+ }
39
+ const EVENTS = [
40
+ { value: "create", labelKey: "create" },
41
+ { value: "update", labelKey: "update" },
42
+ { value: "delete", labelKey: "delete" },
43
+ { value: "publish", labelKey: "publish" },
44
+ { value: "unpublish", labelKey: "unpublish" }
45
+ ];
46
+ const DEFAULT_SEND_EMAIL_ATTRIBUTES = [
47
+ { name: "firstname", type: "string", kind: "scalar" },
48
+ { name: "lastname", type: "string", kind: "scalar" },
49
+ { name: "email", type: "string", kind: "scalar" },
50
+ { name: "message", type: "string", kind: "scalar" }
51
+ ];
52
+ function ActiveTemplatesCard({
53
+ selectedContentTypes,
54
+ hasTrigger,
55
+ onManageContentTypes,
56
+ onEditTemplate
57
+ }) {
58
+ const intl = reactIntl.useIntl();
59
+ return /* @__PURE__ */ jsxRuntime.jsxs(
60
+ designSystem.Box,
61
+ {
62
+ padding: 8,
63
+ background: "neutral0",
64
+ borderColor: "neutral200",
65
+ borderWidth: "1px",
66
+ borderStyle: "solid",
67
+ borderRadius: "8px",
68
+ shadow: "tableShadow",
69
+ height: "100%",
70
+ children: [
71
+ /* @__PURE__ */ jsxRuntime.jsxs(
72
+ designSystem.Flex,
73
+ {
74
+ justifyContent: "space-between",
75
+ alignItems: "center",
76
+ marginBottom: 4,
77
+ gap: 3,
78
+ children: [
79
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", as: "h2", style: { marginBottom: 15 }, children: intl.formatMessage({
80
+ id: "brevo-template-sender.config.activeTemplates",
81
+ defaultMessage: "Active Content-Type templates"
82
+ }) }),
83
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: onManageContentTypes, children: intl.formatMessage({
84
+ id: "brevo-template-sender.config.manageContentTypes",
85
+ defaultMessage: "Manage content types"
86
+ }) })
87
+ ]
88
+ }
89
+ ),
90
+ selectedContentTypes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: intl.formatMessage({
91
+ id: "brevo-template-sender.config.noActiveTemplates",
92
+ defaultMessage: "No content type selected. Click « Manage content types » to enable events and edit templates."
93
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 3, style: { width: "100%" }, children: selectedContentTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsx(
94
+ designSystem.Box,
95
+ {
96
+ padding: 3,
97
+ background: "neutral100",
98
+ borderColor: "neutral200",
99
+ borderWidth: "1px",
100
+ borderStyle: "solid",
101
+ borderRadius: "4px",
102
+ style: { width: "100%", minWidth: 0 },
103
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
104
+ designSystem.Flex,
105
+ {
106
+ justifyContent: "space-between",
107
+ alignItems: "center",
108
+ wrap: "wrap",
109
+ style: { width: "100%", gap: 4 },
110
+ children: [
111
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: ct.displayName }),
113
+ /* @__PURE__ */ jsxRuntime.jsx(
114
+ designSystem.Typography,
115
+ {
116
+ variant: "pi",
117
+ textColor: "neutral600",
118
+ marginLeft: 2,
119
+ children: formatContentTypeUid(ct.uid)
120
+ }
121
+ )
122
+ ] }),
123
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
124
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, wrap: "wrap", children: EVENTS.filter((e) => hasTrigger(ct.uid, e.value)).map(
125
+ (e) => /* @__PURE__ */ jsxRuntime.jsx(
126
+ designSystem.Box,
127
+ {
128
+ paddingLeft: 2,
129
+ paddingRight: 2,
130
+ paddingTop: 1,
131
+ paddingBottom: 1,
132
+ background: "primary100",
133
+ borderRadius: "4px",
134
+ children: /* @__PURE__ */ jsxRuntime.jsx(
135
+ designSystem.Typography,
136
+ {
137
+ variant: "pi",
138
+ textColor: "primary700",
139
+ fontWeight: "bold",
140
+ children: intl.formatMessage({
141
+ id: `brevo-template-sender.event.${e.labelKey}`,
142
+ defaultMessage: e.value
143
+ })
144
+ }
145
+ )
146
+ },
147
+ e.value
148
+ )
149
+ ) }),
150
+ /* @__PURE__ */ jsxRuntime.jsx(
151
+ designSystem.Button,
152
+ {
153
+ variant: "tertiary",
154
+ size: "S",
155
+ onClick: () => onEditTemplate(ct),
156
+ children: intl.formatMessage({
157
+ id: "brevo-template-sender.config.editTemplate",
158
+ defaultMessage: "Edit Template"
159
+ })
160
+ }
161
+ )
162
+ ] })
163
+ ]
164
+ }
165
+ )
166
+ },
167
+ ct.uid
168
+ )) })
169
+ ]
170
+ }
171
+ );
172
+ }
173
+ function ConfigCard({
174
+ settings,
175
+ setSettings,
176
+ saving,
177
+ onSave,
178
+ message,
179
+ onDismissMessage
180
+ }) {
181
+ const intl = reactIntl.useIntl();
182
+ return /* @__PURE__ */ jsxRuntime.jsxs(
183
+ designSystem.Box,
184
+ {
185
+ padding: 8,
186
+ background: "neutral0",
187
+ borderColor: "neutral200",
188
+ borderWidth: "1px",
189
+ borderStyle: "solid",
190
+ borderRadius: "8px",
191
+ shadow: "tableShadow",
192
+ height: "100%",
193
+ children: [
194
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", as: "h2", style: { marginBottom: 15 }, children: intl.formatMessage({
195
+ id: "brevo-template-sender.config.configTitle",
196
+ defaultMessage: "Config"
197
+ }) }),
198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: intl.formatMessage({
199
+ id: "brevo-template-sender.settings.description",
200
+ defaultMessage: "Enter the information required to send emails via Brevo. The API key is in your Brevo account (Settings → API Keys)."
201
+ }) }),
202
+ message && /* @__PURE__ */ jsxRuntime.jsx(
203
+ designSystem.Alert,
204
+ {
205
+ marginBottom: 4,
206
+ variant: message.type === "error" ? "danger" : "success",
207
+ onClose: onDismissMessage,
208
+ closeLabel: intl.formatMessage({
209
+ id: "app.components.Button.close",
210
+ defaultMessage: "Close"
211
+ }),
212
+ children: message.text
213
+ }
214
+ ),
215
+ /* @__PURE__ */ jsxRuntime.jsxs(
216
+ designSystem.Flex,
217
+ {
218
+ direction: "column",
219
+ gap: 4,
220
+ marginBottom: 6,
221
+ alignItems: "flex-start",
222
+ marginTop: 4,
223
+ children: [
224
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: "brevo-settings-apiKey", style: { width: "100%" }, children: [
225
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
226
+ id: "brevo-template-sender.settings.apiKey",
227
+ defaultMessage: "Brevo API key"
228
+ }) }),
229
+ /* @__PURE__ */ jsxRuntime.jsxs(
230
+ designSystem.Flex,
231
+ {
232
+ direction: "row",
233
+ gap: 4,
234
+ alignItems: "flex-start",
235
+ wrap: "wrap",
236
+ marginTop: 2,
237
+ children: [
238
+ /* @__PURE__ */ jsxRuntime.jsxs(
239
+ designSystem.Box,
240
+ {
241
+ style: { minWidth: 200, maxWidth: 400, flex: "1 1 200px" },
242
+ children: [
243
+ /* @__PURE__ */ jsxRuntime.jsx(
244
+ designSystem.TextInput,
245
+ {
246
+ id: "brevo-settings-apiKey",
247
+ type: "password",
248
+ value: settings.apiKey,
249
+ onChange: (e) => setSettings((s) => ({ ...s, apiKey: e.target.value })),
250
+ placeholder: settings.apiKeySet ? intl.formatMessage({
251
+ id: "brevo-template-sender.settings.apiKeyPlaceholder",
252
+ defaultMessage: "Leave empty to keep current key"
253
+ }) : intl.formatMessage({
254
+ id: "brevo-template-sender.settings.apiKeyPlaceholderNew",
255
+ defaultMessage: "xkeysib-..."
256
+ }),
257
+ size: "M"
258
+ }
259
+ ),
260
+ settings.apiKey && !settings.apiKey.startsWith("xkeysib-") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning600", fontWeight: "bold", children: intl.formatMessage({
261
+ id: "brevo-template-sender.settings.apiKeyInvalidFormat",
262
+ defaultMessage: "⚠ This doesn't look like a Brevo API key (should start with xkeysib-). Make sure you're not using an SMTP key."
263
+ }) }) }),
264
+ settings.apiKeySet && !settings.apiKey && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
265
+ designSystem.Typography,
266
+ {
267
+ variant: "pi",
268
+ textColor: "success600",
269
+ fontWeight: "bold",
270
+ children: intl.formatMessage({
271
+ id: "brevo-template-sender.settings.apiKeySet",
272
+ defaultMessage: "An API key is already configured."
273
+ })
274
+ }
275
+ ) })
276
+ ]
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 240px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
280
+ intl.formatMessage({
281
+ id: "brevo-template-sender.settings.apiKeyHint",
282
+ defaultMessage: "To find or generate an API key, go to your Brevo account: Settings → SMTP & API → API keys."
283
+ }),
284
+ " ",
285
+ /* @__PURE__ */ jsxRuntime.jsx(
286
+ "a",
287
+ {
288
+ href: "https://help.brevo.com/hc/articles/209467485",
289
+ target: "_blank",
290
+ rel: "noopener noreferrer",
291
+ style: {
292
+ color: "var(--strapi-colors-primary600)",
293
+ textDecoration: "underline"
294
+ },
295
+ children: intl.formatMessage({
296
+ id: "brevo-template-sender.settings.apiKeyHintLink",
297
+ defaultMessage: "Documentation"
298
+ })
299
+ }
300
+ )
301
+ ] }) })
302
+ ]
303
+ }
304
+ )
305
+ ] }),
306
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: "brevo-settings-senderEmail", style: { width: "100%" }, children: [
307
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
308
+ id: "brevo-template-sender.settings.senderEmail",
309
+ defaultMessage: "Sender email"
310
+ }) }),
311
+ /* @__PURE__ */ jsxRuntime.jsxs(
312
+ designSystem.Flex,
313
+ {
314
+ direction: "row",
315
+ gap: 4,
316
+ alignItems: "flex-start",
317
+ wrap: "wrap",
318
+ marginTop: 2,
319
+ children: [
320
+ /* @__PURE__ */ jsxRuntime.jsx(
321
+ designSystem.Box,
322
+ {
323
+ style: { minWidth: 200, maxWidth: 400, flex: "1 1 200px" },
324
+ children: /* @__PURE__ */ jsxRuntime.jsx(
325
+ designSystem.TextInput,
326
+ {
327
+ id: "brevo-settings-senderEmail",
328
+ type: "email",
329
+ value: settings.senderEmail,
330
+ onChange: (e) => setSettings((s) => ({
331
+ ...s,
332
+ senderEmail: e.target.value
333
+ })),
334
+ placeholder: "no-reply@yourdomain.com",
335
+ size: "M"
336
+ }
337
+ )
338
+ }
339
+ ),
340
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 240px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
341
+ intl.formatMessage({
342
+ id: "brevo-template-sender.settings.senderEmailHint",
343
+ defaultMessage: "Remember to authenticate your sender domain in Brevo for better deliverability (SPF, DKIM)."
344
+ }),
345
+ " ",
346
+ /* @__PURE__ */ jsxRuntime.jsx(
347
+ "a",
348
+ {
349
+ href: "https://help.brevo.com/hc/articles/14925263522578",
350
+ target: "_blank",
351
+ rel: "noopener noreferrer",
352
+ style: {
353
+ color: "var(--strapi-colors-primary600)",
354
+ textDecoration: "underline"
355
+ },
356
+ children: intl.formatMessage({
357
+ id: "brevo-template-sender.settings.senderEmailHintLink",
358
+ defaultMessage: "Documentation"
359
+ })
360
+ }
361
+ )
362
+ ] }) })
363
+ ]
364
+ }
365
+ )
366
+ ] }),
367
+ /* @__PURE__ */ jsxRuntime.jsxs(
368
+ designSystem.Field.Root,
369
+ {
370
+ id: "brevo-settings-senderName",
371
+ style: { width: "100%", maxWidth: "400px" },
372
+ children: [
373
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
374
+ id: "brevo-template-sender.settings.senderName",
375
+ defaultMessage: "Sender name (optional)"
376
+ }) }),
377
+ /* @__PURE__ */ jsxRuntime.jsx(
378
+ designSystem.TextInput,
379
+ {
380
+ id: "brevo-settings-senderName",
381
+ value: settings.senderName,
382
+ onChange: (e) => setSettings((s) => ({ ...s, senderName: e.target.value })),
383
+ placeholder: "John Doe",
384
+ size: "M"
385
+ }
386
+ )
387
+ ]
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsxRuntime.jsxs(
391
+ designSystem.Field.Root,
392
+ {
393
+ id: "brevo-settings-openaiApiKey",
394
+ style: { width: "100%" },
395
+ children: [
396
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
397
+ id: "brevo-template-sender.settings.openaiApiKey",
398
+ defaultMessage: "OpenAI API key (optional)"
399
+ }) }),
400
+ /* @__PURE__ */ jsxRuntime.jsxs(
401
+ designSystem.Flex,
402
+ {
403
+ direction: "row",
404
+ gap: 4,
405
+ alignItems: "flex-start",
406
+ wrap: "wrap",
407
+ marginTop: 2,
408
+ children: [
409
+ /* @__PURE__ */ jsxRuntime.jsxs(
410
+ designSystem.Box,
411
+ {
412
+ style: { minWidth: 200, maxWidth: 400, flex: "1 1 200px" },
413
+ children: [
414
+ /* @__PURE__ */ jsxRuntime.jsx(
415
+ designSystem.TextInput,
416
+ {
417
+ id: "brevo-settings-openaiApiKey",
418
+ type: "password",
419
+ value: settings.openaiApiKey,
420
+ onChange: (e) => setSettings((s) => ({
421
+ ...s,
422
+ openaiApiKey: e.target.value
423
+ })),
424
+ placeholder: settings.openaiApiKeySet ? intl.formatMessage({
425
+ id: "brevo-template-sender.settings.apiKeyPlaceholder",
426
+ defaultMessage: "Leave empty to keep current key"
427
+ }) : "sk-...",
428
+ size: "M"
429
+ }
430
+ ),
431
+ settings.openaiApiKeySet && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
432
+ designSystem.Typography,
433
+ {
434
+ variant: "pi",
435
+ textColor: "success600",
436
+ fontWeight: "bold",
437
+ children: intl.formatMessage({
438
+ id: "brevo-template-sender.settings.openaiApiKeySet",
439
+ defaultMessage: "An OpenAI key is configured."
440
+ })
441
+ }
442
+ ) })
443
+ ]
444
+ }
445
+ ),
446
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 240px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: intl.formatMessage({
447
+ id: "brevo-template-sender.settings.openaiApiKeyHint",
448
+ defaultMessage: "Enables « Generate with AI » in the template editor. Create a key at platform.openai.com."
449
+ }) }) })
450
+ ]
451
+ }
452
+ )
453
+ ]
454
+ }
455
+ )
456
+ ]
457
+ }
458
+ ),
459
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onSave, loading: saving, size: "L", children: intl.formatMessage({
460
+ id: "brevo-template-sender.settings.save",
461
+ defaultMessage: "Save settings"
462
+ }) })
463
+ ]
464
+ }
465
+ );
466
+ }
467
+ function ManageContentTypesModal({
468
+ open,
469
+ onOpenChange,
470
+ contentTypes,
471
+ hasTrigger,
472
+ toggleTrigger,
473
+ getTemplateForContentType,
474
+ onEditTemplate
475
+ }) {
476
+ const intl = reactIntl.useIntl();
477
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
478
+ designSystem.Modal.Content,
479
+ {
480
+ labelledBy: "manage-content-types-title",
481
+ style: { maxWidth: "90vw", width: 960 },
482
+ children: [
483
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(
484
+ designSystem.Typography,
485
+ {
486
+ id: "manage-content-types-title",
487
+ variant: "beta",
488
+ as: "h2",
489
+ style: { marginBottom: 15 },
490
+ children: intl.formatMessage({
491
+ id: "brevo-template-sender.config.manageContentTypesTitle",
492
+ defaultMessage: "Manage content types & events"
493
+ })
494
+ }
495
+ ) }),
496
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { children: [
497
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
498
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: intl.formatMessage({
499
+ id: "brevo-template-sender.config.contentType",
500
+ defaultMessage: "Content Type"
501
+ }) }),
502
+ EVENTS.map((e) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: intl.formatMessage({
503
+ id: `brevo-template-sender.event.${e.labelKey}`,
504
+ defaultMessage: e.value
505
+ }) }, e.value)),
506
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: intl.formatMessage({
507
+ id: "brevo-template-sender.config.editTemplate",
508
+ defaultMessage: "Edit Template"
509
+ }) })
510
+ ] }) }),
511
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: contentTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
512
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
513
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, paddingBottom: 1, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: ct.displayName }) }),
514
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatContentTypeUid(ct.uid) }) })
515
+ ] }),
516
+ EVENTS.map((e) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
517
+ designSystem.Checkbox,
518
+ {
519
+ checked: hasTrigger(ct.uid, e.value),
520
+ onCheckedChange: (checked) => toggleTrigger(ct.uid, e.value, checked === true)
521
+ }
522
+ ) }, e.value)),
523
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
524
+ designSystem.Button,
525
+ {
526
+ variant: "tertiary",
527
+ size: "S",
528
+ disabled: !getTemplateForContentType(ct.uid),
529
+ onClick: () => onEditTemplate(ct),
530
+ children: intl.formatMessage({
531
+ id: "brevo-template-sender.config.editTemplate",
532
+ defaultMessage: "Edit Template"
533
+ })
534
+ }
535
+ ) })
536
+ ] }, ct.uid)) })
537
+ ] }) }),
538
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(
540
+ designSystem.Button,
541
+ {
542
+ variant: "tertiary",
543
+ onClick: () => onOpenChange(false),
544
+ children: intl.formatMessage({
545
+ id: "app.components.Button.cancel",
546
+ defaultMessage: "Cancel"
547
+ })
548
+ }
549
+ ),
550
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => onOpenChange(false), children: intl.formatMessage({
551
+ id: "brevo-template-sender.config.closeManage",
552
+ defaultMessage: "Close"
553
+ }) })
554
+ ] })
555
+ ]
556
+ }
557
+ ) });
558
+ }
559
+ function SendEmailApiCard({
560
+ onConfigureTemplate
561
+ }) {
562
+ const intl = reactIntl.useIntl();
563
+ const [docModalOpen, setDocModalOpen] = react.useState(false);
564
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
565
+ /* @__PURE__ */ jsxRuntime.jsxs(
566
+ designSystem.Box,
567
+ {
568
+ padding: 8,
569
+ background: "neutral0",
570
+ borderColor: "neutral200",
571
+ borderWidth: "1px",
572
+ borderStyle: "solid",
573
+ borderRadius: "8px",
574
+ shadow: "tableShadow",
575
+ height: "100%",
576
+ children: [
577
+ /* @__PURE__ */ jsxRuntime.jsxs(
578
+ designSystem.Flex,
579
+ {
580
+ justifyContent: "space-between",
581
+ alignItems: "center",
582
+ marginBottom: 4,
583
+ gap: 3,
584
+ wrap: "wrap",
585
+ children: [
586
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", as: "h2", style: { marginBottom: 0 }, children: intl.formatMessage({
587
+ id: "brevo-template-sender.config.sendEmailApi",
588
+ defaultMessage: "Send Email API"
589
+ }) }),
590
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
591
+ /* @__PURE__ */ jsxRuntime.jsx(
592
+ designSystem.Typography,
593
+ {
594
+ variant: "pi",
595
+ textColor: "neutral700",
596
+ style: { fontFamily: "monospace", fontSize: 12 },
597
+ children: "POST /api/brevo-template-sender/send"
598
+ }
599
+ ),
600
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: onConfigureTemplate, children: intl.formatMessage({
601
+ id: "brevo-template-sender.sendEmail.configureTemplate",
602
+ defaultMessage: "Configure template"
603
+ }) }),
604
+ /* @__PURE__ */ jsxRuntime.jsx(
605
+ designSystem.Button,
606
+ {
607
+ variant: "tertiary",
608
+ size: "S",
609
+ onClick: () => setDocModalOpen(true),
610
+ children: intl.formatMessage({
611
+ id: "brevo-template-sender.sendEmail.documentation",
612
+ defaultMessage: "Documentation"
613
+ })
614
+ }
615
+ )
616
+ ] })
617
+ ]
618
+ }
619
+ ),
620
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: intl.formatMessage({
621
+ id: "brevo-template-sender.sendEmail.description",
622
+ defaultMessage: "API that can be called at any time to send an email with the default template. Recipients are configured in the template (above). Dynamic values (params) are set by the caller."
623
+ }) })
624
+ ]
625
+ }
626
+ ),
627
+ /* @__PURE__ */ jsxRuntime.jsx(
628
+ designSystem.Modal.Root,
629
+ {
630
+ open: docModalOpen,
631
+ onOpenChange: (open) => setDocModalOpen(open),
632
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
633
+ designSystem.Modal.Content,
634
+ {
635
+ labelledBy: "send-email-api-doc-title",
636
+ style: { maxWidth: 560 },
637
+ children: [
638
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "send-email-api-doc-title", variant: "beta", as: "h2", children: intl.formatMessage({
639
+ id: "brevo-template-sender.sendEmail.docModalTitle",
640
+ defaultMessage: "Send Email API — Documentation"
641
+ }) }) }),
642
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(
643
+ designSystem.Flex,
644
+ {
645
+ direction: "column",
646
+ gap: 6,
647
+ alignItems: "flex-start",
648
+ style: { width: "100%" },
649
+ children: [
650
+ /* @__PURE__ */ jsxRuntime.jsx(
651
+ designSystem.Typography,
652
+ {
653
+ variant: "pi",
654
+ textColor: "neutral700",
655
+ lineHeight: 1.6,
656
+ style: { textAlign: "left", width: "100%" },
657
+ children: intl.formatMessage({
658
+ id: "brevo-template-sender.sendEmail.docModalIntro",
659
+ defaultMessage: "Call this endpoint to send an email using the default template (configured above)."
660
+ })
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingTop: 2, paddingBottom: 2, style: { width: "100%" }, children: [
664
+ /* @__PURE__ */ jsxRuntime.jsx(
665
+ designSystem.Typography,
666
+ {
667
+ variant: "epsilon",
668
+ fontWeight: "bold",
669
+ textColor: "neutral800",
670
+ marginBottom: 2,
671
+ children: intl.formatMessage({
672
+ id: "brevo-template-sender.sendEmail.docModalEndpoint",
673
+ defaultMessage: "Endpoint"
674
+ })
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsxRuntime.jsxs(
678
+ designSystem.Flex,
679
+ {
680
+ gap: 2,
681
+ alignItems: "center",
682
+ wrap: "wrap",
683
+ justifyContent: "flex-start",
684
+ children: [
685
+ /* @__PURE__ */ jsxRuntime.jsx(
686
+ designSystem.Box,
687
+ {
688
+ paddingLeft: 2,
689
+ paddingRight: 2,
690
+ paddingTop: 1,
691
+ paddingBottom: 1,
692
+ background: "primary100",
693
+ borderRadius: "4px",
694
+ children: /* @__PURE__ */ jsxRuntime.jsx(
695
+ designSystem.Typography,
696
+ {
697
+ variant: "pi",
698
+ fontWeight: "bold",
699
+ textColor: "primary700",
700
+ style: { fontSize: 11 },
701
+ children: "POST"
702
+ }
703
+ )
704
+ }
705
+ ),
706
+ /* @__PURE__ */ jsxRuntime.jsx(
707
+ designSystem.Typography,
708
+ {
709
+ variant: "pi",
710
+ textColor: "neutral800",
711
+ style: {
712
+ fontFamily: "monospace",
713
+ fontSize: 13,
714
+ wordBreak: "break-all"
715
+ },
716
+ children: "/api/brevo-template-sender/send"
717
+ }
718
+ )
719
+ ]
720
+ }
721
+ )
722
+ ] }),
723
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingTop: 2, paddingBottom: 2, style: { width: "100%" }, children: [
724
+ /* @__PURE__ */ jsxRuntime.jsx(
725
+ designSystem.Typography,
726
+ {
727
+ variant: "epsilon",
728
+ fontWeight: "bold",
729
+ textColor: "neutral800",
730
+ marginBottom: 2,
731
+ children: intl.formatMessage({
732
+ id: "brevo-template-sender.sendEmail.docModalBody",
733
+ defaultMessage: "Request body (JSON)"
734
+ })
735
+ }
736
+ ),
737
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, alignItems: "flex-start", children: [
738
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
739
+ /* @__PURE__ */ jsxRuntime.jsx(
740
+ designSystem.Typography,
741
+ {
742
+ variant: "pi",
743
+ fontWeight: "semiBold",
744
+ textColor: "neutral800",
745
+ style: { fontFamily: "monospace", fontSize: 12 },
746
+ children: "to "
747
+ }
748
+ ),
749
+ /* @__PURE__ */ jsxRuntime.jsx(
750
+ designSystem.Typography,
751
+ {
752
+ variant: "pi",
753
+ textColor: "neutral500",
754
+ style: { fontSize: 11 },
755
+ children: intl.formatMessage({
756
+ id: "brevo-template-sender.sendEmail.docModalBodyToOptional",
757
+ defaultMessage: "(optional)"
758
+ })
759
+ }
760
+ ),
761
+ /* @__PURE__ */ jsxRuntime.jsx(
762
+ designSystem.Typography,
763
+ {
764
+ variant: "pi",
765
+ textColor: "neutral600",
766
+ lineHeight: 1.5,
767
+ marginLeft: 2,
768
+ children: intl.formatMessage({
769
+ id: "brevo-template-sender.sendEmail.docModalBodyTo",
770
+ defaultMessage: " — Override recipient email. If omitted, recipients configured in the template are used."
771
+ })
772
+ }
773
+ )
774
+ ] }),
775
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
776
+ /* @__PURE__ */ jsxRuntime.jsx(
777
+ designSystem.Typography,
778
+ {
779
+ variant: "pi",
780
+ fontWeight: "semiBold",
781
+ textColor: "neutral800",
782
+ style: { fontFamily: "monospace", fontSize: 12 },
783
+ children: "params"
784
+ }
785
+ ),
786
+ /* @__PURE__ */ jsxRuntime.jsx(
787
+ designSystem.Typography,
788
+ {
789
+ variant: "pi",
790
+ textColor: "neutral600",
791
+ lineHeight: 1.5,
792
+ marginLeft: 2,
793
+ children: intl.formatMessage({
794
+ id: "brevo-template-sender.sendEmail.docModalBodyParams",
795
+ defaultMessage: "Object with dynamic values for the template placeholders (e.g. {{firstname}}, {{message}}). Keys must match the placeholders used in the template."
796
+ })
797
+ }
798
+ )
799
+ ] })
800
+ ] })
801
+ ] }),
802
+ /* @__PURE__ */ jsxRuntime.jsxs(
803
+ designSystem.Box,
804
+ {
805
+ paddingTop: 2,
806
+ paddingBottom: 2,
807
+ style: { width: "100%", minWidth: 0 },
808
+ children: [
809
+ /* @__PURE__ */ jsxRuntime.jsx(
810
+ designSystem.Typography,
811
+ {
812
+ variant: "epsilon",
813
+ fontWeight: "bold",
814
+ textColor: "neutral800",
815
+ marginBottom: 2,
816
+ children: intl.formatMessage({
817
+ id: "brevo-template-sender.sendEmail.docModalExample",
818
+ defaultMessage: "Example"
819
+ })
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsxRuntime.jsx(
823
+ designSystem.Box,
824
+ {
825
+ padding: 4,
826
+ background: "neutral100",
827
+ borderRadius: "4px",
828
+ borderWidth: "1px",
829
+ borderColor: "neutral200",
830
+ borderStyle: "solid",
831
+ style: {
832
+ width: "100%",
833
+ minWidth: 0,
834
+ boxSizing: "border-box"
835
+ },
836
+ children: /* @__PURE__ */ jsxRuntime.jsx(
837
+ designSystem.Typography,
838
+ {
839
+ variant: "pi",
840
+ textColor: "neutral800",
841
+ as: "pre",
842
+ style: {
843
+ fontFamily: "monospace",
844
+ fontSize: 12,
845
+ lineHeight: 1.6,
846
+ whiteSpace: "pre-wrap",
847
+ margin: 0,
848
+ textAlign: "left",
849
+ width: "100%"
850
+ },
851
+ children: `// Recipients configured in the template (admin)
852
+ {
853
+ "params": {
854
+ "firstname": "John",
855
+ "message": "Hello!"
856
+ }
857
+ }
858
+
859
+ // Override recipient (optional)
860
+ {
861
+ "to": "user@example.com",
862
+ "params": {
863
+ "firstname": "John",
864
+ "message": "Hello!"
865
+ }
866
+ }`
867
+ }
868
+ )
869
+ }
870
+ )
871
+ ]
872
+ }
873
+ )
874
+ ]
875
+ }
876
+ ) }),
877
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => setDocModalOpen(false), children: intl.formatMessage({
878
+ id: "app.components.Button.close",
879
+ defaultMessage: "Close"
880
+ }) }) })
881
+ ]
882
+ }
883
+ )
884
+ }
885
+ )
886
+ ] });
887
+ }
888
+ const STAR_COLOR_GRAY = "#6b7280";
889
+ const STAR_COLOR_GOLD = "#ca8a04";
890
+ function getRepoPath(url) {
891
+ return url.replace(/^https?:\/\/github\.com\/?/i, "").replace(/\/$/, "");
892
+ }
893
+ function SupportPluginCard({
894
+ supportRepoUrl,
895
+ bmcButtonImg: bmcButtonImg2
896
+ }) {
897
+ const intl = reactIntl.useIntl();
898
+ const [starHighlight, setStarHighlight] = react.useState(false);
899
+ const repoPath = getRepoPath(supportRepoUrl);
900
+ const repoUrl = repoPath ? `https://github.com/${repoPath}` : supportRepoUrl;
901
+ const starColor = starHighlight ? STAR_COLOR_GOLD : STAR_COLOR_GRAY;
902
+ return /* @__PURE__ */ jsxRuntime.jsxs(
903
+ designSystem.Box,
904
+ {
905
+ padding: 8,
906
+ background: "neutral0",
907
+ borderColor: "neutral200",
908
+ borderWidth: "1px",
909
+ borderStyle: "solid",
910
+ borderRadius: "8px",
911
+ shadow: "tableShadow",
912
+ height: "100%",
913
+ position: "relative",
914
+ children: [
915
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", as: "h2", style: { marginBottom: 15 }, children: intl.formatMessage({
916
+ id: "brevo-template-sender.promo.title",
917
+ defaultMessage: "Support the plugin"
918
+ }) }),
919
+ /* @__PURE__ */ jsxRuntime.jsx(
920
+ designSystem.Box,
921
+ {
922
+ padding: 3,
923
+ marginBottom: 4,
924
+ background: "primary100",
925
+ borderColor: "primary200",
926
+ borderWidth: "1px",
927
+ borderStyle: "solid",
928
+ borderRadius: "4px",
929
+ style: { width: "100%" },
930
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "primary700", children: intl.formatMessage({
931
+ id: "brevo-template-sender.promo.badge",
932
+ defaultMessage: "This block is hidden in run start so your end users won't see this block. As a developer, you can support the plugin."
933
+ }) })
934
+ }
935
+ ),
936
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: intl.formatMessage({
937
+ id: "brevo-template-sender.promo.description",
938
+ defaultMessage: "Find this plugin helpful? Support me by buying a coffee and/or leave a star to the repo on GitHub."
939
+ }) }),
940
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", alignItems: "center", style: { marginTop: 15 }, children: [
941
+ /* @__PURE__ */ jsxRuntime.jsx(
942
+ "a",
943
+ {
944
+ href: "https://www.buymeacoffee.com/dupflo",
945
+ target: "_blank",
946
+ rel: "noopener noreferrer",
947
+ children: /* @__PURE__ */ jsxRuntime.jsx(
948
+ "img",
949
+ {
950
+ src: bmcButtonImg2,
951
+ alt: "Buy Me A Coffee",
952
+ style: { height: 60, width: 217 }
953
+ }
954
+ )
955
+ }
956
+ ),
957
+ /* @__PURE__ */ jsxRuntime.jsxs(
958
+ designSystem.Flex,
959
+ {
960
+ as: "a",
961
+ href: repoUrl,
962
+ target: "_blank",
963
+ rel: "noopener noreferrer",
964
+ gap: 2,
965
+ paddingTop: 2,
966
+ paddingBottom: 2,
967
+ paddingLeft: 3,
968
+ paddingRight: 3,
969
+ background: "neutral100",
970
+ borderColor: "neutral200",
971
+ borderWidth: "1px",
972
+ borderStyle: "solid",
973
+ borderRadius: "4px",
974
+ alignItems: "center",
975
+ style: { textDecoration: "none", cursor: "pointer" },
976
+ onMouseEnter: () => setStarHighlight(true),
977
+ onMouseLeave: () => setStarHighlight(false),
978
+ onMouseDown: () => setStarHighlight(true),
979
+ onMouseUp: () => setStarHighlight(false),
980
+ children: [
981
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { color: starColor, display: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Star, { "aria-hidden": true, style: { width: 16, height: 16, flexShrink: 0 } }) }),
982
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral700", fontWeight: "semiBold", children: intl.formatMessage({
983
+ id: "brevo-template-sender.promo.starGitHub",
984
+ defaultMessage: "Star on GitHub"
985
+ }) })
986
+ ]
987
+ }
988
+ )
989
+ ] })
990
+ ]
991
+ }
992
+ );
993
+ }
994
+ const SAMPLE = {
995
+ logo_url: "",
996
+ email: "email@example.com",
997
+ subject: "Subject",
998
+ message: "Message...",
999
+ firstname: "John",
1000
+ lastname: "Doe",
1001
+ mailto: "contact@example.com"
1002
+ };
1003
+ function getDefaultEmailTemplateHtml(greeting, body) {
1004
+ return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:600px;margin:0 auto;font-family:sans-serif;">
1005
+ <tr>
1006
+ <td style="padding:24px;background:#fff;border-bottom:1px solid #eee;">
1007
+ <img src="{{logo_url}}" alt="Logo" style="max-height:80px;display:block;" />
1008
+ </td>
1009
+ </tr>
1010
+ <tr>
1011
+ <td style="padding:24px;background:#fff;">
1012
+ <p style="margin:0 0 16px;font-size:16px;line-height:1.5;">${greeting}</p>
1013
+ <p style="margin:0 0 16px;font-size:16px;line-height:1.5;">${body}</p>
1014
+ </td>
1015
+ </tr>
1016
+ </table>`;
1017
+ }
1018
+ const ModalContentLarge = styled__default.default(designSystem.Modal.Content)`
1019
+ width: 100%;
1020
+ max-width: 720px;
1021
+ height: 95vh;
1022
+ max-height: 95vh;
1023
+ `;
1024
+ const ModalContentHtmlEditor = styled__default.default(designSystem.Modal.Content)`
1025
+ width: 90vw;
1026
+ max-width: 1400px;
1027
+ height: 85vh;
1028
+ max-height: 85vh;
1029
+ `;
1030
+ const PreviewOnlyLayout = styled__default.default(designSystem.Box)`
1031
+ display: flex;
1032
+ flex-direction: column;
1033
+ min-height: 400px;
1034
+ height: calc(95vh - 320px);
1035
+ `;
1036
+ const GridLayoutHtmlEditor = styled__default.default(designSystem.Box)`
1037
+ display: grid;
1038
+ grid-template-columns: 1fr;
1039
+ min-height: 480px;
1040
+ height: calc(85vh - 180px);
1041
+ `;
1042
+ const Column = styled__default.default(designSystem.Box)`
1043
+ display: flex;
1044
+ flex-direction: column;
1045
+ min-height: 0;
1046
+ border-radius: 6px;
1047
+ border: 1px solid var(--strapi-colors-neutral200);
1048
+ background: var(--strapi-colors-neutral50);
1049
+ `;
1050
+ const ColumnLabel = styled__default.default.div`
1051
+ padding: 10px 14px;
1052
+ font-size: 11px;
1053
+ font-weight: 600;
1054
+ text-transform: uppercase;
1055
+ letter-spacing: 0.5px;
1056
+ color: var(--strapi-colors-neutral600);
1057
+ background: var(--strapi-colors-neutral100);
1058
+ border-bottom: 1px solid var(--strapi-colors-neutral200);
1059
+ border-radius: 6px 6px 0 0;
1060
+ `;
1061
+ const PreviewHeader = styled__default.default.div`
1062
+ display: flex;
1063
+ align-items: center;
1064
+ justify-content: space-between;
1065
+ padding: 10px 14px;
1066
+ font-size: 11px;
1067
+ font-weight: 600;
1068
+ text-transform: uppercase;
1069
+ letter-spacing: 0.5px;
1070
+ color: var(--strapi-colors-neutral600);
1071
+ background: var(--strapi-colors-neutral100);
1072
+ border-bottom: 1px solid var(--strapi-colors-neutral200);
1073
+ border-radius: 6px 6px 0 0;
1074
+ `;
1075
+ const PreviewBody = styled__default.default.div`
1076
+ flex: 1;
1077
+ min-height: 0;
1078
+ padding: 8px;
1079
+ display: flex;
1080
+ flex-direction: column;
1081
+ `;
1082
+ styled__default.default.div`
1083
+ padding: 10px;
1084
+ overflow-y: auto;
1085
+ flex: 1;
1086
+ `;
1087
+ styled__default.default.div`
1088
+ padding: 10px 14px;
1089
+ margin-bottom: 8px;
1090
+ font-family: monospace;
1091
+ font-size: 13px;
1092
+ background: ${(p) => p.$isLogo ? "var(--strapi-colors-primary100)" : p.$isRelation ? "var(--strapi-colors-secondary100, var(--strapi-colors-primary100))" : "var(--strapi-colors-neutral0)"};
1093
+ border: 1px solid
1094
+ ${(p) => p.$isLogo ? "var(--strapi-colors-primary200)" : p.$isRelation ? "var(--strapi-colors-secondary200, var(--strapi-colors-primary200))" : "var(--strapi-colors-neutral200)"};
1095
+ border-radius: 6px;
1096
+ cursor: grab;
1097
+ user-select: none;
1098
+ &:active {
1099
+ cursor: grabbing;
1100
+ }
1101
+ `;
1102
+ const RelationLabel = styled__default.default.span`
1103
+ display: inline-flex;
1104
+ align-items: center;
1105
+ padding: 4px 10px;
1106
+ font-size: 10px;
1107
+ font-weight: 700;
1108
+ text-transform: uppercase;
1109
+ letter-spacing: 0.06em;
1110
+ color: var(--strapi-colors-neutral700);
1111
+ background: var(--strapi-colors-neutral200);
1112
+ border: 1px solid var(--strapi-colors-neutral400);
1113
+ border-radius: 6px;
1114
+ margin-left: 8px;
1115
+ vertical-align: middle;
1116
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1117
+ `;
1118
+ const ModalContentFieldPicker = styled__default.default(designSystem.Modal.Content)`
1119
+ width: 90%;
1120
+ max-width: 420px;
1121
+ `;
1122
+ const FieldPickerList = styled__default.default.div`
1123
+ padding: 8px 0;
1124
+ max-height: 60vh;
1125
+ overflow-y: auto;
1126
+ `;
1127
+ const FieldPickerItem = styled__default.default.button`
1128
+ display: flex;
1129
+ align-items: center;
1130
+ width: 100%;
1131
+ padding: 10px 14px;
1132
+ margin-bottom: 6px;
1133
+ font-family: monospace;
1134
+ font-size: 13px;
1135
+ text-align: left;
1136
+ border: 1px solid var(--strapi-colors-neutral200);
1137
+ border-radius: 6px;
1138
+ background: var(--strapi-colors-neutral0);
1139
+ cursor: pointer;
1140
+ transition:
1141
+ background 0.15s,
1142
+ border-color 0.15s;
1143
+ &:hover {
1144
+ background: var(--strapi-colors-neutral100);
1145
+ border-color: var(--strapi-colors-neutral300);
1146
+ }
1147
+ &:last-child {
1148
+ margin-bottom: 0;
1149
+ }
1150
+ `;
1151
+ const FieldPickerItemRelation = styled__default.default(FieldPickerItem)`
1152
+ background: ${(p) => p.$isLogo ? "var(--strapi-colors-primary100)" : "var(--strapi-colors-secondary100, var(--strapi-colors-primary100))"};
1153
+ border-color: ${(p) => p.$isLogo ? "var(--strapi-colors-primary200)" : "var(--strapi-colors-secondary200, var(--strapi-colors-primary200))"};
1154
+ &:hover {
1155
+ background: ${(p) => p.$isLogo ? "var(--strapi-colors-primary200)" : "var(--strapi-colors-secondary200, var(--strapi-colors-primary200))"};
1156
+ }
1157
+ `;
1158
+ const Textarea = styled__default.default.textarea`
1159
+ flex: 1;
1160
+ min-height: 240px;
1161
+ font-family: monospace;
1162
+ font-size: 13px;
1163
+ padding: 14px;
1164
+ border: none;
1165
+ resize: none;
1166
+ outline: none;
1167
+ background: var(--strapi-colors-neutral0);
1168
+ `;
1169
+ const PreviewFrame = styled__default.default.iframe`
1170
+ flex: 1;
1171
+ min-height: 240px;
1172
+ border: none;
1173
+ background: white;
1174
+ width: 100%;
1175
+ `;
1176
+ styled__default.default.label`
1177
+ display: block;
1178
+ font-size: 11px;
1179
+ font-weight: 600;
1180
+ text-transform: uppercase;
1181
+ letter-spacing: 0.5px;
1182
+ color: var(--strapi-colors-neutral600);
1183
+ margin-bottom: 6px;
1184
+ `;
1185
+ const RecipientsTagsRow = styled__default.default.div`
1186
+ display: flex;
1187
+ flex-wrap: wrap;
1188
+ gap: 6px;
1189
+ margin-top: 8px;
1190
+ `;
1191
+ const Badge = styled__default.default.span`
1192
+ display: inline-flex;
1193
+ align-items: center;
1194
+ padding: 4px 10px;
1195
+ font-size: 12px;
1196
+ background: var(--strapi-colors-primary100);
1197
+ color: var(--strapi-colors-primary700);
1198
+ border-radius: 4px;
1199
+ gap: 6px;
1200
+ `;
1201
+ const BadgeDynamic = styled__default.default.span`
1202
+ display: inline-flex;
1203
+ align-items: center;
1204
+ padding: 4px 10px;
1205
+ font-size: 12px;
1206
+ font-family: monospace;
1207
+ background: var(
1208
+ --strapi-colors-secondary100,
1209
+ var(--strapi-colors-primary100)
1210
+ );
1211
+ color: var(--strapi-colors-secondary700, var(--strapi-colors-primary700));
1212
+ border: 1px solid
1213
+ var(--strapi-colors-secondary200, var(--strapi-colors-primary200));
1214
+ border-radius: 4px;
1215
+ gap: 6px;
1216
+ `;
1217
+ const BadgeRemove = styled__default.default.button`
1218
+ padding: 0;
1219
+ margin: 0;
1220
+ border: none;
1221
+ background: none;
1222
+ cursor: pointer;
1223
+ color: var(--strapi-colors-neutral600);
1224
+ font-size: 14px;
1225
+ line-height: 1;
1226
+ &:hover {
1227
+ color: var(--strapi-colors-danger600);
1228
+ }
1229
+ `;
1230
+ const RelationBadgeInTag = styled__default.default.button`
1231
+ display: inline-flex;
1232
+ align-items: center;
1233
+ padding: 4px 10px;
1234
+ margin-left: 8px;
1235
+ font-size: 10px;
1236
+ font-weight: 700;
1237
+ text-transform: uppercase;
1238
+ letter-spacing: 0.06em;
1239
+ color: var(--strapi-colors-neutral700);
1240
+ background: var(--strapi-colors-neutral200);
1241
+ border: 1px solid var(--strapi-colors-neutral400);
1242
+ border-radius: 6px;
1243
+ cursor: pointer;
1244
+ vertical-align: middle;
1245
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1246
+ &:hover {
1247
+ background: var(--strapi-colors-neutral300);
1248
+ border-color: var(--strapi-colors-neutral500);
1249
+ }
1250
+ `;
1251
+ function getByPath(obj, path) {
1252
+ if (path === "") return obj;
1253
+ const parts = path.split(".").filter(Boolean);
1254
+ let current = obj;
1255
+ for (const p of parts) {
1256
+ if (current == null || typeof current !== "object") return void 0;
1257
+ current = current[p];
1258
+ }
1259
+ return current;
1260
+ }
1261
+ function replacePlaceholders(template, params) {
1262
+ const regex = /\{\{\s*([a-zA-Z0-9_.]+)\s*\}\}/g;
1263
+ return template.replace(regex, (_, path) => {
1264
+ const value = getByPath(params, path.trim());
1265
+ if (value != null && typeof value === "object" && !Array.isArray(value) && "url" in value) {
1266
+ return String(value.url ?? "");
1267
+ }
1268
+ if (Array.isArray(value)) {
1269
+ return value.map(
1270
+ (v) => v && typeof v === "object" && "url" in v ? v.url : String(v)
1271
+ ).join(", ");
1272
+ }
1273
+ return String(value ?? `{{${path}}}`);
1274
+ });
1275
+ }
1276
+ function wrapHtml(html) {
1277
+ const t = html.trim();
1278
+ if (t.toLowerCase().startsWith("<!doctype") || t.toLowerCase().startsWith("<html"))
1279
+ return html;
1280
+ return `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body style="margin:0;padding:12px;">${html}</body></html>`;
1281
+ }
1282
+ function parseRecipients(value) {
1283
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
1284
+ }
1285
+ function formatRecipients(emails) {
1286
+ return emails.join(", ");
1287
+ }
1288
+ function getPlaceholderRootKey(placeholder) {
1289
+ const match = placeholder.trim().match(/\{\{\s*([^}.]+)/);
1290
+ return match ? match[1].trim() : "";
1291
+ }
1292
+ function TemplateModal({
1293
+ contentTypeUid,
1294
+ attributes,
1295
+ displayName,
1296
+ trigger,
1297
+ saveError = null,
1298
+ openaiApiKeySet = false,
1299
+ defaultTemplateCode,
1300
+ onClose,
1301
+ onDismissError,
1302
+ onSave,
1303
+ onSaved
1304
+ }) {
1305
+ const intl = reactIntl.useIntl();
1306
+ const { get, post, put } = admin.useFetchClient();
1307
+ const isDefaultTemplate = Boolean(defaultTemplateCode);
1308
+ const [subject, setSubject] = react.useState(trigger?.subject ?? "");
1309
+ const [html, setHtml] = react.useState(trigger?.html ?? "");
1310
+ const [recipientsStr, setRecipientsStr] = react.useState(trigger?.recipients ?? "");
1311
+ const [recipientsInputValue, setRecipientsInputValue] = react.useState("");
1312
+ const [logoUrl, setLogoUrl] = react.useState("");
1313
+ const textareaRef = react.useRef(null);
1314
+ const subjectInputRef = react.useRef(null);
1315
+ const [previewHtml, setPreviewHtml] = react.useState("");
1316
+ const [htmlEditorOpen, setHtmlEditorOpen] = react.useState(false);
1317
+ const [fieldPickerFor, setFieldPickerFor] = react.useState(null);
1318
+ const [recipientDynamicHint, setRecipientDynamicHint] = react.useState(null);
1319
+ const [depthModalFor, setDepthModalFor] = react.useState(null);
1320
+ const [depthAttributes, setDepthAttributes] = react.useState(
1321
+ []
1322
+ );
1323
+ const [depthLoading, setDepthLoading] = react.useState(false);
1324
+ const [depthPickerFromFieldPicker, setDepthPickerFromFieldPicker] = react.useState(null);
1325
+ const [depthModalForHtml, setDepthModalForHtml] = react.useState(null);
1326
+ const [depthAttributesForPicker, setDepthAttributesForPicker] = react.useState([]);
1327
+ const [depthLoadingForPicker, setDepthLoadingForPicker] = react.useState(false);
1328
+ const [depthAttributesForHtmlDrop, setDepthAttributesForHtmlDrop] = react.useState([]);
1329
+ const [depthLoadingForHtmlDrop, setDepthLoadingForHtmlDrop] = react.useState(false);
1330
+ const [aiModalOpen, setAiModalOpen] = react.useState(false);
1331
+ const [aiPrompt, setAiPrompt] = react.useState("");
1332
+ const [aiLoading, setAiLoading] = react.useState(false);
1333
+ const [aiError, setAiError] = react.useState(null);
1334
+ const [templateMode, setTemplateMode] = react.useState(
1335
+ trigger?.templateMode ?? "html"
1336
+ );
1337
+ const [brevoTemplateIdStr, setBrevoTemplateIdStr] = react.useState(
1338
+ trigger?.brevoTemplateId != null ? String(trigger.brevoTemplateId) : ""
1339
+ );
1340
+ const [savingDefault, setSavingDefault] = react.useState(false);
1341
+ const [defaultTemplateLoading, setDefaultTemplateLoading] = react.useState(isDefaultTemplate);
1342
+ const [errors, setErrors] = react.useState({});
1343
+ const recipientsList = react.useMemo(
1344
+ () => parseRecipients(recipientsStr),
1345
+ [recipientsStr]
1346
+ );
1347
+ const isPlaceholder = (value) => /^\s*\{\{[^}]+\}\}\s*$/.test(value);
1348
+ const isRelationPlaceholder = (value) => {
1349
+ if (!isPlaceholder(value)) return false;
1350
+ const rootKey = getPlaceholderRootKey(value);
1351
+ const attr = attributes.find((a) => a.name === rootKey);
1352
+ return attr && (attr.kind === "relation" || attr.kind === "media" || attr.kind === "component");
1353
+ };
1354
+ react.useEffect(() => {
1355
+ if (depthModalFor === null) return;
1356
+ const placeholder = recipientsList[depthModalFor];
1357
+ const rootKey = getPlaceholderRootKey(placeholder ?? "");
1358
+ if (!rootKey) {
1359
+ setDepthAttributes([]);
1360
+ return;
1361
+ }
1362
+ setDepthLoading(true);
1363
+ const base = "/brevo-template-sender";
1364
+ get(
1365
+ `${base}/depth-attributes?contentTypeUid=${encodeURIComponent(contentTypeUid)}&attributeName=${encodeURIComponent(rootKey)}`
1366
+ ).then((res) => {
1367
+ const list = res?.data?.attributes ?? [];
1368
+ setDepthAttributes(Array.isArray(list) ? list : []);
1369
+ }).finally(() => setDepthLoading(false));
1370
+ }, [depthModalFor, contentTypeUid, get, recipientsList]);
1371
+ react.useEffect(() => {
1372
+ if (depthPickerFromFieldPicker === null) return;
1373
+ const { rootKey } = depthPickerFromFieldPicker;
1374
+ setDepthLoadingForPicker(true);
1375
+ const base = "/brevo-template-sender";
1376
+ get(
1377
+ `${base}/depth-attributes?contentTypeUid=${encodeURIComponent(contentTypeUid)}&attributeName=${encodeURIComponent(rootKey)}`
1378
+ ).then((res) => {
1379
+ const list = res?.data?.attributes ?? [];
1380
+ setDepthAttributesForPicker(Array.isArray(list) ? list : []);
1381
+ }).finally(() => setDepthLoadingForPicker(false));
1382
+ }, [depthPickerFromFieldPicker, contentTypeUid, get]);
1383
+ react.useEffect(() => {
1384
+ if (depthModalForHtml === null) return;
1385
+ const { rootKey } = depthModalForHtml;
1386
+ setDepthLoadingForHtmlDrop(true);
1387
+ const base = "/brevo-template-sender";
1388
+ get(
1389
+ `${base}/depth-attributes?contentTypeUid=${encodeURIComponent(contentTypeUid)}&attributeName=${encodeURIComponent(rootKey)}`
1390
+ ).then((res) => {
1391
+ const list = res?.data?.attributes ?? [];
1392
+ setDepthAttributesForHtmlDrop(Array.isArray(list) ? list : []);
1393
+ }).finally(() => setDepthLoadingForHtmlDrop(false));
1394
+ }, [depthModalForHtml, contentTypeUid, get]);
1395
+ const initialHtml = trigger?.html ?? "";
1396
+ react.useEffect(() => {
1397
+ if (isDefaultTemplate) return;
1398
+ if (!initialHtml.trim()) {
1399
+ const greeting = intl.formatMessage({
1400
+ id: "brevo-template-sender.modal.defaultTemplateGreeting",
1401
+ defaultMessage: "Hello,"
1402
+ });
1403
+ const body = intl.formatMessage({
1404
+ id: "brevo-template-sender.modal.defaultTemplateBody",
1405
+ defaultMessage: "Content of your message..."
1406
+ });
1407
+ setHtml(getDefaultEmailTemplateHtml(greeting, body));
1408
+ }
1409
+ }, [initialHtml, intl, isDefaultTemplate]);
1410
+ react.useEffect(() => {
1411
+ if (!defaultTemplateCode) return;
1412
+ let cancelled = false;
1413
+ setDefaultTemplateLoading(true);
1414
+ const base = "/brevo-template-sender";
1415
+ get(
1416
+ `${base}/default-template?code=${encodeURIComponent(defaultTemplateCode)}`
1417
+ ).then((res) => {
1418
+ if (cancelled) return;
1419
+ const raw = res?.data ?? res;
1420
+ const t = raw?.template;
1421
+ if (t) {
1422
+ setSubject(t.subject ?? "");
1423
+ setHtml(t.html ?? "");
1424
+ setTemplateMode(t.templateMode === "brevo" ? "brevo" : "html");
1425
+ setBrevoTemplateIdStr(
1426
+ t.brevoTemplateId != null ? String(t.brevoTemplateId) : ""
1427
+ );
1428
+ setRecipientsStr(t.recipients ?? "");
1429
+ } else {
1430
+ setSubject("");
1431
+ setHtml(
1432
+ getDefaultEmailTemplateHtml(
1433
+ intl.formatMessage({
1434
+ id: "brevo-template-sender.modal.defaultTemplateGreeting",
1435
+ defaultMessage: "Hello,"
1436
+ }),
1437
+ intl.formatMessage({
1438
+ id: "brevo-template-sender.modal.defaultTemplateBody",
1439
+ defaultMessage: "Content of your message..."
1440
+ })
1441
+ )
1442
+ );
1443
+ setTemplateMode("html");
1444
+ setBrevoTemplateIdStr("");
1445
+ }
1446
+ }).catch(() => {
1447
+ if (!cancelled) setHtml("<p>Hello {{firstname}}</p>");
1448
+ }).finally(() => {
1449
+ if (!cancelled) setDefaultTemplateLoading(false);
1450
+ });
1451
+ return () => {
1452
+ cancelled = true;
1453
+ };
1454
+ }, [defaultTemplateCode, get, intl]);
1455
+ react.useEffect(() => {
1456
+ let cancelled = false;
1457
+ const backendUrl = typeof window?.strapi?.backendURL === "string" ? window.strapi.backendURL.replace(/\/$/, "") : window.location.origin;
1458
+ Promise.all([
1459
+ get("/admin/project-settings").catch(() => ({ data: null })),
1460
+ get("/brevo-template-sender/settings").catch(() => ({ data: null }))
1461
+ ]).then(([projectRes, brevoRes]) => {
1462
+ if (cancelled) return;
1463
+ const menuLogoUrl = projectRes?.data?.menuLogo?.url;
1464
+ const brevoLogoUrl = brevoRes?.data?.logoUrl;
1465
+ const resolved = typeof menuLogoUrl === "string" && menuLogoUrl.trim() && `${backendUrl}${menuLogoUrl.trim().startsWith("/") ? "" : "/"}${menuLogoUrl.trim()}` || (typeof brevoLogoUrl === "string" && brevoLogoUrl.trim() ? brevoLogoUrl.trim() : "");
1466
+ setLogoUrl(resolved);
1467
+ });
1468
+ return () => {
1469
+ cancelled = true;
1470
+ };
1471
+ }, [get]);
1472
+ const params = react.useMemo(
1473
+ () => ({
1474
+ ...Object.fromEntries(
1475
+ attributes.map((a) => [
1476
+ a.name,
1477
+ SAMPLE[a.name] ?? (a.kind === "media" ? { url: "https://via.placeholder.com/120x40" } : a.kind !== "scalar" ? { title: `(${a.name})`, url: "#" } : `(${a.name})`)
1478
+ ])
1479
+ ),
1480
+ logo_url: logoUrl || "https://via.placeholder.com/120x40?text=Logo"
1481
+ }),
1482
+ [attributes, logoUrl]
1483
+ );
1484
+ react.useEffect(() => {
1485
+ const out = replacePlaceholders(html, params);
1486
+ setPreviewHtml(wrapHtml(out));
1487
+ }, [html, params]);
1488
+ const fields = react.useMemo(
1489
+ () => [
1490
+ {
1491
+ key: "logo_url",
1492
+ label: "{{logo_url}}",
1493
+ isLogo: true,
1494
+ kind: "scalar"
1495
+ },
1496
+ ...attributes.map((a) => ({
1497
+ key: a.name,
1498
+ label: `{{${a.name}}}`,
1499
+ hint: a.kind === "media" ? `→ ex: {{${a.name}.url}}` : a.kind === "relation" || a.kind === "component" ? `→ ex: {{${a.name}.title}} ou {{${a.name}.slug}}` : void 0,
1500
+ kind: a.kind,
1501
+ type: a.type,
1502
+ target: a.target
1503
+ }))
1504
+ ],
1505
+ [attributes]
1506
+ );
1507
+ const handleDrop = (e) => {
1508
+ e.preventDefault();
1509
+ const text = e.dataTransfer.getData("text/plain");
1510
+ const ta = textareaRef.current;
1511
+ if (!ta || !text) return;
1512
+ const start = ta.selectionStart;
1513
+ const end = ta.selectionEnd;
1514
+ if (isRelationPlaceholder(text)) {
1515
+ const rootKey = getPlaceholderRootKey(text);
1516
+ if (rootKey)
1517
+ setDepthModalForHtml({ rootKey, insertStart: start, insertEnd: end });
1518
+ return;
1519
+ }
1520
+ const before = html.slice(0, start);
1521
+ const after = html.slice(end);
1522
+ setHtml(before + text + after);
1523
+ setTimeout(() => {
1524
+ ta.focus();
1525
+ ta.setSelectionRange(start + text.length, start + text.length);
1526
+ }, 0);
1527
+ };
1528
+ const handleGenerateWithAi = async () => {
1529
+ if (!aiPrompt.trim()) return;
1530
+ setAiLoading(true);
1531
+ setAiError(null);
1532
+ try {
1533
+ const base = "/brevo-template-sender";
1534
+ const res = await post(`${base}/generate-html`, {
1535
+ prompt: aiPrompt.trim(),
1536
+ currentHtml: html || void 0
1537
+ });
1538
+ const data = res?.data ?? res;
1539
+ const generated = typeof data?.html === "string" ? data.html : null;
1540
+ if (generated) {
1541
+ setHtml(generated);
1542
+ setAiModalOpen(false);
1543
+ setAiPrompt("");
1544
+ } else {
1545
+ setAiError(
1546
+ intl.formatMessage({
1547
+ id: "brevo-template-sender.ai.generateError",
1548
+ defaultMessage: "Invalid response from server."
1549
+ })
1550
+ );
1551
+ }
1552
+ } catch (err) {
1553
+ const msg = err?.response?.data?.error ?? err?.message ?? intl.formatMessage({
1554
+ id: "brevo-template-sender.ai.generateError",
1555
+ defaultMessage: "Failed to generate."
1556
+ });
1557
+ setAiError(typeof msg === "string" ? msg : "Failed to generate.");
1558
+ } finally {
1559
+ setAiLoading(false);
1560
+ }
1561
+ };
1562
+ const addRecipient = (email) => {
1563
+ const trimmed = email.trim();
1564
+ if (!trimmed) return;
1565
+ if (/^\s*\{\{[^}]+\}\}\s*$/.test(trimmed)) {
1566
+ setRecipientDynamicHint(
1567
+ intl.formatMessage({
1568
+ id: "brevo-template-sender.modal.recipientsUseDynamicButton",
1569
+ defaultMessage: "Use the { } button to add dynamic fields from the content type."
1570
+ })
1571
+ );
1572
+ return;
1573
+ }
1574
+ setRecipientDynamicHint(null);
1575
+ const next = [...recipientsList, trimmed];
1576
+ setRecipientsStr(formatRecipients(next));
1577
+ setRecipientsInputValue("");
1578
+ if (errors.recipients)
1579
+ setErrors((prev) => ({ ...prev, recipients: void 0 }));
1580
+ };
1581
+ const addRecipientPlaceholder = (placeholder) => {
1582
+ const trimmed = placeholder.trim();
1583
+ if (!trimmed) return;
1584
+ const next = [...recipientsList, trimmed];
1585
+ setRecipientsStr(formatRecipients(next));
1586
+ if (errors.recipients)
1587
+ setErrors((prev) => ({ ...prev, recipients: void 0 }));
1588
+ };
1589
+ const insertIntoSubject = (placeholder) => {
1590
+ const trimmed = placeholder.trim();
1591
+ if (!trimmed) return;
1592
+ const input = subjectInputRef.current;
1593
+ if (input && typeof input.selectionStart === "number") {
1594
+ const start = input.selectionStart ?? subject.length;
1595
+ const end = input.selectionEnd ?? start;
1596
+ setSubject(subject.slice(0, start) + trimmed + subject.slice(end));
1597
+ setFieldPickerFor(null);
1598
+ setTimeout(() => {
1599
+ input.focus();
1600
+ const newPos = start + trimmed.length;
1601
+ input.setSelectionRange(newPos, newPos);
1602
+ }, 0);
1603
+ } else {
1604
+ setSubject((prev) => prev + trimmed);
1605
+ setFieldPickerFor(null);
1606
+ setTimeout(() => subjectInputRef.current?.focus(), 0);
1607
+ }
1608
+ };
1609
+ const insertIntoHtml = (placeholder) => {
1610
+ const trimmed = placeholder.trim();
1611
+ if (!trimmed) return;
1612
+ const ta = textareaRef.current;
1613
+ if (ta && typeof ta.selectionStart === "number") {
1614
+ const start = ta.selectionStart;
1615
+ const end = ta.selectionEnd ?? start;
1616
+ setHtml(html.slice(0, start) + trimmed + html.slice(end));
1617
+ setFieldPickerFor(null);
1618
+ setTimeout(() => {
1619
+ ta.focus();
1620
+ const newPos = start + trimmed.length;
1621
+ ta.setSelectionRange(newPos, newPos);
1622
+ }, 0);
1623
+ } else {
1624
+ setHtml((prev) => prev + trimmed);
1625
+ setFieldPickerFor(null);
1626
+ setTimeout(() => textareaRef.current?.focus(), 0);
1627
+ }
1628
+ };
1629
+ const handleFieldPickerSelect = (placeholder) => {
1630
+ if (fieldPickerFor === "recipients") addRecipientPlaceholder(placeholder);
1631
+ else if (fieldPickerFor === "subject") insertIntoSubject(placeholder);
1632
+ else if (fieldPickerFor === "html") insertIntoHtml(placeholder);
1633
+ setFieldPickerFor(null);
1634
+ };
1635
+ const handleDepthAttributeFromPicker = (attrName) => {
1636
+ if (depthPickerFromFieldPicker === null) return;
1637
+ const { rootKey, context } = depthPickerFromFieldPicker;
1638
+ const placeholder = `{{${rootKey}.${attrName}}}`;
1639
+ if (context === "recipients") addRecipientPlaceholder(placeholder);
1640
+ else if (context === "subject") insertIntoSubject(placeholder);
1641
+ else if (context === "html") insertIntoHtml(placeholder);
1642
+ setDepthPickerFromFieldPicker(null);
1643
+ setFieldPickerFor(null);
1644
+ };
1645
+ const applyDepthAttributeForHtmlDrop = (attrName) => {
1646
+ if (depthModalForHtml === null) return;
1647
+ const { rootKey, insertStart, insertEnd } = depthModalForHtml;
1648
+ const placeholder = `{{${rootKey}.${attrName}}}`;
1649
+ setHtml(html.slice(0, insertStart) + placeholder + html.slice(insertEnd));
1650
+ setDepthModalForHtml(null);
1651
+ setTimeout(() => {
1652
+ textareaRef.current?.focus();
1653
+ const newPos = insertStart + placeholder.length;
1654
+ textareaRef.current?.setSelectionRange(newPos, newPos);
1655
+ }, 0);
1656
+ };
1657
+ const removeRecipient = (index) => {
1658
+ const next = recipientsList.filter((_, i) => i !== index);
1659
+ setRecipientsStr(formatRecipients(next));
1660
+ };
1661
+ const applyDepthAttribute = (attrName) => {
1662
+ if (depthModalFor === null) return;
1663
+ const placeholder = recipientsList[depthModalFor];
1664
+ const rootKey = getPlaceholderRootKey(placeholder ?? "");
1665
+ if (!rootKey) return;
1666
+ const newPlaceholder = `{{${rootKey}.${attrName}}}`;
1667
+ const next = recipientsList.map(
1668
+ (item, i) => i === depthModalFor ? newPlaceholder : item
1669
+ );
1670
+ setRecipientsStr(formatRecipients(next));
1671
+ setDepthModalFor(null);
1672
+ };
1673
+ const handleRecipientsKeyDown = (e) => {
1674
+ if (e.key === "," || e.key === "Enter") {
1675
+ e.preventDefault();
1676
+ if (recipientsInputValue.trim()) addRecipient(recipientsInputValue);
1677
+ }
1678
+ };
1679
+ const handleRecipientsBlur = () => {
1680
+ if (recipientsInputValue.trim()) addRecipient(recipientsInputValue);
1681
+ };
1682
+ const openHtmlEditor = () => {
1683
+ if (!html.trim()) {
1684
+ const greeting = intl.formatMessage({
1685
+ id: "brevo-template-sender.modal.defaultTemplateGreeting",
1686
+ defaultMessage: "Hello,"
1687
+ });
1688
+ const body = intl.formatMessage({
1689
+ id: "brevo-template-sender.modal.defaultTemplateBody",
1690
+ defaultMessage: "Content of your message..."
1691
+ });
1692
+ setHtml(getDefaultEmailTemplateHtml(greeting, body));
1693
+ }
1694
+ setHtmlEditorOpen(true);
1695
+ };
1696
+ const handleSave = async () => {
1697
+ const brevoId = brevoTemplateIdStr.trim() ? parseInt(brevoTemplateIdStr.trim(), 10) : void 0;
1698
+ const newErrors = {};
1699
+ if (templateMode === "html" && !subject.trim()) {
1700
+ newErrors.subject = intl.formatMessage({
1701
+ id: "brevo-template-sender.modal.subjectRequired",
1702
+ defaultMessage: "Subject is required."
1703
+ });
1704
+ }
1705
+ if (templateMode === "brevo" && (!Number.isFinite(brevoId) || (brevoId ?? 0) <= 0)) {
1706
+ newErrors.brevoTemplateId = intl.formatMessage({
1707
+ id: "brevo-template-sender.modal.brevoTemplateIdRequired",
1708
+ defaultMessage: "A valid Brevo template ID is required."
1709
+ });
1710
+ }
1711
+ if (recipientsList.length === 0) {
1712
+ newErrors.recipients = intl.formatMessage({
1713
+ id: "brevo-template-sender.modal.recipientsRequired",
1714
+ defaultMessage: "At least one recipient is required."
1715
+ });
1716
+ }
1717
+ if (Object.keys(newErrors).length > 0) {
1718
+ setErrors(newErrors);
1719
+ return;
1720
+ }
1721
+ setErrors({});
1722
+ if (isDefaultTemplate && defaultTemplateCode) {
1723
+ setSavingDefault(true);
1724
+ try {
1725
+ const base = "/brevo-template-sender";
1726
+ const body = {
1727
+ code: defaultTemplateCode,
1728
+ name: "Send Email Default",
1729
+ subject: subject.trim() || "Message",
1730
+ html: templateMode === "html" ? html.trim() : "<p></p>",
1731
+ templateMode,
1732
+ brevoTemplateId: templateMode === "brevo" && Number.isFinite(brevoId) ? brevoId : void 0,
1733
+ recipients: recipientsStr.trim() || void 0
1734
+ };
1735
+ await put(`${base}/default-template`, body);
1736
+ onSaved?.();
1737
+ onClose();
1738
+ } finally {
1739
+ setSavingDefault(false);
1740
+ }
1741
+ return;
1742
+ }
1743
+ onSave({
1744
+ id: trigger?.id ?? `${contentTypeUid}::create`,
1745
+ contentTypeUid,
1746
+ event: trigger?.event ?? "create",
1747
+ subject,
1748
+ html,
1749
+ sendToField: trigger?.sendToField,
1750
+ recipients: recipientsStr.trim() || void 0,
1751
+ templateMode,
1752
+ brevoTemplateId: Number.isFinite(brevoId) ? brevoId : void 0
1753
+ });
1754
+ };
1755
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1756
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContentLarge, { labelledBy: "template-modal-title", children: [
1757
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "template-modal-title", variant: "beta", as: "h2", children: intl.formatMessage(
1758
+ {
1759
+ id: "brevo-template-sender.modal.title",
1760
+ defaultMessage: "Template: {name}"
1761
+ },
1762
+ { name: displayName }
1763
+ ) }) }),
1764
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: defaultTemplateLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { padding: 6, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1765
+ saveError && /* @__PURE__ */ jsxRuntime.jsx(
1766
+ designSystem.Alert,
1767
+ {
1768
+ variant: "danger",
1769
+ marginBottom: 4,
1770
+ onClose: onDismissError,
1771
+ closeLabel: intl.formatMessage({
1772
+ id: "app.components.Button.close",
1773
+ defaultMessage: "Close"
1774
+ }),
1775
+ children: saveError
1776
+ }
1777
+ ),
1778
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginBottom: 4, children: [
1779
+ /* @__PURE__ */ jsxRuntime.jsx(
1780
+ designSystem.Button,
1781
+ {
1782
+ variant: templateMode === "html" ? "default" : "tertiary",
1783
+ size: "S",
1784
+ onClick: () => setTemplateMode("html"),
1785
+ children: intl.formatMessage({
1786
+ id: "brevo-template-sender.modal.modeHtml",
1787
+ defaultMessage: "HTML"
1788
+ })
1789
+ }
1790
+ ),
1791
+ /* @__PURE__ */ jsxRuntime.jsx(
1792
+ designSystem.Button,
1793
+ {
1794
+ variant: templateMode === "brevo" ? "default" : "tertiary",
1795
+ size: "S",
1796
+ onClick: () => setTemplateMode("brevo"),
1797
+ children: intl.formatMessage({
1798
+ id: "brevo-template-sender.modal.modeBrevo",
1799
+ defaultMessage: "Template Brevo"
1800
+ })
1801
+ }
1802
+ )
1803
+ ] }),
1804
+ templateMode === "brevo" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
1805
+ designSystem.Field.Root,
1806
+ {
1807
+ id: "brevo-template-id",
1808
+ error: errors.brevoTemplateId,
1809
+ required: true,
1810
+ children: [
1811
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
1812
+ id: "brevo-template-sender.modal.brevoTemplateId",
1813
+ defaultMessage: "ID du template Brevo"
1814
+ }) }),
1815
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children: intl.formatMessage({
1816
+ id: "brevo-template-sender.modal.brevoTemplateIdHint",
1817
+ defaultMessage: "Saisissez l'ID du template créé dans Brevo (Campagnes → Templates)."
1818
+ }) }),
1819
+ /* @__PURE__ */ jsxRuntime.jsx(
1820
+ designSystem.TextInput,
1821
+ {
1822
+ id: "brevo-template-id",
1823
+ type: "number",
1824
+ value: brevoTemplateIdStr,
1825
+ onChange: (e) => {
1826
+ setBrevoTemplateIdStr(e.target.value);
1827
+ if (errors.brevoTemplateId)
1828
+ setErrors((prev) => ({
1829
+ ...prev,
1830
+ brevoTemplateId: void 0
1831
+ }));
1832
+ },
1833
+ placeholder: "123",
1834
+ size: "M"
1835
+ }
1836
+ ),
1837
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
1838
+ ]
1839
+ }
1840
+ ) }),
1841
+ templateMode === "html" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: "subject", error: errors.subject, required: true, children: [
1842
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
1843
+ id: "brevo-template-sender.modal.subject",
1844
+ defaultMessage: "Subject"
1845
+ }) }),
1846
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "stretch", children: [
1847
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", minWidth: 0, children: /* @__PURE__ */ jsxRuntime.jsx(
1848
+ designSystem.TextInput,
1849
+ {
1850
+ id: "subject",
1851
+ ref: subjectInputRef,
1852
+ value: subject,
1853
+ onChange: (e) => {
1854
+ setSubject(e.target.value);
1855
+ if (errors.subject)
1856
+ setErrors((prev) => ({
1857
+ ...prev,
1858
+ subject: void 0
1859
+ }));
1860
+ },
1861
+ placeholder: "E.g. New message {{email}}",
1862
+ size: "M"
1863
+ }
1864
+ ) }),
1865
+ /* @__PURE__ */ jsxRuntime.jsx(
1866
+ designSystem.Button,
1867
+ {
1868
+ type: "button",
1869
+ variant: "tertiary",
1870
+ size: "M",
1871
+ style: { minHeight: 40 },
1872
+ onClick: () => setFieldPickerFor("subject"),
1873
+ title: intl.formatMessage({
1874
+ id: "brevo-template-sender.modal.recipientsDynamicTitle",
1875
+ defaultMessage: "Insert dynamic field from content type"
1876
+ }),
1877
+ children: intl.formatMessage({
1878
+ id: "brevo-template-sender.modal.recipientsDynamicButton",
1879
+ defaultMessage: "{ }"
1880
+ })
1881
+ }
1882
+ )
1883
+ ] }),
1884
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
1885
+ ] }) }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 5, children: /* @__PURE__ */ jsxRuntime.jsxs(
1887
+ designSystem.Field.Root,
1888
+ {
1889
+ id: "template-recipients",
1890
+ error: errors.recipients,
1891
+ required: true,
1892
+ hint: intl.formatMessage({
1893
+ id: "brevo-template-sender.modal.recipientsHint",
1894
+ defaultMessage: "Enter an address then click « Add » or press Enter. Addresses appear as tags below. Use the { } button for dynamic fields."
1895
+ }),
1896
+ children: [
1897
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
1898
+ id: "brevo-template-sender.modal.recipientsLabel",
1899
+ defaultMessage: "Recipients"
1900
+ }) }),
1901
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
1902
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "stretch", children: [
1903
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", minWidth: 0, children: /* @__PURE__ */ jsxRuntime.jsx(
1904
+ designSystem.TextInput,
1905
+ {
1906
+ id: "template-recipients-input",
1907
+ type: "text",
1908
+ value: recipientsInputValue,
1909
+ onChange: (e) => setRecipientsInputValue(e.target.value),
1910
+ onKeyDown: handleRecipientsKeyDown,
1911
+ onBlur: handleRecipientsBlur,
1912
+ placeholder: "email@example.com",
1913
+ size: "M"
1914
+ }
1915
+ ) }),
1916
+ /* @__PURE__ */ jsxRuntime.jsx(
1917
+ designSystem.Button,
1918
+ {
1919
+ type: "button",
1920
+ variant: "tertiary",
1921
+ size: "M",
1922
+ style: { minHeight: 40 },
1923
+ onClick: () => setFieldPickerFor("recipients"),
1924
+ title: intl.formatMessage({
1925
+ id: "brevo-template-sender.modal.recipientsDynamicTitle",
1926
+ defaultMessage: "Insert dynamic field from content type"
1927
+ }),
1928
+ children: intl.formatMessage({
1929
+ id: "brevo-template-sender.modal.recipientsDynamicButton",
1930
+ defaultMessage: "{ }"
1931
+ })
1932
+ }
1933
+ ),
1934
+ /* @__PURE__ */ jsxRuntime.jsx(
1935
+ designSystem.Button,
1936
+ {
1937
+ type: "button",
1938
+ variant: "tertiary",
1939
+ size: "M",
1940
+ style: { minHeight: 40 },
1941
+ onClick: () => addRecipient(recipientsInputValue),
1942
+ children: intl.formatMessage({
1943
+ id: "brevo-template-sender.modal.recipientsAdd",
1944
+ defaultMessage: "Add"
1945
+ })
1946
+ }
1947
+ )
1948
+ ] }),
1949
+ recipientsList.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(RecipientsTagsRow, { children: recipientsList.map((item, i) => {
1950
+ const dynamic = isPlaceholder(item);
1951
+ const relationPlaceholder = dynamic && isRelationPlaceholder(item);
1952
+ const B = dynamic ? BadgeDynamic : Badge;
1953
+ return /* @__PURE__ */ jsxRuntime.jsxs(B, { children: [
1954
+ item,
1955
+ relationPlaceholder && /* @__PURE__ */ jsxRuntime.jsx(
1956
+ RelationBadgeInTag,
1957
+ {
1958
+ type: "button",
1959
+ onClick: (e) => {
1960
+ e.stopPropagation();
1961
+ setDepthModalFor(i);
1962
+ },
1963
+ title: intl.formatMessage({
1964
+ id: "brevo-template-sender.modal.relationDepthTitle",
1965
+ defaultMessage: "Choose a depth attribute"
1966
+ }),
1967
+ children: "Relation"
1968
+ }
1969
+ ),
1970
+ /* @__PURE__ */ jsxRuntime.jsx(
1971
+ BadgeRemove,
1972
+ {
1973
+ type: "button",
1974
+ onClick: () => removeRecipient(i),
1975
+ "aria-label": intl.formatMessage({
1976
+ id: "brevo-template-sender.modal.removeRecipient",
1977
+ defaultMessage: "Remove"
1978
+ }),
1979
+ children: "×"
1980
+ }
1981
+ )
1982
+ ] }, `${item}-${i}`);
1983
+ }) }),
1984
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
1985
+ ]
1986
+ }
1987
+ ) }),
1988
+ templateMode === "html" && /* @__PURE__ */ jsxRuntime.jsxs(PreviewOnlyLayout, { children: [
1989
+ /* @__PURE__ */ jsxRuntime.jsxs(PreviewHeader, { children: [
1990
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: intl.formatMessage({
1991
+ id: "brevo-template-sender.modal.preview",
1992
+ defaultMessage: "Preview"
1993
+ }) }),
1994
+ /* @__PURE__ */ jsxRuntime.jsx(
1995
+ designSystem.Button,
1996
+ {
1997
+ variant: "tertiary",
1998
+ size: "S",
1999
+ onClick: openHtmlEditor,
2000
+ children: intl.formatMessage({
2001
+ id: "brevo-template-sender.modal.editHtmlTemplate",
2002
+ defaultMessage: "Edit HTML Template"
2003
+ })
2004
+ }
2005
+ )
2006
+ ] }),
2007
+ /* @__PURE__ */ jsxRuntime.jsx(PreviewBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
2008
+ PreviewFrame,
2009
+ {
2010
+ title: "Preview",
2011
+ srcDoc: previewHtml,
2012
+ sandbox: "allow-same-origin"
2013
+ }
2014
+ ) })
2015
+ ] })
2016
+ ] }) }),
2017
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
2018
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage({
2019
+ id: "app.components.Button.cancel",
2020
+ defaultMessage: "Cancel"
2021
+ }) }),
2022
+ /* @__PURE__ */ jsxRuntime.jsx(
2023
+ designSystem.Button,
2024
+ {
2025
+ onClick: handleSave,
2026
+ loading: savingDefault,
2027
+ disabled: defaultTemplateLoading,
2028
+ children: intl.formatMessage({
2029
+ id: "app.components.Button.save",
2030
+ defaultMessage: "Save"
2031
+ })
2032
+ }
2033
+ )
2034
+ ] })
2035
+ ] }) }),
2036
+ htmlEditorOpen && /* @__PURE__ */ jsxRuntime.jsx(
2037
+ designSystem.Modal.Root,
2038
+ {
2039
+ open: true,
2040
+ onOpenChange: (open) => !open && setHtmlEditorOpen(false),
2041
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContentHtmlEditor, { labelledBy: "html-editor-modal-title", children: [
2042
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "html-editor-modal-title", variant: "beta", as: "h2", children: intl.formatMessage({
2043
+ id: "brevo-template-sender.modal.editHtmlTemplateTitle",
2044
+ defaultMessage: "Edit HTML template"
2045
+ }) }) }),
2046
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(GridLayoutHtmlEditor, { children: /* @__PURE__ */ jsxRuntime.jsxs(Column, { children: [
2047
+ /* @__PURE__ */ jsxRuntime.jsxs(
2048
+ designSystem.Flex,
2049
+ {
2050
+ justifyContent: "space-between",
2051
+ alignItems: "center",
2052
+ marginBottom: 2,
2053
+ wrap: "wrap",
2054
+ gap: 2,
2055
+ children: [
2056
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
2057
+ /* @__PURE__ */ jsxRuntime.jsx(ColumnLabel, { as: "span", children: intl.formatMessage({
2058
+ id: "brevo-template-sender.modal.html",
2059
+ defaultMessage: "Message (HTML)"
2060
+ }) }),
2061
+ /* @__PURE__ */ jsxRuntime.jsx(
2062
+ designSystem.Button,
2063
+ {
2064
+ type: "button",
2065
+ variant: "tertiary",
2066
+ size: "M",
2067
+ style: { minHeight: 32 },
2068
+ onClick: () => setFieldPickerFor("html"),
2069
+ title: intl.formatMessage({
2070
+ id: "brevo-template-sender.modal.recipientsDynamicTitle",
2071
+ defaultMessage: "Insert dynamic field from content type"
2072
+ }),
2073
+ children: intl.formatMessage({
2074
+ id: "brevo-template-sender.modal.recipientsDynamicButton",
2075
+ defaultMessage: "{ }"
2076
+ })
2077
+ }
2078
+ )
2079
+ ] }),
2080
+ /* @__PURE__ */ jsxRuntime.jsx(
2081
+ designSystem.Button,
2082
+ {
2083
+ variant: "tertiary",
2084
+ size: "S",
2085
+ disabled: !openaiApiKeySet,
2086
+ onClick: () => {
2087
+ setAiError(null);
2088
+ setAiPrompt("");
2089
+ setAiModalOpen(true);
2090
+ },
2091
+ children: intl.formatMessage({
2092
+ id: "brevo-template-sender.modal.generateWithAi",
2093
+ defaultMessage: "Generate with AI"
2094
+ })
2095
+ }
2096
+ )
2097
+ ]
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsxRuntime.jsx(
2101
+ Textarea,
2102
+ {
2103
+ ref: textareaRef,
2104
+ value: html,
2105
+ onChange: (e) => setHtml(e.target.value),
2106
+ onDragOver: (e) => e.preventDefault(),
2107
+ onDrop: handleDrop,
2108
+ placeholder: "<p>Type HTML. Use { } to insert dynamic fields.</p>"
2109
+ }
2110
+ )
2111
+ ] }) }) }),
2112
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
2113
+ /* @__PURE__ */ jsxRuntime.jsx(
2114
+ designSystem.Button,
2115
+ {
2116
+ variant: "tertiary",
2117
+ onClick: () => setHtmlEditorOpen(false),
2118
+ children: intl.formatMessage({
2119
+ id: "app.components.Button.cancel",
2120
+ defaultMessage: "Cancel"
2121
+ })
2122
+ }
2123
+ ),
2124
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => setHtmlEditorOpen(false), children: intl.formatMessage({
2125
+ id: "brevo-template-sender.modal.closeEditor",
2126
+ defaultMessage: "Close"
2127
+ }) })
2128
+ ] })
2129
+ ] })
2130
+ }
2131
+ ),
2132
+ aiModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
2133
+ designSystem.Modal.Root,
2134
+ {
2135
+ open: true,
2136
+ onOpenChange: (open) => !open && setAiModalOpen(false),
2137
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2138
+ designSystem.Modal.Content,
2139
+ {
2140
+ labelledBy: "ai-generate-title",
2141
+ style: { maxWidth: 520 },
2142
+ children: [
2143
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "ai-generate-title", variant: "beta", as: "h2", children: intl.formatMessage({
2144
+ id: "brevo-template-sender.modal.generateWithAi",
2145
+ defaultMessage: "Generate with AI"
2146
+ }) }) }),
2147
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
2148
+ aiError && /* @__PURE__ */ jsxRuntime.jsx(
2149
+ designSystem.Alert,
2150
+ {
2151
+ variant: "danger",
2152
+ marginBottom: 3,
2153
+ onClose: () => setAiError(null),
2154
+ closeLabel: "Close",
2155
+ children: aiError
2156
+ }
2157
+ ),
2158
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
2159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: intl.formatMessage({
2160
+ id: "brevo-template-sender.ai.promptLabel",
2161
+ defaultMessage: "Describe the email template you want (or how to improve the current one)"
2162
+ }) }),
2163
+ /* @__PURE__ */ jsxRuntime.jsx(
2164
+ designSystem.TextInput,
2165
+ {
2166
+ placeholder: intl.formatMessage({
2167
+ id: "brevo-template-sender.ai.promptPlaceholder",
2168
+ defaultMessage: "e.g. Confirmation email with logo, greeting and {{firstname}}"
2169
+ }),
2170
+ value: aiPrompt,
2171
+ onChange: (e) => setAiPrompt(e.target.value),
2172
+ disabled: aiLoading
2173
+ }
2174
+ )
2175
+ ] })
2176
+ ] }),
2177
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
2178
+ /* @__PURE__ */ jsxRuntime.jsx(
2179
+ designSystem.Button,
2180
+ {
2181
+ variant: "tertiary",
2182
+ onClick: () => setAiModalOpen(false),
2183
+ disabled: aiLoading,
2184
+ children: intl.formatMessage({
2185
+ id: "app.components.Button.cancel",
2186
+ defaultMessage: "Cancel"
2187
+ })
2188
+ }
2189
+ ),
2190
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleGenerateWithAi, loading: aiLoading, children: intl.formatMessage({
2191
+ id: "brevo-template-sender.ai.generate",
2192
+ defaultMessage: "Generate"
2193
+ }) })
2194
+ ] })
2195
+ ]
2196
+ }
2197
+ )
2198
+ }
2199
+ ),
2200
+ fieldPickerFor !== null && /* @__PURE__ */ jsxRuntime.jsx(
2201
+ designSystem.Modal.Root,
2202
+ {
2203
+ open: true,
2204
+ onOpenChange: (open) => {
2205
+ if (!open) {
2206
+ setFieldPickerFor(null);
2207
+ setDepthPickerFromFieldPicker(null);
2208
+ }
2209
+ },
2210
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContentFieldPicker, { labelledBy: "field-picker-modal-title", children: [
2211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "field-picker-modal-title", variant: "beta", as: "h2", children: depthPickerFromFieldPicker ? intl.formatMessage(
2212
+ {
2213
+ id: "brevo-template-sender.modal.depthModalTitle",
2214
+ defaultMessage: "Choose depth attribute for {placeholder}"
2215
+ },
2216
+ {
2217
+ placeholder: `{{${depthPickerFromFieldPicker.rootKey}}}`
2218
+ }
2219
+ ) : intl.formatMessage({
2220
+ id: "brevo-template-sender.modal.recipientsFieldPickerTitle",
2221
+ defaultMessage: "Insert dynamic field"
2222
+ }) }) }),
2223
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: depthPickerFromFieldPicker ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2224
+ /* @__PURE__ */ jsxRuntime.jsx(
2225
+ designSystem.Typography,
2226
+ {
2227
+ variant: "pi",
2228
+ textColor: "neutral600",
2229
+ marginBottom: 3,
2230
+ children: intl.formatMessage({
2231
+ id: "brevo-template-sender.modal.depthModalHint",
2232
+ defaultMessage: "Click an attribute to use it (e.g. {{relation.id}}, {{relation.url}})."
2233
+ })
2234
+ }
2235
+ ),
2236
+ depthLoadingForPicker ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { padding: 6, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(FieldPickerList, { children: depthAttributesForPicker.map((attr) => {
2237
+ const rootKey = depthPickerFromFieldPicker.rootKey;
2238
+ const label = `{{${rootKey}.${attr.name}}}`;
2239
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2240
+ FieldPickerItem,
2241
+ {
2242
+ type: "button",
2243
+ onClick: () => handleDepthAttributeFromPicker(attr.name),
2244
+ children: label
2245
+ }
2246
+ ) }, attr.name);
2247
+ }) })
2248
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2249
+ /* @__PURE__ */ jsxRuntime.jsx(
2250
+ designSystem.Typography,
2251
+ {
2252
+ variant: "pi",
2253
+ textColor: "neutral600",
2254
+ marginBottom: 3,
2255
+ children: fieldPickerFor === "subject" ? intl.formatMessage({
2256
+ id: "brevo-template-sender.modal.subjectFieldPickerHint",
2257
+ defaultMessage: "Choose a field to insert into the subject line."
2258
+ }) : fieldPickerFor === "html" ? intl.formatMessage({
2259
+ id: "brevo-template-sender.modal.htmlFieldPickerHint",
2260
+ defaultMessage: "Choose a field to insert into the HTML content (e.g. {{firstname}}, {{relation.url}})."
2261
+ }) : intl.formatMessage({
2262
+ id: "brevo-template-sender.modal.recipientsFieldPickerHint",
2263
+ defaultMessage: "Choose a field from the content type. Its value will be used as recipient at send time."
2264
+ })
2265
+ }
2266
+ ),
2267
+ /* @__PURE__ */ jsxRuntime.jsx(FieldPickerList, { children: fields.map((f) => {
2268
+ const isRelation = f.kind === "relation" || f.kind === "component" || f.kind === "media";
2269
+ const Item = isRelation || f.key === "logo_url" ? FieldPickerItemRelation : FieldPickerItem;
2270
+ const onClick = isRelation || f.key === "logo_url" ? () => setDepthPickerFromFieldPicker({
2271
+ rootKey: f.key,
2272
+ context: fieldPickerFor
2273
+ }) : () => handleFieldPickerSelect(f.label);
2274
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(
2275
+ Item,
2276
+ {
2277
+ type: "button",
2278
+ $isLogo: f.key === "logo_url",
2279
+ onClick,
2280
+ children: [
2281
+ f.label,
2282
+ isRelation && /* @__PURE__ */ jsxRuntime.jsx(RelationLabel, { children: "Relation" })
2283
+ ]
2284
+ }
2285
+ ) }, f.key);
2286
+ }) })
2287
+ ] }) }),
2288
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
2289
+ depthPickerFromFieldPicker ? /* @__PURE__ */ jsxRuntime.jsx(
2290
+ designSystem.Button,
2291
+ {
2292
+ variant: "tertiary",
2293
+ onClick: () => setDepthPickerFromFieldPicker(null),
2294
+ children: intl.formatMessage({
2295
+ id: "brevo-template-sender.modal.back",
2296
+ defaultMessage: "Back"
2297
+ })
2298
+ }
2299
+ ) : null,
2300
+ /* @__PURE__ */ jsxRuntime.jsx(
2301
+ designSystem.Button,
2302
+ {
2303
+ variant: "tertiary",
2304
+ onClick: () => {
2305
+ setFieldPickerFor(null);
2306
+ setDepthPickerFromFieldPicker(null);
2307
+ },
2308
+ children: intl.formatMessage({
2309
+ id: "app.components.Button.cancel",
2310
+ defaultMessage: "Cancel"
2311
+ })
2312
+ }
2313
+ )
2314
+ ] })
2315
+ ] })
2316
+ }
2317
+ ),
2318
+ depthModalFor !== null && /* @__PURE__ */ jsxRuntime.jsx(
2319
+ designSystem.Modal.Root,
2320
+ {
2321
+ open: true,
2322
+ onOpenChange: (open) => !open && setDepthModalFor(null),
2323
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContentFieldPicker, { labelledBy: "depth-modal-title", children: [
2324
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "depth-modal-title", variant: "beta", as: "h2", children: intl.formatMessage(
2325
+ {
2326
+ id: "brevo-template-sender.modal.depthModalTitle",
2327
+ defaultMessage: "Choose depth attribute for {placeholder}"
2328
+ },
2329
+ { placeholder: recipientsList[depthModalFor] ?? "" }
2330
+ ) }) }),
2331
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
2332
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 3, children: intl.formatMessage({
2333
+ id: "brevo-template-sender.modal.depthModalHint",
2334
+ defaultMessage: "Click an attribute to use it (e.g. {{relation.id}}, {{relation.url}})."
2335
+ }) }),
2336
+ depthLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { padding: 6, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(FieldPickerList, { children: depthAttributes.map((attr) => {
2337
+ const rootKey = getPlaceholderRootKey(
2338
+ recipientsList[depthModalFor] ?? ""
2339
+ );
2340
+ const label = `{{${rootKey}.${attr.name}}}`;
2341
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2342
+ FieldPickerItem,
2343
+ {
2344
+ type: "button",
2345
+ onClick: () => applyDepthAttribute(attr.name),
2346
+ children: label
2347
+ }
2348
+ ) }, attr.name);
2349
+ }) })
2350
+ ] }),
2351
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => setDepthModalFor(null), children: intl.formatMessage({
2352
+ id: "app.components.Button.cancel",
2353
+ defaultMessage: "Cancel"
2354
+ }) }) })
2355
+ ] })
2356
+ }
2357
+ ),
2358
+ depthModalForHtml !== null && /* @__PURE__ */ jsxRuntime.jsx(
2359
+ designSystem.Modal.Root,
2360
+ {
2361
+ open: true,
2362
+ onOpenChange: (open) => !open && setDepthModalForHtml(null),
2363
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContentFieldPicker, { labelledBy: "depth-html-modal-title", children: [
2364
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "depth-html-modal-title", variant: "beta", as: "h2", children: intl.formatMessage(
2365
+ {
2366
+ id: "brevo-template-sender.modal.depthModalTitle",
2367
+ defaultMessage: "Choose depth attribute for {placeholder}"
2368
+ },
2369
+ { placeholder: `{{${depthModalForHtml.rootKey}}}` }
2370
+ ) }) }),
2371
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
2372
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 3, children: intl.formatMessage({
2373
+ id: "brevo-template-sender.modal.depthModalHint",
2374
+ defaultMessage: "Click an attribute to use it (e.g. {{relation.id}}, {{relation.url}})."
2375
+ }) }),
2376
+ depthLoadingForHtmlDrop ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { padding: 6, justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(FieldPickerList, { children: depthAttributesForHtmlDrop.map((attr) => {
2377
+ const label = `{{${depthModalForHtml.rootKey}.${attr.name}}}`;
2378
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2379
+ FieldPickerItem,
2380
+ {
2381
+ type: "button",
2382
+ onClick: () => applyDepthAttributeForHtmlDrop(attr.name),
2383
+ children: label
2384
+ }
2385
+ ) }, attr.name);
2386
+ }) })
2387
+ ] }),
2388
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(
2389
+ designSystem.Button,
2390
+ {
2391
+ variant: "tertiary",
2392
+ onClick: () => setDepthModalForHtml(null),
2393
+ children: intl.formatMessage({
2394
+ id: "app.components.Button.cancel",
2395
+ defaultMessage: "Cancel"
2396
+ })
2397
+ }
2398
+ ) })
2399
+ ] })
2400
+ }
2401
+ )
2402
+ ] });
2403
+ }
2404
+ function ConfigPage() {
2405
+ const { get, put } = admin.useFetchClient();
2406
+ const intl = reactIntl.useIntl();
2407
+ const [contentTypes, setContentTypes] = react.useState([]);
2408
+ const [triggers, setTriggers] = react.useState([]);
2409
+ const [settings, setSettings] = react.useState({
2410
+ apiKey: "",
2411
+ apiKeySet: false,
2412
+ senderEmail: "",
2413
+ senderName: "",
2414
+ sendEmailTemplateCode: "contact",
2415
+ openaiApiKey: "",
2416
+ openaiApiKeySet: false
2417
+ });
2418
+ const [savingSettings, setSavingSettings] = react.useState(false);
2419
+ const [defaultTemplateModalOpen, setDefaultTemplateModalOpen] = react.useState(false);
2420
+ const [settingsMessage, setSettingsMessage] = react.useState(null);
2421
+ const [triggerSaveError, setTriggerSaveError] = react.useState(null);
2422
+ const [loading, setLoading] = react.useState(true);
2423
+ const [modalContentType, setModalContentType] = react.useState(null);
2424
+ const [manageContentTypesModalOpen, setManageContentTypesModalOpen] = react.useState(false);
2425
+ const [devSupport, setDevSupport] = react.useState(null);
2426
+ const load = react.useCallback(async () => {
2427
+ setLoading(true);
2428
+ try {
2429
+ const base = "/brevo-template-sender";
2430
+ const [ctRes, trRes, settingsRes] = await Promise.all([
2431
+ get(base + "/content-types"),
2432
+ get(base + "/triggers"),
2433
+ get(base + "/settings")
2434
+ ]);
2435
+ const ctData = ctRes?.data ?? ctRes;
2436
+ const trData = trRes?.data ?? trRes;
2437
+ const settingsData = settingsRes?.data ?? settingsRes;
2438
+ const list = Array.isArray(ctData) ? ctData : [];
2439
+ setContentTypes(
2440
+ list.map((ct) => ({
2441
+ ...ct,
2442
+ attributes: Array.isArray(ct.attributes) ? ct.attributes.map(
2443
+ (a) => typeof a === "string" ? { name: a, type: "string", kind: "scalar" } : a
2444
+ ) : []
2445
+ }))
2446
+ );
2447
+ setTriggers(Array.isArray(trData) ? trData : []);
2448
+ if (settingsData && typeof settingsData === "object") {
2449
+ setSettings({
2450
+ apiKey: "",
2451
+ apiKeySet: Boolean(settingsData.apiKeySet),
2452
+ senderEmail: String(settingsData.senderEmail ?? ""),
2453
+ senderName: String(settingsData.senderName ?? ""),
2454
+ sendEmailTemplateCode: String(
2455
+ settingsData.sendEmailTemplateCode ?? "contact"
2456
+ ),
2457
+ openaiApiKey: "",
2458
+ openaiApiKeySet: Boolean(settingsData.openaiApiKeySet)
2459
+ });
2460
+ setDevSupport({
2461
+ isDevelopment: Boolean(settingsData.isDevelopment),
2462
+ supportRepoUrl: String(
2463
+ settingsData.supportRepoUrl ?? "https://github.com/calistock/strapi-plugin-brevo-template-sender"
2464
+ ),
2465
+ supportBmcUrl: String(
2466
+ settingsData.supportBmcUrl ?? "https://buymeacoffee.com/dupflo"
2467
+ )
2468
+ });
2469
+ }
2470
+ } finally {
2471
+ setLoading(false);
2472
+ }
2473
+ }, [get]);
2474
+ const saveSettings = async () => {
2475
+ setSavingSettings(true);
2476
+ setSettingsMessage(null);
2477
+ try {
2478
+ const base = "/brevo-template-sender";
2479
+ const body = {
2480
+ senderEmail: settings.senderEmail,
2481
+ senderName: settings.senderName
2482
+ };
2483
+ if (settings.apiKey) body.apiKey = settings.apiKey;
2484
+ body.openaiApiKey = settings.openaiApiKey ?? "";
2485
+ await put(base + "/settings", body);
2486
+ setSettings((s) => ({
2487
+ ...s,
2488
+ apiKey: "",
2489
+ apiKeySet: s.apiKeySet || Boolean(settings.apiKey)
2490
+ }));
2491
+ setSettingsMessage({
2492
+ type: "success",
2493
+ text: intl.formatMessage({
2494
+ id: "brevo-template-sender.settings.savedSuccess",
2495
+ defaultMessage: "Settings saved."
2496
+ })
2497
+ });
2498
+ } catch (e) {
2499
+ setSettingsMessage({
2500
+ type: "error",
2501
+ text: e?.message ?? intl.formatMessage({
2502
+ id: "brevo-template-sender.settings.savedError",
2503
+ defaultMessage: "Failed to save settings."
2504
+ })
2505
+ });
2506
+ } finally {
2507
+ setSavingSettings(false);
2508
+ }
2509
+ };
2510
+ react.useEffect(() => {
2511
+ load();
2512
+ }, [load]);
2513
+ const hasTrigger = (uid, event) => triggers.some((t) => t.contentTypeUid === uid && t.event === event);
2514
+ const toggleTrigger = async (contentTypeUid, event, checked) => {
2515
+ const existingForCt = triggers.filter(
2516
+ (t) => t.contentTypeUid === contentTypeUid
2517
+ );
2518
+ const template = existingForCt[0] ?? null;
2519
+ let next = triggers.filter(
2520
+ (t) => !(t.contentTypeUid === contentTypeUid && t.event === event)
2521
+ );
2522
+ if (checked) {
2523
+ next = [
2524
+ ...next,
2525
+ {
2526
+ id: makeId(contentTypeUid, event),
2527
+ contentTypeUid,
2528
+ event,
2529
+ subject: template?.subject ?? "",
2530
+ html: template?.html ?? "",
2531
+ sendToField: template?.sendToField,
2532
+ recipients: template?.recipients,
2533
+ templateMode: template?.templateMode ?? "html",
2534
+ brevoTemplateId: template?.brevoTemplateId
2535
+ }
2536
+ ];
2537
+ }
2538
+ setTriggers(next);
2539
+ try {
2540
+ const base = "/brevo-template-sender";
2541
+ await put(base + "/triggers", { body: { triggers: next } });
2542
+ } catch (e) {
2543
+ setTriggers(triggers);
2544
+ }
2545
+ };
2546
+ const getTemplateForContentType = (uid) => triggers.find((t) => t.contentTypeUid === uid) ?? null;
2547
+ const openModal = (ct, closeManageModal = false) => {
2548
+ if (closeManageModal) setManageContentTypesModalOpen(false);
2549
+ const trigger = getTemplateForContentType(ct.uid) ?? null;
2550
+ setModalContentType({
2551
+ contentTypeUid: ct.uid,
2552
+ attributes: ct.attributes,
2553
+ displayName: ct.displayName,
2554
+ trigger
2555
+ });
2556
+ };
2557
+ const selectedContentTypes = react.useMemo(
2558
+ () => contentTypes.filter(
2559
+ (ct) => triggers.some((t) => t.contentTypeUid === ct.uid)
2560
+ ),
2561
+ [contentTypes, triggers]
2562
+ );
2563
+ const saveTrigger = async (updated) => {
2564
+ setTriggerSaveError(null);
2565
+ const baseTrigger = {
2566
+ subject: updated.subject,
2567
+ html: updated.html,
2568
+ sendToField: updated.sendToField,
2569
+ recipients: updated.recipients,
2570
+ templateMode: updated.templateMode ?? "html",
2571
+ brevoTemplateId: updated.brevoTemplateId
2572
+ };
2573
+ const next = triggers.map(
2574
+ (t) => t.contentTypeUid === updated.contentTypeUid ? { ...t, ...baseTrigger, id: t.id } : t
2575
+ );
2576
+ try {
2577
+ const base = "/brevo-template-sender";
2578
+ await put(base + "/triggers", { triggers: next });
2579
+ setTriggers(next);
2580
+ setModalContentType(null);
2581
+ } catch (e) {
2582
+ setTriggerSaveError(
2583
+ e?.message ?? "Erreur lors de l'enregistrement du template."
2584
+ );
2585
+ }
2586
+ };
2587
+ if (loading) {
2588
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) });
2589
+ }
2590
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 10, children: [
2591
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "start", children: [
2592
+ /* @__PURE__ */ jsxRuntime.jsx(BrevoLogo, { style: { height: 32, width: "auto" }, "aria-hidden": true }),
2593
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", as: "h1", children: intl.formatMessage({
2594
+ id: "brevo-template-sender.config.title",
2595
+ defaultMessage: "HTML Sender configuration"
2596
+ }) })
2597
+ ] }) }),
2598
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: intl.formatMessage({
2599
+ id: "brevo-template-sender.config.description",
2600
+ defaultMessage: "Check the events (Create, Update, Delete, Publish, Unpublish) that should trigger an email. One template per content type: edit it via « Edit Template »."
2601
+ }) }) }),
2602
+ /* @__PURE__ */ jsxRuntime.jsxs(
2603
+ designSystem.Box,
2604
+ {
2605
+ style: {
2606
+ display: "grid",
2607
+ gridTemplateColumns: "1fr 1fr",
2608
+ gap: 24,
2609
+ marginBottom: 40
2610
+ },
2611
+ children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx(
2613
+ ConfigCard,
2614
+ {
2615
+ settings,
2616
+ setSettings,
2617
+ saving: savingSettings,
2618
+ onSave: saveSettings,
2619
+ message: settingsMessage,
2620
+ onDismissMessage: () => setSettingsMessage(null)
2621
+ }
2622
+ ),
2623
+ /* @__PURE__ */ jsxRuntime.jsx(
2624
+ ActiveTemplatesCard,
2625
+ {
2626
+ selectedContentTypes,
2627
+ hasTrigger,
2628
+ onManageContentTypes: () => setManageContentTypesModalOpen(true),
2629
+ onEditTemplate: (ct) => openModal(ct)
2630
+ }
2631
+ ),
2632
+ /* @__PURE__ */ jsxRuntime.jsx(
2633
+ SendEmailApiCard,
2634
+ {
2635
+ onConfigureTemplate: () => setDefaultTemplateModalOpen(true)
2636
+ }
2637
+ ),
2638
+ devSupport?.isDevelopment && /* @__PURE__ */ jsxRuntime.jsx(
2639
+ SupportPluginCard,
2640
+ {
2641
+ supportRepoUrl: devSupport.supportRepoUrl,
2642
+ bmcButtonImg
2643
+ }
2644
+ )
2645
+ ]
2646
+ }
2647
+ ),
2648
+ /* @__PURE__ */ jsxRuntime.jsx(
2649
+ ManageContentTypesModal,
2650
+ {
2651
+ open: manageContentTypesModalOpen,
2652
+ onOpenChange: setManageContentTypesModalOpen,
2653
+ contentTypes,
2654
+ hasTrigger,
2655
+ toggleTrigger,
2656
+ getTemplateForContentType,
2657
+ onEditTemplate: (ct) => openModal(ct, true)
2658
+ }
2659
+ ),
2660
+ defaultTemplateModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
2661
+ TemplateModal,
2662
+ {
2663
+ contentTypeUid: "",
2664
+ attributes: DEFAULT_SEND_EMAIL_ATTRIBUTES,
2665
+ displayName: intl.formatMessage({
2666
+ id: "brevo-template-sender.sendEmail.defaultTemplateName",
2667
+ defaultMessage: "Send Email API (default)"
2668
+ }),
2669
+ trigger: null,
2670
+ defaultTemplateCode: settings.sendEmailTemplateCode || "contact",
2671
+ openaiApiKeySet: settings.openaiApiKeySet,
2672
+ onClose: () => setDefaultTemplateModalOpen(false),
2673
+ onSave: () => {
2674
+ },
2675
+ onSaved: () => load()
2676
+ }
2677
+ ),
2678
+ modalContentType && /* @__PURE__ */ jsxRuntime.jsx(
2679
+ TemplateModal,
2680
+ {
2681
+ contentTypeUid: modalContentType.contentTypeUid,
2682
+ attributes: modalContentType.attributes,
2683
+ displayName: modalContentType.displayName,
2684
+ trigger: modalContentType.trigger,
2685
+ saveError: triggerSaveError,
2686
+ openaiApiKeySet: settings.openaiApiKeySet,
2687
+ onClose: () => {
2688
+ setModalContentType(null);
2689
+ setTriggerSaveError(null);
2690
+ },
2691
+ onDismissError: () => setTriggerSaveError(null),
2692
+ onSave: saveTrigger
2693
+ }
2694
+ )
2695
+ ] });
2696
+ }
2697
+ exports.default = ConfigPage;