@infuro/cms-core 1.0.59 → 1.0.60

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 (33) hide show
  1. package/dist/admin.cjs +442 -37
  2. package/dist/admin.js +443 -38
  3. package/dist/api.cjs +38 -38
  4. package/dist/api.js +5 -5
  5. package/dist/auth.cjs +51 -51
  6. package/dist/auth.js +3 -3
  7. package/dist/{chunk-3EOM4V2M.cjs → chunk-4U6DATGZ.cjs} +7 -7
  8. package/dist/{chunk-BNKZNLEX.js → chunk-6DBDJ4VD.js} +2 -2
  9. package/dist/{chunk-UQWZUZ5X.cjs → chunk-73WVHINE.cjs} +30 -6
  10. package/dist/{chunk-UCKN4BBY.cjs → chunk-77I3KS7V.cjs} +4 -1
  11. package/dist/{chunk-MCCA7WLQ.cjs → chunk-BSLNXKV2.cjs} +282 -128
  12. package/dist/{chunk-77NUXO6A.cjs → chunk-J7FMZK66.cjs} +10 -2
  13. package/dist/{chunk-BTUEUA5H.js → chunk-JAEUYYGA.js} +202 -48
  14. package/dist/{chunk-BC22I7C7.cjs → chunk-KM2I6AFP.cjs} +9 -9
  15. package/dist/{chunk-JIWUVQ6B.js → chunk-MVHDC3XF.js} +10 -2
  16. package/dist/{chunk-RK5ETF2I.js → chunk-NR44CK5E.js} +1 -1
  17. package/dist/{chunk-MXIWUFBP.js → chunk-O2642WVS.js} +28 -4
  18. package/dist/{chunk-HCIRL37O.js → chunk-T3HPSF7K.js} +4 -1
  19. package/dist/{chunk-HYW3MUXT.js → chunk-URN7GS23.js} +30 -16
  20. package/dist/{chunk-HXLC56ZK.cjs → chunk-YSYX4GUD.cjs} +32 -18
  21. package/dist/{emit-order-notification-trigger-HZQPPSFB.js → emit-order-notification-trigger-CZU3V2WE.js} +2 -2
  22. package/dist/{emit-order-notification-trigger-NR3VN6C6.cjs → emit-order-notification-trigger-TNEZOYEE.cjs} +4 -4
  23. package/dist/index.cjs +339 -301
  24. package/dist/index.d.cts +31 -0
  25. package/dist/index.d.ts +31 -0
  26. package/dist/index.js +54 -16
  27. package/dist/migrations/1782800000000-AddPaymentSuccessFailedRefundTriggers.ts +32 -0
  28. package/dist/migrations/1782900000000-AddEnableTurnstileToForms.ts +29 -0
  29. package/dist/{order-notification-dispatcher-2FCZFZUD.cjs → order-notification-dispatcher-KJVGLK5E.cjs} +5 -5
  30. package/dist/{order-notification-dispatcher-64WDCHHG.js → order-notification-dispatcher-ZSFKEYMP.js} +2 -2
  31. package/dist/{rbac-debug-db-6EMIVMGF.js → rbac-debug-db-5R6XFPSV.js} +1 -1
  32. package/dist/{rbac-debug-db-6PWTLBEL.cjs → rbac-debug-db-JKK2MCWF.cjs} +2 -2
  33. package/package.json +1 -1
package/dist/admin.cjs CHANGED
@@ -361,7 +361,15 @@ var init_vendor_scope = __esm({
361
361
  ...VENDOR_SCOPED_STORE_ENTITIES,
362
362
  "order_discounts",
363
363
  "order_addresses",
364
- "event_products"
364
+ "event_products",
365
+ "attendees",
366
+ "order-attendees",
367
+ "order_attendees",
368
+ "scan",
369
+ "verify-attendee-qr",
370
+ "verify_attendee_qr",
371
+ "forms",
372
+ "cms-forms"
365
373
  ]);
366
374
  __name(isVendorGroupName, "isVendorGroupName");
367
375
  __name(isPlatformAdministrator, "isPlatformAdministrator");
