strapi-plugin-magic-mail 2.3.10 → 2.4.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,376 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const designSystem = require("@strapi/design-system");
6
+ const admin = require("@strapi/strapi/admin");
7
+ const outline = require("@heroicons/react/24/outline");
8
+ const styled = require("styled-components");
9
+ const StyledButtons = require("./StyledButtons-M7vUd2N4.js");
10
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
+ const fadeIn = styled.keyframes`
13
+ from { opacity: 0; transform: translateY(10px); }
14
+ to { opacity: 1; transform: translateY(0); }
15
+ `;
16
+ const PageContainer = styled__default.default(designSystem.Box)`
17
+ ${styled.css`animation: ${fadeIn} 0.4s ease-out;`}
18
+ max-width: 900px;
19
+ margin: 0 auto;
20
+ padding: 40px 32px;
21
+ `;
22
+ const PageHeader = styled__default.default(designSystem.Flex)`
23
+ margin-bottom: 32px;
24
+ flex-direction: column;
25
+ align-items: flex-start;
26
+ gap: 8px;
27
+ `;
28
+ const PageTitle = styled__default.default(designSystem.Typography)`
29
+ font-size: 28px;
30
+ font-weight: 700;
31
+ color: ${(props) => props.theme.colors.neutral800};
32
+ display: block;
33
+ `;
34
+ const PageSubtitle = styled__default.default(designSystem.Typography)`
35
+ font-size: 14px;
36
+ color: ${(props) => props.theme.colors.neutral600};
37
+ display: block;
38
+ `;
39
+ const ActionBar = styled__default.default(designSystem.Flex)`
40
+ margin-bottom: 32px;
41
+ padding: 16px 20px;
42
+ background: ${(props) => props.theme.colors.neutral0};
43
+ border: 1px solid ${(props) => props.theme.colors.neutral200};
44
+ border-radius: 8px;
45
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
46
+ `;
47
+ const SettingsSection = styled__default.default(designSystem.Box)`
48
+ background: ${(props) => props.theme.colors.neutral0};
49
+ border: 1px solid ${(props) => props.theme.colors.neutral200};
50
+ border-radius: 12px;
51
+ overflow: hidden;
52
+ margin-bottom: 24px;
53
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
54
+ `;
55
+ const SectionHeader = styled__default.default(designSystem.Flex)`
56
+ padding: 20px 24px;
57
+ background: ${(props) => props.theme.colors.neutral100};
58
+ border-bottom: 1px solid ${(props) => props.theme.colors.neutral200};
59
+ `;
60
+ const SectionIcon = styled__default.default(designSystem.Box)`
61
+ width: 44px;
62
+ height: 44px;
63
+ border-radius: 10px;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ margin-right: 16px;
68
+ background: ${(props) => props.bgColor || "#E0F2FE"};
69
+ flex-shrink: 0;
70
+ `;
71
+ const SectionContent = styled__default.default(designSystem.Box)`
72
+ padding: 24px;
73
+ `;
74
+ const SettingRow = styled__default.default(designSystem.Flex)`
75
+ padding: 16px 0;
76
+ border-bottom: 1px solid ${(props) => props.theme.colors.neutral150};
77
+
78
+ &:last-child {
79
+ border-bottom: none;
80
+ padding-bottom: 0;
81
+ }
82
+
83
+ &:first-child {
84
+ padding-top: 0;
85
+ }
86
+ `;
87
+ const SettingInfo = styled__default.default(designSystem.Flex)`
88
+ flex: 1;
89
+ padding-right: 24px;
90
+ flex-direction: column;
91
+ align-items: flex-start;
92
+ gap: 6px;
93
+ `;
94
+ const SettingLabel = styled__default.default(designSystem.Typography)`
95
+ font-size: 14px;
96
+ font-weight: 600;
97
+ color: ${(props) => props.theme.colors.neutral800};
98
+ display: block;
99
+ `;
100
+ const SettingDescription = styled__default.default(designSystem.Typography)`
101
+ font-size: 13px;
102
+ color: ${(props) => props.theme.colors.neutral500};
103
+ line-height: 1.5;
104
+ display: block;
105
+ `;
106
+ const ToggleWrapper = styled__default.default(designSystem.Box)`
107
+ flex-shrink: 0;
108
+ display: flex;
109
+ align-items: center;
110
+ `;
111
+ const LoaderContainer = styled__default.default(designSystem.Flex)`
112
+ min-height: 400px;
113
+ align-items: center;
114
+ justify-content: center;
115
+ flex-direction: column;
116
+ gap: 16px;
117
+ `;
118
+ const InfoBox = styled__default.default(designSystem.Box)`
119
+ background: #EFF6FF;
120
+ border: 1px solid #BFDBFE;
121
+ border-radius: 8px;
122
+ padding: 12px 16px;
123
+ margin-top: 16px;
124
+ `;
125
+ const CodeSnippet = styled__default.default.code`
126
+ background: #1E293B;
127
+ color: #E2E8F0;
128
+ padding: 2px 8px;
129
+ border-radius: 4px;
130
+ font-size: 12px;
131
+ font-family: 'Monaco', 'Menlo', monospace;
132
+ `;
133
+ const PluginSettingsPage = () => {
134
+ const { get, put } = admin.useFetchClient();
135
+ const { toggleNotification } = admin.useNotification();
136
+ const [loading, setLoading] = React.useState(true);
137
+ const [settings, setSettings] = React.useState({
138
+ enableLinkTracking: true,
139
+ enableOpenTracking: true,
140
+ trackingBaseUrl: "",
141
+ defaultFromName: "",
142
+ defaultFromEmail: "",
143
+ unsubscribeUrl: "",
144
+ enableUnsubscribeHeader: true
145
+ });
146
+ const [hasChanges, setHasChanges] = React.useState(false);
147
+ const [saving, setSaving] = React.useState(false);
148
+ const fetchSettings = async () => {
149
+ setLoading(true);
150
+ try {
151
+ const response = await get("/magic-mail/settings");
152
+ if (response.data?.data) {
153
+ setSettings({
154
+ enableLinkTracking: response.data.data.enableLinkTracking ?? true,
155
+ enableOpenTracking: response.data.data.enableOpenTracking ?? true,
156
+ trackingBaseUrl: response.data.data.trackingBaseUrl || "",
157
+ defaultFromName: response.data.data.defaultFromName || "",
158
+ defaultFromEmail: response.data.data.defaultFromEmail || "",
159
+ unsubscribeUrl: response.data.data.unsubscribeUrl || "",
160
+ enableUnsubscribeHeader: response.data.data.enableUnsubscribeHeader ?? true
161
+ });
162
+ setHasChanges(false);
163
+ }
164
+ } catch (err) {
165
+ console.error("[MagicMail] Error fetching settings:", err);
166
+ toggleNotification({
167
+ type: "danger",
168
+ message: "Failed to load settings"
169
+ });
170
+ } finally {
171
+ setLoading(false);
172
+ }
173
+ };
174
+ const saveSettings = async () => {
175
+ setSaving(true);
176
+ try {
177
+ await put("/magic-mail/settings", settings);
178
+ toggleNotification({
179
+ type: "success",
180
+ message: "Settings saved successfully!"
181
+ });
182
+ setHasChanges(false);
183
+ } catch (err) {
184
+ console.error("[MagicMail] Error saving settings:", err);
185
+ toggleNotification({
186
+ type: "danger",
187
+ message: "Failed to save settings"
188
+ });
189
+ } finally {
190
+ setSaving(false);
191
+ }
192
+ };
193
+ const handleChange = (key, value) => {
194
+ setSettings((prev) => ({ ...prev, [key]: value }));
195
+ setHasChanges(true);
196
+ };
197
+ React.useEffect(() => {
198
+ fetchSettings();
199
+ }, []);
200
+ if (loading) {
201
+ return /* @__PURE__ */ jsxRuntime.jsx(PageContainer, { children: /* @__PURE__ */ jsxRuntime.jsx(LoaderContainer, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading settings..." }) }) });
202
+ }
203
+ return /* @__PURE__ */ jsxRuntime.jsxs(PageContainer, { children: [
204
+ /* @__PURE__ */ jsxRuntime.jsxs(PageHeader, { children: [
205
+ /* @__PURE__ */ jsxRuntime.jsx(PageTitle, { children: "Plugin Settings" }),
206
+ /* @__PURE__ */ jsxRuntime.jsx(PageSubtitle, { children: "Configure email tracking, analytics, and default sender information" })
207
+ ] }),
208
+ /* @__PURE__ */ jsxRuntime.jsxs(ActionBar, { justifyContent: "space-between", alignItems: "center", children: [
209
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: hasChanges ? "You have unsaved changes" : "All changes saved" }),
210
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
211
+ /* @__PURE__ */ jsxRuntime.jsx(
212
+ StyledButtons.TertiaryButton,
213
+ {
214
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowPathIcon, { style: { width: 18, height: 18 } }),
215
+ onClick: fetchSettings,
216
+ children: "Refresh"
217
+ }
218
+ ),
219
+ /* @__PURE__ */ jsxRuntime.jsx(
220
+ StyledButtons.GradientButton,
221
+ {
222
+ onClick: saveSettings,
223
+ loading: saving,
224
+ disabled: !hasChanges,
225
+ children: "Save Changes"
226
+ }
227
+ )
228
+ ] })
229
+ ] }),
230
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { children: [
231
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionHeader, { alignItems: "center", children: [
232
+ /* @__PURE__ */ jsxRuntime.jsx(SectionIcon, { bgColor: "#DBEAFE", children: /* @__PURE__ */ jsxRuntime.jsx(outline.LinkIcon, { style: { width: 22, height: 22, color: "#2563EB" } }) }),
233
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
234
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Link Tracking" }),
235
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Track when recipients click links in your emails" })
236
+ ] })
237
+ ] }),
238
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
239
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingRow, { alignItems: "center", children: [
240
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingInfo, { children: [
241
+ /* @__PURE__ */ jsxRuntime.jsx(SettingLabel, { children: "Enable Link Tracking" }),
242
+ /* @__PURE__ */ jsxRuntime.jsx(SettingDescription, { children: "Rewrite all links in outgoing emails to track click-through rates. This helps measure email engagement and campaign effectiveness." })
243
+ ] }),
244
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(
245
+ designSystem.Toggle,
246
+ {
247
+ checked: settings.enableLinkTracking,
248
+ onChange: (e) => handleChange("enableLinkTracking", e.target.checked)
249
+ }
250
+ ) })
251
+ ] }),
252
+ settings.enableLinkTracking && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
253
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { marginTop: "20px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
254
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Tracking Base URL" }),
255
+ /* @__PURE__ */ jsxRuntime.jsx(
256
+ designSystem.TextInput,
257
+ {
258
+ placeholder: "https://api.yoursite.com",
259
+ value: settings.trackingBaseUrl,
260
+ onChange: (e) => handleChange("trackingBaseUrl", e.target.value),
261
+ style: { marginTop: "8px" }
262
+ }
263
+ ),
264
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { style: { marginTop: "8px" }, children: "The base URL for tracking links. Leave empty to use the server URL. Must be the public URL where your Strapi API is accessible." })
265
+ ] }) }),
266
+ /* @__PURE__ */ jsxRuntime.jsxs(InfoBox, { children: [
267
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "primary700", fontWeight: "medium", children: "Per-Email Override" }),
268
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "primary600", style: { marginTop: "4px" }, children: [
269
+ "Disable tracking for sensitive emails (password resets, magic links) by passing",
270
+ " ",
271
+ /* @__PURE__ */ jsxRuntime.jsx(CodeSnippet, { children: "skipLinkTracking: true" }),
272
+ " when sending."
273
+ ] })
274
+ ] })
275
+ ] })
276
+ ] })
277
+ ] }),
278
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { children: [
279
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionHeader, { alignItems: "center", children: [
280
+ /* @__PURE__ */ jsxRuntime.jsx(SectionIcon, { bgColor: "#DCFCE7", children: /* @__PURE__ */ jsxRuntime.jsx(outline.EyeIcon, { style: { width: 22, height: 22, color: "#16A34A" } }) }),
281
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
282
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Open Tracking" }),
283
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Track when recipients open your emails" })
284
+ ] })
285
+ ] }),
286
+ /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(SettingRow, { alignItems: "center", children: [
287
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingInfo, { children: [
288
+ /* @__PURE__ */ jsxRuntime.jsx(SettingLabel, { children: "Enable Open Tracking" }),
289
+ /* @__PURE__ */ jsxRuntime.jsx(SettingDescription, { children: "Inject an invisible tracking pixel into emails to detect when they are opened. Note: Some email clients block tracking pixels." })
290
+ ] }),
291
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(
292
+ designSystem.Toggle,
293
+ {
294
+ checked: settings.enableOpenTracking,
295
+ onChange: (e) => handleChange("enableOpenTracking", e.target.checked)
296
+ }
297
+ ) })
298
+ ] }) })
299
+ ] }),
300
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { children: [
301
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionHeader, { alignItems: "center", children: [
302
+ /* @__PURE__ */ jsxRuntime.jsx(SectionIcon, { bgColor: "#FEF3C7", children: /* @__PURE__ */ jsxRuntime.jsx(outline.EnvelopeIcon, { style: { width: 22, height: 22, color: "#D97706" } }) }),
303
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
304
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Unsubscribe Settings" }),
305
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "GDPR-compliant List-Unsubscribe headers" })
306
+ ] })
307
+ ] }),
308
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
309
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingRow, { alignItems: "center", children: [
310
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingInfo, { children: [
311
+ /* @__PURE__ */ jsxRuntime.jsx(SettingLabel, { children: "Enable List-Unsubscribe Header" }),
312
+ /* @__PURE__ */ jsxRuntime.jsx(SettingDescription, { children: 'Add RFC 8058 compliant unsubscribe headers. This enables the "Unsubscribe" button in email clients like Gmail and Outlook.' })
313
+ ] }),
314
+ /* @__PURE__ */ jsxRuntime.jsx(ToggleWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(
315
+ designSystem.Toggle,
316
+ {
317
+ checked: settings.enableUnsubscribeHeader,
318
+ onChange: (e) => handleChange("enableUnsubscribeHeader", e.target.checked)
319
+ }
320
+ ) })
321
+ ] }),
322
+ settings.enableUnsubscribeHeader && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { marginTop: "20px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
323
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Unsubscribe URL" }),
324
+ /* @__PURE__ */ jsxRuntime.jsx(
325
+ designSystem.TextInput,
326
+ {
327
+ placeholder: "https://yoursite.com/unsubscribe?email={{email}}",
328
+ value: settings.unsubscribeUrl,
329
+ onChange: (e) => handleChange("unsubscribeUrl", e.target.value),
330
+ style: { marginTop: "8px" }
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { style: { marginTop: "8px" }, children: "The URL where users are directed when clicking unsubscribe. Leave empty to disable the header." })
334
+ ] }) })
335
+ ] })
336
+ ] }),
337
+ /* @__PURE__ */ jsxRuntime.jsxs(SettingsSection, { children: [
338
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionHeader, { alignItems: "center", children: [
339
+ /* @__PURE__ */ jsxRuntime.jsx(SectionIcon, { bgColor: "#F3E8FF", children: /* @__PURE__ */ jsxRuntime.jsx(outline.UserIcon, { style: { width: 22, height: 22, color: "#9333EA" } }) }),
340
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
341
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Default Sender" }),
342
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Fallback sender information when not specified per email" })
343
+ ] })
344
+ ] }),
345
+ /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
346
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { marginBottom: "20px" }, children: [
347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Default From Name" }),
348
+ /* @__PURE__ */ jsxRuntime.jsx(
349
+ designSystem.TextInput,
350
+ {
351
+ placeholder: "Your Company",
352
+ value: settings.defaultFromName,
353
+ onChange: (e) => handleChange("defaultFromName", e.target.value),
354
+ style: { marginTop: "8px" }
355
+ }
356
+ ),
357
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { style: { marginTop: "8px" }, children: "The sender name shown in recipients' inboxes" })
358
+ ] }),
359
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
360
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Default From Email" }),
361
+ /* @__PURE__ */ jsxRuntime.jsx(
362
+ designSystem.TextInput,
363
+ {
364
+ placeholder: "noreply@yourcompany.com",
365
+ value: settings.defaultFromEmail,
366
+ onChange: (e) => handleChange("defaultFromEmail", e.target.value),
367
+ style: { marginTop: "8px" }
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { style: { marginTop: "8px" }, children: "Must be a verified sender address" })
371
+ ] })
372
+ ] }) })
373
+ ] })
374
+ ] });
375
+ };
376
+ exports.default = PluginSettingsPage;