@vicacha-devs/strapi-plugin-inbox 0.0.0-stage → 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.
Files changed (90) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +17 -2
  3. package/dist/admin/InboxHomePage-B5xUPD7Y.mjs +2153 -0
  4. package/dist/admin/InboxHomePage-BCHbPE2t.js +2171 -0
  5. package/dist/admin/LocalizationRoutingTab-CEXbHNe4.mjs +282 -0
  6. package/dist/admin/LocalizationRoutingTab-Dt6U-kXn.js +281 -0
  7. package/dist/admin/SettingsPage-Dif7Cq7I.mjs +819 -0
  8. package/dist/admin/SettingsPage-qB_efKCl.js +837 -0
  9. package/dist/admin/ar-CbjX4o07.mjs +268 -0
  10. package/dist/admin/ar-KedDMvsH.js +268 -0
  11. package/dist/admin/de-BrJIGJEV.mjs +268 -0
  12. package/dist/admin/de-rj5pqQ0S.js +268 -0
  13. package/dist/admin/en-BE8TARw0.mjs +268 -0
  14. package/dist/admin/en-CuTmgXZL.js +268 -0
  15. package/dist/admin/es-Berf1dRb.js +268 -0
  16. package/dist/admin/es-Ce4nMwC7.mjs +268 -0
  17. package/dist/admin/fr-CfdjWCiu.js +268 -0
  18. package/dist/admin/fr-Y3GKi6mQ.mjs +268 -0
  19. package/dist/admin/index-5e9dAMBs.mjs +822 -0
  20. package/dist/admin/index-DLfoIVZP.js +839 -0
  21. package/dist/admin/index.js +5 -0
  22. package/dist/admin/index.mjs +5 -0
  23. package/dist/admin/it-DXXnvX5J.mjs +268 -0
  24. package/dist/admin/it-DkJ3WwLA.js +268 -0
  25. package/dist/admin/pt-D7WKv-MR.js +268 -0
  26. package/dist/admin/pt-QDCJ6lOH.mjs +268 -0
  27. package/dist/admin/ru-DgYTw9aE.js +268 -0
  28. package/dist/admin/ru-DzP0H4fh.mjs +268 -0
  29. package/dist/admin/src/components/Initializer.d.ts +6 -0
  30. package/dist/admin/src/components/PluginIcon.d.ts +3 -0
  31. package/dist/admin/src/components/inbox/AlertBannersTab.d.ts +10 -0
  32. package/dist/admin/src/components/inbox/CaseworkDetailPanel.d.ts +15 -0
  33. package/dist/admin/src/components/inbox/CaseworkPayloadChecklistColumn.d.ts +12 -0
  34. package/dist/admin/src/components/inbox/CaseworkSubmissionsTab.d.ts +12 -0
  35. package/dist/admin/src/components/inbox/FormBuilderTab.d.ts +17 -0
  36. package/dist/admin/src/components/inbox/FormChecklistAndLocalePanels.d.ts +17 -0
  37. package/dist/admin/src/components/inbox/FormRoutingDrawer.d.ts +15 -0
  38. package/dist/admin/src/components/inbox/SubscribersTab.d.ts +10 -0
  39. package/dist/admin/src/components/settings/AntiSpamCaptchaTab.d.ts +9 -0
  40. package/dist/admin/src/components/settings/BridgesNotificationsTab.d.ts +8 -0
  41. package/dist/admin/src/components/settings/CaptchaProviderCard.d.ts +21 -0
  42. package/dist/admin/src/components/settings/LocalizationRoutingTab.d.ts +16 -0
  43. package/dist/admin/src/index.d.ts +21 -0
  44. package/dist/admin/src/pages/InboxHomePage.d.ts +3 -0
  45. package/dist/admin/src/pages/SettingsPage.d.ts +3 -0
  46. package/dist/admin/src/pluginId.d.ts +3 -0
  47. package/dist/admin/src/translations/cmLabels.d.ts +1 -0
  48. package/dist/admin/src/translations/cmPlaceholders.d.ts +1 -0
  49. package/dist/admin/src/utils/getTranslation.d.ts +3 -0
  50. package/dist/admin/tr-5q4NwTgF.mjs +268 -0
  51. package/dist/admin/tr-CT-24fLA.js +268 -0
  52. package/dist/admin/zh-CYhWM5Qr.mjs +268 -0
  53. package/dist/admin/zh-CmCHcNcd.js +268 -0
  54. package/dist/server/index.js +3108 -0
  55. package/dist/server/index.mjs +3108 -0
  56. package/dist/server/src/bootstrap.d.ts +5 -0
  57. package/dist/server/src/config/index.d.ts +5 -0
  58. package/dist/server/src/content-types/alert-banner/index.d.ts +73 -0
  59. package/dist/server/src/content-types/alert-banner/schema.json.d.ts +73 -0
  60. package/dist/server/src/content-types/index.d.ts +253 -0
  61. package/dist/server/src/content-types/submission/index.d.ts +111 -0
  62. package/dist/server/src/content-types/submission/schema.json.d.ts +126 -0
  63. package/dist/server/src/content-types/subscriber/index.d.ts +69 -0
  64. package/dist/server/src/content-types/subscriber/schema.json.d.ts +69 -0
  65. package/dist/server/src/controllers/caseworkHandlers.d.ts +10 -0
  66. package/dist/server/src/controllers/communityHandlers.d.ts +10 -0
  67. package/dist/server/src/controllers/formHandlers.d.ts +14 -0
  68. package/dist/server/src/controllers/inboxController.d.ts +40 -0
  69. package/dist/server/src/controllers/index.d.ts +2 -0
  70. package/dist/server/src/destroy.d.ts +5 -0
  71. package/dist/server/src/index.d.ts +2 -0
  72. package/dist/server/src/register.d.ts +5 -0
  73. package/dist/server/src/routes/admin.d.ts +12 -0
  74. package/dist/server/src/routes/content-api.d.ts +13 -0
  75. package/dist/server/src/routes/index.d.ts +26 -0
  76. package/dist/server/src/services/alertService.d.ts +16 -0
  77. package/dist/server/src/services/docHelper.d.ts +28 -0
  78. package/dist/server/src/services/formBuilderMapper.d.ts +22 -0
  79. package/dist/server/src/services/formBuilderSync.d.ts +13 -0
  80. package/dist/server/src/services/formService.d.ts +40 -0
  81. package/dist/server/src/services/index.d.ts +2 -0
  82. package/dist/server/src/services/settings.d.ts +9 -0
  83. package/dist/server/src/services/submissionEcosystemBridges.d.ts +21 -0
  84. package/dist/server/src/services/submissionFormBuilderBridge.d.ts +23 -0
  85. package/dist/server/src/services/submissionService.d.ts +58 -0
  86. package/dist/server/src/services/subscriberService.d.ts +22 -0
  87. package/dist/shared/index.d.ts +1 -0
  88. package/dist/shared/index.js +917 -0
  89. package/dist/shared/index.mjs +917 -0
  90. package/package.json +125 -3