@@ -887,7 +895,7 @@ var init_admin_config_context = __esm({
887
895
  var CMS_VERSION;
888
896
  var init_cms_version = __esm({
889
897
  "src/lib/cms-version.ts"() {
890
- CMS_VERSION = "1.0.59" ;
898
+ CMS_VERSION = "1.0.60" ;
891
899
  }
892
900
  });
893
901
  function useCatalogCategories(enabled = true) {
@@ -1609,6 +1617,15 @@ var init_builtin_plugin_descriptors = __esm({
1609
1617
  enabled: true,
1610
1618
  icon: "auth_providers",
1611
1619
  settingsGroup: "auth_providers"
1620
+ },
1621
+ {
1622
+ name: "turnstile",
1623
+ label: "Cloudflare Turnstile",
1624
+ version: "1.0.0",
1625
+ description: "Protect forms, registration, and authentication from spam and bots with Cloudflare Turnstile.",
1626
+ enabled: true,
1627
+ icon: "turnstile",
1628
+ settingsGroup: "turnstile"
1612
1629
  }
1613
1630
  ];
1614
1631
  }
@@ -6343,6 +6360,7 @@ function FormBuilder({ formId, duplicateFromId }) {
6343
6360
  campaign: "",
6344
6361
  slug: "",
6345
6362
  published: false,
6363
+ enableTurnstile: false,
6346
6364
  fields: []
6347
6365
  });
6348
6366
  const [errors, setErrors] = React25.useState([]);
@@ -6376,6 +6394,7 @@ function FormBuilder({ formId, duplicateFromId }) {
6376
6394
  campaign: data.campaign || "",
6377
6395
  slug: slugNext,
6378
6396
  published: isDup ? false : data.published,
6397
+ enableTurnstile: Boolean(data.enableTurnstile ?? data.enable_turnstile),
6379
6398
  fields: (data.fields ?? []).map((field, index) => ({
6380
6399
  ...field,
6381
6400
  id: isDup ? `field_dup_${Date.now()}_${index}` : field?.id != null ? String(field.id) : `field_${Date.now()}_${index}`,
@@ -6684,7 +6703,21 @@ function FormBuilder({ formId, duplicateFromId }) {
6684
6703
  })), "onCheckedChange")
6685
6704
  }), /* @__PURE__ */ React.createElement("label", {
6686
6705
  className: "text-sm font-medium text-gray-700"
6687
- }, "Published"))))), /* @__PURE__ */ React.createElement("div", {
6706
+ }, "Published")), /* @__PURE__ */ React.createElement("div", {
6707
+ className: "flex items-center space-x-2 pt-2 border-t border-gray-100"
6708
+ }, /* @__PURE__ */ React.createElement(Switch, {
6709
+ checked: formData.enableTurnstile ?? false,
6710
+ onCheckedChange: /* @__PURE__ */ __name((checked) => setFormData((prev) => ({
6711
+ ...prev,
6712
+ enableTurnstile: checked
6713
+ })), "onCheckedChange")
6714
+ }), /* @__PURE__ */ React.createElement("div", {
6715
+ className: "flex flex-col"
6716
+ }, /* @__PURE__ */ React.createElement("label", {
6717
+ className: "text-sm font-medium text-gray-700"
6718
+ }, "Enable Cloudflare Turnstile"), /* @__PURE__ */ React.createElement("span", {
6719
+ className: "text-xs text-gray-500"
6720
+ }, "Requires user verification before submission (when Turnstile plugin is active)")))))), /* @__PURE__ */ React.createElement("div", {
6688
6721
  className: "lg:col-span-2"
6689
6722
  }, /* @__PURE__ */ React.createElement("div", {
6690
6723
  className: "bg-white rounded-lg shadow"
@@ -16177,6 +16210,7 @@ var init_order_notification_bindings_shared = __esm({
16177
16210
  "orderNumber",
16178
16211
  "customerName",
16179
16212
  "customerPhone",
16213
+ "customerEmail",
16180
16214
  "eventName",
16181
16215
  "vendorName",
16182
16216
  "total",
@@ -16184,7 +16218,8 @@ var init_order_notification_bindings_shared = __esm({
16184
16218
  "vendorId",
16185
16219
  "refundAmount",
16186
16220
  "invoiceNumber",
16187
- "invoiceUrl"
16221
+ "invoiceAttachment",
16222
+ "orderDetails"
16188
16223
  ];
16189
16224
  __name(useNotificationVariables, "useNotificationVariables");
16190
16225
  EMAIL_TRIGGER_DEFAULTS = {
@@ -16216,6 +16251,43 @@ If you have any questions, please contact us.
16216
16251
 
16217
16252
  Thank you!`,
16218
16253
  bodyPlaceholder: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been cancelled.\n\nIf a refund is due, it will be returned to your original payment method."
16254
+ },
16255
+ payment_success: {
16256
+ subject: "Payment Successful for Order {{orderNumber}}",
16257
+ subjectPlaceholder: "e.g. Payment Successful for Order {{orderNumber}}",
16258
+ body: `Hi {{customerName}},
16259
+
16260
+ We have received your payment for order {{orderNumber}} of {{total}} {{currency}}.
16261
+
16262
+ Order Details:
16263
+ {{orderDetails}}
16264
+
16265
+ Thank you!`,
16266
+ bodyPlaceholder: "Hi {{customerName}},\n\nPayment for order {{orderNumber}} was successful.\n\nThank you!"
16267
+ },
16268
+ payment_failed: {
16269
+ subject: "Payment Failed for Order {{orderNumber}}",
16270
+ subjectPlaceholder: "e.g. Payment Failed for Order {{orderNumber}}",
16271
+ body: `Hi {{customerName}},
16272
+
16273
+ Your payment for order {{orderNumber}} of {{total}} {{currency}} could not be completed or remains unpaid.
16274
+
16275
+ If you were charged, please contact us with your order number {{orderNumber}}.
16276
+
16277
+ Thank you!`,
16278
+ bodyPlaceholder: "Hi {{customerName}},\n\nPayment for order {{orderNumber}} failed or remains unpaid.\n\nThank you!"
16279
+ },
16280
+ payment_refund_initiated: {
16281
+ subject: "Refund Initiated for Order {{orderNumber}}",
16282
+ subjectPlaceholder: "e.g. Refund Initiated for Order {{orderNumber}}",
16283
+ body: `Hi {{customerName}},
16284
+
16285
+ Your order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated.
16286
+
16287
+ The amount will be credited back to your original payment method.
16288
+
16289
+ Thank you!`,
16290
+ bodyPlaceholder: "Hi {{customerName}},\n\nRefund of {{refundAmount}} {{currency}} for order {{orderNumber}} has been initiated."
16219
16291
  }
16220
16292
  };
16221
16293
  VENDOR_EMAIL_TRIGGER_DEFAULTS = {
@@ -16240,12 +16312,48 @@ Thank you!`,
16240
16312
 
16241
16313
  Order {{orderNumber}} for {{total}} {{currency}} has been cancelled.
16242
16314
 
16243
- Customer: {{customerName}
16315
+ Customer: {{customerName}}
16244
16316
  Order Items:
16245
16317
  {{orderDetails}}
16246
16318
 
16247
16319
  Thank you.`,
16248
16320
  bodyPlaceholder: "Hi {{vendorName}},\n\nOrder {{orderNumber}} was cancelled."
16321
+ },
16322
+ payment_success: {
16323
+ subject: "Payment Confirmed for Order {{orderNumber}} - {{customerName}}",
16324
+ subjectPlaceholder: "e.g. Payment Confirmed for Order {{orderNumber}}",
16325
+ body: `Hi {{vendorName}},
16326
+
16327
+ Payment has been confirmed for order {{orderNumber}} ({{total}} {{currency}}).
16328
+
16329
+ Customer: {{customerName}}
16330
+ Order Items:
16331
+ {{orderDetails}}
16332
+
16333
+ Thank you!`,
16334
+ bodyPlaceholder: "Hi {{vendorName}},\n\nPayment for order {{orderNumber}} confirmed."
16335
+ },
16336
+ payment_failed: {
16337
+ subject: "Payment Unpaid / Failed for Order {{orderNumber}}",
16338
+ subjectPlaceholder: "e.g. Payment Unpaid for Order {{orderNumber}}",
16339
+ body: `Hi {{vendorName}},
16340
+
16341
+ Payment for order {{orderNumber}} ({{total}} {{currency}}) is unpaid or failed.
16342
+
16343
+ Customer: {{customerName}}
16344
+
16345
+ Thank you.`,
16346
+ bodyPlaceholder: "Hi {{vendorName}},\n\nPayment for order {{orderNumber}} failed or unpaid."
16347
+ },
16348
+ payment_refund_initiated: {
16349
+ subject: "Refund Initiated for Order {{orderNumber}}",
16350
+ subjectPlaceholder: "e.g. Refund Initiated for Order {{orderNumber}}",
16351
+ body: `Hi {{vendorName}},
16352
+
16353
+ Order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated for {{customerName}}.
16354
+
16355
+ Thank you.`,
16356
+ bodyPlaceholder: "Hi {{vendorName}},\n\nRefund initiated for order {{orderNumber}}."
16249
16357
  }
16250
16358
  };
16251
16359
  ADMIN_EMAIL_TRIGGER_DEFAULTS = {
@@ -16277,6 +16385,42 @@ Customer: {{customerName}}
16277
16385
  Vendor: {{vendorName}}
16278
16386
  Refund Due: {{refundAmount}} {{currency}}`,
16279
16387
  bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16388
+ },
16389
+ payment_success: {
16390
+ subject: "Payment Success Alert: Order {{orderNumber}} ({{total}} {{currency}})",
16391
+ subjectPlaceholder: "e.g. Payment Success Alert: Order {{orderNumber}}",
16392
+ body: `Hi Admin,
16393
+
16394
+ Payment of {{total}} {{currency}} was successfully captured for order {{orderNumber}}.
16395
+
16396
+ Customer: {{customerName}}
16397
+ Vendor: {{vendorName}}
16398
+
16399
+ Order Summary:
16400
+ {{orderDetails}}`,
16401
+ bodyPlaceholder: "Payment received for order {{orderNumber}}."
16402
+ },
16403
+ payment_failed: {
16404
+ subject: "Payment Failed / Unpaid Alert: Order {{orderNumber}}",
16405
+ subjectPlaceholder: "e.g. Payment Failed Alert: Order {{orderNumber}}",
16406
+ body: `Hi Admin,
16407
+
16408
+ Payment for order {{orderNumber}} ({{total}} {{currency}}) is unpaid or failed.
16409
+
16410
+ Customer: {{customerName}}
16411
+ Vendor: {{vendorName}}`,
16412
+ bodyPlaceholder: "Payment failed/unpaid for order {{orderNumber}}."
16413
+ },
16414
+ payment_refund_initiated: {
16415
+ subject: "Refund Initiated Alert: Order {{orderNumber}}",
16416
+ subjectPlaceholder: "e.g. Refund Initiated Alert: Order {{orderNumber}}",
16417
+ body: `Hi Admin,
16418
+
16419
+ Order {{orderNumber}} was cancelled and refund of {{refundAmount}} {{currency}} was initiated.
16420
+
16421
+ Customer: {{customerName}}
16422
+ Vendor: {{vendorName}}`,
16423
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16280
16424
  }
16281
16425
  };
16282
16426
  __name(getEmailTriggerDefaults, "getEmailTriggerDefaults");
@@ -16292,6 +16436,24 @@ Refund Due: {{refundAmount}} {{currency}}`,
16292
16436
  subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16293
16437
  body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} has been cancelled.`,
16294
16438
  bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} has been cancelled."
16439
+ },
16440
+ payment_success: {
16441
+ subject: "Payment Successful!",
16442
+ subjectPlaceholder: "e.g. Payment Successful!",
16443
+ body: `Hi {{customerName}}, payment for order {{orderNumber}} ({{total}} {{currency}}) was successful.`,
16444
+ bodyPlaceholder: "Payment for order {{orderNumber}} successful."
16445
+ },
16446
+ payment_failed: {
16447
+ subject: "Payment Unpaid / Failed",
16448
+ subjectPlaceholder: "e.g. Payment Unpaid / Failed",
16449
+ body: `Hi {{customerName}}, payment for order {{orderNumber}} failed or remains unpaid.`,
16450
+ bodyPlaceholder: "Payment for order {{orderNumber}} failed."
16451
+ },
16452
+ payment_refund_initiated: {
16453
+ subject: "Refund Initiated",
16454
+ subjectPlaceholder: "e.g. Refund Initiated",
16455
+ body: `Hi {{customerName}}, a refund of {{refundAmount}} {{currency}} for order {{orderNumber}} has been initiated.`,
16456
+ bodyPlaceholder: "Refund for order {{orderNumber}} initiated."
16295
16457
  }
16296
16458
  };
16297
16459
  VENDOR_PUSH_TRIGGER_DEFAULTS = {
@@ -16306,6 +16468,24 @@ Refund Due: {{refundAmount}} {{currency}}`,
16306
16468
  subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16307
16469
  body: `Hi {{vendorName}}, order {{orderNumber}} from {{customerName}} has been cancelled.`,
16308
16470
  bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} was cancelled."
16471
+ },
16472
+ payment_success: {
16473
+ subject: "Payment Confirmed: {{orderNumber}}",
16474
+ subjectPlaceholder: "e.g. Payment Confirmed: {{orderNumber}}",
16475
+ body: `Hi {{vendorName}}, payment for order {{orderNumber}} from {{customerName}} has been confirmed.`,
16476
+ bodyPlaceholder: "Payment for order {{orderNumber}} confirmed."
16477
+ },
16478
+ payment_failed: {
16479
+ subject: "Payment Unpaid: {{orderNumber}}",
16480
+ subjectPlaceholder: "e.g. Payment Unpaid: {{orderNumber}}",
16481
+ body: `Hi {{vendorName}}, payment for order {{orderNumber}} from {{customerName}} is unpaid or failed.`,
16482
+ bodyPlaceholder: "Payment for order {{orderNumber}} unpaid."
16483
+ },
16484
+ payment_refund_initiated: {
16485
+ subject: "Refund Initiated: {{orderNumber}}",
16486
+ subjectPlaceholder: "e.g. Refund Initiated: {{orderNumber}}",
16487
+ body: `Hi {{vendorName}}, refund initiated for order {{orderNumber}} ({{customerName}}).`,
16488
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16309
16489
  }
16310
16490
  };
16311
16491
  ADMIN_PUSH_TRIGGER_DEFAULTS = {
@@ -16337,6 +16517,24 @@ Customer: {{customerName}}
16337
16517
  Vendor: {{vendorName}}
16338
16518
  Refund Due: {{refundAmount}} {{currency}}`,
16339
16519
  bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16520
+ },
16521
+ payment_success: {
16522
+ subject: "Payment Success: {{orderNumber}}",
16523
+ subjectPlaceholder: "e.g. Payment Success: {{orderNumber}}",
16524
+ body: `Payment of {{total}} {{currency}} captured for order {{orderNumber}} ({{customerName}}).`,
16525
+ bodyPlaceholder: "Payment captured for order {{orderNumber}}."
16526
+ },
16527
+ payment_failed: {
16528
+ subject: "Payment Failed/Unpaid: {{orderNumber}}",
16529
+ subjectPlaceholder: "e.g. Payment Failed: {{orderNumber}}",
16530
+ body: `Payment for order {{orderNumber}} ({{customerName}}) is unpaid or failed.`,
16531
+ bodyPlaceholder: "Payment failed for order {{orderNumber}}."
16532
+ },
16533
+ payment_refund_initiated: {
16534
+ subject: "Refund Initiated: {{orderNumber}}",
16535
+ subjectPlaceholder: "e.g. Refund Initiated: {{orderNumber}}",
16536
+ body: `Refund of {{refundAmount}} {{currency}} initiated for order {{orderNumber}}.`,
16537
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16340
16538
  }
16341
16539
  };
16342
16540
  __name(getPushTriggerDefaults, "getPushTriggerDefaults");
@@ -16990,6 +17188,7 @@ var init_emailTriggersBindingsPage = __esm({
16990
17188
  orderNumber: "Display order number",
16991
17189
  customerName: "Customer full name",
16992
17190
  customerPhone: "Customer phone",
17191
+ customerEmail: "Customer email address",
16993
17192
  eventName: "Event title / product name",
16994
17193
  vendorName: "Vendor / store name",
16995
17194
  total: "Order total amount",
@@ -16997,7 +17196,8 @@ var init_emailTriggersBindingsPage = __esm({
16997
17196
  vendorId: "Vendor / store ID",
16998
17197
  refundAmount: "Refund amount (refund triggers)",
16999
17198
  invoiceNumber: "Generated invoice number",
17000
- invoiceUrl: "Link to download invoice PDF"
17199
+ invoiceAttachment: "Include this variable to attach Tax Invoice PDF to email",
17200
+ orderDetails: "Formatted item details summary"
17001
17201
  };
17002
17202
  __name(EmailTriggerBindings, "EmailTriggerBindings");
17003
17203
  __name(TriggerEmailEditor, "TriggerEmailEditor");
@@ -17295,6 +17495,7 @@ var init_pushTriggersBindingsPage = __esm({
17295
17495
  orderNumber: "Display order number",
17296
17496
  customerName: "Customer full name",
17297
17497
  customerPhone: "Customer phone",
17498
+ customerEmail: "Customer email address",
17298
17499
  eventName: "Event title / product name",
17299
17500
  vendorName: "Vendor / store name",
17300
17501
  total: "Order total amount",
@@ -17302,7 +17503,8 @@ var init_pushTriggersBindingsPage = __esm({
17302
17503
  vendorId: "Vendor / store ID",
17303
17504
  refundAmount: "Refund amount (refund triggers)",
17304
17505
  invoiceNumber: "Generated invoice number",
17305
- invoiceUrl: "Link to download invoice PDF"
17506
+ invoiceAttachment: "Include this variable to attach Tax Invoice PDF to email",
17507
+ orderDetails: "Formatted item details summary"
17306
17508
  };
17307
17509
  __name(PushTriggerBindings, "PushTriggerBindings");
17308
17510
  }
@@ -18140,6 +18342,177 @@ var init_AuthProvidersPluginSettings = __esm({
18140
18342
  __name(AuthProvidersPluginSettings, "AuthProvidersPluginSettings");
18141
18343
  }
18142
18344
  });
18345
+ function TurnstilePluginSettings({ settingsGroup, onEnabledChange }) {
18346
+ const [loading, setLoading] = React25.useState(true);
18347
+ const [saving, setSaving] = React25.useState(false);
18348
+ const [enabled, setEnabled] = React25.useState(false);
18349
+ const [siteKey, setSiteKey] = React25.useState("");
18350
+ const [secretKey, setSecretKey] = React25.useState("");
18351
+ const [hasSavedSecret, setHasSavedSecret] = React25.useState(false);
18352
+ const [showSecret, setShowSecret] = React25.useState(false);
18353
+ const onEnabledChangeRef = React25.useRef(onEnabledChange);
18354
+ onEnabledChangeRef.current = onEnabledChange;
18355
+ React25.useEffect(() => {
18356
+ let cancelled = false;
18357
+ (async () => {
18358
+ setLoading(true);
18359
+ try {
18360
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
18361
+ cache: "no-store"
18362
+ });
18363
+ const data = res.ok ? await res.json() : {};
18364
+ if (cancelled) return;
18365
+ const isEnabled = data.enabled === "true" || data.enabled === "1";
18366
+ setEnabled(isEnabled);
18367
+ setSiteKey(String(data.siteKey ?? data.TURNSTILE_SITE_KEY ?? "").trim());
18368
+ const secret = String(data.secretKey ?? data.TURNSTILE_SECRET_KEY ?? "").trim();
18369
+ setSecretKey("");
18370
+ setHasSavedSecret(Boolean(secret));
18371
+ } catch {
18372
+ } finally {
18373
+ if (!cancelled) setLoading(false);
18374
+ }
18375
+ })();
18376
+ return () => {
18377
+ cancelled = true;
18378
+ };
18379
+ }, [
18380
+ settingsGroup
18381
+ ]);
18382
+ const handleSave = /* @__PURE__ */ __name(async () => {
18383
+ setSaving(true);
18384
+ try {
18385
+ const payload = {
18386
+ enabled: {
18387
+ value: enabled ? "true" : "false",
18388
+ type: "public"
18389
+ },
18390
+ siteKey: {
18391
+ value: siteKey.trim(),
18392
+ type: "public"
18393
+ }
18394
+ };
18395
+ if (secretKey.trim()) {
18396
+ payload.secretKey = {
18397
+ value: secretKey.trim(),
18398
+ type: "private",
18399
+ encrypted: true
18400
+ };
18401
+ }
18402
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
18403
+ method: "PUT",
18404
+ headers: {
18405
+ "Content-Type": "application/json"
18406
+ },
18407
+ body: JSON.stringify(payload)
18408
+ });
18409
+ if (res.ok) {
18410
+ sonner.toast.success("Turnstile settings saved");
18411
+ if (secretKey.trim()) {
18412
+ setHasSavedSecret(true);
18413
+ setSecretKey("");
18414
+ }
18415
+ onEnabledChangeRef.current?.(enabled);
18416
+ } else {
18417
+ const err = await res.json().catch(() => ({}));
18418
+ sonner.toast.error(err?.message || "Failed to save settings");
18419
+ }
18420
+ } catch {
18421
+ sonner.toast.error("Failed to save settings");
18422
+ } finally {
18423
+ setSaving(false);
18424
+ }
18425
+ }, "handleSave");
18426
+ if (loading) {
18427
+ return /* @__PURE__ */ React.createElement("div", {
18428
+ className: "flex items-center justify-center p-8 text-muted-foreground text-sm"
18429
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
18430
+ className: "mr-2 h-4 w-4 animate-spin"
18431
+ }), "Loading settings\u2026");
18432
+ }
18433
+ return /* @__PURE__ */ React.createElement("div", {
18434
+ className: "space-y-6"
18435
+ }, /* @__PURE__ */ React.createElement("div", {
18436
+ className: "rounded-lg border border-gray-200 bg-gray-50/80 p-4 dark:border-gray-700 dark:bg-gray-800/40"
18437
+ }, /* @__PURE__ */ React.createElement("div", {
18438
+ className: "flex items-center justify-between"
18439
+ }, /* @__PURE__ */ React.createElement("div", {
18440
+ className: "space-y-0.5"
18441
+ }, /* @__PURE__ */ React.createElement(Label3, {
18442
+ htmlFor: `${settingsGroup}-enabled`,
18443
+ className: "text-sm font-semibold text-gray-900 dark:text-white"
18444
+ }, "Enable Cloudflare Turnstile"), /* @__PURE__ */ React.createElement("p", {
18445
+ className: "text-xs text-muted-foreground"
18446
+ }, "Master switch for bot protection. When enabled, helps protect supported forms from automated bots, while forms can apply their configured bot protection settings.")), /* @__PURE__ */ React.createElement(Switch, {
18447
+ id: `${settingsGroup}-enabled`,
18448
+ checked: enabled,
18449
+ onCheckedChange: setEnabled
18450
+ }))), /* @__PURE__ */ React.createElement("div", {
18451
+ className: "space-y-4"
18452
+ }, /* @__PURE__ */ React.createElement("div", {
18453
+ className: "space-y-1.5"
18454
+ }, /* @__PURE__ */ React.createElement(Label3, {
18455
+ htmlFor: `${settingsGroup}-siteKey`,
18456
+ className: "text-sm font-medium"
18457
+ }, "Turnstile Site Key (Public)"), /* @__PURE__ */ React.createElement(Input, {
18458
+ id: `${settingsGroup}-siteKey`,
18459
+ value: siteKey,
18460
+ onChange: /* @__PURE__ */ __name((e) => setSiteKey(e.target.value), "onChange"),
18461
+ placeholder: "0x4AAAAAA...",
18462
+ className: "h-9 font-mono text-sm"
18463
+ }), /* @__PURE__ */ React.createElement("p", {
18464
+ className: "text-xs text-muted-foreground"
18465
+ }, "Obtained from your Cloudflare dashboard under Turnstile Sites. Safe to expose to the frontend.")), /* @__PURE__ */ React.createElement("div", {
18466
+ className: "space-y-1.5"
18467
+ }, /* @__PURE__ */ React.createElement("div", {
18468
+ className: "flex items-center justify-between"
18469
+ }, /* @__PURE__ */ React.createElement(Label3, {
18470
+ htmlFor: `${settingsGroup}-secretKey`,
18471
+ className: "text-sm font-medium"
18472
+ }, "Turnstile Secret Key (Private)"), hasSavedSecret && /* @__PURE__ */ React.createElement("span", {
18473
+ className: "inline-flex items-center gap-1 text-xs font-medium text-emerald-600 dark:text-emerald-400"
18474
+ }, /* @__PURE__ */ React.createElement(LucideIcons.ShieldCheck, {
18475
+ className: "h-3.5 w-3.5"
18476
+ }), "Secret key configured")), /* @__PURE__ */ React.createElement("div", {
18477
+ className: "relative"
18478
+ }, /* @__PURE__ */ React.createElement(Input, {
18479
+ id: `${settingsGroup}-secretKey`,
18480
+ type: showSecret ? "text" : "password",
18481
+ value: secretKey,
18482
+ onChange: /* @__PURE__ */ __name((e) => setSecretKey(e.target.value), "onChange"),
18483
+ placeholder: hasSavedSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (leave blank to keep unchanged)" : "0x4AAAAAA...",
18484
+ className: "h-9 font-mono text-sm pr-10"
18485
+ }), /* @__PURE__ */ React.createElement("button", {
18486
+ type: "button",
18487
+ className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200",
18488
+ onClick: /* @__PURE__ */ __name(() => setShowSecret(!showSecret), "onClick")
18489
+ }, showSecret ? /* @__PURE__ */ React.createElement(LucideIcons.EyeOff, {
18490
+ className: "h-4 w-4"
18491
+ }) : /* @__PURE__ */ React.createElement(LucideIcons.Eye, {
18492
+ className: "h-4 w-4"
18493
+ }))), /* @__PURE__ */ React.createElement("p", {
18494
+ className: "text-xs text-muted-foreground"
18495
+ }, "Used only on the server to verify tokens with Cloudflare Siteverify API. Never sent to browsers."))), /* @__PURE__ */ React.createElement(Button, {
18496
+ onClick: handleSave,
18497
+ disabled: saving,
18498
+ size: "sm",
18499
+ className: "gap-2"
18500
+ }, saving ? /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
18501
+ className: "h-4 w-4 animate-spin"
18502
+ }) : /* @__PURE__ */ React.createElement(LucideIcons.Save, {
18503
+ className: "h-4 w-4"
18504
+ }), "Save Settings"));
18505
+ }
18506
+ var init_TurnstilePluginSettings = __esm({
18507
+ "src/admin/components/TurnstilePluginSettings.tsx"() {
18508
+ "use client";
18509
+ init_button();
18510
+ init_input();
18511
+ init_label();
18512
+ init_switch();
18513
+ __name(TurnstilePluginSettings, "TurnstilePluginSettings");
18514
+ }
18515
+ });
18143
18516
 
