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