@@ -0,0 +1,819 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Box, Flex, Typography, Badge, Button, Field, TextInput, Tabs, NumberInput, SingleSelect, SingleSelectOption, Checkbox, Main, Divider } from "@strapi/design-system";
3
+ import { useFetchClient } from "@strapi/strapi/admin";
4
+ import * as React from "react";
5
+ import { useIntl } from "react-intl";
6
+ import { p as parseCsvList, D as DEFAULT_INBOX_SETTINGS, P as PLUGIN_ID } from "./index-5e9dAMBs.mjs";
7
+ import { L as LocalizationRoutingTab } from "./LocalizationRoutingTab-CEXbHNe4.mjs";
8
+ const subCardStyle$1 = {
9
+ border: "1px solid rgba(128, 128, 128, 0.2)",
10
+ display: "flex",
11
+ flexDirection: "column",
12
+ justifyContent: "space-between"
13
+ };
14
+ const CaptchaProviderCard = ({
15
+ t,
16
+ title,
17
+ isDefaultActive,
18
+ testButtonLabel,
19
+ onTestSecret,
20
+ siteKeyName,
21
+ siteKeyLabel,
22
+ siteKeyPlaceholder,
23
+ siteKeyValue,
24
+ siteKeyHint,
25
+ onSiteKeyChange,
26
+ secretKeyName,
27
+ secretKeyLabel,
28
+ secretKeyPlaceholder,
29
+ secretKeyValue,
30
+ secretKeyHint,
31
+ onSecretKeyChange
32
+ }) => /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral100", hasRadius: true, style: subCardStyle$1, children: /* @__PURE__ */ jsxs(Box, { children: [
33
+ /* @__PURE__ */ jsxs(
34
+ Flex,
35
+ {
36
+ justifyContent: "space-between",
37
+ alignItems: "center",
38
+ paddingBottom: 3,
39
+ wrap: "wrap",
40
+ gap: 2,
41
+ children: [
42
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
43
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", children: title }),
44
+ /* @__PURE__ */ jsx(Badge, { children: isDefaultActive ? t("settings.ui.default_active", "Default Active") : t("settings.ui.standby", "Standby") })
45
+ ] }),
46
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "S", onClick: onTestSecret, children: testButtonLabel })
47
+ ]
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
51
+ /* @__PURE__ */ jsxs(Field.Root, { name: siteKeyName, hint: siteKeyHint, children: [
52
+ /* @__PURE__ */ jsx(Field.Label, { children: siteKeyLabel }),
53
+ /* @__PURE__ */ jsx(
54
+ TextInput,
55
+ {
56
+ name: siteKeyName,
57
+ placeholder: siteKeyPlaceholder,
58
+ value: siteKeyValue,
59
+ onChange: (e) => onSiteKeyChange(e.target.value)
60
+ }
61
+ ),
62
+ /* @__PURE__ */ jsx(Field.Hint, {})
63
+ ] }),
64
+ /* @__PURE__ */ jsxs(Field.Root, { name: secretKeyName, hint: secretKeyHint, children: [
65
+ /* @__PURE__ */ jsx(Field.Label, { children: secretKeyLabel }),
66
+ /* @__PURE__ */ jsx(
67
+ TextInput,
68
+ {
69
+ name: secretKeyName,
70
+ type: "password",
71
+ placeholder: secretKeyPlaceholder,
72
+ value: secretKeyValue,
73
+ onChange: (e) => onSecretKeyChange(e.target.value)
74
+ }
75
+ ),
76
+ /* @__PURE__ */ jsx(Field.Hint, {})
77
+ ] })
78
+ ] })
79
+ ] }) });
80
+ const CAPTCHA_PROVIDER_OPTIONS = [
81
+ { value: "none", label: "None (Honeypot + IP Rate Limiting only)" },
82
+ { value: "turnstile", label: "Cloudflare Turnstile (Recommended)" },
83
+ { value: "recaptcha", label: "Google reCAPTCHA v2" }
84
+ ];
85
+ const AntiSpamCaptchaTab = ({
86
+ t,
87
+ settings,
88
+ setSettings,
89
+ onTestCaptcha
90
+ }) => {
91
+ return /* @__PURE__ */ jsx(Tabs.Content, { value: "antispam", children: /* @__PURE__ */ jsxs(
92
+ Box,
93
+ {
94
+ padding: 6,
95
+ background: "neutral0",
96
+ hasRadius: true,
97
+ shadow: "filterShadow",
98
+ style: { borderTopLeftRadius: 0, borderTopRightRadius: 0 },
99
+ children: [
100
+ /* @__PURE__ */ jsxs(
101
+ Flex,
102
+ {
103
+ justifyContent: "space-between",
104
+ alignItems: "center",
105
+ paddingBottom: 2,
106
+ wrap: "wrap",
107
+ gap: 2,
108
+ children: [
109
+ /* @__PURE__ */ jsxs(Box, { children: [
110
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", tag: "h2", children: t(
111
+ "settings.ui.2_anti_spam_guard_honeypot_rate_limiting_captcha",
112
+ "Anti-Spam Guard (Honeypot, Rate Limiting & CAPTCHA)"
113
+ ) }),
114
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
115
+ "settings.ui.three_stage_protection_invisible_bot_trap_field_",
116
+ "Three-stage protection: invisible bot trap field, per-IP sliding-window rate limiter, and fail-closed CAPTCHA verification."
117
+ ) })
118
+ ] }),
119
+ /* @__PURE__ */ jsxs(Badge, { children: [
120
+ settings.defaultRateLimitPerHour || 15,
121
+ " ",
122
+ t("settings.ui.req_per_hr_ip", "req/hr per IP")
123
+ ] })
124
+ ]
125
+ }
126
+ ),
127
+ /* @__PURE__ */ jsxs(
128
+ Box,
129
+ {
130
+ paddingTop: 4,
131
+ style: {
132
+ display: "grid",
133
+ gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
134
+ gap: 20
135
+ },
136
+ children: [
137
+ /* @__PURE__ */ jsxs(
138
+ Field.Root,
139
+ {
140
+ name: "defaultHoneypotField",
141
+ hint: t(
142
+ "settings.ui.hidden_trap_field_name_bots_that_populate_this_f",
143
+ "Hidden trap field name; bots that populate this field are rejected with HTTP 403 Forbidden."
144
+ ),
145
+ children: [
146
+ /* @__PURE__ */ jsx(Field.Label, { children: t(
147
+ "field.default_honeypot_field_name",
148
+ "Default Honeypot Field Name"
149
+ ) }),
150
+ /* @__PURE__ */ jsx(
151
+ TextInput,
152
+ {
153
+ name: "defaultHoneypotField",
154
+ placeholder: t(
155
+ "placeholder.e_g_website_url",
156
+ "e.g., _website_url"
157
+ ),
158
+ value: settings.defaultHoneypotField || "",
159
+ onChange: (e) => setSettings({
160
+ ...settings,
161
+ defaultHoneypotField: e.target.value
162
+ })
163
+ }
164
+ ),
165
+ /* @__PURE__ */ jsx(Field.Hint, {})
166
+ ]
167
+ }
168
+ ),
169
+ /* @__PURE__ */ jsxs(
170
+ Field.Root,
171
+ {
172
+ name: "defaultRateLimitPerHour",
173
+ hint: t(
174
+ "settings.ui.maximum_submissions_allowed_per_ip_and_form_slug",
175
+ "Maximum submissions allowed per IP and form slug within a rolling 60-minute window."
176
+ ),
177
+ children: [
178
+ /* @__PURE__ */ jsx(Field.Label, { children: t(
179
+ "field.default_hourly_rate_limit_per_ip",
180
+ "Default Hourly Rate Limit per IP"
181
+ ) }),
182
+ /* @__PURE__ */ jsx(
183
+ NumberInput,
184
+ {
185
+ name: "defaultRateLimitPerHour",
186
+ placeholder: t("placeholder.e_g_15", "e.g., 15"),
187
+ value: settings.defaultRateLimitPerHour ?? 15,
188
+ onValueChange: (val) => setSettings({
189
+ ...settings,
190
+ defaultRateLimitPerHour: typeof val === "number" && val > 0 ? val : 15
191
+ })
192
+ }
193
+ ),
194
+ /* @__PURE__ */ jsx(Field.Hint, {})
195
+ ]
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsxs(
199
+ Field.Root,
200
+ {
201
+ name: "defaultCaptchaProvider",
202
+ hint: t(
203
+ "settings.ui.server_side_token_verification_enforced_on_publi",
204
+ "Server-side token verification enforced on public submissions unless overridden per form."
205
+ ),
206
+ children: [
207
+ /* @__PURE__ */ jsx(Field.Label, { children: t("field.default_captcha_provider", "Default CAPTCHA Provider") }),
208
+ /* @__PURE__ */ jsx(
209
+ SingleSelect,
210
+ {
211
+ placeholder: t(
212
+ "placeholder.select_captcha_provider",
213
+ "Select CAPTCHA provider"
214
+ ),
215
+ value: settings.defaultCaptchaProvider || "none",
216
+ onChange: (val) => setSettings({
217
+ ...settings,
218
+ defaultCaptchaProvider: String(val)
219
+ }),
220
+ children: CAPTCHA_PROVIDER_OPTIONS.map((opt) => /* @__PURE__ */ jsx(SingleSelectOption, { value: opt.value, children: opt.label }, opt.value))
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsx(Field.Hint, {})
224
+ ]
225
+ }
226
+ )
227
+ ]
228
+ }
229
+ ),
230
+ /* @__PURE__ */ jsxs(
231
+ Box,
232
+ {
233
+ paddingTop: 5,
234
+ style: {
235
+ display: "grid",
236
+ gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
237
+ gap: 20
238
+ },
239
+ children: [
240
+ /* @__PURE__ */ jsx(
241
+ CaptchaProviderCard,
242
+ {
243
+ t,
244
+ title: t(
245
+ "settings.ui.cloudflare_turnstile",
246
+ "Cloudflare Turnstile"
247
+ ),
248
+ isDefaultActive: settings.defaultCaptchaProvider === "turnstile",
249
+ testButtonLabel: t(
250
+ "settings.ui.test_turnstile_secret",
251
+ "Test Turnstile Secret"
252
+ ),
253
+ onTestSecret: () => onTestCaptcha("turnstile", settings.turnstileSecretKey),
254
+ siteKeyName: "turnstileSiteKey",
255
+ siteKeyLabel: t(
256
+ "field.turnstile_site_key_public_widget_key",
257
+ "Turnstile Site Key (Public Widget Key)"
258
+ ),
259
+ siteKeyPlaceholder: t(
260
+ "placeholder.e_g_0x4aaaaaaabkmyinuke",
261
+ "e.g., 0x4AAAAAAABkMYinukE_..."
262
+ ),
263
+ siteKeyValue: settings.turnstileSiteKey || "",
264
+ siteKeyHint: t(
265
+ "settings.ui.public_widget_key_rendered_by_the_frontend_form_",
266
+ "Public widget key rendered by the frontend form component."
267
+ ),
268
+ onSiteKeyChange: (val) => setSettings({ ...settings, turnstileSiteKey: val }),
269
+ secretKeyName: "turnstileSecretKey",
270
+ secretKeyLabel: t(
271
+ "field.turnstile_secret_key_server_verification",
272
+ "Turnstile Secret Key (Server Verification)"
273
+ ),
274
+ secretKeyPlaceholder: t(
275
+ "placeholder.e_g_0x4aaaaaaabkmyin",
276
+ "e.g., 0x4AAAAAAABkMYin..."
277
+ ),
278
+ secretKeyValue: settings.turnstileSecretKey || "",
279
+ secretKeyHint: t(
280
+ "settings.ui.verified_against_challenges_cloudflare_com_turns",
281
+ "Verified against challenges.cloudflare.com/turnstile/v0/siteverify."
282
+ ),
283
+ onSecretKeyChange: (val) => setSettings({ ...settings, turnstileSecretKey: val })
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsx(
287
+ CaptchaProviderCard,
288
+ {
289
+ t,
290
+ title: t("settings.ui.google_recaptcha_v2", "Google reCAPTCHA v2"),
291
+ isDefaultActive: settings.defaultCaptchaProvider === "recaptcha",
292
+ testButtonLabel: t(
293
+ "settings.ui.test_recaptcha_secret",
294
+ "Test reCAPTCHA Secret"
295
+ ),
296
+ onTestSecret: () => onTestCaptcha("recaptcha", settings.recaptchaSecretKey),
297
+ siteKeyName: "recaptchaSiteKey",
298
+ siteKeyLabel: t(
299
+ "field.recaptcha_v2_site_key_public_widget_key",
300
+ "reCAPTCHA v2 Site Key (Public Widget Key)"
301
+ ),
302
+ siteKeyPlaceholder: t(
303
+ "placeholder.e_g_6leixactaaaaajczvrqyhh71umiegnq_mxjizkhi",
304
+ "e.g., 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
305
+ ),
306
+ siteKeyValue: settings.recaptchaSiteKey || "",
307
+ siteKeyHint: t(
308
+ "settings.ui.public_site_key_used_by_client_forms_when_recapt",
309
+ "Public site key used by client forms when reCAPTCHA v2 is enabled."
310
+ ),
311
+ onSiteKeyChange: (val) => setSettings({ ...settings, recaptchaSiteKey: val }),
312
+ secretKeyName: "recaptchaSecretKey",
313
+ secretKeyLabel: t(
314
+ "field.recaptcha_v2_secret_key_server_verification",
315
+ "reCAPTCHA v2 Secret Key (Server Verification)"
316
+ ),
317
+ secretKeyPlaceholder: t(
318
+ "placeholder.e_g_6leixactaaaaagg_vfi1tnrwxmznfuojj4wifjwe",
319
+ "e.g., 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
320
+ ),
321
+ secretKeyValue: settings.recaptchaSecretKey || "",
322
+ secretKeyHint: t(
323
+ "settings.ui.verified_against_www_google_com_recaptcha_api_si",
324
+ "Verified against www.google.com/recaptcha/api/siteverify."
325
+ ),
326
+ onSecretKeyChange: (val) => setSettings({ ...settings, recaptchaSecretKey: val })
327
+ }
328
+ )
329
+ ]
330
+ }
331
+ )
332
+ ]
333
+ }
334
+ ) });
335
+ };
336
+ const subCardStyle = {
337
+ border: "1px solid rgba(128, 128, 128, 0.2)",
338
+ display: "flex",
339
+ flexDirection: "column",
340
+ justifyContent: "space-between"
341
+ };
342
+ const BridgesNotificationsTab = ({ t, settings, setSettings }) => {
343
+ const canonicalNotifyEmails = (settings.defaultNotifyEmails || []).join(", ");
344
+ const [notifyEmailsDraft, setNotifyEmailsDraft] = React.useState(
345
+ canonicalNotifyEmails
346
+ );
347
+ React.useEffect(() => {
348
+ setNotifyEmailsDraft(
349
+ (prev) => parseCsvList(prev).join(", ") === canonicalNotifyEmails ? prev : canonicalNotifyEmails
350
+ );
351
+ }, [canonicalNotifyEmails]);
352
+ return /* @__PURE__ */ jsx(Tabs.Content, { value: "bridges", children: /* @__PURE__ */ jsxs(
353
+ Box,
354
+ {
355
+ padding: 6,
356
+ background: "neutral0",
357
+ hasRadius: true,
358
+ shadow: "filterShadow",
359
+ style: { borderTopLeftRadius: 0, borderTopRightRadius: 0 },
360
+ children: [
361
+ /* @__PURE__ */ jsxs(
362
+ Flex,
363
+ {
364
+ justifyContent: "space-between",
365
+ alignItems: "center",
366
+ paddingBottom: 2,
367
+ wrap: "wrap",
368
+ gap: 2,
369
+ children: [
370
+ /* @__PURE__ */ jsxs(Box, { children: [
371
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", tag: "h2", children: t(
372
+ "settings.ui.3_double_opt_in_notifications_ecosystem_bridges",
373
+ "Double Opt-In, Notifications & Ecosystem Bridges"
374
+ ) }),
375
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
376
+ "settings.ui.control_subscriber_confirmation_workflows_staff_",
377
+ "Control subscriber confirmation workflows, staff email/webhook notifications, and 1-click integrations with Form Builder CMS, Bookings, and Certificates."
378
+ ) })
379
+ ] }),
380
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", children: [
381
+ /* @__PURE__ */ jsxs(Badge, { children: [
382
+ t("settings.ui.double_opt_in_label", "Double Opt-In"),
383
+ ":",
384
+ " ",
385
+ settings.enableDoubleOptIn ? t("common.on", "On") : t("common.off", "Off")
386
+ ] }),
387
+ /* @__PURE__ */ jsxs(Badge, { children: [
388
+ t("settings.ui.bookings_bridge_label", "Bookings Bridge"),
389
+ ":",
390
+ " ",
391
+ settings.enableBookingBridge ? t("common.on", "On") : t("common.off", "Off")
392
+ ] }),
393
+ /* @__PURE__ */ jsxs(Badge, { children: [
394
+ t(
395
+ "settings.ui.certificates_bridge_label",
396
+ "Certificates Bridge"
397
+ ),
398
+ ":",
399
+ " ",
400
+ settings.enableCertificateBridge ? t("common.on", "On") : t("common.off", "Off")
401
+ ] })
402
+ ] })
403
+ ]
404
+ }
405
+ ),
406
+ /* @__PURE__ */ jsxs(
407
+ Box,
408
+ {
409
+ paddingTop: 4,
410
+ style: {
411
+ display: "grid",
412
+ gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
413
+ gap: 16
414
+ },
415
+ children: [
416
+ /* @__PURE__ */ jsxs(
417
+ Box,
418
+ {
419
+ padding: 4,
420
+ background: "neutral100",
421
+ hasRadius: true,
422
+ style: subCardStyle,
423
+ children: [
424
+ /* @__PURE__ */ jsxs(Box, { children: [
425
+ /* @__PURE__ */ jsxs(
426
+ Flex,
427
+ {
428
+ justifyContent: "space-between",
429
+ alignItems: "center",
430
+ paddingBottom: 2,
431
+ children: [
432
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: t(
433
+ "settings.ui.subscriber_double_opt_in",
434
+ "Subscriber Double Opt-In"
435
+ ) }),
436
+ /* @__PURE__ */ jsx(Badge, { children: settings.enableDoubleOptIn ? t("common.enabled", "Enabled") : t("common.disabled", "Disabled") })
437
+ ]
438
+ }
439
+ ),
440
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
441
+ "settings.ui.double_opt_in_desc",
442
+ "New subscribers start in pending status with a cryptographic confirmation token and 1-click unsubscribe token."
443
+ ) })
444
+ ] }),
445
+ /* @__PURE__ */ jsx(Box, { paddingTop: 3, children: /* @__PURE__ */ jsx(
446
+ Checkbox,
447
+ {
448
+ checked: Boolean(settings.enableDoubleOptIn),
449
+ onCheckedChange: (checked) => setSettings({
450
+ ...settings,
451
+ enableDoubleOptIn: Boolean(checked)
452
+ }),
453
+ children: t(
454
+ "checkbox.require_email_confirmation_before_activating_sub",
455
+ "Require email confirmation before activating subscribers"
456
+ )
457
+ }
458
+ ) })
459
+ ]
460
+ }
461
+ ),
462
+ /* @__PURE__ */ jsxs(
463
+ Box,
464
+ {
465
+ padding: 4,
466
+ background: "neutral100",
467
+ hasRadius: true,
468
+ style: subCardStyle,
469
+ children: [
470
+ /* @__PURE__ */ jsxs(Box, { children: [
471
+ /* @__PURE__ */ jsxs(
472
+ Flex,
473
+ {
474
+ justifyContent: "space-between",
475
+ alignItems: "center",
476
+ paddingBottom: 2,
477
+ children: [
478
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: t(
479
+ "settings.ui.bookings_plugin_bridge",
480
+ "Bookings Plugin Bridge"
481
+ ) }),
482
+ /* @__PURE__ */ jsx(Badge, { children: settings.enableBookingBridge ? t("common.enabled", "Enabled") : t("common.disabled", "Disabled") })
483
+ ]
484
+ }
485
+ ),
486
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
487
+ "settings.ui.bookings_bridge_desc",
488
+ "Enables 1-click appointment scheduling from service-desk submissions into @vicacha-devs/strapi-plugin-bookings."
489
+ ) })
490
+ ] }),
491
+ /* @__PURE__ */ jsx(Box, { paddingTop: 3, children: /* @__PURE__ */ jsx(
492
+ Checkbox,
493
+ {
494
+ checked: Boolean(settings.enableBookingBridge),
495
+ onCheckedChange: (checked) => setSettings({
496
+ ...settings,
497
+ enableBookingBridge: Boolean(checked)
498
+ }),
499
+ children: t(
500
+ "checkbox.enable_bookings_bridge",
501
+ "Enable @vicacha-devs/strapi-plugin-bookings bridge"
502
+ )
503
+ }
504
+ ) })
505
+ ]
506
+ }
507
+ ),
508
+ /* @__PURE__ */ jsxs(
509
+ Box,
510
+ {
511
+ padding: 4,
512
+ background: "neutral100",
513
+ hasRadius: true,
514
+ style: subCardStyle,
515
+ children: [
516
+ /* @__PURE__ */ jsxs(Box, { children: [
517
+ /* @__PURE__ */ jsxs(
518
+ Flex,
519
+ {
520
+ justifyContent: "space-between",
521
+ alignItems: "center",
522
+ paddingBottom: 2,
523
+ children: [
524
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: t(
525
+ "settings.ui.certificates_plugin_bridge",
526
+ "Certificates Plugin Bridge"
527
+ ) }),
528
+ /* @__PURE__ */ jsx(Badge, { children: settings.enableCertificateBridge ? t("common.enabled", "Enabled") : t("common.disabled", "Disabled") })
529
+ ]
530
+ }
531
+ ),
532
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
533
+ "settings.ui.certificates_bridge_desc",
534
+ "Enables 1-click certificate issuance (Nikah, Shahada, Halal, Consular) via @vicacha-devs/strapi-plugin-certificates after checklist verification."
535
+ ) })
536
+ ] }),
537
+ /* @__PURE__ */ jsx(Box, { paddingTop: 3, children: /* @__PURE__ */ jsx(
538
+ Checkbox,
539
+ {
540
+ checked: Boolean(settings.enableCertificateBridge),
541
+ onCheckedChange: (checked) => setSettings({
542
+ ...settings,
543
+ enableCertificateBridge: Boolean(checked)
544
+ }),
545
+ children: t(
546
+ "checkbox.enable_certificates_bridge",
547
+ "Enable @vicacha-devs/strapi-plugin-certificates bridge"
548
+ )
549
+ }
550
+ ) })
551
+ ]
552
+ }
553
+ )
554
+ ]
555
+ }
556
+ ),
557
+ /* @__PURE__ */ jsxs(
558
+ Box,
559
+ {
560
+ paddingTop: 5,
561
+ style: {
562
+ display: "grid",
563
+ gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
564
+ gap: 20
565
+ },
566
+ children: [
567
+ /* @__PURE__ */ jsxs(
568
+ Field.Root,
569
+ {
570
+ name: "defaultNotifyEmails",
571
+ hint: t(
572
+ "settings.ui.default_staff_email_addresses_notified_when_a_ne",
573
+ "Default staff email addresses notified when a new form submission or service-desk application is received."
574
+ ),
575
+ children: [
576
+ /* @__PURE__ */ jsx(Field.Label, { children: t(
577
+ "field.default_staff_notification_emails_comma_separate",
578
+ "Default Staff Notification Emails (comma-separated)"
579
+ ) }),
580
+ /* @__PURE__ */ jsx(
581
+ TextInput,
582
+ {
583
+ name: "defaultNotifyEmails",
584
+ placeholder: t(
585
+ "placeholder.e_g_info_mezquitabogota_org_imam_mezquitabogota_",
586
+ "e.g., info@mezquitabogota.org, imam@mezquitabogota.org"
587
+ ),
588
+ value: notifyEmailsDraft,
589
+ onChange: (e) => {
590
+ const nextRaw = e.target.value;
591
+ setNotifyEmailsDraft(nextRaw);
592
+ setSettings({
593
+ ...settings,
594
+ defaultNotifyEmails: parseCsvList(nextRaw)
595
+ });
596
+ },
597
+ onBlur: () => setNotifyEmailsDraft(canonicalNotifyEmails)
598
+ }
599
+ ),
600
+ /* @__PURE__ */ jsx(Field.Hint, {})
601
+ ]
602
+ }
603
+ ),
604
+ /* @__PURE__ */ jsxs(
605
+ Field.Root,
606
+ {
607
+ name: "outboundWebhookUrl",
608
+ hint: t(
609
+ "settings.ui.optional_https_webhook_endpoint_notified_on_subm",
610
+ "Optional HTTPS webhook endpoint notified on submission creation, checklist verification, and status transitions."
611
+ ),
612
+ children: [
613
+ /* @__PURE__ */ jsx(Field.Label, { children: t(
614
+ "field.outbound_webhook_url_optional",
615
+ "Outbound Webhook URL (Optional)"
616
+ ) }),
617
+ /* @__PURE__ */ jsx(
618
+ TextInput,
619
+ {
620
+ name: "outboundWebhookUrl",
621
+ placeholder: t(
622
+ "placeholder.e_g_https_hooks_mezquitabogota_org_inbox_events",
623
+ "e.g., https://hooks.mezquitabogota.org/inbox-events"
624
+ ),
625
+ value: settings.outboundWebhookUrl || "",
626
+ onChange: (e) => setSettings({
627
+ ...settings,
628
+ outboundWebhookUrl: e.target.value
629
+ })
630
+ }
631
+ ),
632
+ /* @__PURE__ */ jsx(Field.Hint, {})
633
+ ]
634
+ }
635
+ )
636
+ ]
637
+ }
638
+ )
639
+ ]
640
+ }
641
+ ) });
642
+ };
643
+ const SettingsPage = () => {
644
+ const { formatMessage } = useIntl();
645
+ const t = (id, defaultMessage) => formatMessage({ id: `${PLUGIN_ID}.${id}`, defaultMessage });
646
+ const { get, put, post } = useFetchClient();
647
+ const [settings, setSettings] = React.useState(
648
+ DEFAULT_INBOX_SETTINGS
649
+ );
650
+ const [savedNotice, setSavedNotice] = React.useState("");
651
+ const [captchaTestResult, setCaptchaTestResult] = React.useState(null);
652
+ React.useEffect(() => {
653
+ const fetchSettings = async () => {
654
+ try {
655
+ const res = await get(`/${PLUGIN_ID}/settings`);
656
+ if (res?.data?.data) {
657
+ setSettings({
658
+ ...DEFAULT_INBOX_SETTINGS,
659
+ ...res.data.data
660
+ });
661
+ }
662
+ } catch {
663
+ }
664
+ };
665
+ void fetchSettings();
666
+ }, []);
667
+ const handleSave = async () => {
668
+ const res = await put(`/${PLUGIN_ID}/settings`, settings);
669
+ if (res?.data?.data) {
670
+ setSettings({
671
+ ...DEFAULT_INBOX_SETTINGS,
672
+ ...res.data.data
673
+ });
674
+ setSavedNotice(
675
+ "Inbox, Anti-Spam & Casework settings saved successfully."
676
+ );
677
+ }
678
+ };
679
+ const handleResetDefaults = () => {
680
+ setSettings(DEFAULT_INBOX_SETTINGS);
681
+ setSavedNotice(
682
+ 'Settings reset to defaults in editor. Click "Save Settings" to persist.'
683
+ );
684
+ };
685
+ const handleTestCaptcha = async (provider, secret) => {
686
+ const res = await post(`/${PLUGIN_ID}/captcha/test`, {
687
+ provider,
688
+ secret: secret || ""
689
+ });
690
+ const out = res?.data?.data;
691
+ const isOk = Boolean(out?.ok);
692
+ setCaptchaTestResult({
693
+ ok: isOk,
694
+ message: isOk ? `${provider === "turnstile" ? "Cloudflare Turnstile" : "Google reCAPTCHA"} secret key verified!` : `Verification failed (${provider}): ${out?.reason || "Invalid or empty secret key"}`
695
+ });
696
+ };
697
+ return /* @__PURE__ */ jsx(Main, { children: /* @__PURE__ */ jsxs(Box, { padding: 8, children: [
698
+ /* @__PURE__ */ jsxs(
699
+ Flex,
700
+ {
701
+ justifyContent: "space-between",
702
+ alignItems: "flex-start",
703
+ paddingBottom: 4,
704
+ wrap: "wrap",
705
+ gap: 4,
706
+ children: [
707
+ /* @__PURE__ */ jsxs(Box, { children: [
708
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: t(
709
+ "settings.ui.inbox_anti_spam_casework_settings",
710
+ "Inbox, Anti-Spam & Casework Settings"
711
+ ) }),
712
+ /* @__PURE__ */ jsx(Box, { paddingTop: 1, children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t(
713
+ "settings.ui.configure_multi_locale_forms_secondary_hijri_cal",
714
+ "Configure multi-locale forms, anti-spam protection, double opt-in subscribers, and Bookings / Certificates bridges"
715
+ ) }) }),
716
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, paddingTop: 3, wrap: "wrap", children: [
717
+ /* @__PURE__ */ jsxs(Badge, { children: [
718
+ t("settings.ui.locales_badge", "Locales"),
719
+ " (",
720
+ (settings.supportedLocales || []).length,
721
+ "):",
722
+ " ",
723
+ (settings.supportedLocales || []).join(", ")
724
+ ] }),
725
+ /* @__PURE__ */ jsxs(Badge, { children: [
726
+ t("settings.ui.captcha_badge", "CAPTCHA"),
727
+ ":",
728
+ " ",
729
+ settings.defaultCaptchaProvider || "none"
730
+ ] }),
731
+ /* @__PURE__ */ jsxs(Badge, { children: [
732
+ t(
733
+ "settings.ui.form_builder_bridge_badge",
734
+ "Form Builder Bridge"
735
+ ),
736
+ ":",
737
+ " ",
738
+ settings.enableFormBuilderBridge !== false ? t("common.enabled", "Enabled") : t("common.disabled", "Disabled")
739
+ ] }),
740
+ /* @__PURE__ */ jsxs(Badge, { children: [
741
+ t("settings.ui.double_opt_in_label", "Double Opt-In"),
742
+ ":",
743
+ " ",
744
+ settings.enableDoubleOptIn ? t("common.enabled", "Enabled") : t("common.disabled", "Disabled")
745
+ ] })
746
+ ] })
747
+ ] }),
748
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
749
+ /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: handleResetDefaults, children: t("settings.ui.reset_to_defaults", "Reset to Defaults") }),
750
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, children: t("settings.ui.save_settings", "Save Settings") })
751
+ ] })
752
+ ]
753
+ }
754
+ ),
755
+ savedNotice ? /* @__PURE__ */ jsx(Box, { padding: 3, marginBottom: 4, background: "success100", hasRadius: true, children: /* @__PURE__ */ jsx(Typography, { textColor: "success700", fontWeight: "semiBold", children: savedNotice }) }) : null,
756
+ captchaTestResult ? /* @__PURE__ */ jsx(
757
+ Box,
758
+ {
759
+ padding: 3,
760
+ marginBottom: 4,
761
+ background: captchaTestResult.ok ? "success100" : "danger100",
762
+ hasRadius: true,
763
+ children: /* @__PURE__ */ jsx(
764
+ Typography,
765
+ {
766
+ textColor: captchaTestResult.ok ? "success700" : "danger700",
767
+ fontWeight: "semiBold",
768
+ children: captchaTestResult.message
769
+ }
770
+ )
771
+ }
772
+ ) : null,
773
+ /* @__PURE__ */ jsx(Divider, {}),
774
+ /* @__PURE__ */ jsx(Box, { paddingTop: 6, children: /* @__PURE__ */ jsxs(Tabs.Root, { defaultValue: "routing", children: [
775
+ /* @__PURE__ */ jsxs(
776
+ Tabs.List,
777
+ {
778
+ "aria-label": t(
779
+ "settings.ui.inbox_anti_spam_casework_settings",
780
+ "Inbox Settings"
781
+ ),
782
+ children: [
783
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "routing", children: t("settings.tab.routing", "Localization & Routing") }),
784
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "antispam", children: t("settings.tab.antispam", "Anti-Spam Guard & CAPTCHA") }),
785
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "bridges", children: t("settings.tab.bridges", "Double Opt-In & Ecosystem Bridges") })
786
+ ]
787
+ }
788
+ ),
789
+ /* @__PURE__ */ jsx(
790
+ LocalizationRoutingTab,
791
+ {
792
+ t,
793
+ settings,
794
+ setSettings
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsx(
798
+ AntiSpamCaptchaTab,
799
+ {
800
+ t,
801
+ settings,
802
+ setSettings,
803
+ onTestCaptcha: handleTestCaptcha
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsx(
807
+ BridgesNotificationsTab,
808
+ {
809
+ t,
810
+ settings,
811
+ setSettings
812
+ }
813
+ )
814
+ ] }) })
815
+ ] }) });
816
+ };
817
+ export {
818
+ SettingsPage
819
+ };