18144
18517
  // src/plugins/llm/chat-email-intent.ts
18145
18518
  function normalizeIntentKey(raw) {
@@ -18518,6 +18891,7 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
18518
18891
  const isSocialMedia = settingsGroup === "social_media";
18519
18892
  const isMultiVendor = settingsGroup === "multi_vendor";
18520
18893
  const isAuthProviders = settingsGroup === "auth_providers";
18894
+ const isTurnstile = settingsGroup === "turnstile";
18521
18895
  const [enabled, setEnabled] = React25.useState(true);
18522
18896
  const [botName, setBotName] = React25.useState("");
18523
18897
  const [icon, setIcon] = React25.useState("");
@@ -19861,6 +20235,15 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
19861
20235
  }, "onEnabledChange")
19862
20236
  });
19863
20237
  }
20238
+ if (isTurnstile) {
20239
+ return /* @__PURE__ */ React.createElement(TurnstilePluginSettings, {
20240
+ settingsGroup,
20241
+ onEnabledChange: /* @__PURE__ */ __name((value) => {
20242
+ setEnabled(value);
20243
+ onSaved?.();
20244
+ }, "onEnabledChange")
20245
+ });
20246
+ }
19864
20247
  if (loading) return /* @__PURE__ */ React.createElement("div", {
19865
20248
  className: "text-sm text-gray-500 dark:text-gray-400"
19866
20249
  }, "Loading...");
