@infuro/cms-core 1.0.58 → 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 (35) hide show
  1. package/dist/admin.cjs +442 -37
  2. package/dist/admin.js +443 -38
  3. package/dist/api.cjs +41 -37
  4. package/dist/api.d.cts +2 -1
  5. package/dist/api.d.ts +2 -1
  6. package/dist/api.js +5 -5
  7. package/dist/auth.cjs +51 -51
  8. package/dist/auth.js +3 -3
  9. package/dist/{chunk-3EOM4V2M.cjs → chunk-4U6DATGZ.cjs} +7 -7
  10. package/dist/{chunk-BNKZNLEX.js → chunk-6DBDJ4VD.js} +2 -2
  11. package/dist/{chunk-UQWZUZ5X.cjs → chunk-73WVHINE.cjs} +30 -6
  12. package/dist/{chunk-UCKN4BBY.cjs → chunk-77I3KS7V.cjs} +4 -1
  13. package/dist/{chunk-BHUEXLD2.cjs → chunk-BSLNXKV2.cjs} +415 -131
  14. package/dist/{chunk-77NUXO6A.cjs → chunk-J7FMZK66.cjs} +10 -2
  15. package/dist/{chunk-AGU3B5JW.js → chunk-JAEUYYGA.js} +335 -52
  16. package/dist/{chunk-BC22I7C7.cjs → chunk-KM2I6AFP.cjs} +9 -9
  17. package/dist/{chunk-JIWUVQ6B.js → chunk-MVHDC3XF.js} +10 -2
  18. package/dist/{chunk-RK5ETF2I.js → chunk-NR44CK5E.js} +1 -1
  19. package/dist/{chunk-MXIWUFBP.js → chunk-O2642WVS.js} +28 -4
  20. package/dist/{chunk-HCIRL37O.js → chunk-T3HPSF7K.js} +4 -1
  21. package/dist/{chunk-HYW3MUXT.js → chunk-URN7GS23.js} +30 -16
  22. package/dist/{chunk-HXLC56ZK.cjs → chunk-YSYX4GUD.cjs} +32 -18
  23. package/dist/{emit-order-notification-trigger-HZQPPSFB.js → emit-order-notification-trigger-CZU3V2WE.js} +2 -2
  24. package/dist/{emit-order-notification-trigger-NR3VN6C6.cjs → emit-order-notification-trigger-TNEZOYEE.cjs} +4 -4
  25. package/dist/index.cjs +342 -300
  26. package/dist/index.d.cts +33 -1
  27. package/dist/index.d.ts +33 -1
  28. package/dist/index.js +54 -16
  29. package/dist/migrations/1782800000000-AddPaymentSuccessFailedRefundTriggers.ts +32 -0
  30. package/dist/migrations/1782900000000-AddEnableTurnstileToForms.ts +29 -0
  31. package/dist/{order-notification-dispatcher-2FCZFZUD.cjs → order-notification-dispatcher-KJVGLK5E.cjs} +5 -5
  32. package/dist/{order-notification-dispatcher-64WDCHHG.js → order-notification-dispatcher-ZSFKEYMP.js} +2 -2
  33. package/dist/{rbac-debug-db-6EMIVMGF.js → rbac-debug-db-5R6XFPSV.js} +1 -1
  34. package/dist/{rbac-debug-db-6PWTLBEL.cjs → rbac-debug-db-JKK2MCWF.cjs} +2 -2
  35. package/package.json +1 -1
package/dist/admin.js CHANGED
@@ -10,7 +10,7 @@ import { Slot } from '@radix-ui/react-slot';
10
10
  import { cva } from 'class-variance-authority';
11
11
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
12
12
  import * as LucideIcons from 'lucide-react';
13
- import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, Smartphone, ShieldCheck, QrCode, Car, Send, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, HardDrive, Folder, Film } from 'lucide-react';
13
+ import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, ShieldCheck, EyeOff, Smartphone, QrCode, Car, Send, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, HardDrive, Folder, Film } from 'lucide-react';
14
14
  import Image from 'next/image';
15
15
  import Link2 from 'next/link';
16
16
  import { usePathname, useSearchParams, useRouter } from 'next/navigation';
@@ -327,7 +327,15 @@ var init_vendor_scope = __esm({
327
327
  ...VENDOR_SCOPED_STORE_ENTITIES,
328
328
  "order_discounts",
329
329
  "order_addresses",
330
- "event_products"
330
+ "event_products",
331
+ "attendees",
332
+ "order-attendees",
333
+ "order_attendees",
334
+ "scan",
335
+ "verify-attendee-qr",
336
+ "verify_attendee_qr",
337
+ "forms",
338
+ "cms-forms"
331
339
  ]);
332
340
  __name(isVendorGroupName, "isVendorGroupName");
333
341
  __name(isPlatformAdministrator, "isPlatformAdministrator");