@@ -21201,6 +21584,10 @@ function PluginsPage() {
21201
21584
  const ap = pluginDescriptors.find((p) => p.name === "auth_providers");
21202
21585
  if (ap) setSelectedName(ap.name);
21203
21586
  }
21587
+ if (q === "turnstile" || q === "cloudflare_turnstile" || q === "captcha") {
21588
+ const ts = pluginDescriptors.find((p) => p.name === "turnstile");
21589
+ if (ts) setSelectedName(ts.name);
21590
+ }
21204
21591
  }, [
21205
21592
  searchParams,
21206
21593
  pluginDescriptors
@@ -21313,6 +21700,7 @@ var init_PluginsPage = __esm({
21313
21700
  init_select();
21314
21701
  init_EventNotificationsPluginSettings();
21315
21702
  init_AuthProvidersPluginSettings();
21703
+ init_TurnstilePluginSettings();
21316
21704
  init_ImageOrUrlField();
21317
21705
  init_chat_email_intent();
21318
21706
  init_llm_agent_scope();
@@ -21332,7 +21720,8 @@ var init_PluginsPage = __esm({
21332
21720
  share: LucideIcons.Share2,
21333
21721
  whatsapp: LucideIcons.MessageCircle,
21334
21722
  event_notifications: LucideIcons.Bell,
21335
- auth_providers: LucideIcons.KeyRound
21723
+ auth_providers: LucideIcons.KeyRound,
21724
+ turnstile: LucideIcons.ShieldCheck
21336
21725
  };
21337
21726
  __name(normalizeSmsProviderChoice, "normalizeSmsProviderChoice");
21338
21727
  __name(normalizeMsg91ApiMode, "normalizeMsg91ApiMode");
@@ -22294,7 +22683,7 @@ function VendorTeamPage() {
22294
22683
  }
22295
22684
  }, []);
22296
22685
  React25.useEffect(() => {
22297
- if (canManage) load();
22686
+ if (canManage) void load();
22298
22687
  else setLoading(false);
22299
22688
  }, [
22300
22689
  canManage,
@@ -22352,10 +22741,18 @@ function VendorTeamPage() {
22352
22741
  }, "closeInviteDialog");
22353
22742
  if (!canManage) {
22354
22743
  return /* @__PURE__ */ React25__namespace.default.createElement("div", {
22355
- className: "p-6 max-w-lg"
22744
+ className: "min-w-0 rounded-lg bg-white shadow-md"
22745
+ }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
22746
+ className: "rounded-t-lg border-b border-gray-700 bg-gray-800 px-4 py-2.5"
22747
+ }, /* @__PURE__ */ React25__namespace.default.createElement("h1", {
22748
+ className: "text-base font-semibold text-white"
22749
+ }, "Vendor Team"), /* @__PURE__ */ React25__namespace.default.createElement("p", {
22750
+ className: "mt-0.5 text-xs text-gray-300"
22751
+ }, "Invite members and assign a store role")), /* @__PURE__ */ React25__namespace.default.createElement("div", {
22752
+ className: "p-6"
22356
22753
  }, /* @__PURE__ */ React25__namespace.default.createElement("p", {
22357
22754
  className: "text-sm text-red-600"
22358
- }, "Only the store owner can manage the vendor team."));
22755
+ }, "Only the store owner can manage the vendor team.")));
22359
22756
  }
22360
22757
  const handleAdd = /* @__PURE__ */ __name(async (e) => {
22361
22758
  e.preventDefault();
@@ -22457,17 +22854,21 @@ function VendorTeamPage() {
22457
22854
  }
22458
22855
  }, "handleRemove");
22459
22856
  return /* @__PURE__ */ React25__namespace.default.createElement("div", {
22460
- className: "p-6 max-w-3xl"
22857
+ className: "min-w-0 rounded-lg bg-white shadow-md"
22858
+ }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
22859
+ className: "rounded-t-lg border-b border-gray-700 bg-gray-800 px-4 py-2.5"
22461
22860
  }, /* @__PURE__ */ React25__namespace.default.createElement("h1", {
22462
- className: "text-xl font-semibold text-gray-900"
22861
+ className: "text-base font-semibold text-white"
22463
22862
  }, "Vendor Team"), /* @__PURE__ */ React25__namespace.default.createElement("p", {
22464
- className: "mt-1 text-sm text-gray-600"
22465
- }, "Invite member and assign a store role."), /* @__PURE__ */ React25__namespace.default.createElement("div", {
22466
- className: "mt-6 rounded-lg border border-gray-200 overflow-hidden"
22863
+ className: "mt-0.5 text-xs text-gray-300"
22864
+ }, "Invite member and assign a store role")), /* @__PURE__ */ React25__namespace.default.createElement("div", {
22865
+ className: "p-6 space-y-6"
22866
+ }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
22867
+ className: "rounded-lg border border-gray-200 overflow-hidden"
22467
22868
  }, /* @__PURE__ */ React25__namespace.default.createElement("table", {
22468
22869
  className: "min-w-full text-sm"
22469
22870
  }, /* @__PURE__ */ React25__namespace.default.createElement("thead", {
22470
- className: "bg-gray-50 text-left text-gray-600"
22871
+ className: "bg-gray-50 text-left text-gray-600 border-b border-gray-200"
22471
22872
  }, /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("th", {
22472
22873
  className: "px-3 py-2 font-medium"
22473
22874
  }, "Name"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
@@ -22477,8 +22878,10 @@ function VendorTeamPage() {
22477
22878
  }, "Phone"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
22478
22879
  className: "px-3 py-2 font-medium"
22479
22880
  }, "Store role"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
22480
- className: "px-3 py-2 font-medium"
22481
- }))), /* @__PURE__ */ React25__namespace.default.createElement("tbody", null, loading ? /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("td", {
22881
+ className: "px-3 py-2 font-medium text-right"
22882
+ }))), /* @__PURE__ */ React25__namespace.default.createElement("tbody", {
22883
+ className: "divide-y divide-gray-100"
22884
+ }, loading ? /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("td", {
22482
22885
  colSpan: 5,
22483
22886
  className: "px-3 py-4 text-gray-500"
22484
22887
  }, "Loading\u2026")) : members.length === 0 ? /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("td", {
@@ -22488,15 +22891,17 @@ function VendorTeamPage() {
22488
22891
  key: m.vendorUserId,
22489
22892
  className: "border-t border-gray-100"
22490
22893
  }, /* @__PURE__ */ React25__namespace.default.createElement("td", {
22491
- className: "px-3 py-2"
22894
+ className: "px-3 py-2 font-medium text-gray-900"
22492
22895
  }, m.name ?? "\u2014"), /* @__PURE__ */ React25__namespace.default.createElement("td", {
22493
- className: "px-3 py-2"
22896
+ className: "px-3 py-2 text-gray-600"
22494
22897
  }, m.email), /* @__PURE__ */ React25__namespace.default.createElement("td", {
22495
- className: "px-3 py-2"
22898
+ className: "px-3 py-2 text-gray-600"
22496
22899
  }, m.phone ?? "\u2014"), /* @__PURE__ */ React25__namespace.default.createElement("td", {
22497
22900
  className: "px-3 py-2"
22498
- }, m.isOwnerRole ? /* @__PURE__ */ React25__namespace.default.createElement("span", null, m.vendorRoleName ?? "Owner") : /* @__PURE__ */ React25__namespace.default.createElement("select", {
22499
- className: "rounded border border-gray-200 px-2 py-1 text-sm",
22901
+ }, m.isOwnerRole ? /* @__PURE__ */ React25__namespace.default.createElement("span", {
22902
+ className: "text-gray-900 font-medium"
22903
+ }, m.vendorRoleName ?? "Owner") : /* @__PURE__ */ React25__namespace.default.createElement("select", {
22904
+ className: "rounded border border-gray-200 px-2 py-1 text-sm bg-white",
22500
22905
  value: m.vendorRoleId ?? "",
22501
22906
  disabled: roleUpdating === m.userId,
22502
22907
  onChange: /* @__PURE__ */ __name((e) => handleRoleChange(m.userId, Number(e.target.value)), "onChange")
@@ -22512,7 +22917,7 @@ function VendorTeamPage() {
22512
22917
  onClick: /* @__PURE__ */ __name(() => handleRemove(m.userId), "onClick")
22513
22918
  }, "Remove"))))))), /* @__PURE__ */ React25__namespace.default.createElement("form", {
22514
22919
  onSubmit: handleAdd,
22515
- className: "mt-8 space-y-4 rounded-lg border border-gray-200 p-4"
22920
+ className: "space-y-4 rounded-lg border border-gray-200 p-4"
22516
22921
  }, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
22517
22922
  className: "text-sm font-medium text-gray-900"
22518
22923
  }, "Invite team member"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