@@ -853,7 +861,7 @@ var init_admin_config_context = __esm({
853
861
  var CMS_VERSION;
854
862
  var init_cms_version = __esm({
855
863
  "src/lib/cms-version.ts"() {
856
- CMS_VERSION = "1.0.58" ;
864
+ CMS_VERSION = "1.0.60" ;
857
865
  }
858
866
  });
859
867
  function useCatalogCategories(enabled = true) {
@@ -1575,6 +1583,15 @@ var init_builtin_plugin_descriptors = __esm({
1575
1583
  enabled: true,
1576
1584
  icon: "auth_providers",
1577
1585
  settingsGroup: "auth_providers"
1586
+ },
1587
+ {
1588
+ name: "turnstile",
1589
+ label: "Cloudflare Turnstile",
1590
+ version: "1.0.0",
1591
+ description: "Protect forms, registration, and authentication from spam and bots with Cloudflare Turnstile.",
1592
+ enabled: true,
1593
+ icon: "turnstile",
1594
+ settingsGroup: "turnstile"
1578
1595
  }
1579
1596
  ];
1580
1597
  }
@@ -6309,6 +6326,7 @@ function FormBuilder({ formId, duplicateFromId }) {
6309
6326
  campaign: "",
6310
6327
  slug: "",
6311
6328
  published: false,
6329
+ enableTurnstile: false,
6312
6330
  fields: []
6313
6331
  });
6314
6332
  const [errors, setErrors] = useState([]);
@@ -6342,6 +6360,7 @@ function FormBuilder({ formId, duplicateFromId }) {
6342
6360
  campaign: data.campaign || "",
6343
6361
  slug: slugNext,
6344
6362
  published: isDup ? false : data.published,
6363
+ enableTurnstile: Boolean(data.enableTurnstile ?? data.enable_turnstile),
6345
6364
  fields: (data.fields ?? []).map((field, index) => ({
6346
6365
  ...field,
6347
6366
  id: isDup ? `field_dup_${Date.now()}_${index}` : field?.id != null ? String(field.id) : `field_${Date.now()}_${index}`,
@@ -6650,7 +6669,21 @@ function FormBuilder({ formId, duplicateFromId }) {
6650
6669
  })), "onCheckedChange")
6651
6670
  }), /* @__PURE__ */ React.createElement("label", {
6652
6671
  className: "text-sm font-medium text-gray-700"
6653
- }, "Published"))))), /* @__PURE__ */ React.createElement("div", {
6672
+ }, "Published")), /* @__PURE__ */ React.createElement("div", {
6673
+ className: "flex items-center space-x-2 pt-2 border-t border-gray-100"
6674
+ }, /* @__PURE__ */ React.createElement(Switch, {
6675
+ checked: formData.enableTurnstile ?? false,
6676
+ onCheckedChange: /* @__PURE__ */ __name((checked) => setFormData((prev) => ({
6677
+ ...prev,
6678
+ enableTurnstile: checked
6679
+ })), "onCheckedChange")
6680
+ }), /* @__PURE__ */ React.createElement("div", {
6681
+ className: "flex flex-col"
6682
+ }, /* @__PURE__ */ React.createElement("label", {
6683
+ className: "text-sm font-medium text-gray-700"
6684
+ }, "Enable Cloudflare Turnstile"), /* @__PURE__ */ React.createElement("span", {
6685
+ className: "text-xs text-gray-500"
6686
+ }, "Requires user verification before submission (when Turnstile plugin is active)")))))), /* @__PURE__ */ React.createElement("div", {
6654
6687
  className: "lg:col-span-2"
6655
6688
  }, /* @__PURE__ */ React.createElement("div", {
6656
6689
  className: "bg-white rounded-lg shadow"
@@ -16143,6 +16176,7 @@ var init_order_notification_bindings_shared = __esm({
16143
16176
  "orderNumber",
16144
16177
  "customerName",
16145
16178
  "customerPhone",
16179
+ "customerEmail",
16146
16180
  "eventName",
16147
16181
  "vendorName",
16148
16182
  "total",
@@ -16150,7 +16184,8 @@ var init_order_notification_bindings_shared = __esm({
16150
16184
  "vendorId",
16151
16185
  "refundAmount",
16152
16186
  "invoiceNumber",
16153
- "invoiceUrl"
16187
+ "invoiceAttachment",
16188
+ "orderDetails"
16154
16189
  ];
16155
16190
  __name(useNotificationVariables, "useNotificationVariables");
16156
16191
  EMAIL_TRIGGER_DEFAULTS = {
@@ -16182,6 +16217,43 @@ If you have any questions, please contact us.
16182
16217
 
16183
16218
  Thank you!`,
16184
16219
  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."
16220
+ },
16221
+ payment_success: {
16222
+ subject: "Payment Successful for Order {{orderNumber}}",
16223
+ subjectPlaceholder: "e.g. Payment Successful for Order {{orderNumber}}",
16224
+ body: `Hi {{customerName}},
16225
+
16226
+ We have received your payment for order {{orderNumber}} of {{total}} {{currency}}.
16227
+
16228
+ Order Details:
16229
+ {{orderDetails}}
16230
+
16231
+ Thank you!`,
16232
+ bodyPlaceholder: "Hi {{customerName}},\n\nPayment for order {{orderNumber}} was successful.\n\nThank you!"
16233
+ },
16234
+ payment_failed: {
16235
+ subject: "Payment Failed for Order {{orderNumber}}",
16236
+ subjectPlaceholder: "e.g. Payment Failed for Order {{orderNumber}}",
16237
+ body: `Hi {{customerName}},
16238
+
16239
+ Your payment for order {{orderNumber}} of {{total}} {{currency}} could not be completed or remains unpaid.
16240
+
16241
+ If you were charged, please contact us with your order number {{orderNumber}}.
16242
+
16243
+ Thank you!`,
16244
+ bodyPlaceholder: "Hi {{customerName}},\n\nPayment for order {{orderNumber}} failed or remains unpaid.\n\nThank you!"
16245
+ },
16246
+ payment_refund_initiated: {
16247
+ subject: "Refund Initiated for Order {{orderNumber}}",
16248
+ subjectPlaceholder: "e.g. Refund Initiated for Order {{orderNumber}}",
16249
+ body: `Hi {{customerName}},
16250
+
16251
+ Your order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated.
16252
+
16253
+ The amount will be credited back to your original payment method.
16254
+
16255
+ Thank you!`,
16256
+ bodyPlaceholder: "Hi {{customerName}},\n\nRefund of {{refundAmount}} {{currency}} for order {{orderNumber}} has been initiated."
16185
16257
  }
16186
16258
  };
16187
16259
  VENDOR_EMAIL_TRIGGER_DEFAULTS = {
@@ -16206,12 +16278,48 @@ Thank you!`,
16206
16278
 
16207
16279
  Order {{orderNumber}} for {{total}} {{currency}} has been cancelled.
16208
16280
 
16209
- Customer: {{customerName}
16281
+ Customer: {{customerName}}
16210
16282
  Order Items:
16211
16283
  {{orderDetails}}
16212
16284
 
16213
16285
  Thank you.`,
16214
16286
  bodyPlaceholder: "Hi {{vendorName}},\n\nOrder {{orderNumber}} was cancelled."
16287
+ },
16288
+ payment_success: {
16289
+ subject: "Payment Confirmed for Order {{orderNumber}} - {{customerName}}",
16290
+ subjectPlaceholder: "e.g. Payment Confirmed for Order {{orderNumber}}",
16291
+ body: `Hi {{vendorName}},
16292
+
16293
+ Payment has been confirmed for order {{orderNumber}} ({{total}} {{currency}}).
16294
+
16295
+ Customer: {{customerName}}
16296
+ Order Items:
16297
+ {{orderDetails}}
16298
+
16299
+ Thank you!`,
16300
+ bodyPlaceholder: "Hi {{vendorName}},\n\nPayment for order {{orderNumber}} confirmed."
16301
+ },
16302
+ payment_failed: {
16303
+ subject: "Payment Unpaid / Failed for Order {{orderNumber}}",
16304
+ subjectPlaceholder: "e.g. Payment Unpaid for Order {{orderNumber}}",
16305
+ body: `Hi {{vendorName}},
16306
+
16307
+ Payment for order {{orderNumber}} ({{total}} {{currency}}) is unpaid or failed.
16308
+
16309
+ Customer: {{customerName}}
16310
+
16311
+ Thank you.`,
16312
+ bodyPlaceholder: "Hi {{vendorName}},\n\nPayment for order {{orderNumber}} failed or unpaid."
16313
+ },
16314
+ payment_refund_initiated: {
16315
+ subject: "Refund Initiated for Order {{orderNumber}}",
16316
+ subjectPlaceholder: "e.g. Refund Initiated for Order {{orderNumber}}",
16317
+ body: `Hi {{vendorName}},
16318
+
16319
+ Order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated for {{customerName}}.
16320
+
16321
+ Thank you.`,
16322
+ bodyPlaceholder: "Hi {{vendorName}},\n\nRefund initiated for order {{orderNumber}}."
16215
16323
  }
16216
16324
  };
16217
16325
  ADMIN_EMAIL_TRIGGER_DEFAULTS = {
@@ -16243,6 +16351,42 @@ Customer: {{customerName}}
16243
16351
  Vendor: {{vendorName}}
16244
16352
  Refund Due: {{refundAmount}} {{currency}}`,
16245
16353
  bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16354
+ },
16355
+ payment_success: {
16356
+ subject: "Payment Success Alert: Order {{orderNumber}} ({{total}} {{currency}})",
16357
+ subjectPlaceholder: "e.g. Payment Success Alert: Order {{orderNumber}}",
16358
+ body: `Hi Admin,
16359
+
16360
+ Payment of {{total}} {{currency}} was successfully captured for order {{orderNumber}}.
16361
+
16362
+ Customer: {{customerName}}
16363
+ Vendor: {{vendorName}}
16364
+
16365
+ Order Summary:
16366
+ {{orderDetails}}`,
16367
+ bodyPlaceholder: "Payment received for order {{orderNumber}}."
16368
+ },
16369
+ payment_failed: {
16370
+ subject: "Payment Failed / Unpaid Alert: Order {{orderNumber}}",
16371
+ subjectPlaceholder: "e.g. Payment Failed Alert: Order {{orderNumber}}",
16372
+ body: `Hi Admin,
16373
+
16374
+ Payment for order {{orderNumber}} ({{total}} {{currency}}) is unpaid or failed.
16375
+
16376
+ Customer: {{customerName}}
16377
+ Vendor: {{vendorName}}`,
16378
+ bodyPlaceholder: "Payment failed/unpaid for order {{orderNumber}}."
16379
+ },
16380
+ payment_refund_initiated: {
16381
+ subject: "Refund Initiated Alert: Order {{orderNumber}}",
16382
+ subjectPlaceholder: "e.g. Refund Initiated Alert: Order {{orderNumber}}",
16383
+ body: `Hi Admin,
16384
+
16385
+ Order {{orderNumber}} was cancelled and refund of {{refundAmount}} {{currency}} was initiated.
16386
+
16387
+ Customer: {{customerName}}
16388
+ Vendor: {{vendorName}}`,
16389
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16246
16390
  }
16247
16391
  };
16248
16392
  __name(getEmailTriggerDefaults, "getEmailTriggerDefaults");
@@ -16258,6 +16402,24 @@ Refund Due: {{refundAmount}} {{currency}}`,
16258
16402
  subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16259
16403
  body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} has been cancelled.`,
16260
16404
  bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} has been cancelled."
16405
+ },
16406
+ payment_success: {
16407
+ subject: "Payment Successful!",
16408
+ subjectPlaceholder: "e.g. Payment Successful!",
16409
+ body: `Hi {{customerName}}, payment for order {{orderNumber}} ({{total}} {{currency}}) was successful.`,
16410
+ bodyPlaceholder: "Payment for order {{orderNumber}} successful."
16411
+ },
16412
+ payment_failed: {
16413
+ subject: "Payment Unpaid / Failed",
16414
+ subjectPlaceholder: "e.g. Payment Unpaid / Failed",
16415
+ body: `Hi {{customerName}}, payment for order {{orderNumber}} failed or remains unpaid.`,
16416
+ bodyPlaceholder: "Payment for order {{orderNumber}} failed."
16417
+ },
16418
+ payment_refund_initiated: {
16419
+ subject: "Refund Initiated",
16420
+ subjectPlaceholder: "e.g. Refund Initiated",
16421
+ body: `Hi {{customerName}}, a refund of {{refundAmount}} {{currency}} for order {{orderNumber}} has been initiated.`,
16422
+ bodyPlaceholder: "Refund for order {{orderNumber}} initiated."
16261
16423
  }
16262
16424
  };
16263
16425
  VENDOR_PUSH_TRIGGER_DEFAULTS = {
@@ -16272,6 +16434,24 @@ Refund Due: {{refundAmount}} {{currency}}`,
16272
16434
  subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16273
16435
  body: `Hi {{vendorName}}, order {{orderNumber}} from {{customerName}} has been cancelled.`,
16274
16436
  bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} was cancelled."
16437
+ },
16438
+ payment_success: {
16439
+ subject: "Payment Confirmed: {{orderNumber}}",
16440
+ subjectPlaceholder: "e.g. Payment Confirmed: {{orderNumber}}",
16441
+ body: `Hi {{vendorName}}, payment for order {{orderNumber}} from {{customerName}} has been confirmed.`,
16442
+ bodyPlaceholder: "Payment for order {{orderNumber}} confirmed."
16443
+ },
16444
+ payment_failed: {
16445
+ subject: "Payment Unpaid: {{orderNumber}}",
16446
+ subjectPlaceholder: "e.g. Payment Unpaid: {{orderNumber}}",
16447
+ body: `Hi {{vendorName}}, payment for order {{orderNumber}} from {{customerName}} is unpaid or failed.`,
16448
+ bodyPlaceholder: "Payment for order {{orderNumber}} unpaid."
16449
+ },
16450
+ payment_refund_initiated: {
16451
+ subject: "Refund Initiated: {{orderNumber}}",
16452
+ subjectPlaceholder: "e.g. Refund Initiated: {{orderNumber}}",
16453
+ body: `Hi {{vendorName}}, refund initiated for order {{orderNumber}} ({{customerName}}).`,
16454
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16275
16455
  }
16276
16456
  };
16277
16457
  ADMIN_PUSH_TRIGGER_DEFAULTS = {
@@ -16303,6 +16483,24 @@ Customer: {{customerName}}
16303
16483
  Vendor: {{vendorName}}
16304
16484
  Refund Due: {{refundAmount}} {{currency}}`,
16305
16485
  bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16486
+ },
16487
+ payment_success: {
16488
+ subject: "Payment Success: {{orderNumber}}",
16489
+ subjectPlaceholder: "e.g. Payment Success: {{orderNumber}}",
16490
+ body: `Payment of {{total}} {{currency}} captured for order {{orderNumber}} ({{customerName}}).`,
16491
+ bodyPlaceholder: "Payment captured for order {{orderNumber}}."
16492
+ },
16493
+ payment_failed: {
16494
+ subject: "Payment Failed/Unpaid: {{orderNumber}}",
16495
+ subjectPlaceholder: "e.g. Payment Failed: {{orderNumber}}",
16496
+ body: `Payment for order {{orderNumber}} ({{customerName}}) is unpaid or failed.`,
16497
+ bodyPlaceholder: "Payment failed for order {{orderNumber}}."
16498
+ },
16499
+ payment_refund_initiated: {
16500
+ subject: "Refund Initiated: {{orderNumber}}",
16501
+ subjectPlaceholder: "e.g. Refund Initiated: {{orderNumber}}",
16502
+ body: `Refund of {{refundAmount}} {{currency}} initiated for order {{orderNumber}}.`,
16503
+ bodyPlaceholder: "Refund initiated for order {{orderNumber}}."
16306
16504
  }
16307
16505
  };
16308
16506
  __name(getPushTriggerDefaults, "getPushTriggerDefaults");
@@ -16956,6 +17154,7 @@ var init_emailTriggersBindingsPage = __esm({
16956
17154
  orderNumber: "Display order number",
16957
17155
  customerName: "Customer full name",
16958
17156
  customerPhone: "Customer phone",
17157
+ customerEmail: "Customer email address",
16959
17158
  eventName: "Event title / product name",
16960
17159
  vendorName: "Vendor / store name",
16961
17160
  total: "Order total amount",
@@ -16963,7 +17162,8 @@ var init_emailTriggersBindingsPage = __esm({
16963
17162
  vendorId: "Vendor / store ID",
16964
17163
  refundAmount: "Refund amount (refund triggers)",
16965
17164
  invoiceNumber: "Generated invoice number",
16966
- invoiceUrl: "Link to download invoice PDF"
17165
+ invoiceAttachment: "Include this variable to attach Tax Invoice PDF to email",
17166
+ orderDetails: "Formatted item details summary"
16967
17167
  };
16968
17168
  __name(EmailTriggerBindings, "EmailTriggerBindings");
16969
17169
  __name(TriggerEmailEditor, "TriggerEmailEditor");
@@ -17261,6 +17461,7 @@ var init_pushTriggersBindingsPage = __esm({
17261
17461
  orderNumber: "Display order number",
17262
17462
  customerName: "Customer full name",
17263
17463
  customerPhone: "Customer phone",
17464
+ customerEmail: "Customer email address",
17264
17465
  eventName: "Event title / product name",
17265
17466
  vendorName: "Vendor / store name",
17266
17467
  total: "Order total amount",
@@ -17268,7 +17469,8 @@ var init_pushTriggersBindingsPage = __esm({
17268
17469
  vendorId: "Vendor / store ID",
17269
17470
  refundAmount: "Refund amount (refund triggers)",
17270
17471
  invoiceNumber: "Generated invoice number",
17271
- invoiceUrl: "Link to download invoice PDF"
17472
+ invoiceAttachment: "Include this variable to attach Tax Invoice PDF to email",
17473
+ orderDetails: "Formatted item details summary"
17272
17474
  };
17273
17475
  __name(PushTriggerBindings, "PushTriggerBindings");
17274
17476
  }
@@ -18106,6 +18308,177 @@ var init_AuthProvidersPluginSettings = __esm({
18106
18308
  __name(AuthProvidersPluginSettings, "AuthProvidersPluginSettings");
18107
18309
  }
18108
18310
  });
18311
+ function TurnstilePluginSettings({ settingsGroup, onEnabledChange }) {
18312
+ const [loading, setLoading] = useState(true);
18313
+ const [saving, setSaving] = useState(false);
18314
+ const [enabled, setEnabled] = useState(false);
18315
+ const [siteKey, setSiteKey] = useState("");
18316
+ const [secretKey, setSecretKey] = useState("");
18317
+ const [hasSavedSecret, setHasSavedSecret] = useState(false);
18318
+ const [showSecret, setShowSecret] = useState(false);
18319
+ const onEnabledChangeRef = useRef(onEnabledChange);
18320
+ onEnabledChangeRef.current = onEnabledChange;
18321
+ useEffect(() => {
18322
+ let cancelled = false;
18323
+ (async () => {
18324
+ setLoading(true);
18325
+ try {
18326
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
18327
+ cache: "no-store"
18328
+ });
18329
+ const data = res.ok ? await res.json() : {};
18330
+ if (cancelled) return;
18331
+ const isEnabled = data.enabled === "true" || data.enabled === "1";
18332
+ setEnabled(isEnabled);
18333
+ setSiteKey(String(data.siteKey ?? data.TURNSTILE_SITE_KEY ?? "").trim());
18334
+ const secret = String(data.secretKey ?? data.TURNSTILE_SECRET_KEY ?? "").trim();
18335
+ setSecretKey("");
18336
+ setHasSavedSecret(Boolean(secret));
18337
+ } catch {
18338
+ } finally {
18339
+ if (!cancelled) setLoading(false);
18340
+ }
18341
+ })();
18342
+ return () => {
18343
+ cancelled = true;
18344
+ };
18345
+ }, [
18346
+ settingsGroup
18347
+ ]);
18348
+ const handleSave = /* @__PURE__ */ __name(async () => {
18349
+ setSaving(true);
18350
+ try {
18351
+ const payload = {
18352
+ enabled: {
18353
+ value: enabled ? "true" : "false",
18354
+ type: "public"
18355
+ },
18356
+ siteKey: {
18357
+ value: siteKey.trim(),
18358
+ type: "public"
18359
+ }
18360
+ };
18361
+ if (secretKey.trim()) {
18362
+ payload.secretKey = {
18363
+ value: secretKey.trim(),
18364
+ type: "private",
18365
+ encrypted: true
18366
+ };
18367
+ }
18368
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
18369
+ method: "PUT",
18370
+ headers: {
18371
+ "Content-Type": "application/json"
18372
+ },
18373
+ body: JSON.stringify(payload)
18374
+ });
18375
+ if (res.ok) {
18376
+ toast.success("Turnstile settings saved");
18377
+ if (secretKey.trim()) {
18378
+ setHasSavedSecret(true);
18379
+ setSecretKey("");
18380
+ }
18381
+ onEnabledChangeRef.current?.(enabled);
18382
+ } else {
18383
+ const err = await res.json().catch(() => ({}));
18384
+ toast.error(err?.message || "Failed to save settings");
18385
+ }
18386
+ } catch {
18387
+ toast.error("Failed to save settings");
18388
+ } finally {
18389
+ setSaving(false);
18390
+ }
18391
+ }, "handleSave");
18392
+ if (loading) {
18393
+ return /* @__PURE__ */ React.createElement("div", {
18394
+ className: "flex items-center justify-center p-8 text-muted-foreground text-sm"
18395
+ }, /* @__PURE__ */ React.createElement(Loader2, {
18396
+ className: "mr-2 h-4 w-4 animate-spin"
18397
+ }), "Loading settings\u2026");
18398
+ }
18399
+ return /* @__PURE__ */ React.createElement("div", {
18400
+ className: "space-y-6"
18401
+ }, /* @__PURE__ */ React.createElement("div", {
18402
+ className: "rounded-lg border border-gray-200 bg-gray-50/80 p-4 dark:border-gray-700 dark:bg-gray-800/40"
18403
+ }, /* @__PURE__ */ React.createElement("div", {
18404
+ className: "flex items-center justify-between"
18405
+ }, /* @__PURE__ */ React.createElement("div", {
18406
+ className: "space-y-0.5"
18407
+ }, /* @__PURE__ */ React.createElement(Label3, {
18408
+ htmlFor: `${settingsGroup}-enabled`,
18409
+ className: "text-sm font-semibold text-gray-900 dark:text-white"
18410
+ }, "Enable Cloudflare Turnstile"), /* @__PURE__ */ React.createElement("p", {
18411
+ className: "text-xs text-muted-foreground"
18412
+ }, "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, {
18413
+ id: `${settingsGroup}-enabled`,
18414
+ checked: enabled,
18415
+ onCheckedChange: setEnabled
18416
+ }))), /* @__PURE__ */ React.createElement("div", {
18417
+ className: "space-y-4"
18418
+ }, /* @__PURE__ */ React.createElement("div", {
18419
+ className: "space-y-1.5"
18420
+ }, /* @__PURE__ */ React.createElement(Label3, {
18421
+ htmlFor: `${settingsGroup}-siteKey`,
18422
+ className: "text-sm font-medium"
18423
+ }, "Turnstile Site Key (Public)"), /* @__PURE__ */ React.createElement(Input, {
18424
+ id: `${settingsGroup}-siteKey`,
18425
+ value: siteKey,
18426
+ onChange: /* @__PURE__ */ __name((e) => setSiteKey(e.target.value), "onChange"),
18427
+ placeholder: "0x4AAAAAA...",
18428
+ className: "h-9 font-mono text-sm"
18429
+ }), /* @__PURE__ */ React.createElement("p", {
18430
+ className: "text-xs text-muted-foreground"
18431
+ }, "Obtained from your Cloudflare dashboard under Turnstile Sites. Safe to expose to the frontend.")), /* @__PURE__ */ React.createElement("div", {
18432
+ className: "space-y-1.5"
18433
+ }, /* @__PURE__ */ React.createElement("div", {
18434
+ className: "flex items-center justify-between"
18435
+ }, /* @__PURE__ */ React.createElement(Label3, {
18436
+ htmlFor: `${settingsGroup}-secretKey`,
18437
+ className: "text-sm font-medium"
18438
+ }, "Turnstile Secret Key (Private)"), hasSavedSecret && /* @__PURE__ */ React.createElement("span", {
18439
+ className: "inline-flex items-center gap-1 text-xs font-medium text-emerald-600 dark:text-emerald-400"
18440
+ }, /* @__PURE__ */ React.createElement(ShieldCheck, {
18441
+ className: "h-3.5 w-3.5"
18442
+ }), "Secret key configured")), /* @__PURE__ */ React.createElement("div", {
18443
+ className: "relative"
18444
+ }, /* @__PURE__ */ React.createElement(Input, {
18445
+ id: `${settingsGroup}-secretKey`,
18446
+ type: showSecret ? "text" : "password",
18447
+ value: secretKey,
18448
+ onChange: /* @__PURE__ */ __name((e) => setSecretKey(e.target.value), "onChange"),
18449
+ 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...",
18450
+ className: "h-9 font-mono text-sm pr-10"
18451
+ }), /* @__PURE__ */ React.createElement("button", {
18452
+ type: "button",
18453
+ className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200",
18454
+ onClick: /* @__PURE__ */ __name(() => setShowSecret(!showSecret), "onClick")
18455
+ }, showSecret ? /* @__PURE__ */ React.createElement(EyeOff, {
18456
+ className: "h-4 w-4"
18457
+ }) : /* @__PURE__ */ React.createElement(Eye, {
18458
+ className: "h-4 w-4"
18459
+ }))), /* @__PURE__ */ React.createElement("p", {
18460
+ className: "text-xs text-muted-foreground"
18461
+ }, "Used only on the server to verify tokens with Cloudflare Siteverify API. Never sent to browsers."))), /* @__PURE__ */ React.createElement(Button, {
18462
+ onClick: handleSave,
18463
+ disabled: saving,
18464
+ size: "sm",
18465
+ className: "gap-2"
18466
+ }, saving ? /* @__PURE__ */ React.createElement(Loader2, {
18467
+ className: "h-4 w-4 animate-spin"
18468
+ }) : /* @__PURE__ */ React.createElement(Save, {
18469
+ className: "h-4 w-4"
18470
+ }), "Save Settings"));
18471
+ }
18472
+ var init_TurnstilePluginSettings = __esm({
18473
+ "src/admin/components/TurnstilePluginSettings.tsx"() {
18474
+ "use client";
18475
+ init_button();
18476
+ init_input();
18477
+ init_label();
18478
+ init_switch();
18479
+ __name(TurnstilePluginSettings, "TurnstilePluginSettings");
18480
+ }
18481
+ });
18109
18482
 
18110
18483
  // src/plugins/llm/chat-email-intent.ts
18111
18484
  function normalizeIntentKey(raw) {
@@ -18484,6 +18857,7 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
18484
18857
  const isSocialMedia = settingsGroup === "social_media";
18485
18858
  const isMultiVendor = settingsGroup === "multi_vendor";
18486
18859
  const isAuthProviders = settingsGroup === "auth_providers";
18860
+ const isTurnstile = settingsGroup === "turnstile";
18487
18861
  const [enabled, setEnabled] = useState(true);
18488
18862
  const [botName, setBotName] = useState("");
18489
18863
  const [icon, setIcon] = useState("");
@@ -19827,6 +20201,15 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
19827
20201
  }, "onEnabledChange")
19828
20202
  });