@@ -22546,7 +22951,7 @@ function VendorTeamPage() {
22546
22951
  htmlFor: "teamRole"
22547
22952
  }, "Store role"), /* @__PURE__ */ React25__namespace.default.createElement("select", {
22548
22953
  id: "teamRole",
22549
- className: "mt-1 w-full rounded-md border border-gray-200 px-3 py-2 text-sm",
22954
+ className: "mt-1 w-full rounded-md border border-gray-200 px-3 py-2 text-sm bg-white",
22550
22955
  value: vendorRoleId,
22551
22956
  onChange: /* @__PURE__ */ __name((e) => setVendorRoleId(e.target.value ? Number(e.target.value) : ""), "onChange")
22552
22957
  }, assignableRoles.map((r) => /* @__PURE__ */ React25__namespace.default.createElement("option", {
@@ -22585,7 +22990,7 @@ function VendorTeamPage() {
22585
22990
  }, message), /* @__PURE__ */ React25__namespace.default.createElement(Button, {
22586
22991
  type: "submit",
22587
22992
  disabled: submitting
22588
- }, submitting ? "Inviting\u2026" : "Invite member")), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
22993
+ }, submitting ? "Inviting\u2026" : "Invite member"))), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
22589
22994
  open: inviteDialog != null,
22590
22995
  onOpenChange: /* @__PURE__ */ __name((open) => {
22591
22996
  if (!open) closeInviteDialog();
@@ -22605,7 +23010,7 @@ function VendorTeamPage() {
22605
23010
  variant: "outline",
22606
23011
  onClick: /* @__PURE__ */ __name(() => void copyInviteLink(), "onClick")
22607
23012
  }, linkCopied ? /* @__PURE__ */ React25__namespace.default.createElement(LucideIcons.Check, {
22608
- className: "h-4 w-4 mr-2"
23013
+ className: "h-4 w-4 mr-2 text-green-600"
22609
23014
  }) : /* @__PURE__ */ React25__namespace.default.createElement(LucideIcons.Copy, {
22610
23015
  className: "h-4 w-4 mr-2"
22611
23016
  }), linkCopied ? "Copied" : "Copy invite link")), /* @__PURE__ */ React25__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React25__namespace.default.createElement(Button, {
@@ -30620,15 +31025,15 @@ function ProductEditPage({ productId }) {
30620
31025
  })));
30621
31026
  }
30622
31027
  }
30623
- if (!cancelled && formsRes.ok) {
30624
- const d = await formsRes.json();
30625
- if (Array.isArray(d.data)) {
30626
- setContactForms(d.data.filter((row) => row.id != null).map((row) => ({
30627
- id: Number(row.id),
30628
- name: row.name?.trim() || `Form #${row.id}`,
30629
- slug: row.slug
30630
- })));
30631
- }
31028
+ const formsResActual = formsRes.ok ? formsRes : await fetch("/api/admin/cms-forms/product-options").catch(() => null);
31029
+ if (!cancelled && formsResActual && formsResActual.ok) {
31030
+ const d = await formsResActual.json();
31031
+ const rows = Array.isArray(d.data) ? d.data : Array.isArray(d) ? d : [];
31032
+ setContactForms(rows.filter((row) => row.id != null || row.value != null).map((row) => ({
31033
+ id: Number(row.id ?? row.value),
31034
+ name: row.name?.trim() || row.label?.trim() || `Form #${row.id ?? row.value}`,
31035
+ slug: row.slug
31036
+ })));
30632
31037
  }
30633
31038
  if (!cancelled && refundRes.ok) {
30634
31039
  const d = await refundRes.json();