19829
20203
  }
20204
+ if (isTurnstile) {
20205
+ return /* @__PURE__ */ React.createElement(TurnstilePluginSettings, {
20206
+ settingsGroup,
20207
+ onEnabledChange: /* @__PURE__ */ __name((value) => {
20208
+ setEnabled(value);
20209
+ onSaved?.();
20210
+ }, "onEnabledChange")
20211
+ });
20212
+ }
19830
20213
  if (loading) return /* @__PURE__ */ React.createElement("div", {
19831
20214
  className: "text-sm text-gray-500 dark:text-gray-400"
19832
20215
  }, "Loading...");
@@ -21167,6 +21550,10 @@ function PluginsPage() {
21167
21550
  const ap = pluginDescriptors.find((p) => p.name === "auth_providers");
21168
21551
  if (ap) setSelectedName(ap.name);
21169
21552
  }
21553
+ if (q === "turnstile" || q === "cloudflare_turnstile" || q === "captcha") {
21554
+ const ts = pluginDescriptors.find((p) => p.name === "turnstile");
21555
+ if (ts) setSelectedName(ts.name);
21556
+ }
21170
21557
  }, [
21171
21558
  searchParams,
21172
21559
  pluginDescriptors
@@ -21279,6 +21666,7 @@ var init_PluginsPage = __esm({
21279
21666
  init_select();
21280
21667
  init_EventNotificationsPluginSettings();
21281
21668
  init_AuthProvidersPluginSettings();
21669
+ init_TurnstilePluginSettings();
21282
21670
  init_ImageOrUrlField();
21283
21671
  init_chat_email_intent();
21284
21672
  init_llm_agent_scope();
@@ -21298,7 +21686,8 @@ var init_PluginsPage = __esm({
21298
21686
  share: Share2,
21299
21687
  whatsapp: MessageCircle,
21300
21688
  event_notifications: Bell,
21301
- auth_providers: KeyRound
21689
+ auth_providers: KeyRound,
21690
+ turnstile: ShieldCheck
21302
21691
  };
21303
21692
  __name(normalizeSmsProviderChoice, "normalizeSmsProviderChoice");
21304
21693
  __name(normalizeMsg91ApiMode, "normalizeMsg91ApiMode");
@@ -22260,7 +22649,7 @@ function VendorTeamPage() {
22260
22649
  }
22261
22650
  }, []);
22262
22651
  useEffect(() => {
22263
- if (canManage) load();
22652
+ if (canManage) void load();
22264
22653
  else setLoading(false);
22265
22654
  }, [
22266
22655
  canManage,
@@ -22318,10 +22707,18 @@ function VendorTeamPage() {
22318
22707
  }, "closeInviteDialog");
22319
22708
  if (!canManage) {
22320
22709
  return /* @__PURE__ */ React25__default.createElement("div", {
22321
- className: "p-6 max-w-lg"
22710
+ className: "min-w-0 rounded-lg bg-white shadow-md"
22711
+ }, /* @__PURE__ */ React25__default.createElement("div", {
22712
+ className: "rounded-t-lg border-b border-gray-700 bg-gray-800 px-4 py-2.5"
22713
+ }, /* @__PURE__ */ React25__default.createElement("h1", {
22714
+ className: "text-base font-semibold text-white"
22715
+ }, "Vendor Team"), /* @__PURE__ */ React25__default.createElement("p", {
22716
+ className: "mt-0.5 text-xs text-gray-300"
22717
+ }, "Invite members and assign a store role")), /* @__PURE__ */ React25__default.createElement("div", {
22718
+ className: "p-6"
22322
22719
  }, /* @__PURE__ */ React25__default.createElement("p", {
22323
22720
  className: "text-sm text-red-600"
22324
- }, "Only the store owner can manage the vendor team."));
22721
+ }, "Only the store owner can manage the vendor team.")));
22325
22722
  }
22326
22723
  const handleAdd = /* @__PURE__ */ __name(async (e) => {
22327
22724
  e.preventDefault();
@@ -22423,17 +22820,21 @@ function VendorTeamPage() {
22423
22820
  }
22424
22821
  }, "handleRemove");
22425
22822
  return /* @__PURE__ */ React25__default.createElement("div", {
22426
- className: "p-6 max-w-3xl"
22823
+ className: "min-w-0 rounded-lg bg-white shadow-md"
22824
+ }, /* @__PURE__ */ React25__default.createElement("div", {
22825
+ className: "rounded-t-lg border-b border-gray-700 bg-gray-800 px-4 py-2.5"
22427
22826
  }, /* @__PURE__ */ React25__default.createElement("h1", {
22428
- className: "text-xl font-semibold text-gray-900"
22827
+ className: "text-base font-semibold text-white"
22429
22828
  }, "Vendor Team"), /* @__PURE__ */ React25__default.createElement("p", {
22430
- className: "mt-1 text-sm text-gray-600"
22431
- }, "Invite member and assign a store role."), /* @__PURE__ */ React25__default.createElement("div", {
22432
- className: "mt-6 rounded-lg border border-gray-200 overflow-hidden"
22829
+ className: "mt-0.5 text-xs text-gray-300"
22830
+ }, "Invite member and assign a store role")), /* @__PURE__ */ React25__default.createElement("div", {
22831
+ className: "p-6 space-y-6"
22832
+ }, /* @__PURE__ */ React25__default.createElement("div", {
22833
+ className: "rounded-lg border border-gray-200 overflow-hidden"
22433
22834
  }, /* @__PURE__ */ React25__default.createElement("table", {
22434
22835
  className: "min-w-full text-sm"
22435
22836
  }, /* @__PURE__ */ React25__default.createElement("thead", {
22436
- className: "bg-gray-50 text-left text-gray-600"
22837
+ className: "bg-gray-50 text-left text-gray-600 border-b border-gray-200"
22437
22838
  }, /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("th", {
22438
22839
  className: "px-3 py-2 font-medium"
22439
22840
  }, "Name"), /* @__PURE__ */ React25__default.createElement("th", {
@@ -22443,8 +22844,10 @@ function VendorTeamPage() {
22443
22844
  }, "Phone"), /* @__PURE__ */ React25__default.createElement("th", {
22444
22845
  className: "px-3 py-2 font-medium"
22445
22846
  }, "Store role"), /* @__PURE__ */ React25__default.createElement("th", {
22446
- className: "px-3 py-2 font-medium"
22447
- }))), /* @__PURE__ */ React25__default.createElement("tbody", null, loading ? /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("td", {
22847
+ className: "px-3 py-2 font-medium text-right"
22848
+ }))), /* @__PURE__ */ React25__default.createElement("tbody", {
22849
+ className: "divide-y divide-gray-100"
22850
+ }, loading ? /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("td", {
22448
22851
  colSpan: 5,
22449
22852
  className: "px-3 py-4 text-gray-500"
22450
22853
  }, "Loading\u2026")) : members.length === 0 ? /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("td", {
@@ -22454,15 +22857,17 @@ function VendorTeamPage() {
22454
22857
  key: m.vendorUserId,
22455
22858
  className: "border-t border-gray-100"
22456
22859
  }, /* @__PURE__ */ React25__default.createElement("td", {
22457
- className: "px-3 py-2"
22860
+ className: "px-3 py-2 font-medium text-gray-900"
22458
22861
  }, m.name ?? "\u2014"), /* @__PURE__ */ React25__default.createElement("td", {
22459
- className: "px-3 py-2"
22862
+ className: "px-3 py-2 text-gray-600"
22460
22863
  }, m.email), /* @__PURE__ */ React25__default.createElement("td", {
22461
- className: "px-3 py-2"
22864
+ className: "px-3 py-2 text-gray-600"
22462
22865
  }, m.phone ?? "\u2014"), /* @__PURE__ */ React25__default.createElement("td", {
22463
22866
  className: "px-3 py-2"
22464
- }, m.isOwnerRole ? /* @__PURE__ */ React25__default.createElement("span", null, m.vendorRoleName ?? "Owner") : /* @__PURE__ */ React25__default.createElement("select", {
22465
- className: "rounded border border-gray-200 px-2 py-1 text-sm",
22867
+ }, m.isOwnerRole ? /* @__PURE__ */ React25__default.createElement("span", {
22868
+ className: "text-gray-900 font-medium"
22869
+ }, m.vendorRoleName ?? "Owner") : /* @__PURE__ */ React25__default.createElement("select", {
22870
+ className: "rounded border border-gray-200 px-2 py-1 text-sm bg-white",
22466
22871
  value: m.vendorRoleId ?? "",
22467
22872
  disabled: roleUpdating === m.userId,
22468
22873
  onChange: /* @__PURE__ */ __name((e) => handleRoleChange(m.userId, Number(e.target.value)), "onChange")
@@ -22478,7 +22883,7 @@ function VendorTeamPage() {
22478
22883
  onClick: /* @__PURE__ */ __name(() => handleRemove(m.userId), "onClick")
22479
22884
  }, "Remove"))))))), /* @__PURE__ */ React25__default.createElement("form", {
22480
22885
  onSubmit: handleAdd,
22481
- className: "mt-8 space-y-4 rounded-lg border border-gray-200 p-4"
22886
+ className: "space-y-4 rounded-lg border border-gray-200 p-4"
22482
22887
  }, /* @__PURE__ */ React25__default.createElement("h2", {
22483
22888
  className: "text-sm font-medium text-gray-900"
22484
22889
  }, "Invite team member"), /* @__PURE__ */ React25__default.createElement("div", {
@@ -22512,7 +22917,7 @@ function VendorTeamPage() {
22512
22917
  htmlFor: "teamRole"
22513
22918
  }, "Store role"), /* @__PURE__ */ React25__default.createElement("select", {
22514
22919
  id: "teamRole",
22515
- className: "mt-1 w-full rounded-md border border-gray-200 px-3 py-2 text-sm",
22920
+ className: "mt-1 w-full rounded-md border border-gray-200 px-3 py-2 text-sm bg-white",
22516
22921
  value: vendorRoleId,
22517
22922
  onChange: /* @__PURE__ */ __name((e) => setVendorRoleId(e.target.value ? Number(e.target.value) : ""), "onChange")
22518
22923
  }, assignableRoles.map((r) => /* @__PURE__ */ React25__default.createElement("option", {
@@ -22551,7 +22956,7 @@ function VendorTeamPage() {
22551
22956
  }, message), /* @__PURE__ */ React25__default.createElement(Button, {
22552
22957
  type: "submit",
22553
22958
  disabled: submitting
22554
- }, submitting ? "Inviting\u2026" : "Invite member")), /* @__PURE__ */ React25__default.createElement(Dialog, {
22959
+ }, submitting ? "Inviting\u2026" : "Invite member"))), /* @__PURE__ */ React25__default.createElement(Dialog, {
22555
22960
  open: inviteDialog != null,
22556
22961
  onOpenChange: /* @__PURE__ */ __name((open) => {
22557
22962
  if (!open) closeInviteDialog();
@@ -22571,7 +22976,7 @@ function VendorTeamPage() {
22571
22976
  variant: "outline",
22572
22977
  onClick: /* @__PURE__ */ __name(() => void copyInviteLink(), "onClick")
22573
22978
  }, linkCopied ? /* @__PURE__ */ React25__default.createElement(Check, {
22574
- className: "h-4 w-4 mr-2"
22979
+ className: "h-4 w-4 mr-2 text-green-600"
22575
22980
  }) : /* @__PURE__ */ React25__default.createElement(Copy, {
22576
22981
  className: "h-4 w-4 mr-2"
22577
22982
  }), linkCopied ? "Copied" : "Copy invite link")), /* @__PURE__ */ React25__default.createElement(DialogFooter, null, /* @__PURE__ */ React25__default.createElement(Button, {
@@ -30586,15 +30991,15 @@ function ProductEditPage({ productId }) {
30586
30991
  })));
30587
30992
  }
30588
30993
  }
30589
- if (!cancelled && formsRes.ok) {
30590
- const d = await formsRes.json();
30591
- if (Array.isArray(d.data)) {
30592
- setContactForms(d.data.filter((row) => row.id != null).map((row) => ({
30593
- id: Number(row.id),
30594
- name: row.name?.trim() || `Form #${row.id}`,
30595
- slug: row.slug
30596
- })));
30597
- }
30994
+ const formsResActual = formsRes.ok ? formsRes : await fetch("/api/admin/cms-forms/product-options").catch(() => null);
30995
+ if (!cancelled && formsResActual && formsResActual.ok) {
30996
+ const d = await formsResActual.json();
30997
+ const rows = Array.isArray(d.data) ? d.data : Array.isArray(d) ? d : [];
30998
+ setContactForms(rows.filter((row) => row.id != null || row.value != null).map((row) => ({
30999
+ id: Number(row.id ?? row.value),
31000
+ name: row.name?.trim() || row.label?.trim() || `Form #${row.id ?? row.value}`,
31001
+ slug: row.slug
31002
+ })));
30598
31003
  }
30599
31004
  if (!cancelled && refundRes.ok) {
30600
31005
  const d = await refundRes.json();