@infuro/cms-core 1.0.47 → 1.0.51

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 (40) hide show
  1. package/README.md +30 -16
  2. package/dist/admin.cjs +2012 -437
  3. package/dist/admin.d.cts +13 -1
  4. package/dist/admin.d.ts +13 -1
  5. package/dist/admin.js +2013 -438
  6. package/dist/api.cjs +37 -37
  7. package/dist/api.js +5 -5
  8. package/dist/auth.cjs +52 -23
  9. package/dist/auth.d.cts +101 -2
  10. package/dist/auth.d.ts +101 -2
  11. package/dist/auth.js +3 -2
  12. package/dist/chunk-3EOM4V2M.cjs +452 -0
  13. package/dist/{chunk-2HU5R2JE.js → chunk-AYERBA7I.js} +1 -7
  14. package/dist/{chunk-YXH2UUEZ.js → chunk-CTXBYO2J.js} +290 -59
  15. package/dist/{chunk-LMJ7RKPF.cjs → chunk-JN4AFHZ4.cjs} +311 -59
  16. package/dist/{chunk-JE22VP6S.js → chunk-MXIWUFBP.js} +1 -1
  17. package/dist/{chunk-W42UZLQO.js → chunk-NUXR7BUG.js} +2819 -2597
  18. package/dist/chunk-RK5ETF2I.js +434 -0
  19. package/dist/{chunk-4PMK3RNA.cjs → chunk-TJ2MIQUT.cjs} +1 -7
  20. package/dist/{chunk-KOTXDSQB.cjs → chunk-UQWZUZ5X.cjs} +1 -1
  21. package/dist/{chunk-YC4NUZCS.js → chunk-WRKV6MHB.js} +414 -2
  22. package/dist/{chunk-XUCKZPML.cjs → chunk-YOW457RA.cjs} +2872 -2657
  23. package/dist/{chunk-Q3HQEM4R.cjs → chunk-YV5PK4JW.cjs} +421 -1
  24. package/dist/cli.cjs +13 -22
  25. package/dist/cli.js +13 -22
  26. package/dist/{emit-order-notification-trigger-6XX7LSC4.js → emit-order-notification-trigger-HZQPPSFB.js} +1 -1
  27. package/dist/{emit-order-notification-trigger-NASG7ZEO.cjs → emit-order-notification-trigger-NR3VN6C6.cjs} +3 -3
  28. package/dist/{event-order-defaults-7Z3UZOEH.cjs → event-order-defaults-DU7V3YND.cjs} +9 -9
  29. package/dist/{event-order-defaults-XQ3IDPD7.js → event-order-defaults-H4RT7NMR.js} +1 -1
  30. package/dist/index.cjs +379 -309
  31. package/dist/index.d.cts +136 -6
  32. package/dist/index.d.ts +136 -6
  33. package/dist/index.js +58 -24
  34. package/dist/migrations/1782400000000-CreateUserDeviceTokens.ts +36 -0
  35. package/dist/migrations/1782500000000-AddPushToOrderNotificationBindingsChannelEnum.ts +15 -0
  36. package/dist/{order-notification-dispatcher-6WNG24NY.js → order-notification-dispatcher-AT4IFAGL.js} +1 -1
  37. package/dist/{order-notification-dispatcher-6XSU3AR7.cjs → order-notification-dispatcher-YBAWDOIO.cjs} +7 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-VUQFARRT.cjs +0 -182
  40. package/dist/chunk-ZF2RQWXB.js +0 -171
package/dist/admin.cjs CHANGED
@@ -26,10 +26,10 @@ require('jodit/es2021/jodit.min.css');
26
26
  var chart_js = require('chart.js');
27
27
  var reactChartjs2 = require('react-chartjs-2');
28
28
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
29
+ var countryStateCity = require('country-state-city');
29
30
  var reactQrCode = require('react-qr-code');
30
31
  require('typeorm');
31
32
  var core = require('@craftjs/core');
32
- var countryStateCity = require('country-state-city');
33
33
  var nextThemes = require('next-themes');
34
34
 
35
35
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -264,11 +264,20 @@ var init_infuro_favicon = __esm({
264
264
  function isSuperAdminGroupName(name) {
265
265
  return name === ADMIN_GROUP_NAME;
266
266
  }
267
+ function hasEntityPermission(record, entity, action) {
268
+ const p = record?.[entity];
269
+ if (!p) return false;
270
+ if (action === "create") return p.c;
271
+ if (action === "read") return p.r;
272
+ if (action === "update") return p.u;
273
+ return p.d;
274
+ }
267
275
  var ADMIN_GROUP_NAME;
268
276
  var init_permission_entities = __esm({
269
277
  "src/auth/permission-entities.ts"() {
270
278
  ADMIN_GROUP_NAME = "Administrator";
271
279
  __name(isSuperAdminGroupName, "isSuperAdminGroupName");
280
+ __name(hasEntityPermission, "hasEntityPermission");
272
281
  }
273
282
  });
274
283
 
@@ -470,6 +479,395 @@ var init_Header = __esm({
470
479
  __name(AdminHeader, "AdminHeader");
471
480
  }
472
481
  });
482
+
483
+ // src/lib/vendor-role-defaults.ts
484
+ function full() {
485
+ return {
486
+ canCreate: true,
487
+ canRead: true,
488
+ canUpdate: true,
489
+ canDelete: true
490
+ };
491
+ }
492
+ function read() {
493
+ return {
494
+ canCreate: false,
495
+ canRead: true,
496
+ canUpdate: false,
497
+ canDelete: false
498
+ };
499
+ }
500
+ function none() {
501
+ return {
502
+ canCreate: false,
503
+ canRead: false,
504
+ canUpdate: false,
505
+ canDelete: false
506
+ };
507
+ }
508
+ function readUpdate() {
509
+ return {
510
+ canCreate: false,
511
+ canRead: true,
512
+ canUpdate: true,
513
+ canDelete: false
514
+ };
515
+ }
516
+ function storePerms(spec) {
517
+ const resolve = /* @__PURE__ */ __name((v) => {
518
+ if (v === "full") return full();
519
+ if (v === "read") return read();
520
+ if (v === "none") return none();
521
+ return v;
522
+ }, "resolve");
523
+ const out = {};
524
+ for (const entity of STORE_ENTITIES) {
525
+ out[entity] = resolve(spec[entity] ?? "none");
526
+ }
527
+ return out;
528
+ }
529
+ var VENDOR_RBAC_ENTITIES, STORE_ENTITIES;
530
+ var init_vendor_role_defaults = __esm({
531
+ "src/lib/vendor-role-defaults.ts"() {
532
+ init_vendor_scope();
533
+ VENDOR_RBAC_ENTITIES = [
534
+ ...VENDOR_STORE_RBAC_ENTITIES,
535
+ "dashboard",
536
+ "settings",
537
+ "team",
538
+ "roles"
539
+ ];
540
+ STORE_ENTITIES = [
541
+ ...VENDOR_STORE_RBAC_ENTITIES
542
+ ];
543
+ __name(full, "full");
544
+ __name(read, "read");
545
+ __name(none, "none");
546
+ __name(readUpdate, "readUpdate");
547
+ __name(storePerms, "storePerms");
548
+ [
549
+ {
550
+ name: "Owner",
551
+ description: "Full store access and team/role management",
552
+ isSystem: true,
553
+ isOwnerRole: true,
554
+ permissions: {
555
+ ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
556
+ e,
557
+ "full"
558
+ ]))),
559
+ dashboard: read(),
560
+ settings: readUpdate(),
561
+ team: full(),
562
+ roles: full()
563
+ }
564
+ },
565
+ {
566
+ name: "Manager",
567
+ description: "Day-to-day store operations",
568
+ isSystem: true,
569
+ isOwnerRole: false,
570
+ permissions: {
571
+ ...storePerms({
572
+ products: "full",
573
+ collections: "full",
574
+ brands: "full",
575
+ product_categories: "full",
576
+ orders: "full",
577
+ vendor_customers: "full",
578
+ discounts: "full",
579
+ order_discounts: "full",
580
+ order_addresses: "full",
581
+ payments: "read",
582
+ taxes: "read",
583
+ attributes: "read"
584
+ }),
585
+ dashboard: read(),
586
+ settings: read(),
587
+ team: none(),
588
+ roles: none()
589
+ }
590
+ },
591
+ {
592
+ name: "Catalog Staff",
593
+ description: "Manage products and catalog content",
594
+ isSystem: true,
595
+ isOwnerRole: false,
596
+ permissions: {
597
+ ...storePerms({
598
+ products: "full",
599
+ collections: "full",
600
+ brands: "full",
601
+ product_categories: "full",
602
+ attributes: "full",
603
+ orders: "read",
604
+ vendor_customers: "read"
605
+ }),
606
+ dashboard: read(),
607
+ settings: none(),
608
+ team: none(),
609
+ roles: none()
610
+ }
611
+ },
612
+ {
613
+ name: "Fulfillment",
614
+ description: "Orders and fulfillment",
615
+ isSystem: true,
616
+ isOwnerRole: false,
617
+ permissions: {
618
+ ...storePerms({
619
+ products: "read",
620
+ orders: "full",
621
+ order_addresses: "full",
622
+ order_discounts: "read",
623
+ vendor_customers: "read"
624
+ }),
625
+ dashboard: read(),
626
+ settings: none(),
627
+ team: none(),
628
+ roles: none()
629
+ }
630
+ },
631
+ {
632
+ name: "Viewer",
633
+ description: "Read-only access to store data",
634
+ isSystem: true,
635
+ isOwnerRole: false,
636
+ permissions: {
637
+ ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
638
+ e,
639
+ "read"
640
+ ]))),
641
+ dashboard: read(),
642
+ settings: none(),
643
+ team: none(),
644
+ roles: none()
645
+ }
646
+ }
647
+ ];
648
+ }
649
+ });
650
+
651
+ // src/auth/rbac-debug.ts
652
+ function summarizeEntityPerms(entityPerms) {
653
+ const out = {};
654
+ if (!entityPerms) return out;
655
+ for (const [entity, p] of Object.entries(entityPerms)) {
656
+ out[entity] = {
657
+ c: p.c,
658
+ r: p.r,
659
+ u: p.u,
660
+ d: p.d
661
+ };
662
+ }
663
+ return out;
664
+ }
665
+ function vendorPortalFallbackReason(user, entity, action) {
666
+ if (!isVendorPortalUser(user)) return null;
667
+ if (entity === "dashboard" && action === "read") return "vendor_portal_dashboard_fallback";
668
+ if (entity === "analytics" && action === "read") return "vendor_portal_analytics_fallback";
669
+ if (entity === "settings" && action === "read") return "vendor_portal_settings_read_fallback";
670
+ if (entity === "upload" && (action === "create" || action === "read")) {
671
+ return "vendor_portal_upload_fallback";
672
+ }
673
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity) && action === "read") return "vendor_portal_store_read_fallback";
674
+ return null;
675
+ }
676
+ function explainSessionEntityAccess(user, entity, action) {
677
+ const userSnapshot = {
678
+ email: user?.email ?? null,
679
+ id: user?.id ?? null,
680
+ groupId: user?.groupId ?? null,
681
+ groupName: user?.groupName ?? null,
682
+ isRBACAdmin: user?.isRBACAdmin ?? false,
683
+ adminAccess: user?.adminAccess ?? null,
684
+ vendorIds: user?.vendorIds ?? [],
685
+ activeVendorId: user?.activeVendorId ?? null,
686
+ vendorRole: user?.vendorRole ?? null,
687
+ vendorRoleId: user?.vendorRoleId ?? null,
688
+ vendorRoleName: user?.vendorRoleName ?? null,
689
+ isVendorRoleOwner: user?.isVendorRoleOwner ?? false,
690
+ vendorEntityPerms: summarizeEntityPerms(user?.vendorEntityPerms),
691
+ isVendorPortal: user?.isVendorPortal ?? false,
692
+ isVendorOwner: user?.isVendorOwner ?? false,
693
+ isVendorPortalComputed: isVendorPortalUser(user),
694
+ isVendorOwnerComputed: isVendorOwner(user),
695
+ jwtEntityPerms: summarizeEntityPerms(user?.entityPerms),
696
+ jwtEntityPermForTarget: user?.entityPerms?.[entity] ?? null
697
+ };
698
+ if (!user?.email) {
699
+ return {
700
+ allowed: false,
701
+ reason: "no_session_email",
702
+ userSnapshot
703
+ };
704
+ }
705
+ if (user.isRBACAdmin) {
706
+ return {
707
+ allowed: true,
708
+ reason: "platform_administrator",
709
+ userSnapshot
710
+ };
711
+ }
712
+ if ((isVendorPortalUser(user) || (user.vendorIds?.length ?? 0) > 0) && entity === "upload" && (action === "create" || action === "read")) {
713
+ return {
714
+ allowed: true,
715
+ reason: "vendor_portal_upload",
716
+ userSnapshot
717
+ };
718
+ }
719
+ const vendorIds = user.vendorIds ?? [];
720
+ const isStaff = user.vendorRole === "staff";
721
+ const vendorPerms = user.vendorEntityPerms;
722
+ const hasVendorRolePerms = vendorPerms != null && Object.keys(vendorPerms).length > 0;
723
+ const enforceVendorRoleMatrix = vendorIds.length > 0 && (hasVendorRolePerms || user.vendorRoleId != null);
724
+ if (enforceVendorRoleMatrix) {
725
+ const vendorRbacSet = new Set(VENDOR_RBAC_ENTITIES);
726
+ if (vendorRbacSet.has(entity) || entity === "analytics") {
727
+ if (entity === "analytics" && action === "read") {
728
+ const allowed3 = hasEntityPermission(vendorPerms, "dashboard", "read");
729
+ return {
730
+ allowed: allowed3,
731
+ reason: allowed3 ? "vendor_role_analytics_via_dashboard" : "vendor_role_analytics_denied",
732
+ userSnapshot
733
+ };
734
+ }
735
+ if (user.isVendorRoleOwner === true) {
736
+ return {
737
+ allowed: true,
738
+ reason: "vendor_owner_role_full",
739
+ userSnapshot
740
+ };
741
+ }
742
+ const allowed2 = hasEntityPermission(vendorPerms, entity, action);
743
+ return {
744
+ allowed: allowed2,
745
+ reason: allowed2 ? "vendor_role_permissions_allow" : "vendor_role_permissions_deny",
746
+ userSnapshot
747
+ };
748
+ }
749
+ }
750
+ if (vendorIds.length > 0) {
751
+ if (entity === "dashboard" && action === "read") {
752
+ return {
753
+ allowed: true,
754
+ reason: "vendor_link_dashboard_read",
755
+ userSnapshot
756
+ };
757
+ }
758
+ if (entity === "analytics" && action === "read") {
759
+ return {
760
+ allowed: true,
761
+ reason: "vendor_link_analytics_read",
762
+ userSnapshot
763
+ };
764
+ }
765
+ if (entity === "settings") {
766
+ if (action === "read") {
767
+ return {
768
+ allowed: true,
769
+ reason: "vendor_link_settings_read",
770
+ userSnapshot
771
+ };
772
+ }
773
+ if (action === "update" && !isStaff) {
774
+ return {
775
+ allowed: true,
776
+ reason: "vendor_link_settings_update",
777
+ userSnapshot
778
+ };
779
+ }
780
+ }
781
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
782
+ if (isStaff && action === "read") {
783
+ return {
784
+ allowed: true,
785
+ reason: "vendor_staff_store_read",
786
+ userSnapshot
787
+ };
788
+ }
789
+ if (!isStaff) {
790
+ return {
791
+ allowed: true,
792
+ reason: "vendor_link_store_full",
793
+ userSnapshot
794
+ };
795
+ }
796
+ return {
797
+ allowed: false,
798
+ reason: "vendor_staff_store_write_denied",
799
+ userSnapshot
800
+ };
801
+ }
802
+ }
803
+ if (isVendorPortalUser(user) && isVendorOwner(user)) {
804
+ if (entity === "dashboard" && action === "read") {
805
+ return {
806
+ allowed: true,
807
+ reason: "vendor_owner_group_dashboard",
808
+ userSnapshot
809
+ };
810
+ }
811
+ if (entity === "analytics" && action === "read") {
812
+ return {
813
+ allowed: true,
814
+ reason: "vendor_owner_group_analytics",
815
+ userSnapshot
816
+ };
817
+ }
818
+ if (entity === "settings" && (action === "read" || action === "update")) {
819
+ return {
820
+ allowed: true,
821
+ reason: "vendor_owner_group_settings",
822
+ userSnapshot
823
+ };
824
+ }
825
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
826
+ return {
827
+ allowed: true,
828
+ reason: "vendor_owner_group_store",
829
+ userSnapshot
830
+ };
831
+ }
832
+ }
833
+ const vendorPortalReason = vendorPortalFallbackReason(user, entity, action);
834
+ if (vendorPortalReason) {
835
+ return {
836
+ allowed: true,
837
+ reason: vendorPortalReason,
838
+ userSnapshot
839
+ };
840
+ }
841
+ const explicit = user.entityPerms?.[entity];
842
+ if (explicit !== void 0) {
843
+ const allowed2 = hasEntityPermission(user.entityPerms, entity, action);
844
+ return {
845
+ allowed: allowed2,
846
+ reason: allowed2 ? "jwt_entity_perms_allow" : "jwt_entity_perms_deny",
847
+ userSnapshot
848
+ };
849
+ }
850
+ const allowed = hasEntityPermission(user.entityPerms, entity, action);
851
+ return {
852
+ allowed,
853
+ reason: allowed ? "entity_perms_fallback_allow" : "no_matching_rule",
854
+ userSnapshot
855
+ };
856
+ }
857
+ function sessionHasEntityAccessFromExplanation(user, entity, action) {
858
+ return explainSessionEntityAccess(user, entity, action).allowed;
859
+ }
860
+ var init_rbac_debug = __esm({
861
+ "src/auth/rbac-debug.ts"() {
862
+ init_permission_entities();
863
+ init_vendor_role_defaults();
864
+ init_vendor_scope();
865
+ __name(summarizeEntityPerms, "summarizeEntityPerms");
866
+ __name(vendorPortalFallbackReason, "vendorPortalFallbackReason");
867
+ __name(explainSessionEntityAccess, "explainSessionEntityAccess");
868
+ __name(sessionHasEntityAccessFromExplanation, "sessionHasEntityAccessFromExplanation");
869
+ }
870
+ });
473
871
  var defaultValue; exports.AdminConfigContext = void 0;
474
872
  var init_admin_config_context = __esm({
475
873
  "src/admin/admin-config-context.tsx"() {
@@ -489,7 +887,7 @@ var init_admin_config_context = __esm({
489
887
  var CMS_VERSION;
490
888
  var init_cms_version = __esm({
491
889
  "src/lib/cms-version.ts"() {
492
- CMS_VERSION = "1.0.47" ;
890
+ CMS_VERSION = "1.0.51" ;
493
891
  }
494
892
  });
495
893
  function useCatalogCategories(enabled = true) {
@@ -624,6 +1022,10 @@ function AdminSidebar({ variant = "sidebar" }) {
624
1022
  const sectionCls7 = "mb-5";
625
1023
  const headingCls = "text-[11px] font-semibold text-gray-400 uppercase tracking-wider px-2.5 mb-1.5";
626
1024
  const asideCls = isDrawer ? "w-full h-full min-h-0 bg-white flex flex-col overflow-hidden" : "w-52 h-full min-h-0 bg-white border-r border-gray-200 flex-shrink-0 flex flex-col overflow-hidden";
1025
+ const canReadEntity = /* @__PURE__ */ __name((entity) => {
1026
+ if (!sessionUser) return true;
1027
+ return sessionHasEntityAccessFromExplanation(sessionUser, entity, "read");
1028
+ }, "canReadEntity");
627
1029
  return /* @__PURE__ */ React.createElement("aside", {
628
1030
  className: asideCls
629
1031
  }, /* @__PURE__ */ React.createElement("div", {
@@ -641,30 +1043,40 @@ function AdminSidebar({ variant = "sidebar" }) {
641
1043
  className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
642
1044
  }, /* @__PURE__ */ React.createElement(LucideIcons.LayoutDashboard, {
643
1045
  className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
644
- }), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) => /* @__PURE__ */ React.createElement("div", {
645
- key: section.title,
646
- className: sectionCls7
647
- }, /* @__PURE__ */ React.createElement("h3", {
648
- className: headingCls
649
- }, section.title), /* @__PURE__ */ React.createElement("ul", {
650
- className: "space-y-0.5"
651
- }, section.items.map((item) => {
652
- const Icon2 = getIconForItem(item.icon);
653
- return /* @__PURE__ */ React.createElement("li", {
654
- key: item.href
655
- }, /* @__PURE__ */ React.createElement(Link2__default.default, {
656
- href: item.href,
657
- className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
658
- }, /* @__PURE__ */ React.createElement(Icon2, {
659
- className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
660
- }), item.label));
661
- })))), showPlatformNav && customNavSections.length === 0 && customNavItems.length > 0 && /* @__PURE__ */ React.createElement("div", {
1046
+ }), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) => {
1047
+ const filteredItems = section.items.filter((item) => {
1048
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1049
+ return canReadEntity(entity);
1050
+ });
1051
+ if (filteredItems.length === 0) return null;
1052
+ return /* @__PURE__ */ React.createElement("div", {
1053
+ key: section.title,
1054
+ className: sectionCls7
1055
+ }, /* @__PURE__ */ React.createElement("h3", {
1056
+ className: headingCls
1057
+ }, section.title), /* @__PURE__ */ React.createElement("ul", {
1058
+ className: "space-y-0.5"
1059
+ }, filteredItems.map((item) => {
1060
+ const Icon2 = getIconForItem(item.icon);
1061
+ return /* @__PURE__ */ React.createElement("li", {
1062
+ key: item.href
1063
+ }, /* @__PURE__ */ React.createElement(Link2__default.default, {
1064
+ href: item.href,
1065
+ className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
1066
+ }, /* @__PURE__ */ React.createElement(Icon2, {
1067
+ className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
1068
+ }), item.label));
1069
+ })));
1070
+ }), showPlatformNav && customNavSections.length === 0 && customNavItems.length > 0 && /* @__PURE__ */ React.createElement("div", {
662
1071
  className: sectionCls7
663
1072
  }, /* @__PURE__ */ React.createElement("h3", {
664
1073
  className: headingCls
665
1074
  }, "Custom"), /* @__PURE__ */ React.createElement("ul", {
666
1075
  className: "space-y-0.5"
667
- }, customNavItems.map((item) => {
1076
+ }, customNavItems.filter((item) => {
1077
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1078
+ return canReadEntity(entity);
1079
+ }).map((item) => {
668
1080
  const Icon2 = getIconForItem(item.icon);
669
1081
  return /* @__PURE__ */ React.createElement("li", {
670
1082
  key: item.href
@@ -685,85 +1097,92 @@ function AdminSidebar({ variant = "sidebar" }) {
685
1097
  className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
686
1098
  }, /* @__PURE__ */ React.createElement(LucideIcons.LayoutDashboard, {
687
1099
  className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
688
- }), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) => /* @__PURE__ */ React.createElement("div", {
689
- key: section.title,
690
- className: sectionCls7
691
- }, /* @__PURE__ */ React.createElement("h3", {
692
- className: headingCls
693
- }, section.title), /* @__PURE__ */ React.createElement("ul", {
694
- className: "space-y-0.5"
695
- }, section.items.map((item) => {
696
- const Icon2 = getIconForItem(item.icon);
697
- return /* @__PURE__ */ React.createElement("li", {
698
- key: item.href
699
- }, /* @__PURE__ */ React.createElement(Link2__default.default, {
700
- href: item.href,
701
- className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
702
- }, /* @__PURE__ */ React.createElement(Icon2, {
703
- className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
704
- }), item.label));
705
- })))), showStoreNav && /* @__PURE__ */ React.createElement("div", {
1100
+ }), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) => {
1101
+ const filteredItems = section.items.filter((item) => {
1102
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1103
+ return canReadEntity(entity);
1104
+ });
1105
+ if (filteredItems.length === 0) return null;
1106
+ return /* @__PURE__ */ React.createElement("div", {
1107
+ key: section.title,
1108
+ className: sectionCls7
1109
+ }, /* @__PURE__ */ React.createElement("h3", {
1110
+ className: headingCls
1111
+ }, section.title), /* @__PURE__ */ React.createElement("ul", {
1112
+ className: "space-y-0.5"
1113
+ }, filteredItems.map((item) => {
1114
+ const Icon2 = getIconForItem(item.icon);
1115
+ return /* @__PURE__ */ React.createElement("li", {
1116
+ key: item.href
1117
+ }, /* @__PURE__ */ React.createElement(Link2__default.default, {
1118
+ href: item.href,
1119
+ className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
1120
+ }, /* @__PURE__ */ React.createElement(Icon2, {
1121
+ className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
1122
+ }), item.label));
1123
+ })));
1124
+ }), showStoreNav && /* @__PURE__ */ React.createElement("div", {
706
1125
  className: sectionCls7
707
1126
  }, /* @__PURE__ */ React.createElement("h3", {
708
1127
  className: headingCls
709
1128
  }, "Store"), /* @__PURE__ */ React.createElement("ul", {
710
1129
  className: "space-y-0.5"
711
- }, showVendorOnboard && multiVendorEnabled !== false && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1130
+ }, showVendorOnboard && multiVendorEnabled !== false && canReadEntity("vendors") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
712
1131
  href: "/admin/vendors",
713
1132
  className: `${linkCls} ${isActive("/admin/vendors") ? linkActive : linkInactive}`
714
1133
  }, /* @__PURE__ */ React.createElement(LucideIcons.Store, {
715
1134
  className: `h-4 w-4 mr-2 ${isActive("/admin/vendors") ? iconActive : iconInactive}`
716
- }), "Vendors")), showVendorCategories && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1135
+ }), "Vendors")), showVendorCategories && canReadEntity("product_categories") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
717
1136
  href: "/admin/product_categories",
718
1137
  className: `${linkCls} ${isActive("/admin/product_categories") ? linkActive : linkInactive}`
719
1138
  }, /* @__PURE__ */ React.createElement(LucideIcons.FolderTree, {
720
1139
  className: `h-4 w-4 mr-2 ${isActive("/admin/product_categories") ? iconActive : iconInactive}`
721
- }), "Categories")), showVendorCollections && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1140
+ }), "Categories")), showVendorCollections && canReadEntity("collections") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
722
1141
  href: "/admin/collections",
723
1142
  className: `${linkCls} ${isActive("/admin/collections") ? linkActive : linkInactive}`
724
1143
  }, /* @__PURE__ */ React.createElement(LucideIcons.Layers, {
725
1144
  className: `h-4 w-4 mr-2 ${isActive("/admin/collections") ? iconActive : iconInactive}`
726
- }), "Collections")), showVendorBrands && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1145
+ }), "Collections")), showVendorBrands && canReadEntity("brands") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
727
1146
  href: "/admin/brands",
728
1147
  className: `${linkCls} ${isActive("/admin/brands") ? linkActive : linkInactive}`
729
1148
  }, /* @__PURE__ */ React.createElement(LucideIcons.Building2, {
730
1149
  className: `h-4 w-4 mr-2 ${isActive("/admin/brands") ? iconActive : iconInactive}`
731
- }), "Brands")), !hasCustomEventsNav && eventsEnabled !== false ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1150
+ }), "Brands")), !hasCustomEventsNav && eventsEnabled !== false && canReadEntity("events") ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
732
1151
  href: "/admin/events",
733
1152
  className: `${linkCls} ${isActive("/admin/events") ? linkActive : linkInactive}`
734
1153
  }, /* @__PURE__ */ React.createElement(LucideIcons.CalendarDays, {
735
1154
  className: `h-4 w-4 mr-2 ${isActive("/admin/events") ? iconActive : iconInactive}`
736
- }), "Events")) : null, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1155
+ }), "Events")) : null, canReadEntity("products") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
737
1156
  href: "/admin/products",
738
1157
  className: `${linkCls} ${isActive("/admin/products") ? linkActive : linkInactive}`
739
1158
  }, /* @__PURE__ */ React.createElement(LucideIcons.ShoppingBag, {
740
1159
  className: `h-4 w-4 mr-2 ${isActive("/admin/products") ? iconActive : iconInactive}`
741
- }), "Products")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1160
+ }), "Products")), canReadEntity("combos") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
742
1161
  href: "/admin/combos",
743
1162
  className: `${linkCls} ${isActive("/admin/combos") ? linkActive : linkInactive}`
744
1163
  }, /* @__PURE__ */ React.createElement(LucideIcons.Package, {
745
1164
  className: `h-4 w-4 mr-2 ${isActive("/admin/combos") ? iconActive : iconInactive}`
746
- }), "Combos")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1165
+ }), "Combos")), canReadEntity("taxes") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
747
1166
  href: "/admin/taxes",
748
1167
  className: `${linkCls} ${isActive("/admin/taxes") ? linkActive : linkInactive}`
749
1168
  }, /* @__PURE__ */ React.createElement(LucideIcons.Receipt, {
750
1169
  className: `h-4 w-4 mr-2 ${isActive("/admin/taxes") ? iconActive : iconInactive}`
751
- }), "Taxes")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1170
+ }), "Taxes")), canReadEntity("discounts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
752
1171
  href: "/admin/discounts",
753
1172
  className: `${linkCls} ${isActive("/admin/discounts") ? linkActive : linkInactive}`
754
1173
  }, /* @__PURE__ */ React.createElement(LucideIcons.BadgePercent, {
755
1174
  className: `h-4 w-4 mr-2 ${isActive("/admin/discounts") ? iconActive : iconInactive}`
756
- }), "Discounts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1175
+ }), "Discounts")), canReadEntity("orders") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
757
1176
  href: "/admin/orders",
758
1177
  className: `${linkCls} ${isActive("/admin/orders") ? linkActive : linkInactive}`
759
1178
  }, /* @__PURE__ */ React.createElement(LucideIcons.ShoppingCart, {
760
1179
  className: `h-4 w-4 mr-2 ${isActive("/admin/orders") ? iconActive : iconInactive}`
761
- }), "Orders")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1180
+ }), "Orders")), canReadEntity("payments") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
762
1181
  href: "/admin/payments",
763
1182
  className: `${linkCls} ${isActive("/admin/payments") ? linkActive : linkInactive}`
764
1183
  }, /* @__PURE__ */ React.createElement(LucideIcons.CreditCard, {
765
1184
  className: `h-4 w-4 mr-2 ${isActive("/admin/payments") ? iconActive : iconInactive}`
766
- }), "Payments")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1185
+ }), "Payments")), canReadEntity("vendor_customers") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
767
1186
  href: "/admin/vendor_customers",
768
1187
  className: `${linkCls} ${isActive("/admin/vendor_customers") ? linkActive : linkInactive}`
769
1188
  }, /* @__PURE__ */ React.createElement(LucideIcons.Users, {
@@ -774,32 +1193,32 @@ function AdminSidebar({ variant = "sidebar" }) {
774
1193
  className: headingCls
775
1194
  }, "Management"), /* @__PURE__ */ React.createElement("ul", {
776
1195
  className: "space-y-0.5"
777
- }, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1196
+ }, canReadEntity("contacts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
778
1197
  href: "/admin/contacts",
779
1198
  className: `${linkCls} ${isActive("/admin/contacts") ? linkActive : linkInactive}`
780
1199
  }, /* @__PURE__ */ React.createElement(LucideIcons.Inbox, {
781
1200
  className: `h-4 w-4 mr-2 ${isActive("/admin/contacts") ? iconActive : iconInactive}`
782
- }), "Contacts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1201
+ }), "Contacts")), canReadEntity("blogs") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
783
1202
  href: "/admin/blogs",
784
1203
  className: `${linkCls} ${isActive("/admin/blogs") ? linkActive : linkInactive}`
785
1204
  }, /* @__PURE__ */ React.createElement(LucideIcons.File, {
786
1205
  className: `h-4 w-4 mr-2 ${isActive("/admin/blogs") ? iconActive : iconInactive}`
787
- }), "Blogs")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1206
+ }), "Blogs")), canReadEntity("pages") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
788
1207
  href: "/admin/pages",
789
1208
  className: `${linkCls} ${isActive("/admin/pages") ? linkActive : linkInactive}`
790
1209
  }, /* @__PURE__ */ React.createElement(LucideIcons.LinkIcon, {
791
1210
  className: `h-4 w-4 mr-2 ${isActive("/admin/pages") ? iconActive : iconInactive}`
792
- }), "Pages")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1211
+ }), "Pages")), canReadEntity("forms") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
793
1212
  href: "/admin/forms",
794
1213
  className: `${linkCls} ${isActive("/admin/forms") ? linkActive : linkInactive}`
795
1214
  }, /* @__PURE__ */ React.createElement(LucideIcons.ClipboardList, {
796
1215
  className: `h-4 w-4 mr-2 ${isActive("/admin/forms") ? iconActive : iconInactive}`
797
- }), "Forms")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1216
+ }), "Forms")), canReadEntity("form_submissions") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
798
1217
  href: "/admin/submissions",
799
1218
  className: `${linkCls} ${isActive("/admin/submissions") ? linkActive : linkInactive}`
800
1219
  }, /* @__PURE__ */ React.createElement(LucideIcons.MessageSquare, {
801
1220
  className: `h-4 w-4 mr-2 ${isActive("/admin/submissions") ? iconActive : iconInactive}`
802
- }), "Submissions")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1221
+ }), "Submissions")), canReadEntity("upload") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
803
1222
  href: "/admin/media",
804
1223
  className: `${linkCls} ${isActive("/admin/media") ? linkActive : linkInactive}`
805
1224
  }, /* @__PURE__ */ React.createElement(LucideIcons.Image, {
@@ -836,12 +1255,12 @@ function AdminSidebar({ variant = "sidebar" }) {
836
1255
  className: headingCls
837
1256
  }, "System"), /* @__PURE__ */ React.createElement("ul", {
838
1257
  className: "space-y-0.5"
839
- }, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1258
+ }, canReadEntity("users") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
840
1259
  href: "/admin/users",
841
1260
  className: `${linkCls} ${isActive("/admin/users") ? linkActive : linkInactive}`
842
1261
  }, /* @__PURE__ */ React.createElement(LucideIcons.Users, {
843
1262
  className: `h-4 w-4 mr-2 ${isActive("/admin/users") ? iconActive : iconInactive}`
844
- }), "Users")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
1263
+ }), "Users")), canReadEntity("roles") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
845
1264
  href: "/admin/roles",
846
1265
  className: `${linkCls} ${isActive("/admin/roles") ? linkActive : linkInactive}`
847
1266
  }, /* @__PURE__ */ React.createElement(LucideIcons.Shield, {
@@ -888,6 +1307,7 @@ var init_Sidebar = __esm({
888
1307
  "src/components/Admin/Sidebar.tsx"() {
889
1308
  "use client";
890
1309
  init_vendor_scope();
1310
+ init_rbac_debug();
891
1311
  init_admin_config_context();
892
1312
  init_infuro_favicon();
893
1313
  init_cms_version();
@@ -1180,6 +1600,15 @@ var init_builtin_plugin_descriptors = __esm({
1180
1600
  enabled: true,
1181
1601
  icon: "event_notifications",
1182
1602
  settingsGroup: "event_notifications"
1603
+ },
1604
+ {
1605
+ name: "auth_providers",
1606
+ label: "Authentication Provider",
1607
+ version: "1.0.0",
1608
+ description: "Enable Google sign-in for admin and vendor users (existing accounts only).",
1609
+ enabled: true,
1610
+ icon: "auth_providers",
1611
+ settingsGroup: "auth_providers"
1183
1612
  }
1184
1613
  ];
1185
1614
  }
@@ -1505,7 +1934,15 @@ function useEventsSettings() {
1505
1934
  requireEventApproval
1506
1935
  };
1507
1936
  }
1508
- function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, theme, themeRegistry, pluginDescriptors = [] }) {
1937
+ function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, extraNotificationVariables, theme, themeRegistry, pluginDescriptors = [] }) {
1938
+ React25.useEffect(() => {
1939
+ document.documentElement.style.overflow = "hidden";
1940
+ document.body.style.overflow = "hidden";
1941
+ return () => {
1942
+ document.documentElement.style.overflow = "";
1943
+ document.body.style.overflow = "";
1944
+ };
1945
+ }, []);
1509
1946
  const resolvedTheme = useResolvedTheme(theme, themeRegistry);
1510
1947
  const { storeEnabled, currency } = useStoreEnabled();
1511
1948
  const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
@@ -1526,6 +1963,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1526
1963
  customCrudConfigs,
1527
1964
  categoryRelatedProductLabels,
1528
1965
  renderOrderDetailFooter,
1966
+ extraNotificationVariables,
1529
1967
  theme: resolvedTheme,
1530
1968
  themeRegistry,
1531
1969
  pluginDescriptors: mergedPluginDescriptors,
@@ -1544,6 +1982,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1544
1982
  customCrudConfigs,
1545
1983
  categoryRelatedProductLabels,
1546
1984
  renderOrderDetailFooter,
1985
+ extraNotificationVariables,
1547
1986
  resolvedTheme,
1548
1987
  themeRegistry,
1549
1988
  mergedPluginDescriptors,
@@ -4400,6 +4839,8 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4400
4839
  const hasLoadedRef = React25.useRef(false);
4401
4840
  const isMobile = useIsMobile();
4402
4841
  const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
4842
+ const { data: session } = react.useSession();
4843
+ const sessionUser = session?.user;
4403
4844
  const listColumns = React25.useMemo(() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [], [
4404
4845
  columns
4405
4846
  ]);
@@ -4940,6 +5381,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4940
5381
  withListFrom
4941
5382
  ]);
4942
5383
  const resourceName = apiEndpoint.replace("/api/", "");
5384
+ const canCreate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "create");
5385
+ const canUpdate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "update");
5386
+ const canDelete = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "delete");
4943
5387
  const dedicatedRecordEditHref = /* @__PURE__ */ __name((recordId) => addEditPage && (resourceName === "orders" || resourceName === "events") ? withListFrom(`${addEditPageUrl}/${recordId}/edit`) : addEditPage ? withListFrom(`${addEditPageUrl}/${recordId}`) : "", "dedicatedRecordEditHref");
4944
5388
  const showDedicatedDuplicate = addEditPage && !CRUD_NO_DUPLICATE_RESOURCES.has(resourceName);
4945
5389
  const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
@@ -5099,15 +5543,15 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5099
5543
  onClick: /* @__PURE__ */ __name(() => setBulkDialogOpen(true), "onClick"),
5100
5544
  variant: "outline",
5101
5545
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5102
- }, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
5546
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Download, {
5103
5547
  className: "h-3.5 w-3.5 mr-1"
5104
5548
  }), "Import"), /* @__PURE__ */ React.createElement(Button, {
5105
5549
  onClick: handleExport,
5106
5550
  variant: "outline",
5107
5551
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5108
- }, /* @__PURE__ */ React.createElement(LucideIcons.Download, {
5552
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
5109
5553
  className: "h-3.5 w-3.5 mr-1"
5110
- }), "Export"), !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5554
+ }), "Export"), canCreate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5111
5555
  onClick: /* @__PURE__ */ __name(() => {
5112
5556
  setEditingItem(null);
5113
5557
  setDuplicateSeed(null);
@@ -5116,7 +5560,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5116
5560
  className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8"
5117
5561
  }, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
5118
5562
  className: "h-3.5 w-3.5"
5119
- }), "Add"), addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2__default.default, {
5563
+ }), "Add"), canCreate && addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2__default.default, {
5120
5564
  href: dedicatedCreateHref,
5121
5565
  className: "inline-flex items-center gap-1 bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8 px-3 rounded-md font-medium"
5122
5566
  }, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
@@ -5275,7 +5719,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5275
5719
  onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
5276
5720
  }, /* @__PURE__ */ React.createElement("div", {
5277
5721
  className: "flex items-center justify-center gap-1"
5278
- }, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5722
+ }, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, canUpdate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5279
5723
  variant: "outline",
5280
5724
  size: "icon",
5281
5725
  className: "h-7 w-7",
@@ -5285,7 +5729,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5285
5729
  className: "h-3.5 w-3.5"
5286
5730
  }), /* @__PURE__ */ React.createElement("span", {
5287
5731
  className: "sr-only"
5288
- }, "Edit")), addEditPage && /* @__PURE__ */ React.createElement(Button, {
5732
+ }, "Edit")), canUpdate && addEditPage && /* @__PURE__ */ React.createElement(Button, {
5289
5733
  variant: "outline",
5290
5734
  size: "icon",
5291
5735
  className: "h-7 w-7",
@@ -5295,7 +5739,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5295
5739
  className: "h-3.5 w-3.5"
5296
5740
  }), /* @__PURE__ */ React.createElement("span", {
5297
5741
  className: "sr-only"
5298
- }, "Edit")), showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5742
+ }, "Edit")), canCreate && showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5299
5743
  variant: "outline",
5300
5744
  size: "icon",
5301
5745
  className: "h-7 w-7",
@@ -5321,7 +5765,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5321
5765
  className: "h-3.5 w-3.5"
5322
5766
  }), /* @__PURE__ */ React.createElement("span", {
5323
5767
  className: "sr-only"
5324
- }, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
5768
+ }, "Resend invite")), canDelete && /* @__PURE__ */ React.createElement(Button, {
5325
5769
  variant: "outline",
5326
5770
  size: "icon",
5327
5771
  className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
@@ -5459,6 +5903,7 @@ var init_CRUD = __esm({
5459
5903
  init_dropdown_menu();
5460
5904
  init_utils();
5461
5905
  init_use_mobile();
5906
+ init_rbac_debug();
5462
5907
  init_BulkUploadDialog();
5463
5908
  init_admin_list_return_url();
5464
5909
  init_build_crud_list_filters_from_columns();
@@ -9174,28 +9619,75 @@ var init_AuthPageLayout = __esm({
9174
9619
  }
9175
9620
  });
9176
9621
 
9622
+ // src/auth/auth-providers-settings.ts
9623
+ function parseEnabled(raw) {
9624
+ if (typeof raw === "boolean") return raw;
9625
+ if (typeof raw === "number") return raw !== 0;
9626
+ if (typeof raw === "string") {
9627
+ const n = raw.trim().toLowerCase();
9628
+ return n === "true" || n === "1" || n === "yes" || n === "on";
9629
+ }
9630
+ return false;
9631
+ }
9632
+ function isGoogleAuthPubliclyEnabled(settings) {
9633
+ if (!settings) return false;
9634
+ if (settings.googleEnabled !== void 0 && settings.googleEnabled !== "") {
9635
+ return parseEnabled(settings.googleEnabled);
9636
+ }
9637
+ return parseEnabled(settings.enabled);
9638
+ }
9639
+ var init_auth_providers_settings = __esm({
9640
+ "src/auth/auth-providers-settings.ts"() {
9641
+ init_auth_debug();
9642
+ __name(parseEnabled, "parseEnabled");
9643
+ __name(isGoogleAuthPubliclyEnabled, "isGoogleAuthPubliclyEnabled");
9644
+ }
9645
+ });
9646
+
9177
9647
  // src/admin/pages/SignInPage.tsx
9178
9648
  var SignInPage_exports = {};
9179
9649
  __export(SignInPage_exports, {
9180
9650
  default: () => exports.AdminSignInPage
9181
9651
  });
9652
+ function oauthErrorMessage(code) {
9653
+ if (!code) return "";
9654
+ const c = code.toLowerCase();
9655
+ if (c === "accessdenied" || c === "access_denied") {
9656
+ return "No account for this Google email. Ask an admin to invite you first.";
9657
+ }
9658
+ if (c === "oauthaccountnotlinked") {
9659
+ return "This Google account is not linked to an existing user. Sign in with email/password or ask an admin to invite you.";
9660
+ }
9661
+ if (c === "oauthcallback" || c === "oauthsignin" || c === "oauthcreateaccount") {
9662
+ return "Google sign-in failed. Check Authentication Provider settings and the redirect URI in Google Cloud Console.";
9663
+ }
9664
+ if (c === "configuration") {
9665
+ return "Google sign-in is not configured correctly.";
9666
+ }
9667
+ return "Invalid email or password";
9668
+ }
9182
9669
  var SigninPage; exports.AdminSignInPage = void 0;
9183
9670
  var init_SignInPage = __esm({
9184
9671
  "src/admin/pages/SignInPage.tsx"() {
9185
9672
  "use client";
9186
9673
  init_AuthPageLayout();
9187
9674
  init_auth_debug();
9675
+ init_auth_providers_settings();
9676
+ __name(oauthErrorMessage, "oauthErrorMessage");
9188
9677
  SigninPage = /* @__PURE__ */ __name(() => {
9189
9678
  const [email, setEmail] = React25.useState("");
9190
9679
  const [password, setPassword] = React25.useState("");
9191
9680
  const [error, setError] = React25.useState("");
9192
9681
  const [success, setSuccess] = React25.useState("");
9193
9682
  const [loading, setLoading] = React25.useState(false);
9683
+ const [googleLoading, setGoogleLoading] = React25.useState(false);
9684
+ const [googleEnabled, setGoogleEnabled] = React25.useState(false);
9194
9685
  const { status } = react.useSession();
9195
9686
  const searchParams = navigation.useSearchParams();
9196
9687
  React25.useEffect(() => {
9197
- if (searchParams.get("error")) {
9198
- setError("Invalid email or password");
9688
+ const err = searchParams.get("error");
9689
+ if (err) {
9690
+ setError(oauthErrorMessage(err));
9199
9691
  }
9200
9692
  if (searchParams.get("activated") === "1") {
9201
9693
  setSuccess("Account activated. Sign in with your new password.");
@@ -9206,6 +9698,23 @@ var init_SignInPage = __esm({
9206
9698
  }, [
9207
9699
  searchParams
9208
9700
  ]);
9701
+ React25.useEffect(() => {
9702
+ let cancelled = false;
9703
+ (async () => {
9704
+ try {
9705
+ const res = await fetch("/api/settings/auth_providers", {
9706
+ cache: "no-store"
9707
+ });
9708
+ if (!res.ok) return;
9709
+ const data = await res.json();
9710
+ if (!cancelled) setGoogleEnabled(isGoogleAuthPubliclyEnabled(data));
9711
+ } catch {
9712
+ }
9713
+ })();
9714
+ return () => {
9715
+ cancelled = true;
9716
+ };
9717
+ }, []);
9209
9718
  React25.useEffect(() => {
9210
9719
  logAuthClient("SignInPage session status", {
9211
9720
  status
@@ -9263,6 +9772,19 @@ var init_SignInPage = __esm({
9263
9772
  setLoading(false);
9264
9773
  }
9265
9774
  }, "handleSubmit");
9775
+ const handleGoogle = /* @__PURE__ */ __name(async () => {
9776
+ setGoogleLoading(true);
9777
+ setError("");
9778
+ try {
9779
+ logAuthClient("SignInPage Google signIn start");
9780
+ await react.signIn("google", {
9781
+ callbackUrl: "/admin/dashboard"
9782
+ });
9783
+ } catch {
9784
+ setError("Google sign-in failed. Please try again.");
9785
+ setGoogleLoading(false);
9786
+ }
9787
+ }, "handleGoogle");
9266
9788
  return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
9267
9789
  className: "w-full shadow-md rounded-lg bg-white px-4 py-6 sm:px-6 sm:py-8"
9268
9790
  }, /* @__PURE__ */ React.createElement("h3", {
@@ -9315,9 +9837,42 @@ var init_SignInPage = __esm({
9315
9837
  className: "text-xs font-medium text-gray-600 hover:underline"
9316
9838
  }, "Forgot password?")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", {
9317
9839
  type: "submit",
9318
- disabled: loading,
9840
+ disabled: loading || googleLoading,
9319
9841
  className: "w-full rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
9320
- }, loading ? "Signing in..." : "Sign in")))));
9842
+ }, loading ? "Signing in..." : "Sign in"))), googleEnabled && /* @__PURE__ */ React.createElement("div", {
9843
+ className: "mt-6"
9844
+ }, /* @__PURE__ */ React.createElement("div", {
9845
+ className: "relative mb-4"
9846
+ }, /* @__PURE__ */ React.createElement("div", {
9847
+ className: "absolute inset-0 flex items-center"
9848
+ }, /* @__PURE__ */ React.createElement("div", {
9849
+ className: "w-full border-t border-gray-200"
9850
+ })), /* @__PURE__ */ React.createElement("div", {
9851
+ className: "relative flex justify-center text-xs"
9852
+ }, /* @__PURE__ */ React.createElement("span", {
9853
+ className: "bg-white px-2 text-gray-500"
9854
+ }, "or"))), /* @__PURE__ */ React.createElement("button", {
9855
+ type: "button",
9856
+ onClick: handleGoogle,
9857
+ disabled: loading || googleLoading,
9858
+ className: "flex w-full items-center justify-center gap-2 rounded border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-800 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
9859
+ }, /* @__PURE__ */ React.createElement("svg", {
9860
+ className: "h-4 w-4",
9861
+ viewBox: "0 0 24 24",
9862
+ "aria-hidden": "true"
9863
+ }, /* @__PURE__ */ React.createElement("path", {
9864
+ fill: "#4285F4",
9865
+ d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
9866
+ }), /* @__PURE__ */ React.createElement("path", {
9867
+ fill: "#34A853",
9868
+ d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
9869
+ }), /* @__PURE__ */ React.createElement("path", {
9870
+ fill: "#FBBC05",
9871
+ d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
9872
+ }), /* @__PURE__ */ React.createElement("path", {
9873
+ fill: "#EA4335",
9874
+ d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
9875
+ })), googleLoading ? "Redirecting\u2026" : "Continue with Google"))));
9321
9876
  }, "SigninPage");
9322
9877
  exports.AdminSignInPage = SigninPage;
9323
9878
  }
@@ -15417,6 +15972,18 @@ var init_email_recipients = __esm({
15417
15972
  __name(serializeEmailRecipients, "serializeEmailRecipients");
15418
15973
  }
15419
15974
  });
15975
+ function useNotificationVariables() {
15976
+ const config = React25.useContext(exports.AdminConfigContext);
15977
+ const extra = config?.extraNotificationVariables ?? [];
15978
+ const vars = [
15979
+ ...KNOWN_VARS,
15980
+ ...extra.map((e) => e.name)
15981
+ ];
15982
+ return {
15983
+ vars,
15984
+ extra
15985
+ };
15986
+ }
15420
15987
  function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15421
15988
  if (audience === "vendor") {
15422
15989
  return VENDOR_EMAIL_TRIGGER_DEFAULTS[triggerKey] ?? {
@@ -15441,6 +16008,30 @@ function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15441
16008
  bodyPlaceholder: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been updated."
15442
16009
  };
15443
16010
  }
16011
+ function getPushTriggerDefaults(triggerKey, audience = "customer") {
16012
+ if (audience === "vendor") {
16013
+ return VENDOR_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
16014
+ subject: "Vendor Push Update",
16015
+ body: "Hi {{vendorName}}, order {{orderNumber}} update.",
16016
+ subjectPlaceholder: "e.g. Vendor Push Update",
16017
+ bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} update."
16018
+ };
16019
+ }
16020
+ if (audience === "admin") {
16021
+ return ADMIN_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
16022
+ subject: "Admin Push Alert",
16023
+ body: "Admin Alert: Order {{orderNumber}} update.",
16024
+ subjectPlaceholder: "e.g. Admin Push Alert",
16025
+ bodyPlaceholder: "Admin Alert: Order {{orderNumber}} update."
16026
+ };
16027
+ }
16028
+ return PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
16029
+ subject: "Order Update",
16030
+ body: "Hi {{customerName}}, order {{orderNumber}} update.",
16031
+ subjectPlaceholder: "e.g. Order Update",
16032
+ bodyPlaceholder: "Hi {{customerName}}, order {{orderNumber}} update."
16033
+ };
16034
+ }
15444
16035
  async function jsonFetch(url, init) {
15445
16036
  const res = await fetch(url, {
15446
16037
  credentials: "include",
@@ -15532,10 +16123,11 @@ function useOrderNotificationTriggerBindings(channel, audienceType = "customers"
15532
16123
  rebind
15533
16124
  };
15534
16125
  }
15535
- var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS;
16126
+ var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS, PUSH_TRIGGER_DEFAULTS, VENDOR_PUSH_TRIGGER_DEFAULTS, ADMIN_PUSH_TRIGGER_DEFAULTS;
15536
16127
  var init_order_notification_bindings_shared = __esm({
15537
16128
  "src/admin/components/order-notification-bindings-shared.ts"() {
15538
16129
  "use client";
16130
+ init_admin_config_context();
15539
16131
  KNOWN_VARS = [
15540
16132
  "orderId",
15541
16133
  "orderNumber",
@@ -15548,10 +16140,9 @@ var init_order_notification_bindings_shared = __esm({
15548
16140
  "vendorId",
15549
16141
  "refundAmount",
15550
16142
  "invoiceNumber",
15551
- "invoiceUrl",
15552
- "ticketQr",
15553
- "qrImageUrl"
16143
+ "invoiceUrl"
15554
16144
  ];
16145
+ __name(useNotificationVariables, "useNotificationVariables");
15555
16146
  EMAIL_TRIGGER_DEFAULTS = {
15556
16147
  order_placed: {
15557
16148
  subject: "Your order {{orderNumber}} is confirmed",
@@ -15629,7 +16220,7 @@ Order Summary:
15629
16220
  Customer Name: {{customerName}}
15630
16221
  Customer Email: {{customerEmail}}
15631
16222
  Customer Phone: {{customerPhone}}`,
15632
- bodyPlaceholder: "Admin Notification:\n\nNew order {{orderNumber}} placed by {{customerName}}."
16223
+ bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
15633
16224
  },
15634
16225
  order_cancelled: {
15635
16226
  subject: "Order {{orderNumber}} cancelled",
@@ -15645,6 +16236,66 @@ Refund Due: {{refundAmount}} {{currency}}`,
15645
16236
  }
15646
16237
  };
15647
16238
  __name(getEmailTriggerDefaults, "getEmailTriggerDefaults");
16239
+ PUSH_TRIGGER_DEFAULTS = {
16240
+ order_placed: {
16241
+ subject: "Order {{orderNumber}} Confirmed!",
16242
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Confirmed!",
16243
+ body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} is confirmed.`,
16244
+ bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} is confirmed."
16245
+ },
16246
+ order_cancelled: {
16247
+ subject: "Order {{orderNumber}} Cancelled",
16248
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16249
+ body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} has been cancelled.`,
16250
+ bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} has been cancelled."
16251
+ }
16252
+ };
16253
+ VENDOR_PUSH_TRIGGER_DEFAULTS = {
16254
+ order_placed: {
16255
+ subject: "New Order Received: {{orderNumber}}",
16256
+ subjectPlaceholder: "e.g. New Order Received: {{orderNumber}}",
16257
+ body: `Hi {{vendorName}}, you received a new order {{orderNumber}} for {{total}} {{currency}} from {{customerName}}.`,
16258
+ bodyPlaceholder: "Hi {{vendorName}}, you received a new order {{orderNumber}}."
16259
+ },
16260
+ order_cancelled: {
16261
+ subject: "Order {{orderNumber}} Cancelled",
16262
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16263
+ body: `Hi {{vendorName}}, order {{orderNumber}} from {{customerName}} has been cancelled.`,
16264
+ bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} was cancelled."
16265
+ }
16266
+ };
16267
+ ADMIN_PUSH_TRIGGER_DEFAULTS = {
16268
+ order_placed: {
16269
+ subject: "New Order {{orderNumber}} ({{total}} {{currency}})",
16270
+ subjectPlaceholder: "e.g. New Order {{orderNumber}} placed",
16271
+ body: `Hi Admin,
16272
+
16273
+ A new order {{orderNumber}} of {{total}} {{currency}} was placed.
16274
+
16275
+ Vendor: {{vendorName}}
16276
+
16277
+ Order Summary:
16278
+ {{orderDetails}}
16279
+
16280
+ Customer Name: {{customerName}}
16281
+ Customer Email: {{customerEmail}}
16282
+ Customer Phone: {{customerPhone}}`,
16283
+ bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
16284
+ },
16285
+ order_cancelled: {
16286
+ subject: "Order {{orderNumber}} Cancelled",
16287
+ subjectPlaceholder: "e.g. Order {{orderNumber}} cancelled",
16288
+ body: `Hi Admin,
16289
+
16290
+ Order {{orderNumber}} has been cancelled.
16291
+
16292
+ Customer: {{customerName}}
16293
+ Vendor: {{vendorName}}
16294
+ Refund Due: {{refundAmount}} {{currency}}`,
16295
+ bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16296
+ }
16297
+ };
16298
+ __name(getPushTriggerDefaults, "getPushTriggerDefaults");
15648
16299
  __name(jsonFetch, "jsonFetch");
15649
16300
  __name(useOrderNotificationTriggerBindings, "useOrderNotificationTriggerBindings");
15650
16301
  }
@@ -16195,16 +16846,23 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16195
16846
  cancelled = true;
16196
16847
  };
16197
16848
  }, []);
16849
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16198
16850
  const visibleVars = React25.useMemo(() => {
16199
- return KNOWN_VARS.filter((v) => {
16851
+ return availableVars.filter((v) => {
16200
16852
  if (v === "vendorName" || v === "vendorId") return multiVendorOn;
16201
16853
  if (v === "eventName") return eventsOn;
16202
16854
  return true;
16203
16855
  });
16204
16856
  }, [
16857
+ availableVars,
16205
16858
  multiVendorOn,
16206
16859
  eventsOn
16207
16860
  ]);
16861
+ const getVarHint = /* @__PURE__ */ __name((v) => {
16862
+ if (v in VAR_HINTS) return VAR_HINTS[v];
16863
+ const match = extraVars.find((e) => e.name === v);
16864
+ return match?.hint || match?.label || v;
16865
+ }, "getVarHint");
16208
16866
  return /* @__PURE__ */ React.createElement("div", {
16209
16867
  className: "space-y-3 border-t border-gray-100 pt-4 first:border-t-0 first:pt-0 dark:border-gray-700"
16210
16868
  }, /* @__PURE__ */ React.createElement("div", {
@@ -16267,7 +16925,7 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16267
16925
  }, visibleVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16268
16926
  key: v,
16269
16927
  type: "button",
16270
- title: VAR_HINTS[v],
16928
+ title: getVarHint(v),
16271
16929
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16272
16930
  className: "rounded border border-gray-200 bg-white px-2 py-1 font-mono text-[11px] text-gray-700 hover:border-blue-300 hover:bg-blue-50 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:hover:border-blue-700 dark:hover:bg-blue-950/40"
16273
16931
  }, `{{${v}}}`))), /* @__PURE__ */ React.createElement("p", {
@@ -16295,14 +16953,588 @@ var init_emailTriggersBindingsPage = __esm({
16295
16953
  vendorId: "Vendor / store ID",
16296
16954
  refundAmount: "Refund amount (refund triggers)",
16297
16955
  invoiceNumber: "Generated invoice number",
16298
- invoiceUrl: "Link to download invoice PDF",
16299
- ticketQr: "Embedded ticket QR code image",
16300
- qrImageUrl: "Direct QR code image URL"
16956
+ invoiceUrl: "Link to download invoice PDF"
16301
16957
  };
16302
16958
  __name(EmailTriggerBindings, "EmailTriggerBindings");
16303
16959
  __name(TriggerEmailEditor, "TriggerEmailEditor");
16304
16960
  }
16305
16961
  });
16962
+ function PushTriggerBindings({ audience = "customer" }) {
16963
+ const audienceType = audience === "vendor" ? "vendors" : audience === "admin" ? "admin" : "customers";
16964
+ const { triggers, bindings, loading, reload, rebind } = useOrderNotificationTriggerBindings("mobile", audienceType);
16965
+ const [selectedKey, setSelectedKey] = React25.useState("");
16966
+ const [templates, setTemplates] = React25.useState([]);
16967
+ const [loadingTpl, setLoadingTpl] = React25.useState(true);
16968
+ const [title, setTitle] = React25.useState("");
16969
+ const [body, setBody] = React25.useState("");
16970
+ const [saving, setSaving] = React25.useState(false);
16971
+ const bodyRef = React25.useRef(null);
16972
+ const subjectRef = React25.useRef(null);
16973
+ const lastFocused = React25.useRef("body");
16974
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16975
+ const getVarHint = /* @__PURE__ */ __name((v) => {
16976
+ if (v in VAR_HINTS2) return VAR_HINTS2[v];
16977
+ const match = extraVars.find((e) => e.name === v);
16978
+ return match?.hint || match?.label || v;
16979
+ }, "getVarHint");
16980
+ React25.useEffect(() => {
16981
+ let cancelled = false;
16982
+ (async () => {
16983
+ try {
16984
+ setLoadingTpl(true);
16985
+ const res = await fetch("/api/message-templates/email", {
16986
+ cache: "no-store"
16987
+ });
16988
+ if (!res.ok) throw new Error("Failed to load message templates");
16989
+ const data = await res.json();
16990
+ if (!cancelled) setTemplates(Array.isArray(data.items) ? data.items : []);
16991
+ } catch {
16992
+ if (!cancelled) sonner.toast.error("Could not load message templates");
16993
+ } finally {
16994
+ if (!cancelled) setLoadingTpl(false);
16995
+ }
16996
+ })();
16997
+ return () => {
16998
+ cancelled = true;
16999
+ };
17000
+ }, []);
17001
+ React25.useEffect(() => {
17002
+ if (triggers.length > 0 && !selectedKey) {
17003
+ setSelectedKey(triggers[0].triggerKey);
17004
+ }
17005
+ }, [
17006
+ triggers,
17007
+ selectedKey
17008
+ ]);
17009
+ const activeTrigger = React25.useMemo(() => triggers.find((t) => t.triggerKey === selectedKey), [
17010
+ triggers,
17011
+ selectedKey
17012
+ ]);
17013
+ const bindingMap = React25.useMemo(() => {
17014
+ const m = /* @__PURE__ */ new Map();
17015
+ for (const b of bindings) {
17016
+ if (b.messageTemplateId != null) m.set(b.triggerKey, b.messageTemplateId);
17017
+ }
17018
+ return m;
17019
+ }, [
17020
+ bindings
17021
+ ]);
17022
+ const activeBindingTplId = selectedKey ? bindingMap.get(selectedKey) ?? null : null;
17023
+ const activeTemplate = React25.useMemo(() => {
17024
+ if (!activeBindingTplId) return null;
17025
+ return templates.find((t) => t.id === activeBindingTplId) ?? null;
17026
+ }, [
17027
+ activeBindingTplId,
17028
+ templates
17029
+ ]);
17030
+ const defaults = React25.useMemo(() => {
17031
+ if (!selectedKey) return {
17032
+ subject: "",
17033
+ body: "",
17034
+ subjectPlaceholder: "",
17035
+ bodyPlaceholder: ""
17036
+ };
17037
+ return getPushTriggerDefaults(selectedKey, audience);
17038
+ }, [
17039
+ selectedKey,
17040
+ audience
17041
+ ]);
17042
+ React25.useEffect(() => {
17043
+ if (activeTemplate) {
17044
+ setTitle(activeTemplate.subject ?? "");
17045
+ setBody(activeTemplate.body ?? "");
17046
+ } else {
17047
+ setTitle(defaults.subject);
17048
+ setBody(defaults.body);
17049
+ }
17050
+ }, [
17051
+ activeTemplate,
17052
+ defaults,
17053
+ selectedKey
17054
+ ]);
17055
+ const insertVariable = /* @__PURE__ */ __name((varName) => {
17056
+ const token = `{{${varName}}}`;
17057
+ if (lastFocused.current === "subject" && subjectRef.current) {
17058
+ const el = subjectRef.current;
17059
+ const start = el.selectionStart ?? title.length;
17060
+ const end = el.selectionEnd ?? title.length;
17061
+ const next = title.slice(0, start) + token + title.slice(end);
17062
+ setTitle(next);
17063
+ setTimeout(() => {
17064
+ el.focus();
17065
+ el.setSelectionRange(start + token.length, start + token.length);
17066
+ }, 0);
17067
+ return;
17068
+ }
17069
+ if (bodyRef.current) {
17070
+ const el = bodyRef.current;
17071
+ const start = el.selectionStart ?? body.length;
17072
+ const end = el.selectionEnd ?? body.length;
17073
+ const next = body.slice(0, start) + token + body.slice(end);
17074
+ setBody(next);
17075
+ setTimeout(() => {
17076
+ el.focus();
17077
+ el.setSelectionRange(start + token.length, start + token.length);
17078
+ }, 0);
17079
+ } else {
17080
+ setBody((prev) => prev + token);
17081
+ }
17082
+ }, "insertVariable");
17083
+ const handleSave = /* @__PURE__ */ __name(async () => {
17084
+ if (!selectedKey) return;
17085
+ setSaving(true);
17086
+ try {
17087
+ let tplId = activeBindingTplId;
17088
+ if (tplId) {
17089
+ const res = await jsonFetch(`/api/message-templates/email/${tplId}`, {
17090
+ method: "PATCH",
17091
+ headers: {
17092
+ "Content-Type": "application/json"
17093
+ },
17094
+ body: JSON.stringify({
17095
+ name: `${audienceType}_push_${selectedKey}`,
17096
+ subject: title,
17097
+ body
17098
+ })
17099
+ });
17100
+ if (res.item) {
17101
+ setTemplates((prev) => prev.map((t) => t.id === res.item.id ? res.item : t));
17102
+ }
17103
+ } else {
17104
+ const res = await jsonFetch("/api/message-templates/email", {
17105
+ method: "POST",
17106
+ headers: {
17107
+ "Content-Type": "application/json"
17108
+ },
17109
+ body: JSON.stringify({
17110
+ name: `${audienceType}_push_${selectedKey}`,
17111
+ subject: title,
17112
+ body
17113
+ })
17114
+ });
17115
+ const createdItem = res.item || res;
17116
+ tplId = createdItem.id;
17117
+ if (createdItem) {
17118
+ setTemplates((prev) => [
17119
+ createdItem,
17120
+ ...prev
17121
+ ]);
17122
+ }
17123
+ }
17124
+ await rebind(selectedKey, tplId, `${audienceType}_push_${selectedKey}`);
17125
+ sonner.toast.success("Push notification template saved");
17126
+ await reload();
17127
+ } catch (e) {
17128
+ const msg = e instanceof Error ? e.message : "Save failed";
17129
+ sonner.toast.error(msg);
17130
+ } finally {
17131
+ setSaving(false);
17132
+ }
17133
+ }, "handleSave");
17134
+ if (loading || loadingTpl) {
17135
+ return /* @__PURE__ */ React.createElement("div", {
17136
+ className: "flex items-center gap-2 p-6 text-sm text-gray-500"
17137
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
17138
+ className: "h-4 w-4 animate-spin"
17139
+ }), " Loading push notification triggers\u2026");
17140
+ }
17141
+ return /* @__PURE__ */ React.createElement("div", {
17142
+ className: "space-y-6"
17143
+ }, /* @__PURE__ */ React.createElement("div", {
17144
+ className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4"
17145
+ }, /* @__PURE__ */ React.createElement("div", {
17146
+ className: "space-y-1"
17147
+ }, /* @__PURE__ */ React.createElement("h3", {
17148
+ className: "text-base font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
17149
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Smartphone, {
17150
+ className: "h-5 w-5 text-gray-700 dark:text-gray-300"
17151
+ }), "Mobile Push Notification Templates"), /* @__PURE__ */ React.createElement("p", {
17152
+ className: "text-xs text-gray-500"
17153
+ }, "Configure push notification title and message body per trigger for", " ", /* @__PURE__ */ React.createElement("span", {
17154
+ className: "font-semibold capitalize text-gray-700 dark:text-gray-300"
17155
+ }, audience, "s"), ".")), /* @__PURE__ */ React.createElement(Button, {
17156
+ type: "button",
17157
+ onClick: handleSave,
17158
+ disabled: saving || !selectedKey,
17159
+ className: "gap-2 shrink-0"
17160
+ }, saving ? /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
17161
+ className: "h-4 w-4 animate-spin"
17162
+ }) : /* @__PURE__ */ React.createElement(LucideIcons.Save, {
17163
+ className: "h-4 w-4"
17164
+ }), "Save Template")), /* @__PURE__ */ React.createElement("div", {
17165
+ className: "grid grid-cols-1 md:grid-cols-4 gap-6"
17166
+ }, /* @__PURE__ */ React.createElement("div", {
17167
+ className: "md:col-span-1 space-y-2"
17168
+ }, /* @__PURE__ */ React.createElement(Label3, {
17169
+ className: "text-xs font-semibold text-gray-500 uppercase tracking-wider"
17170
+ }, "Triggers"), /* @__PURE__ */ React.createElement("div", {
17171
+ className: "flex flex-col gap-1"
17172
+ }, triggers.map((t) => {
17173
+ const isBound = bindingMap.has(t.triggerKey);
17174
+ const isSelected = t.triggerKey === selectedKey;
17175
+ return /* @__PURE__ */ React.createElement("button", {
17176
+ key: t.triggerKey,
17177
+ type: "button",
17178
+ onClick: /* @__PURE__ */ __name(() => setSelectedKey(t.triggerKey), "onClick"),
17179
+ className: `flex flex-col items-start p-3 rounded-lg border text-left transition-all ${isSelected ? "border-gray-900 bg-gray-900 text-white dark:border-gray-100 dark:bg-gray-100 dark:text-gray-900 shadow-sm" : "border-gray-200 bg-white hover:bg-gray-50 text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300"}`
17180
+ }, /* @__PURE__ */ React.createElement("div", {
17181
+ className: "flex items-center justify-between w-full"
17182
+ }, /* @__PURE__ */ React.createElement("span", {
17183
+ className: "font-medium text-sm"
17184
+ }, t.label), isBound && /* @__PURE__ */ React.createElement("span", {
17185
+ className: `text-[10px] px-1.5 py-0.5 rounded font-mono ${isSelected ? "bg-white/20 text-white dark:bg-black/20 dark:text-gray-900" : "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300"}`
17186
+ }, "Configured")), /* @__PURE__ */ React.createElement("span", {
17187
+ className: `text-xs mt-1 ${isSelected ? "text-gray-300 dark:text-gray-600" : "text-gray-500 dark:text-gray-400"}`
17188
+ }, t.triggerKey));
17189
+ }))), /* @__PURE__ */ React.createElement("div", {
17190
+ className: "md:col-span-3 space-y-5 bg-white dark:bg-gray-900 p-5 rounded-xl border border-gray-200 dark:border-gray-800 shadow-sm"
17191
+ }, activeTrigger ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h4", {
17192
+ className: "text-sm font-semibold text-gray-900 dark:text-gray-100"
17193
+ }, activeTrigger.label, " (", activeTrigger.triggerKey, ")"), activeTrigger.description && /* @__PURE__ */ React.createElement("p", {
17194
+ className: "text-xs text-gray-500 mt-0.5"
17195
+ }, activeTrigger.description)), /* @__PURE__ */ React.createElement("div", {
17196
+ className: "space-y-1.5"
17197
+ }, /* @__PURE__ */ React.createElement(Label3, {
17198
+ className: "text-xs font-medium text-gray-700 dark:text-gray-300"
17199
+ }, "Insert Variables"), /* @__PURE__ */ React.createElement("div", {
17200
+ className: "flex flex-wrap gap-1.5"
17201
+ }, availableVars.map((v) => /* @__PURE__ */ React.createElement("button", {
17202
+ key: v,
17203
+ type: "button",
17204
+ onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
17205
+ title: getVarHint(v),
17206
+ className: "px-2 py-1 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-xs font-mono border border-gray-200 dark:border-gray-700 transition-colors"
17207
+ }, `{{${v}}}`)))), /* @__PURE__ */ React.createElement("div", {
17208
+ className: "space-y-1.5"
17209
+ }, /* @__PURE__ */ React.createElement(Label3, {
17210
+ htmlFor: "push-title",
17211
+ className: "text-xs font-medium"
17212
+ }, "Notification Title"), /* @__PURE__ */ React.createElement(Input, {
17213
+ id: "push-title",
17214
+ ref: subjectRef,
17215
+ value: title,
17216
+ onFocus: /* @__PURE__ */ __name(() => {
17217
+ lastFocused.current = "subject";
17218
+ }, "onFocus"),
17219
+ onChange: /* @__PURE__ */ __name((e) => setTitle(e.target.value), "onChange"),
17220
+ placeholder: defaults.subjectPlaceholder
17221
+ })), /* @__PURE__ */ React.createElement("div", {
17222
+ className: "space-y-1.5"
17223
+ }, /* @__PURE__ */ React.createElement(Label3, {
17224
+ htmlFor: "push-body",
17225
+ className: "text-xs font-medium"
17226
+ }, "Notification Body"), /* @__PURE__ */ React.createElement(Textarea, {
17227
+ id: "push-body",
17228
+ ref: bodyRef,
17229
+ value: body,
17230
+ onFocus: /* @__PURE__ */ __name(() => {
17231
+ lastFocused.current = "body";
17232
+ }, "onFocus"),
17233
+ onChange: /* @__PURE__ */ __name((e) => setBody(e.target.value), "onChange"),
17234
+ placeholder: defaults.bodyPlaceholder,
17235
+ className: "h-36 font-mono text-xs"
17236
+ }))) : /* @__PURE__ */ React.createElement("div", {
17237
+ className: "p-12 text-center text-sm text-gray-500"
17238
+ }, "Select a trigger from the left to configure push notification template."))));
17239
+ }
17240
+ var VAR_HINTS2;
17241
+ var init_pushTriggersBindingsPage = __esm({
17242
+ "src/admin/pages/pushTriggersBindingsPage.tsx"() {
17243
+ "use client";
17244
+ init_button();
17245
+ init_input();
17246
+ init_textarea();
17247
+ init_label();
17248
+ init_order_notification_bindings_shared();
17249
+ VAR_HINTS2 = {
17250
+ orderId: "Internal order ID",
17251
+ orderNumber: "Display order number",
17252
+ customerName: "Customer full name",
17253
+ customerPhone: "Customer phone",
17254
+ eventName: "Event title / product name",
17255
+ vendorName: "Vendor / store name",
17256
+ total: "Order total amount",
17257
+ currency: "Currency code (e.g. INR)",
17258
+ vendorId: "Vendor / store ID",
17259
+ refundAmount: "Refund amount (refund triggers)",
17260
+ invoiceNumber: "Generated invoice number",
17261
+ invoiceUrl: "Link to download invoice PDF"
17262
+ };
17263
+ __name(PushTriggerBindings, "PushTriggerBindings");
17264
+ }
17265
+ });
17266
+ function PushConfigModal({ open, onOpenChange }) {
17267
+ const [loading, setLoading] = React25.useState(false);
17268
+ const [saving, setSaving] = React25.useState(false);
17269
+ const [testing, setTesting] = React25.useState(false);
17270
+ const [serviceAccountJson, setServiceAccountJson] = React25.useState("");
17271
+ const [projectId, setProjectId] = React25.useState("");
17272
+ const [clientEmail, setClientEmail] = React25.useState("");
17273
+ const [privateKey, setPrivateKey] = React25.useState("");
17274
+ const [isValidJson, setIsValidJson] = React25.useState(false);
17275
+ const [testToken, setTestToken] = React25.useState("");
17276
+ const [testTitle, setTestTitle] = React25.useState("Test Push Notification");
17277
+ const [testBody, setTestBody] = React25.useState("Firebase Push Notification setup is working!");
17278
+ React25.useEffect(() => {
17279
+ if (!open) return;
17280
+ let cancelled = false;
17281
+ (async () => {
17282
+ try {
17283
+ setLoading(true);
17284
+ const res = await fetch("/api/settings/push", {
17285
+ cache: "no-store"
17286
+ });
17287
+ if (res.ok) {
17288
+ const data = await res.json();
17289
+ if (!cancelled && data) {
17290
+ const rawJson = data.firebase_service_account_json || "";
17291
+ setServiceAccountJson(rawJson);
17292
+ if (rawJson.trim()) {
17293
+ parseAndExtract(rawJson, false);
17294
+ } else {
17295
+ setProjectId(data.firebase_project_id || "");
17296
+ setClientEmail(data.firebase_client_email || "");
17297
+ setPrivateKey(data.firebase_private_key || "");
17298
+ }
17299
+ }
17300
+ }
17301
+ } catch {
17302
+ sonner.toast.error("Failed to load push configuration");
17303
+ } finally {
17304
+ if (!cancelled) setLoading(false);
17305
+ }
17306
+ })();
17307
+ return () => {
17308
+ cancelled = true;
17309
+ };
17310
+ }, [
17311
+ open
17312
+ ]);
17313
+ const parseAndExtract = /* @__PURE__ */ __name((input, showToast = true) => {
17314
+ if (!input.trim()) {
17315
+ setIsValidJson(false);
17316
+ return;
17317
+ }
17318
+ try {
17319
+ const parsed = JSON.parse(input);
17320
+ if (parsed.project_id) setProjectId(parsed.project_id);
17321
+ if (parsed.client_email) setClientEmail(parsed.client_email);
17322
+ if (parsed.private_key) setPrivateKey(parsed.private_key);
17323
+ setIsValidJson(true);
17324
+ if (showToast) {
17325
+ sonner.toast.success("Valid Service Account JSON loaded");
17326
+ }
17327
+ } catch {
17328
+ setIsValidJson(false);
17329
+ if (showToast) {
17330
+ sonner.toast.error("Invalid Service Account JSON format");
17331
+ }
17332
+ }
17333
+ }, "parseAndExtract");
17334
+ const handleJsonChange = /* @__PURE__ */ __name((val) => {
17335
+ setServiceAccountJson(val);
17336
+ if (val.trim()) {
17337
+ parseAndExtract(val, false);
17338
+ } else {
17339
+ setIsValidJson(false);
17340
+ }
17341
+ }, "handleJsonChange");
17342
+ const handleSaveConfig = /* @__PURE__ */ __name(async () => {
17343
+ if (serviceAccountJson.trim() && !isValidJson) {
17344
+ sonner.toast.error("Please enter a valid Firebase Service Account JSON file");
17345
+ return;
17346
+ }
17347
+ setSaving(true);
17348
+ try {
17349
+ const res = await fetch("/api/settings/push", {
17350
+ method: "PUT",
17351
+ headers: {
17352
+ "Content-Type": "application/json"
17353
+ },
17354
+ body: JSON.stringify({
17355
+ firebase_service_account_json: {
17356
+ value: serviceAccountJson.trim(),
17357
+ type: "secret"
17358
+ },
17359
+ firebase_project_id: {
17360
+ value: projectId.trim(),
17361
+ type: "secret"
17362
+ },
17363
+ firebase_client_email: {
17364
+ value: clientEmail.trim(),
17365
+ type: "secret"
17366
+ },
17367
+ firebase_private_key: {
17368
+ value: privateKey.trim(),
17369
+ type: "secret"
17370
+ }
17371
+ })
17372
+ });
17373
+ if (!res.ok) {
17374
+ sonner.toast.error("Failed to save push configuration");
17375
+ return;
17376
+ }
17377
+ sonner.toast.success("Firebase Service Account JSON saved successfully");
17378
+ onOpenChange(false);
17379
+ } catch {
17380
+ sonner.toast.error("Failed to save push configuration");
17381
+ } finally {
17382
+ setSaving(false);
17383
+ }
17384
+ }, "handleSaveConfig");
17385
+ const handleSendTestPush = /* @__PURE__ */ __name(async () => {
17386
+ if (!testToken.trim()) {
17387
+ sonner.toast.error("Please enter a target FCM device token to test");
17388
+ return;
17389
+ }
17390
+ setTesting(true);
17391
+ try {
17392
+ const res = await fetch("/api/settings/push/test", {
17393
+ method: "POST",
17394
+ headers: {
17395
+ "Content-Type": "application/json"
17396
+ },
17397
+ body: JSON.stringify({
17398
+ token: testToken.trim(),
17399
+ title: testTitle.trim(),
17400
+ body: testBody.trim()
17401
+ })
17402
+ });
17403
+ const data = await res.json();
17404
+ if (!res.ok || !data.success) {
17405
+ sonner.toast.error(data.error || "Failed to send test push notification");
17406
+ return;
17407
+ }
17408
+ sonner.toast.success("Test push notification sent successfully!");
17409
+ } catch {
17410
+ sonner.toast.error("Failed to send test push notification");
17411
+ } finally {
17412
+ setTesting(false);
17413
+ }
17414
+ }, "handleSendTestPush");
17415
+ if (!open) return null;
17416
+ return /* @__PURE__ */ React.createElement("div", {
17417
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm animate-in fade-in duration-150"
17418
+ }, /* @__PURE__ */ React.createElement("div", {
17419
+ className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-xl bg-white p-6 shadow-2xl dark:bg-gray-900 border border-gray-200 dark:border-gray-800 space-y-6"
17420
+ }, /* @__PURE__ */ React.createElement("div", {
17421
+ className: "flex items-center justify-between border-b border-gray-200 pb-4 dark:border-gray-800"
17422
+ }, /* @__PURE__ */ React.createElement("div", {
17423
+ className: "flex items-center gap-2"
17424
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Smartphone, {
17425
+ className: "h-5 w-5 text-gray-800 dark:text-gray-200"
17426
+ }), /* @__PURE__ */ React.createElement("h2", {
17427
+ className: "text-lg font-semibold text-gray-900 dark:text-gray-100"
17428
+ }, "Firebase Service Account JSON Configuration")), /* @__PURE__ */ React.createElement("button", {
17429
+ type: "button",
17430
+ onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick"),
17431
+ className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
17432
+ }, "\u2715")), loading ? /* @__PURE__ */ React.createElement("div", {
17433
+ className: "flex items-center justify-center py-12"
17434
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
17435
+ className: "h-6 w-6 animate-spin text-gray-500"
17436
+ })) : /* @__PURE__ */ React.createElement("div", {
17437
+ className: "space-y-6"
17438
+ }, /* @__PURE__ */ React.createElement("div", {
17439
+ className: "space-y-2"
17440
+ }, /* @__PURE__ */ React.createElement(Label3, {
17441
+ htmlFor: "service-account-json",
17442
+ className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center justify-between"
17443
+ }, /* @__PURE__ */ React.createElement("span", {
17444
+ className: "flex items-center gap-1.5"
17445
+ }, /* @__PURE__ */ React.createElement(LucideIcons.KeyRound, {
17446
+ className: "h-4 w-4 text-blue-500"
17447
+ }), "Firebase Service Account JSON File"), isValidJson && /* @__PURE__ */ React.createElement("span", {
17448
+ className: "text-xs font-normal text-green-600 flex items-center gap-1"
17449
+ }, /* @__PURE__ */ React.createElement(LucideIcons.CheckCircle2, {
17450
+ className: "h-3.5 w-3.5"
17451
+ }), " Valid JSON")), /* @__PURE__ */ React.createElement(Textarea, {
17452
+ id: "service-account-json",
17453
+ value: serviceAccountJson,
17454
+ onChange: /* @__PURE__ */ __name((e) => handleJsonChange(e.target.value), "onChange"),
17455
+ placeholder: `Paste your entire service-account.json content here...
17456
+ {
17457
+ "type": "service_account",
17458
+ "project_id": "...",
17459
+ "private_key": "...",
17460
+ "client_email": "..."
17461
+ }`,
17462
+ className: "h-44 font-mono text-xs border-gray-300 dark:border-gray-700"
17463
+ })), isValidJson && /* @__PURE__ */ React.createElement("div", {
17464
+ className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
17465
+ }, /* @__PURE__ */ React.createElement(Label3, {
17466
+ className: "text-xs font-semibold text-gray-700 dark:text-gray-300"
17467
+ }, "Extracted Firebase Credentials Preview"), /* @__PURE__ */ React.createElement("div", {
17468
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs"
17469
+ }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
17470
+ className: "font-medium text-gray-500"
17471
+ }, "Project ID:"), /* @__PURE__ */ React.createElement("p", {
17472
+ className: "font-mono text-gray-900 dark:text-gray-100 truncate"
17473
+ }, projectId || "\u2014")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
17474
+ className: "font-medium text-gray-500"
17475
+ }, "Client Email:"), /* @__PURE__ */ React.createElement("p", {
17476
+ className: "font-mono text-gray-900 dark:text-gray-100 truncate"
17477
+ }, clientEmail || "\u2014")))), /* @__PURE__ */ React.createElement("div", {
17478
+ className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
17479
+ }, /* @__PURE__ */ React.createElement(Label3, {
17480
+ className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center gap-1.5"
17481
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Send, {
17482
+ className: "h-3.5 w-3.5 text-green-500"
17483
+ }), "Test Push Notification (Target Device Token)"), /* @__PURE__ */ React.createElement("div", {
17484
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-2"
17485
+ }, /* @__PURE__ */ React.createElement(Input, {
17486
+ value: testTitle,
17487
+ onChange: /* @__PURE__ */ __name((e) => setTestTitle(e.target.value), "onChange"),
17488
+ placeholder: "Test Notification Title",
17489
+ className: "text-xs"
17490
+ }), /* @__PURE__ */ React.createElement(Input, {
17491
+ value: testBody,
17492
+ onChange: /* @__PURE__ */ __name((e) => setTestBody(e.target.value), "onChange"),
17493
+ placeholder: "Test Notification Body",
17494
+ className: "text-xs"
17495
+ })), /* @__PURE__ */ React.createElement("div", {
17496
+ className: "flex gap-2"
17497
+ }, /* @__PURE__ */ React.createElement(Input, {
17498
+ value: testToken,
17499
+ onChange: /* @__PURE__ */ __name((e) => setTestToken(e.target.value), "onChange"),
17500
+ placeholder: "Target FCM Device Token",
17501
+ className: "text-xs font-mono"
17502
+ }), /* @__PURE__ */ React.createElement(Button, {
17503
+ type: "button",
17504
+ variant: "secondary",
17505
+ size: "sm",
17506
+ disabled: testing,
17507
+ onClick: handleSendTestPush,
17508
+ className: "shrink-0 gap-1.5"
17509
+ }, testing ? /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
17510
+ className: "h-3.5 w-3.5 animate-spin"
17511
+ }) : /* @__PURE__ */ React.createElement(LucideIcons.Send, {
17512
+ className: "h-3.5 w-3.5"
17513
+ }), "Send Test"))), /* @__PURE__ */ React.createElement("div", {
17514
+ className: "flex justify-end gap-2 border-t border-gray-200 pt-4 dark:border-gray-800"
17515
+ }, /* @__PURE__ */ React.createElement(Button, {
17516
+ type: "button",
17517
+ variant: "outline",
17518
+ onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick")
17519
+ }, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
17520
+ type: "button",
17521
+ disabled: saving,
17522
+ onClick: handleSaveConfig,
17523
+ className: "gap-1.5"
17524
+ }, saving && /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
17525
+ className: "h-4 w-4 animate-spin"
17526
+ }), "Save Service Account JSON")))));
17527
+ }
17528
+ var init_PushConfigModal = __esm({
17529
+ "src/admin/components/PushConfigModal.tsx"() {
17530
+ "use client";
17531
+ init_button();
17532
+ init_input();
17533
+ init_textarea();
17534
+ init_label();
17535
+ __name(PushConfigModal, "PushConfigModal");
17536
+ }
17537
+ });
16306
17538
  function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledChange, onSaveSettings, saving }) {
16307
17539
  const [mainTab, setMainTab] = React25.useState("email");
16308
17540
  const [audienceTab, setAudienceTab] = React25.useState("customer");
@@ -16310,6 +17542,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16310
17542
  const [emailVendorEnabled, setEmailVendorEnabled] = React25.useState(true);
16311
17543
  const [emailAdminEnabled, setEmailAdminEnabled] = React25.useState(true);
16312
17544
  const [whatsappEnabled, setWhatsappEnabled] = React25.useState(true);
17545
+ const [pushCustomerEnabled, setPushCustomerEnabled] = React25.useState(true);
17546
+ const [pushVendorEnabled, setPushVendorEnabled] = React25.useState(true);
17547
+ const [pushAdminEnabled, setPushAdminEnabled] = React25.useState(true);
17548
+ const [showPushConfigModal, setShowPushConfigModal] = React25.useState(false);
16313
17549
  const [multiVendorOn, setMultiVendorOn] = React25.useState(true);
16314
17550
  const [loading, setLoading] = React25.useState(true);
16315
17551
  const [savingChannel, setSavingChannel] = React25.useState(false);
@@ -16340,6 +17576,9 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16340
17576
  setEmailVendorEnabled(data.email_vendor_enabled !== "false" && data.email_vendor_enabled !== "0");
16341
17577
  setEmailAdminEnabled(data.email_admin_enabled !== "false" && data.email_admin_enabled !== "0");
16342
17578
  setWhatsappEnabled(data.whatsapp_enabled !== "false" && data.whatsapp_enabled !== "0");
17579
+ setPushCustomerEnabled(data.push_customer_enabled !== "false" && data.push_customer_enabled !== "0");
17580
+ setPushVendorEnabled(data.push_vendor_enabled !== "false" && data.push_vendor_enabled !== "0");
17581
+ setPushAdminEnabled(data.push_admin_enabled !== "false" && data.push_admin_enabled !== "0");
16343
17582
  }
16344
17583
  }
16345
17584
  } catch {
@@ -16357,7 +17596,8 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16357
17596
  setSavingChannel(true);
16358
17597
  try {
16359
17598
  const isAnyEmailEnabled = emailCustomerEnabled || emailVendorEnabled || emailAdminEnabled;
16360
- const isAnyChannelEnabled = isAnyEmailEnabled || whatsappEnabled;
17599
+ const isAnyPushEnabled = pushCustomerEnabled || pushVendorEnabled || pushAdminEnabled;
17600
+ const isAnyChannelEnabled = isAnyEmailEnabled || whatsappEnabled || isAnyPushEnabled;
16361
17601
  const res = await fetch(`/api/settings/${settingsGroup}`, {
16362
17602
  method: "PUT",
16363
17603
  headers: {
@@ -16384,6 +17624,22 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16384
17624
  value: whatsappEnabled ? "true" : "false",
16385
17625
  type: "public"
16386
17626
  },
17627
+ push_enabled: {
17628
+ value: isAnyPushEnabled ? "true" : "false",
17629
+ type: "public"
17630
+ },
17631
+ push_customer_enabled: {
17632
+ value: pushCustomerEnabled ? "true" : "false",
17633
+ type: "public"
17634
+ },
17635
+ push_vendor_enabled: {
17636
+ value: pushVendorEnabled ? "true" : "false",
17637
+ type: "public"
17638
+ },
17639
+ push_admin_enabled: {
17640
+ value: pushAdminEnabled ? "true" : "false",
17641
+ type: "public"
17642
+ },
16387
17643
  enabled: {
16388
17644
  value: isAnyChannelEnabled ? "true" : "false",
16389
17645
  type: "public"
@@ -16405,7 +17661,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16405
17661
  }, "handleSaveChannels");
16406
17662
  return /* @__PURE__ */ React.createElement("div", {
16407
17663
  className: "space-y-5"
16408
- }, /* @__PURE__ */ React.createElement("div", {
17664
+ }, /* @__PURE__ */ React.createElement(PushConfigModal, {
17665
+ open: showPushConfigModal,
17666
+ onOpenChange: setShowPushConfigModal
17667
+ }), /* @__PURE__ */ React.createElement("div", {
16409
17668
  className: "border-b border-gray-200 dark:border-gray-700"
16410
17669
  }, /* @__PURE__ */ React.createElement("div", {
16411
17670
  className: "flex gap-1"
@@ -16421,7 +17680,13 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16421
17680
  className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "whatsapp" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
16422
17681
  }, /* @__PURE__ */ React.createElement(LucideIcons.MessageCircle, {
16423
17682
  className: "h-4 w-4"
16424
- }), "WhatsApp template"))), mainTab === "email" && /* @__PURE__ */ React.createElement("div", {
17683
+ }), "WhatsApp template"), /* @__PURE__ */ React.createElement("button", {
17684
+ type: "button",
17685
+ onClick: /* @__PURE__ */ __name(() => setMainTab("push"), "onClick"),
17686
+ className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "push" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
17687
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Smartphone, {
17688
+ className: "h-4 w-4"
17689
+ }), "Mobile notification template"))), mainTab === "email" && /* @__PURE__ */ React.createElement("div", {
16425
17690
  className: "space-y-5"
16426
17691
  }, /* @__PURE__ */ React.createElement("div", {
16427
17692
  className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
@@ -16455,7 +17720,7 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16455
17720
  className: "h-4 w-4 text-blue-600 dark:text-blue-400"
16456
17721
  }), "Enable Customer Email Notifications"), /* @__PURE__ */ React.createElement("p", {
16457
17722
  className: "text-xs text-gray-500 dark:text-gray-400"
16458
- }, "When enabled, order confirmations, tickets, and status updates are emailed to customers.")), /* @__PURE__ */ React.createElement(Switch, {
17723
+ }, "When enabled, order updates, invoices, and ticket details are emailed to customers.")), /* @__PURE__ */ React.createElement(Switch, {
16459
17724
  id: "email-customer-notifications-enabled",
16460
17725
  checked: emailCustomerEnabled,
16461
17726
  onCheckedChange: setEmailCustomerEnabled,
@@ -16471,10 +17736,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16471
17736
  }, /* @__PURE__ */ React.createElement("div", {
16472
17737
  className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
16473
17738
  }, /* @__PURE__ */ React.createElement(LucideIcons.Store, {
16474
- className: "h-4 w-4 text-purple-600 dark:text-purple-400"
17739
+ className: "h-4 w-4 text-purple-600 dark:purple-400"
16475
17740
  }), "Enable Vendor Email Notifications"), /* @__PURE__ */ React.createElement("p", {
16476
17741
  className: "text-xs text-gray-500 dark:text-gray-400"
16477
- }, "When enabled, vendors are emailed automatically when order is placed/cancelled for their products.")), /* @__PURE__ */ React.createElement(Switch, {
17742
+ }, "When enabled, vendors receive email notifications for new orders assigned to them.")), /* @__PURE__ */ React.createElement(Switch, {
16478
17743
  id: "email-vendor-notifications-enabled",
16479
17744
  checked: emailVendorEnabled,
16480
17745
  onCheckedChange: setEmailVendorEnabled,
@@ -16517,7 +17782,104 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16517
17782
  checked: whatsappEnabled,
16518
17783
  onCheckedChange: setWhatsappEnabled,
16519
17784
  disabled: loading
16520
- })), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), /* @__PURE__ */ React.createElement(Button, {
17785
+ })), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), mainTab === "push" && /* @__PURE__ */ React.createElement("div", {
17786
+ className: "space-y-5"
17787
+ }, /* @__PURE__ */ React.createElement("div", {
17788
+ className: "flex items-center justify-between gap-4 rounded-lg border border-blue-100 bg-blue-50/50 p-4 dark:border-blue-900/40 dark:bg-blue-950/20"
17789
+ }, /* @__PURE__ */ React.createElement("div", {
17790
+ className: "space-y-0.5"
17791
+ }, /* @__PURE__ */ React.createElement("div", {
17792
+ className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
17793
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Smartphone, {
17794
+ className: "h-4 w-4 text-blue-600 dark:text-blue-400"
17795
+ }), "Firebase Cloud Messaging (FCM) Setup"), /* @__PURE__ */ React.createElement("p", {
17796
+ className: "text-xs text-gray-500 dark:text-gray-400"
17797
+ }, "Set up your Firebase project credentials to send mobile push notifications to iOS and Android apps.")), /* @__PURE__ */ React.createElement(Button, {
17798
+ type: "button",
17799
+ variant: "outline",
17800
+ size: "sm",
17801
+ onClick: /* @__PURE__ */ __name(() => setShowPushConfigModal(true), "onClick"),
17802
+ className: "gap-1.5 bg-white dark:bg-gray-900 shrink-0"
17803
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Settings, {
17804
+ className: "h-4 w-4 text-gray-600"
17805
+ }), "Set Configuration")), /* @__PURE__ */ React.createElement("div", {
17806
+ className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
17807
+ }, /* @__PURE__ */ React.createElement("button", {
17808
+ type: "button",
17809
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("customer"), "onClick"),
17810
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "customer" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17811
+ }, /* @__PURE__ */ React.createElement(LucideIcons.User, {
17812
+ className: "h-3.5 w-3.5 text-blue-500"
17813
+ }), "Customers"), multiVendorOn && /* @__PURE__ */ React.createElement("button", {
17814
+ type: "button",
17815
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("vendor"), "onClick"),
17816
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "vendor" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17817
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Store, {
17818
+ className: "h-3.5 w-3.5 text-purple-500"
17819
+ }), "Vendors"), /* @__PURE__ */ React.createElement("button", {
17820
+ type: "button",
17821
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("admin"), "onClick"),
17822
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "admin" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17823
+ }, /* @__PURE__ */ React.createElement(LucideIcons.ShieldCheck, {
17824
+ className: "h-3.5 w-3.5 text-amber-500"
17825
+ }), "Admin")), audienceTab === "customer" && /* @__PURE__ */ React.createElement("div", {
17826
+ className: "space-y-4"
17827
+ }, /* @__PURE__ */ React.createElement("div", {
17828
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17829
+ }, /* @__PURE__ */ React.createElement("div", {
17830
+ className: "space-y-0.5"
17831
+ }, /* @__PURE__ */ React.createElement("div", {
17832
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17833
+ }, /* @__PURE__ */ React.createElement(LucideIcons.User, {
17834
+ className: "h-4 w-4 text-blue-600 dark:text-blue-400"
17835
+ }), "Enable Customer Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17836
+ className: "text-xs text-gray-500 dark:text-gray-400"
17837
+ }, "When enabled, order confirmation alerts and updates are pushed directly to customer mobile apps.")), /* @__PURE__ */ React.createElement(Switch, {
17838
+ id: "push-customer-notifications-enabled",
17839
+ checked: pushCustomerEnabled,
17840
+ onCheckedChange: setPushCustomerEnabled,
17841
+ disabled: loading
17842
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17843
+ audience: "customer"
17844
+ })), audienceTab === "vendor" && multiVendorOn && /* @__PURE__ */ React.createElement("div", {
17845
+ className: "space-y-4"
17846
+ }, /* @__PURE__ */ React.createElement("div", {
17847
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17848
+ }, /* @__PURE__ */ React.createElement("div", {
17849
+ className: "space-y-0.5"
17850
+ }, /* @__PURE__ */ React.createElement("div", {
17851
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17852
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Store, {
17853
+ className: "h-4 w-4 text-purple-600 dark:text-purple-400"
17854
+ }), "Enable Vendor Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17855
+ className: "text-xs text-gray-500 dark:text-gray-400"
17856
+ }, "When enabled, vendor mobile app receives instant push alerts for assigned orders.")), /* @__PURE__ */ React.createElement(Switch, {
17857
+ id: "push-vendor-notifications-enabled",
17858
+ checked: pushVendorEnabled,
17859
+ onCheckedChange: setPushVendorEnabled,
17860
+ disabled: loading
17861
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17862
+ audience: "vendor"
17863
+ })), audienceTab === "admin" && /* @__PURE__ */ React.createElement("div", {
17864
+ className: "space-y-4"
17865
+ }, /* @__PURE__ */ React.createElement("div", {
17866
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17867
+ }, /* @__PURE__ */ React.createElement("div", {
17868
+ className: "space-y-0.5"
17869
+ }, /* @__PURE__ */ React.createElement("div", {
17870
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17871
+ }, /* @__PURE__ */ React.createElement(LucideIcons.ShieldCheck, {
17872
+ className: "h-4 w-4 text-amber-600 dark:text-amber-400"
17873
+ }), "Enable Admin Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17874
+ className: "text-xs text-gray-500 dark:text-gray-400"
17875
+ }, "When enabled, store administrators receive mobile push alerts for all orders placed/cancelled.")), /* @__PURE__ */ React.createElement(Switch, {
17876
+ id: "push-admin-notifications-enabled",
17877
+ checked: pushAdminEnabled,
17878
+ onCheckedChange: setPushAdminEnabled,
17879
+ disabled: loading
17880
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17881
+ audience: "admin"
17882
+ }))), /* @__PURE__ */ React.createElement(Button, {
16521
17883
  size: "sm",
16522
17884
  onClick: /* @__PURE__ */ __name(() => void handleSaveChannels(), "onClick"),
16523
17885
  disabled: saving || savingChannel || loading,
@@ -16533,9 +17895,207 @@ var init_EventNotificationsPluginSettings = __esm({
16533
17895
  init_switch();
16534
17896
  init_whatsappTriggerBindingsPage();
16535
17897
  init_emailTriggersBindingsPage();
17898
+ init_pushTriggersBindingsPage();
17899
+ init_PushConfigModal();
16536
17900
  __name(EventNotificationsPluginSettings, "EventNotificationsPluginSettings");
16537
17901
  }
16538
17902
  });
17903
+ function googleRedirectUri() {
17904
+ if (typeof window === "undefined") {
17905
+ const path2 = typeof process !== "undefined" && process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim() || "/api/auth/callback/google";
17906
+ return path2.startsWith("http") ? path2 : path2;
17907
+ }
17908
+ const configured = process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim();
17909
+ if (configured?.startsWith("http")) return configured;
17910
+ const path = configured || "/api/auth/callback/google";
17911
+ return `${window.location.origin}${path.startsWith("/") ? path : `/${path}`}`;
17912
+ }
17913
+ function AuthProvidersPluginSettings({ settingsGroup, onEnabledChange }) {
17914
+ const [loading, setLoading] = React25.useState(true);
17915
+ const [saving, setSaving] = React25.useState(false);
17916
+ const [googleEnabled, setGoogleEnabled] = React25.useState(false);
17917
+ const [googleClientId, setGoogleClientId] = React25.useState("");
17918
+ const [googleClientSecret, setGoogleClientSecret] = React25.useState("");
17919
+ const [hasSavedSecret, setHasSavedSecret] = React25.useState(false);
17920
+ const [copied, setCopied] = React25.useState(false);
17921
+ const onEnabledChangeRef = React25.useRef(onEnabledChange);
17922
+ onEnabledChangeRef.current = onEnabledChange;
17923
+ const redirectUri = React25.useMemo(() => googleRedirectUri(), []);
17924
+ React25.useEffect(() => {
17925
+ let cancelled = false;
17926
+ (async () => {
17927
+ setLoading(true);
17928
+ try {
17929
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17930
+ cache: "no-store"
17931
+ });
17932
+ const data = res.ok ? await res.json() : {};
17933
+ if (cancelled) return;
17934
+ const enabled = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
17935
+ setGoogleEnabled(enabled);
17936
+ setGoogleClientId(String(data.googleClientId ?? "").trim());
17937
+ const secret = String(data.googleClientSecret ?? "").trim();
17938
+ setGoogleClientSecret("");
17939
+ setHasSavedSecret(Boolean(secret));
17940
+ } catch {
17941
+ } finally {
17942
+ if (!cancelled) setLoading(false);
17943
+ }
17944
+ })();
17945
+ return () => {
17946
+ cancelled = true;
17947
+ };
17948
+ }, [
17949
+ settingsGroup
17950
+ ]);
17951
+ const handleCopyRedirect = /* @__PURE__ */ __name(async () => {
17952
+ try {
17953
+ await navigator.clipboard.writeText(redirectUri);
17954
+ setCopied(true);
17955
+ sonner.toast.success("Redirect URI copied");
17956
+ setTimeout(() => setCopied(false), 2e3);
17957
+ } catch {
17958
+ sonner.toast.error("Could not copy redirect URI");
17959
+ }
17960
+ }, "handleCopyRedirect");
17961
+ const handleSave = /* @__PURE__ */ __name(async () => {
17962
+ setSaving(true);
17963
+ try {
17964
+ const payload = {
17965
+ enabled: {
17966
+ value: googleEnabled ? "true" : "false",
17967
+ type: "public"
17968
+ },
17969
+ googleEnabled: {
17970
+ value: googleEnabled ? "true" : "false",
17971
+ type: "public"
17972
+ },
17973
+ googleClientId: {
17974
+ value: googleClientId.trim(),
17975
+ type: "private"
17976
+ }
17977
+ };
17978
+ if (googleClientSecret.trim()) {
17979
+ payload.googleClientSecret = {
17980
+ value: googleClientSecret.trim(),
17981
+ type: "private",
17982
+ encrypted: true
17983
+ };
17984
+ }
17985
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17986
+ method: "PUT",
17987
+ headers: {
17988
+ "Content-Type": "application/json"
17989
+ },
17990
+ body: JSON.stringify(payload)
17991
+ });
17992
+ if (!res.ok) {
17993
+ sonner.toast.error("Failed to save Google auth settings");
17994
+ return;
17995
+ }
17996
+ if (googleClientSecret.trim()) {
17997
+ setHasSavedSecret(true);
17998
+ setGoogleClientSecret("");
17999
+ }
18000
+ onEnabledChangeRef.current?.(googleEnabled);
18001
+ sonner.toast.success("Authentication provider settings saved");
18002
+ } catch {
18003
+ sonner.toast.error("Failed to save Google auth settings");
18004
+ } finally {
18005
+ setSaving(false);
18006
+ }
18007
+ }, "handleSave");
18008
+ if (loading) {
18009
+ return /* @__PURE__ */ React.createElement("p", {
18010
+ className: "text-sm text-gray-500 dark:text-gray-400"
18011
+ }, "Loading\u2026");
18012
+ }
18013
+ return /* @__PURE__ */ React.createElement("div", {
18014
+ className: "space-y-4"
18015
+ }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", {
18016
+ className: "text-sm font-semibold text-gray-900 dark:text-white"
18017
+ }, "Google (Web)"), /* @__PURE__ */ React.createElement("p", {
18018
+ className: "mt-1 text-xs text-gray-500 dark:text-gray-400"
18019
+ }, "Create a Web OAuth client in Google Cloud Console, add the redirect URI below, then paste Client ID and Client Secret here. Only existing admin/vendor users (matching Google email) can sign in.")), /* @__PURE__ */ React.createElement("div", {
18020
+ className: "flex items-center justify-between gap-2"
18021
+ }, /* @__PURE__ */ React.createElement(Label3, {
18022
+ htmlFor: `${settingsGroup}-googleEnabled`,
18023
+ className: "text-sm"
18024
+ }, "Enable Google sign-in"), /* @__PURE__ */ React.createElement(Switch, {
18025
+ id: `${settingsGroup}-googleEnabled`,
18026
+ checked: googleEnabled,
18027
+ onCheckedChange: setGoogleEnabled
18028
+ })), /* @__PURE__ */ React.createElement("div", {
18029
+ className: "space-y-1"
18030
+ }, /* @__PURE__ */ React.createElement(Label3, {
18031
+ htmlFor: `${settingsGroup}-redirectUri`,
18032
+ className: "text-sm"
18033
+ }, "Redirect URI (add in Google Console)"), /* @__PURE__ */ React.createElement("div", {
18034
+ className: "flex gap-2"
18035
+ }, /* @__PURE__ */ React.createElement(Input, {
18036
+ id: `${settingsGroup}-redirectUri`,
18037
+ value: redirectUri,
18038
+ readOnly: true,
18039
+ className: "h-9 flex-1 font-mono text-xs"
18040
+ }), /* @__PURE__ */ React.createElement(Button, {
18041
+ type: "button",
18042
+ size: "sm",
18043
+ variant: "outline",
18044
+ onClick: handleCopyRedirect,
18045
+ className: "shrink-0 gap-1"
18046
+ }, copied ? /* @__PURE__ */ React.createElement(LucideIcons.Check, {
18047
+ className: "h-3.5 w-3.5"
18048
+ }) : /* @__PURE__ */ React.createElement(LucideIcons.Copy, {
18049
+ className: "h-3.5 w-3.5"
18050
+ }), copied ? "Copied" : "Copy")), /* @__PURE__ */ React.createElement("p", {
18051
+ className: "text-xs text-gray-500 dark:text-gray-400"
18052
+ }, "Must match ", /* @__PURE__ */ React.createElement("code", {
18053
+ className: "text-[11px]"
18054
+ }, "NEXTAUTH_URL"), " for this environment.")), /* @__PURE__ */ React.createElement("div", {
18055
+ className: "space-y-1"
18056
+ }, /* @__PURE__ */ React.createElement(Label3, {
18057
+ htmlFor: `${settingsGroup}-googleClientId`,
18058
+ className: "text-sm"
18059
+ }, "Client ID"), /* @__PURE__ */ React.createElement(Input, {
18060
+ id: `${settingsGroup}-googleClientId`,
18061
+ value: googleClientId,
18062
+ onChange: /* @__PURE__ */ __name((e) => setGoogleClientId(e.target.value), "onChange"),
18063
+ placeholder: "xxxxx.apps.googleusercontent.com",
18064
+ className: "h-9 text-sm",
18065
+ autoComplete: "off"
18066
+ })), /* @__PURE__ */ React.createElement("div", {
18067
+ className: "space-y-1"
18068
+ }, /* @__PURE__ */ React.createElement(Label3, {
18069
+ htmlFor: `${settingsGroup}-googleClientSecret`,
18070
+ className: "text-sm"
18071
+ }, "Client Secret ", hasSavedSecret && !googleClientSecret.trim() ? "(saved)" : ""), /* @__PURE__ */ React.createElement(Input, {
18072
+ id: `${settingsGroup}-googleClientSecret`,
18073
+ type: "password",
18074
+ value: googleClientSecret,
18075
+ onChange: /* @__PURE__ */ __name((e) => setGoogleClientSecret(e.target.value), "onChange"),
18076
+ placeholder: hasSavedSecret ? "Leave blank to keep saved secret" : "Paste client secret",
18077
+ className: "h-9 text-sm",
18078
+ autoComplete: "new-password"
18079
+ })), /* @__PURE__ */ React.createElement(Button, {
18080
+ size: "sm",
18081
+ onClick: handleSave,
18082
+ disabled: saving,
18083
+ className: "gap-1"
18084
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Save, {
18085
+ className: "h-3.5 w-3.5"
18086
+ }), saving ? "Saving\u2026" : "Save"));
18087
+ }
18088
+ var init_AuthProvidersPluginSettings = __esm({
18089
+ "src/admin/components/AuthProvidersPluginSettings.tsx"() {
18090
+ "use client";
18091
+ init_button();
18092
+ init_input();
18093
+ init_label();
18094
+ init_switch();
18095
+ __name(googleRedirectUri, "googleRedirectUri");
18096
+ __name(AuthProvidersPluginSettings, "AuthProvidersPluginSettings");
18097
+ }
18098
+ });
16539
18099
 
16540
18100
  // src/plugins/llm/chat-email-intent.ts
16541
18101
  function normalizeIntentKey(raw) {
@@ -16913,6 +18473,7 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
16913
18473
  const isEventNotifications = settingsGroup === "event_notifications";
16914
18474
  const isSocialMedia = settingsGroup === "social_media";
16915
18475
  const isMultiVendor = settingsGroup === "multi_vendor";
18476
+ const isAuthProviders = settingsGroup === "auth_providers";
16916
18477
  const [enabled, setEnabled] = React25.useState(true);
16917
18478
  const [botName, setBotName] = React25.useState("");
16918
18479
  const [icon, setIcon] = React25.useState("");
@@ -17262,6 +18823,10 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17262
18823
  setWhatsappPhoneNumberId(String(data.phoneNumberId ?? data.WHATSAPP_PHONE_NUMBER_ID ?? "").trim());
17263
18824
  setWhatsappWabaId(String(data.wabaId ?? data.WHATSAPP_WABA_ID ?? "").trim());
17264
18825
  }
18826
+ if (isAuthProviders) {
18827
+ const googleOn = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
18828
+ setEnabled(googleOn);
18829
+ }
17265
18830
  if (isSocialMedia) {
17266
18831
  const savedLiToken = String(data.linkedin_access_token ?? "").trim();
17267
18832
  setLinkedinAccessToken(savedLiToken);
@@ -17299,7 +18864,8 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17299
18864
  isWhatsapp,
17300
18865
  isEventNotifications,
17301
18866
  isSocialMedia,
17302
- isMultiVendor
18867
+ isMultiVendor,
18868
+ isAuthProviders
17303
18869
  ]);
17304
18870
  React25.useEffect(() => {
17305
18871
  if (!isSocialMedia || loading || linkedInOrgsPrefetchedRef.current) return;
@@ -17745,6 +19311,18 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17745
19311
  }
17746
19312
  };
17747
19313
  }
19314
+ if (isAuthProviders) {
19315
+ return {
19316
+ enabled: {
19317
+ value: enabled ? "true" : "false",
19318
+ type: "public"
19319
+ },
19320
+ googleEnabled: {
19321
+ value: enabled ? "true" : "false",
19322
+ type: "public"
19323
+ }
19324
+ };
19325
+ }
17748
19326
  if (isSocialMedia) {
17749
19327
  const payload2 = {
17750
19328
  enabled: {
@@ -18230,6 +19808,15 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
18230
19808
  sonner.toast.success("Removed from agent");
18231
19809
  await fetchAttachedKnowledge(slug);
18232
19810
  }, "handleUnlinkKbDoc");
19811
+ if (isAuthProviders) {
19812
+ return /* @__PURE__ */ React.createElement(AuthProvidersPluginSettings, {
19813
+ settingsGroup,
19814
+ onEnabledChange: /* @__PURE__ */ __name((value) => {
19815
+ setEnabled(value);
19816
+ onSaved?.();
19817
+ }, "onEnabledChange")
19818
+ });
19819
+ }
18233
19820
  if (loading) return /* @__PURE__ */ React.createElement("div", {
18234
19821
  className: "text-sm text-gray-500 dark:text-gray-400"
18235
19822
  }, "Loading...");
@@ -19566,6 +21153,10 @@ function PluginsPage() {
19566
21153
  const en = pluginDescriptors.find((p) => p.name === "event_notifications");
19567
21154
  if (en) setSelectedName(en.name);
19568
21155
  }
21156
+ if (q === "auth_providers" || q === "auth-providers" || q === "authentication") {
21157
+ const ap = pluginDescriptors.find((p) => p.name === "auth_providers");
21158
+ if (ap) setSelectedName(ap.name);
21159
+ }
19569
21160
  }, [
19570
21161
  searchParams,
19571
21162
  pluginDescriptors
@@ -19573,10 +21164,13 @@ function PluginsPage() {
19573
21164
  React25.useEffect(() => {
19574
21165
  pluginDescriptors.forEach((p) => {
19575
21166
  if (!p.settingsGroup) return;
19576
- fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => setEnabledMap((prev) => ({
19577
- ...prev,
19578
- [p.name]: data.enabled !== "false"
19579
- }))).catch(() => {
21167
+ fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
21168
+ const on = p.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled !== "false" && data.googleEnabled !== "false" && data.enabled === "true" : data.enabled !== "false";
21169
+ setEnabledMap((prev) => ({
21170
+ ...prev,
21171
+ [p.name]: on
21172
+ }));
21173
+ }).catch(() => {
19580
21174
  });
19581
21175
  });
19582
21176
  }, [
@@ -19586,10 +21180,13 @@ function PluginsPage() {
19586
21180
  const refreshEnabled = /* @__PURE__ */ __name((name) => {
19587
21181
  const d = pluginDescriptors.find((p) => p.name === name);
19588
21182
  if (!d?.settingsGroup) return;
19589
- fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => setEnabledMap((prev) => ({
19590
- ...prev,
19591
- [name]: data.enabled !== "false"
19592
- }))).catch(() => {
21183
+ fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
21184
+ const on = d.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false" : data.enabled !== "false";
21185
+ setEnabledMap((prev) => ({
21186
+ ...prev,
21187
+ [name]: on
21188
+ }));
21189
+ }).catch(() => {
19593
21190
  });
19594
21191
  }, "refreshEnabled");
19595
21192
  return /* @__PURE__ */ React.createElement("div", {
@@ -19671,6 +21268,7 @@ var init_PluginsPage = __esm({
19671
21268
  init_checkbox();
19672
21269
  init_select();
19673
21270
  init_EventNotificationsPluginSettings();
21271
+ init_AuthProvidersPluginSettings();
19674
21272
  init_ImageOrUrlField();
19675
21273
  init_chat_email_intent();
19676
21274
  init_llm_agent_scope();
@@ -19689,7 +21287,8 @@ var init_PluginsPage = __esm({
19689
21287
  pg_boss: LucideIcons.CalendarClock,
19690
21288
  share: LucideIcons.Share2,
19691
21289
  whatsapp: LucideIcons.MessageCircle,
19692
- event_notifications: LucideIcons.Bell
21290
+ event_notifications: LucideIcons.Bell,
21291
+ auth_providers: LucideIcons.KeyRound
19693
21292
  };
19694
21293
  __name(normalizeSmsProviderChoice, "normalizeSmsProviderChoice");
19695
21294
  __name(normalizeMsg91ApiMode, "normalizeMsg91ApiMode");
@@ -19709,183 +21308,6 @@ var init_PluginsPage = __esm({
19709
21308
  }
19710
21309
  });
19711
21310
 
19712
- // src/lib/vendor-role-defaults.ts
19713
- function full() {
19714
- return {
19715
- canCreate: true,
19716
- canRead: true,
19717
- canUpdate: true,
19718
- canDelete: true
19719
- };
19720
- }
19721
- function read() {
19722
- return {
19723
- canCreate: false,
19724
- canRead: true,
19725
- canUpdate: false,
19726
- canDelete: false
19727
- };
19728
- }
19729
- function none() {
19730
- return {
19731
- canCreate: false,
19732
- canRead: false,
19733
- canUpdate: false,
19734
- canDelete: false
19735
- };
19736
- }
19737
- function readUpdate() {
19738
- return {
19739
- canCreate: false,
19740
- canRead: true,
19741
- canUpdate: true,
19742
- canDelete: false
19743
- };
19744
- }
19745
- function storePerms(spec) {
19746
- const resolve = /* @__PURE__ */ __name((v) => {
19747
- if (v === "full") return full();
19748
- if (v === "read") return read();
19749
- if (v === "none") return none();
19750
- return v;
19751
- }, "resolve");
19752
- const out = {};
19753
- for (const entity of STORE_ENTITIES) {
19754
- out[entity] = resolve(spec[entity] ?? "none");
19755
- }
19756
- return out;
19757
- }
19758
- var STORE_ENTITIES;
19759
- var init_vendor_role_defaults = __esm({
19760
- "src/lib/vendor-role-defaults.ts"() {
19761
- init_vendor_scope();
19762
- [
19763
- ...VENDOR_STORE_RBAC_ENTITIES,
19764
- "dashboard",
19765
- "settings",
19766
- "team",
19767
- "roles"
19768
- ];
19769
- STORE_ENTITIES = [
19770
- ...VENDOR_STORE_RBAC_ENTITIES
19771
- ];
19772
- __name(full, "full");
19773
- __name(read, "read");
19774
- __name(none, "none");
19775
- __name(readUpdate, "readUpdate");
19776
- __name(storePerms, "storePerms");
19777
- [
19778
- {
19779
- name: "Owner",
19780
- description: "Full store access and team/role management",
19781
- isSystem: true,
19782
- isOwnerRole: true,
19783
- permissions: {
19784
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
19785
- e,
19786
- "full"
19787
- ]))),
19788
- dashboard: read(),
19789
- settings: readUpdate(),
19790
- team: full(),
19791
- roles: full()
19792
- }
19793
- },
19794
- {
19795
- name: "Manager",
19796
- description: "Day-to-day store operations",
19797
- isSystem: true,
19798
- isOwnerRole: false,
19799
- permissions: {
19800
- ...storePerms({
19801
- products: "full",
19802
- collections: "full",
19803
- brands: "full",
19804
- product_categories: "full",
19805
- orders: "full",
19806
- vendor_customers: "full",
19807
- discounts: "full",
19808
- order_discounts: "full",
19809
- order_addresses: "full",
19810
- payments: "read",
19811
- taxes: "read",
19812
- attributes: "read"
19813
- }),
19814
- dashboard: read(),
19815
- settings: read(),
19816
- team: none(),
19817
- roles: none()
19818
- }
19819
- },
19820
- {
19821
- name: "Catalog Staff",
19822
- description: "Manage products and catalog content",
19823
- isSystem: true,
19824
- isOwnerRole: false,
19825
- permissions: {
19826
- ...storePerms({
19827
- products: "full",
19828
- collections: "full",
19829
- brands: "full",
19830
- product_categories: "full",
19831
- attributes: "full",
19832
- orders: "read",
19833
- vendor_customers: "read"
19834
- }),
19835
- dashboard: read(),
19836
- settings: none(),
19837
- team: none(),
19838
- roles: none()
19839
- }
19840
- },
19841
- {
19842
- name: "Fulfillment",
19843
- description: "Orders and fulfillment",
19844
- isSystem: true,
19845
- isOwnerRole: false,
19846
- permissions: {
19847
- ...storePerms({
19848
- products: "read",
19849
- orders: "full",
19850
- order_addresses: "full",
19851
- order_discounts: "read",
19852
- vendor_customers: "read"
19853
- }),
19854
- dashboard: read(),
19855
- settings: none(),
19856
- team: none(),
19857
- roles: none()
19858
- }
19859
- },
19860
- {
19861
- name: "Viewer",
19862
- description: "Read-only access to store data",
19863
- isSystem: true,
19864
- isOwnerRole: false,
19865
- permissions: {
19866
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
19867
- e,
19868
- "read"
19869
- ]))),
19870
- dashboard: read(),
19871
- settings: none(),
19872
- team: none(),
19873
- roles: none()
19874
- }
19875
- }
19876
- ];
19877
- }
19878
- });
19879
-
19880
- // src/auth/rbac-debug.ts
19881
- var init_rbac_debug = __esm({
19882
- "src/auth/rbac-debug.ts"() {
19883
- init_permission_entities();
19884
- init_vendor_role_defaults();
19885
- init_vendor_scope();
19886
- }
19887
- });
19888
-
19889
21311
  // src/auth/helpers.ts
19890
21312
  function canManageRoles(user) {
19891
21313
  return !!(user?.email && user.isRBACAdmin);
@@ -20043,23 +21465,6 @@ function VendorRolesPage() {
20043
21465
  return next;
20044
21466
  });
20045
21467
  }, "setAllRows");
20046
- const toggleEntityRow = /* @__PURE__ */ __name((entity) => {
20047
- const isOwnerOnlyEntity = entity === "team" || entity === "roles";
20048
- setMatrix((prev) => {
20049
- const current = prev[entity];
20050
- const isAllChecked = current?.canRead && (isOwnerOnlyEntity || current.canCreate && current.canUpdate && current.canDelete);
20051
- return {
20052
- ...prev,
20053
- [entity]: {
20054
- entity,
20055
- canRead: !isAllChecked,
20056
- canCreate: isOwnerOnlyEntity ? false : !isAllChecked,
20057
- canUpdate: isOwnerOnlyEntity ? false : !isAllChecked,
20058
- canDelete: isOwnerOnlyEntity ? false : !isAllChecked
20059
- }
20060
- };
20061
- });
20062
- }, "toggleEntityRow");
20063
21468
  const saveMatrix = /* @__PURE__ */ __name(async () => {
20064
21469
  if (!selectedId) return;
20065
21470
  setSaving(true);
@@ -20162,9 +21567,7 @@ function VendorRolesPage() {
20162
21567
  className: "text-base font-semibold text-white"
20163
21568
  }, "Vendor Store Roles & Permissions"), /* @__PURE__ */ React25__namespace.default.createElement("p", {
20164
21569
  className: "mt-0.5 text-xs text-gray-300"
20165
- }, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles). Permissions are stored in the ", /* @__PURE__ */ React25__namespace.default.createElement("code", {
20166
- className: "font-mono bg-gray-700 px-1 py-0.5 rounded text-gray-200"
20167
- }, "vendor_role_permissions"), " table and apply directly to team members assigned to that role.")), /* @__PURE__ */ React25__namespace.default.createElement("div", {
21570
+ }, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles).")), /* @__PURE__ */ React25__namespace.default.createElement("div", {
20168
21571
  className: "min-w-0 p-6"
20169
21572
  }, error ? /* @__PURE__ */ React25__namespace.default.createElement("p", {
20170
21573
  className: "mb-4 text-sm text-red-600 dark:text-red-400"
@@ -20255,31 +21658,35 @@ function VendorRolesPage() {
20255
21658
  }), " Clear All"))), /* @__PURE__ */ React25__namespace.default.createElement("div", {
20256
21659
  className: "overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700"
20257
21660
  }, /* @__PURE__ */ React25__namespace.default.createElement("table", {
20258
- className: "min-w-full text-xs"
21661
+ className: "min-w-full text-sm"
20259
21662
  }, /* @__PURE__ */ React25__namespace.default.createElement("thead", {
20260
- className: "bg-gray-100 text-left text-gray-700 dark:bg-gray-900 dark:text-gray-300"
20261
- }, /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("th", {
20262
- className: "px-3 py-2 font-medium"
20263
- }, "Entity"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
20264
- className: "px-3 py-2 font-medium"
21663
+ className: "bg-white dark:bg-gray-800"
21664
+ }, /* @__PURE__ */ React25__namespace.default.createElement("tr", {
21665
+ className: "border-b border-gray-200 dark:border-gray-700"
21666
+ }, /* @__PURE__ */ React25__namespace.default.createElement("th", {
21667
+ className: "px-3 py-2.5 text-left font-semibold text-gray-700 dark:text-gray-300"
21668
+ }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
21669
+ className: "flex items-center gap-2"
21670
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "Entity"))), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21671
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
20265
21672
  }, "Create"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
20266
- className: "px-3 py-2 font-medium"
21673
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
20267
21674
  }, "Read"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
20268
- className: "px-3 py-2 font-medium"
21675
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
20269
21676
  }, "Update"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
20270
- className: "px-3 py-2 font-medium"
20271
- }, "Delete"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
20272
- className: "px-3 py-2 font-medium text-right"
20273
- }, "Row Actions"))), /* @__PURE__ */ React25__namespace.default.createElement("tbody", null, entities.map((entity) => {
21677
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
21678
+ }, "Delete"))), /* @__PURE__ */ React25__namespace.default.createElement("tbody", null, entities.map((entity) => {
20274
21679
  const isOwnerOnlyEntity = entity === "team" || entity === "roles";
20275
21680
  return /* @__PURE__ */ React25__namespace.default.createElement("tr", {
20276
21681
  key: entity,
20277
- className: "border-t border-gray-100 dark:border-gray-700 hover:bg-gray-50/50 dark:hover:bg-gray-800/50"
21682
+ className: "border-b border-gray-100 last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800/50"
20278
21683
  }, /* @__PURE__ */ React25__namespace.default.createElement("td", {
20279
- className: "px-3 py-2 font-mono text-gray-800 dark:text-gray-200 font-medium"
20280
- }, entity, isOwnerOnlyEntity && /* @__PURE__ */ React25__namespace.default.createElement("span", {
20281
- className: "ml-2 text-[10px] text-amber-600 dark:text-amber-400 font-normal"
20282
- }, "(Owner managed)")), [
21684
+ className: "px-3 py-2.5 font-mono text-sm text-gray-800 dark:text-gray-200"
21685
+ }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
21686
+ className: "flex items-center gap-2"
21687
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, entity), isOwnerOnlyEntity && /* @__PURE__ */ React25__namespace.default.createElement("span", {
21688
+ className: "text-[10px] text-amber-600 dark:text-amber-400"
21689
+ }, "(Owner managed)"))), [
20283
21690
  "canCreate",
20284
21691
  "canRead",
20285
21692
  "canUpdate",
@@ -20288,21 +21695,15 @@ function VendorRolesPage() {
20288
21695
  const disabled = isOwnerOnlyEntity && key !== "canRead";
20289
21696
  return /* @__PURE__ */ React25__namespace.default.createElement("td", {
20290
21697
  key,
20291
- className: "px-3 py-2"
21698
+ className: "px-3 py-2.5 text-center"
20292
21699
  }, /* @__PURE__ */ React25__namespace.default.createElement("input", {
20293
21700
  type: "checkbox",
20294
21701
  checked: disabled ? false : !!matrix[entity]?.[key],
20295
21702
  disabled,
20296
21703
  onChange: /* @__PURE__ */ __name(() => toggle(entity, key), "onChange"),
20297
- className: disabled ? "cursor-not-allowed opacity-40" : "cursor-pointer"
21704
+ className: disabled ? "h-4 w-4 cursor-not-allowed opacity-40" : "h-4 w-4 cursor-pointer"
20298
21705
  }));
20299
- }), /* @__PURE__ */ React25__namespace.default.createElement("td", {
20300
- className: "px-3 py-2 text-right"
20301
- }, /* @__PURE__ */ React25__namespace.default.createElement("button", {
20302
- type: "button",
20303
- onClick: /* @__PURE__ */ __name(() => toggleEntityRow(entity), "onClick"),
20304
- className: "text-[11px] text-blue-600 hover:underline dark:text-blue-400"
20305
- }, "Toggle")));
21706
+ }));
20306
21707
  })))))))), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
20307
21708
  open: deleteOpen,
20308
21709
  onOpenChange: setDeleteOpen
@@ -20663,7 +22064,7 @@ function PlatformRolesPage() {
20663
22064
  className: "p-2"
20664
22065
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
20665
22066
  className: "flex items-center justify-center gap-1"
20666
- }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "C"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
22067
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "Create"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
20667
22068
  type: "checkbox",
20668
22069
  checked: isColumnAllChecked("canCreate"),
20669
22070
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canCreate"), "onChange")
@@ -20671,7 +22072,7 @@ function PlatformRolesPage() {
20671
22072
  className: "p-2"
20672
22073
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
20673
22074
  className: "flex items-center justify-center gap-1"
20674
- }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "R"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
22075
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "Read"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
20675
22076
  type: "checkbox",
20676
22077
  checked: isColumnAllChecked("canRead"),
20677
22078
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canRead"), "onChange")
@@ -20679,7 +22080,7 @@ function PlatformRolesPage() {
20679
22080
  className: "p-2"
20680
22081
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
20681
22082
  className: "flex items-center justify-center gap-1"
20682
- }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "U"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
22083
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "Update"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
20683
22084
  type: "checkbox",
20684
22085
  checked: isColumnAllChecked("canUpdate"),
20685
22086
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canUpdate"), "onChange")
@@ -20687,7 +22088,7 @@ function PlatformRolesPage() {
20687
22088
  className: "p-2"
20688
22089
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", {
20689
22090
  className: "flex items-center justify-center gap-1"
20690
- }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "D"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
22091
+ }, /* @__PURE__ */ React25__namespace.default.createElement("span", null, "Delete"), /* @__PURE__ */ React25__namespace.default.createElement("input", {
20691
22092
  type: "checkbox",
20692
22093
  checked: isColumnAllChecked("canDelete"),
20693
22094
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canDelete"), "onChange")
@@ -21180,39 +22581,90 @@ var init_VendorTeamPage = __esm({
21180
22581
  __name(VendorTeamPage, "VendorTeamPage");
21181
22582
  }
21182
22583
  });
21183
-
21184
- // src/lib/vendor-profile.ts
21185
- function validateIndiaTaxIds(gstin, pan) {
21186
- if (gstin && !/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin)) {
21187
- return "Invalid GSTIN format (15 characters, e.g. 22AAAAA0000A1Z5)";
22584
+ function splitPhoneAndCountryCode(rawPhone, defaultCode = "+91") {
22585
+ if (!rawPhone || !rawPhone.trim()) {
22586
+ return {
22587
+ countryCode: defaultCode,
22588
+ phoneNumber: ""
22589
+ };
22590
+ }
22591
+ const trimmed = rawPhone.trim();
22592
+ const match = COUNTRY_PHONE_CODES.find((c) => trimmed.startsWith(c.code));
22593
+ if (match) {
22594
+ return {
22595
+ countryCode: match.code,
22596
+ phoneNumber: trimmed.slice(match.code.length).replace(/\s+/g, "")
22597
+ };
22598
+ }
22599
+ if (trimmed.startsWith("+")) {
22600
+ const spaceIdx = trimmed.indexOf(" ");
22601
+ if (spaceIdx > 0) {
22602
+ return {
22603
+ countryCode: trimmed.slice(0, spaceIdx),
22604
+ phoneNumber: trimmed.slice(spaceIdx + 1).replace(/\s+/g, "")
22605
+ };
22606
+ }
22607
+ }
22608
+ return {
22609
+ countryCode: defaultCode,
22610
+ phoneNumber: trimmed.replace(/\D/g, "")
22611
+ };
22612
+ }
22613
+ function formatPhoneWithCountryCode(countryCode, phoneNumber) {
22614
+ const cleanNum = phoneNumber.replace(/\D/g, "");
22615
+ if (!cleanNum) return null;
22616
+ const cleanCode = countryCode.trim() || "+91";
22617
+ return `${cleanCode}${cleanNum}`;
22618
+ }
22619
+ function validateGstin(gstin, options) {
22620
+ const required = options?.required === true;
22621
+ if (!gstin || !gstin.trim()) {
22622
+ if (required) return "GSTIN number is required";
22623
+ return null;
21188
22624
  }
21189
- if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan)) {
22625
+ if (!/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin.trim())) {
22626
+ return "Invalid GSTIN number (15 characters, e.g. 22AAAAA0000A1Z5)";
22627
+ }
22628
+ return null;
22629
+ }
22630
+ function validateIndiaTaxIds(gstin, pan, options) {
22631
+ const gstinErr = validateGstin(gstin, {
22632
+ required: options?.requiredGstin
22633
+ });
22634
+ if (gstinErr) return gstinErr;
22635
+ if (options?.requiredPan && (!pan || !pan.trim())) {
22636
+ return "PAN is required";
22637
+ }
22638
+ if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan.trim())) {
21190
22639
  return "Invalid PAN format (e.g. ABCDE1234F)";
21191
22640
  }
21192
22641
  return null;
21193
22642
  }
21194
22643
  function validateAadhaar(aadhaar) {
21195
22644
  if (!aadhaar) return null;
21196
- if (!/^[0-9]{12}$/.test(aadhaar)) {
22645
+ if (!/^[0-9]{12}$/.test(aadhaar.replace(/\s+/g, ""))) {
21197
22646
  return "Invalid Aadhaar number (12 digits)";
21198
22647
  }
21199
22648
  return null;
21200
22649
  }
21201
22650
  function validatePersonKyc(person, options) {
21202
22651
  const required = options?.required === true;
21203
- if (required && !person.aadhaarNo) return "Aadhaar number is required";
21204
- if (required && !person.panNo) return "PAN is required";
22652
+ if (required && (!person.aadhaarNo || !person.aadhaarNo.trim())) return "Aadhaar number is required";
22653
+ if (required && (!person.panNo || !person.panNo.trim())) return "PAN is required";
21205
22654
  const aadhaarErr = validateAadhaar(person.aadhaarNo ?? null);
21206
22655
  if (aadhaarErr) return aadhaarErr;
21207
- const panErr = validateIndiaTaxIds(null, person.panNo ?? null);
22656
+ const panErr = validateIndiaTaxIds(null, person.panNo ?? null, {
22657
+ requiredPan: required
22658
+ });
21208
22659
  if (panErr) return panErr;
21209
22660
  return null;
21210
22661
  }
21211
22662
  function readOwnerDesignation(metadata) {
21212
- const v = metadata?.ownerDesignation;
21213
- return typeof v === "string" ? v : "";
22663
+ if (!metadata || typeof metadata !== "object") return "";
22664
+ const d = metadata.ownerDesignation;
22665
+ return typeof d === "string" ? d.trim() : "";
21214
22666
  }
21215
- var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES;
22667
+ var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES, COUNTRY_PHONE_CODES;
21216
22668
  var init_vendor_profile = __esm({
21217
22669
  "src/lib/vendor-profile.ts"() {
21218
22670
  VENDOR_REGISTRATION_STATUSES = [
@@ -21259,7 +22711,34 @@ var init_vendor_profile = __esm({
21259
22711
  label: "Other"
21260
22712
  }
21261
22713
  ];
22714
+ COUNTRY_PHONE_CODES = (() => {
22715
+ const list = [];
22716
+ const seenCodes = /* @__PURE__ */ new Set();
22717
+ for (const c of countryStateCity.Country.getAllCountries()) {
22718
+ if (!c.phonecode) continue;
22719
+ const rawCode = c.phonecode.replace(/^\+/, "").trim();
22720
+ if (!rawCode) continue;
22721
+ const code = `+${rawCode}`;
22722
+ const key = `${code}-${c.name}`;
22723
+ if (seenCodes.has(key)) continue;
22724
+ seenCodes.add(key);
22725
+ list.push({
22726
+ code,
22727
+ country: c.name,
22728
+ isoCode: c.isoCode,
22729
+ label: `${code} (${c.name})`
22730
+ });
22731
+ }
22732
+ return list.sort((a, b) => {
22733
+ if (a.code === "+91" && b.code !== "+91") return -1;
22734
+ if (b.code === "+91" && a.code !== "+91") return 1;
22735
+ return a.country.localeCompare(b.country);
22736
+ });
22737
+ })();
22738
+ __name(splitPhoneAndCountryCode, "splitPhoneAndCountryCode");
22739
+ __name(formatPhoneWithCountryCode, "formatPhoneWithCountryCode");
21262
22740
  new Set(VENDOR_REGISTRATION_STATUSES.map((s) => s.value));
22741
+ __name(validateGstin, "validateGstin");
21263
22742
  __name(validateIndiaTaxIds, "validateIndiaTaxIds");
21264
22743
  __name(validateAadhaar, "validateAadhaar");
21265
22744
  __name(validatePersonKyc, "validatePersonKyc");
@@ -21862,10 +23341,26 @@ function SubmissionDetailPage({ submissionId }) {
21862
23341
  }
21863
23342
  const formName = submission.form?.name ?? `Form #${submission.formId}`;
21864
23343
  const contact = submission.contact;
21865
- const fieldLabel = /* @__PURE__ */ __name((key) => {
21866
- const field = submission.form?.fields?.find((f) => String(f.id) === key);
21867
- return field?.label ?? key;
21868
- }, "fieldLabel");
23344
+ const resolveFieldLabel = /* @__PURE__ */ __name((key) => {
23345
+ if (!key) return "\u2014";
23346
+ const rawKey = key.trim();
23347
+ const numericIdMatch = rawKey.match(/^(?:field_)?(\d+)$/i);
23348
+ const fieldIdStr = numericIdMatch ? numericIdMatch[1] : null;
23349
+ const fields = submission?.form?.fields ?? [];
23350
+ const field = fields.find((f) => fieldIdStr !== null && String(f.id) === fieldIdStr || String(f.id) === rawKey || f.name && f.name.trim().toLowerCase() === rawKey.toLowerCase() || f.label && f.label.trim().toLowerCase() === rawKey.toLowerCase());
23351
+ if (field) {
23352
+ if (field.label && field.label.trim() && !/^\d+$/.test(field.label.trim())) {
23353
+ return field.label.trim();
23354
+ }
23355
+ if (field.name && field.name.trim() && !/^\d+$/.test(field.name.trim())) {
23356
+ return field.name.trim().replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
23357
+ }
23358
+ }
23359
+ if (fieldIdStr !== null) {
23360
+ return `Field #${fieldIdStr}`;
23361
+ }
23362
+ return rawKey.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
23363
+ }, "resolveFieldLabel");
21869
23364
  return /* @__PURE__ */ React.createElement("div", {
21870
23365
  className: "rounded-lg bg-white shadow-md"
21871
23366
  }, /* @__PURE__ */ React.createElement(DetailPageHeader, {
@@ -21924,7 +23419,7 @@ function SubmissionDetailPage({ submissionId }) {
21924
23419
  key
21925
23420
  }, /* @__PURE__ */ React.createElement("td", {
21926
23421
  className: "py-2 px-3 text-gray-600 font-medium"
21927
- }, fieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23422
+ }, resolveFieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
21928
23423
  className: "py-2 px-3 text-gray-900 break-words min-w-0"
21929
23424
  }, value === null || value === void 0 ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value)))))))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
21930
23425
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
@@ -23579,7 +25074,7 @@ function OrderPlacementPage({ editOrderId }) {
23579
25074
  productQuery
23580
25075
  ]);
23581
25076
  const recalcPreview = React25.useCallback(async () => {
23582
- const orderLines = lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => ({
25077
+ const orderLines = lines.filter((l) => !l.key.startsWith("reward-")).map((l) => ({
23583
25078
  productId: l.productId,
23584
25079
  quantity: l.quantity
23585
25080
  }));
@@ -23603,19 +25098,22 @@ function OrderPlacementPage({ editOrderId }) {
23603
25098
  body: JSON.stringify({
23604
25099
  orderLines,
23605
25100
  currency,
23606
- discountId: coupons[0]?.discountId ?? null
25101
+ discountId: coupons[0]?.discountId ?? null,
25102
+ discountIds: coupons.map((c) => c.discountId)
23607
25103
  })
23608
25104
  });
23609
25105
  if (!res.ok) throw new Error("Calculate failed");
23610
25106
  const data = await res.json();
23611
25107
  const correctedLines = data.lines.map((cl) => {
23612
25108
  if (cl.productId == null) return cl;
23613
- const cached = productCache.current.get(cl.productId);
25109
+ const pid = Number(cl.productId);
25110
+ const cached = productCache.current.get(pid);
23614
25111
  if (!cl.found && cached) {
23615
- const unitPrice2 = cached.price;
25112
+ const unitPrice2 = Number(cached.price ?? 0);
23616
25113
  const subtotal3 = unitPrice2 * cl.quantity;
23617
25114
  return {
23618
25115
  ...cl,
25116
+ productId: pid,
23619
25117
  found: true,
23620
25118
  unitPrice: unitPrice2,
23621
25119
  subtotal: subtotal3,
@@ -23623,15 +25121,16 @@ function OrderPlacementPage({ editOrderId }) {
23623
25121
  total: subtotal3
23624
25122
  };
23625
25123
  }
23626
- const unitPrice = cached?.price ?? cl.unitPrice;
25124
+ const unitPrice = Number(cached?.price ?? cl.unitPrice ?? 0);
23627
25125
  const subtotal2 = unitPrice * cl.quantity;
23628
- const taxRate = cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal : 0);
23629
- const lineDiscount = cl.subtotal > 0 && subtotal2 > 0 ? cl.discount ?? 0 : 0;
25126
+ const taxRate = Number(cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal * 100 : 0));
25127
+ const lineDiscount = Number(cl.discount ?? 0);
23630
25128
  const discountedBase = Math.max(0, subtotal2 - lineDiscount);
23631
25129
  const tax2 = discountedBase * taxRate / 100;
23632
25130
  const total2 = discountedBase + tax2;
23633
25131
  return {
23634
25132
  ...cl,
25133
+ productId: pid,
23635
25134
  unitPrice,
23636
25135
  subtotal: subtotal2,
23637
25136
  taxRate,
@@ -23639,13 +25138,14 @@ function OrderPlacementPage({ editOrderId }) {
23639
25138
  total: total2
23640
25139
  };
23641
25140
  });
23642
- const rewardDisplayLines = lines.filter((l) => rewardProductIds.current.has(l.productId)).map((l) => {
23643
- const cached = productCache.current.get(l.productId);
25141
+ const rewardDisplayLines = lines.filter((l) => l.key.startsWith("reward-")).map((l) => {
25142
+ const pid = Number(l.productId);
25143
+ const cached = productCache.current.get(pid);
23644
25144
  return {
23645
- productId: l.productId,
25145
+ productId: pid,
23646
25146
  productName: l.label,
23647
25147
  found: true,
23648
- unitPrice: cached?.price ?? 0,
25148
+ unitPrice: Number(cached?.price ?? 0),
23649
25149
  quantity: l.quantity,
23650
25150
  subtotal: 0,
23651
25151
  tax: 0,
@@ -23687,7 +25187,7 @@ function OrderPlacementPage({ editOrderId }) {
23687
25187
  React25.useEffect(() => {
23688
25188
  const automaticCoupons = coupons.filter((c) => c.isAutomatic);
23689
25189
  if (automaticCoupons.length === 0) return;
23690
- const nonRewardLines = lines.filter((l) => !rewardProductIds.current.has(l.productId));
25190
+ const nonRewardLines = lines.filter((l) => !l.key.startsWith("reward-"));
23691
25191
  if (nonRewardLines.length === 0) {
23692
25192
  const autoIds = new Set(automaticCoupons.map((c) => c.discountId));
23693
25193
  const rewardPids = new Set(automaticCoupons.flatMap((c) => c.rewardLines.map((r) => r.productId)));
@@ -23767,7 +25267,7 @@ function OrderPlacementPage({ editOrderId }) {
23767
25267
  cancelled = true;
23768
25268
  };
23769
25269
  }, [
23770
- lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => `${l.productId}:${l.quantity}`).join(","),
25270
+ lines.filter((l) => !l.key.startsWith("reward-")).map((l) => `${l.productId}:${l.quantity}`).join(","),
23771
25271
  coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
23772
25272
  ]);
23773
25273
  function handleRemoveCoupon(couponCode) {
@@ -23889,7 +25389,10 @@ function OrderPlacementPage({ editOrderId }) {
23889
25389
  const calcByProductId = React25.useMemo(() => {
23890
25390
  const m = /* @__PURE__ */ new Map();
23891
25391
  for (const l of preview?.lines ?? []) {
23892
- if (l.productId != null && l.found) m.set(l.productId, l);
25392
+ if (l.productId != null && l.found) {
25393
+ const pid = Number(l.productId);
25394
+ if (Number.isFinite(pid)) m.set(pid, l);
25395
+ }
23893
25396
  }
23894
25397
  return m;
23895
25398
  }, [
@@ -23911,13 +25414,13 @@ function OrderPlacementPage({ editOrderId }) {
23911
25414
  }
23912
25415
  const unknown = preview?.lines.filter((l) => !l.found) ?? [];
23913
25416
  if (unknown.length > 0) {
23914
- sonner.toast.error("Some products are missing or unavailable. Remove invalid lines and try again.");
25417
+ sonner.toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
23915
25418
  return;
23916
25419
  }
23917
25420
  setSubmitting(true);
25421
+ let resolvedContactId = effectiveContactId;
23918
25422
  try {
23919
- let resolvedContactId = placeForSomeoneElse ? subContactId : null;
23920
- if (!resolvedContactId && effectiveEmail) {
25423
+ if (resolvedContactId == null && effectiveEmail) {
23921
25424
  try {
23922
25425
  const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
23923
25426
  if (res2.ok) {
@@ -23939,12 +25442,12 @@ function OrderPlacementPage({ editOrderId }) {
23939
25442
  billingAddress,
23940
25443
  shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
23941
25444
  orderLines: lines.map((l) => {
23942
- const isReward = rewardProductIds.current.has(l.productId);
23943
- const calc = preview?.lines.find((pl) => pl.productId === l.productId);
25445
+ const isReward = l.key.startsWith("reward-");
25446
+ const calc = preview?.lines.find((pl) => Number(pl.productId) === Number(l.productId));
23944
25447
  return {
23945
25448
  productId: l.productId,
23946
25449
  quantity: l.quantity,
23947
- unitPrice: isReward ? 0 : calc?.unitPrice ?? productCache.current.get(l.productId)?.price ?? 0
25450
+ unitPrice: isReward ? 0 : calc?.unitPrice ?? Number(productCache.current.get(l.productId)?.price ?? 0)
23948
25451
  };
23949
25452
  })
23950
25453
  };
@@ -28920,8 +30423,8 @@ function ProductEditPage({ productId }) {
28920
30423
  const [price, setPrice] = React25.useState(0);
28921
30424
  const [defaultPriceStr, setDefaultPriceStr] = React25.useState("");
28922
30425
  const [pricingConfig, setPricingConfig] = React25.useState(DEFAULT_PRICING_CONFIG);
28923
- const [compareAtPrice, setCompareAtPrice] = React25.useState(0);
28924
- const [quantity, setQuantity] = React25.useState(1);
30426
+ const [compareAtPrice, setCompareAtPrice] = React25.useState("");
30427
+ const [quantity, setQuantity] = React25.useState("1");
28925
30428
  const [status, setStatus] = React25.useState("draft");
28926
30429
  const [approvalStatus, setApprovalStatus] = React25.useState("pending");
28927
30430
  const [rejectionReason, setRejectionReason] = React25.useState("");
@@ -29142,8 +30645,8 @@ function ProductEditPage({ productId }) {
29142
30645
  setDefaultPriceStr(product.price != null && Number.isFinite(Number(product.price)) ? String(product.price) : "");
29143
30646
  setPrice(product.price != null ? Number(product.price) : 0);
29144
30647
  const rawCompare = product.compareAtPrice != null ? Number(product.compareAtPrice) : 0;
29145
- setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
29146
- setQuantity(product.quantity ?? 1);
30648
+ setCompareAtPrice(rawCompare != null && Number.isFinite(rawCompare) ? String(rawCompare) : "");
30649
+ setQuantity(product.quantity != null ? String(product.quantity) : "1");
29147
30650
  setStatus(product.status ?? "draft");
29148
30651
  setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
29149
30652
  setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
@@ -29310,22 +30813,6 @@ function ProductEditPage({ productId }) {
29310
30813
  return pricingConfig.defaultCurrency || "INR";
29311
30814
  }
29312
30815
  })();
29313
- const handleNumberChange = /* @__PURE__ */ __name((setter, options) => (e) => {
29314
- const value = e.target.value;
29315
- if (value === "") {
29316
- setter(0);
29317
- return;
29318
- }
29319
- let num = options?.integer ? Number.parseInt(value, 10) : Number(value);
29320
- if (Number.isNaN(num)) {
29321
- setter(0);
29322
- return;
29323
- }
29324
- if (options?.min !== void 0) {
29325
- num = Math.max(options.min, num);
29326
- }
29327
- setter(num);
29328
- }, "handleNumberChange");
29329
30816
  const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
29330
30817
  approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
29331
30818
  setRejectDraft(rejectionReason);
@@ -29398,12 +30885,13 @@ function ProductEditPage({ productId }) {
29398
30885
  return;
29399
30886
  }
29400
30887
  }
29401
- const quantityErrors = validateProductQuantity(quantity, hasVariants, parsedVariants);
30888
+ const quantityValue = quantity === "" ? 0 : Number(quantity);
30889
+ const quantityErrors = validateProductQuantity(quantityValue, hasVariants, parsedVariants);
29402
30890
  if (quantityErrors.length) {
29403
30891
  setErrors(quantityErrors);
29404
30892
  return;
29405
30893
  }
29406
- const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantity;
30894
+ const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantityValue;
29407
30895
  setSaving(true);
29408
30896
  try {
29409
30897
  const compareAtPriceValue = compareAtPrice ? Math.round(Number(compareAtPrice) * 100) / 100 : null;
@@ -30017,9 +31505,8 @@ function ProductEditPage({ productId }) {
30017
31505
  type: "number",
30018
31506
  value: compareAtPrice,
30019
31507
  className: inputCls3,
30020
- onChange: handleNumberChange(setCompareAtPrice, {
30021
- min: 0
30022
- })
31508
+ min: 0,
31509
+ onChange: /* @__PURE__ */ __name((e) => setCompareAtPrice(e.target.value), "onChange")
30023
31510
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
30024
31511
  className: labelCls3
30025
31512
  }, "Contact form"), /* @__PURE__ */ React.createElement("select", {
@@ -30042,10 +31529,7 @@ function ProductEditPage({ productId }) {
30042
31529
  min: 0,
30043
31530
  step: 1,
30044
31531
  value: hasVariants ? productQuantityFromVariants(variantsFromForm(variantRows, pricingConfig.defaultCurrency)) : quantity,
30045
- onChange: handleNumberChange(setQuantity, {
30046
- min: 0,
30047
- integer: true
30048
- }),
31532
+ onChange: /* @__PURE__ */ __name((e) => setQuantity(e.target.value), "onChange"),
30049
31533
  className: `${inputCls3}${hasVariants ? " bg-gray-100" : ""}`,
30050
31534
  required: !hasVariants,
30051
31535
  readOnly: hasVariants,
@@ -30122,8 +31606,18 @@ function ProductEditPage({ productId }) {
30122
31606
  value: row.taxId === "" ? "" : String(row.taxId),
30123
31607
  onChange: /* @__PURE__ */ __name((e) => {
30124
31608
  const v = e.target.value;
31609
+ if (v === "") {
31610
+ setTaxRow(i, {
31611
+ taxId: "",
31612
+ rate: ""
31613
+ });
31614
+ return;
31615
+ }
31616
+ const taxId = Number(v);
31617
+ const selectedTax = taxMasterList.find((t) => t.id === taxId);
30125
31618
  setTaxRow(i, {
30126
- taxId: v === "" ? "" : Number(v)
31619
+ taxId,
31620
+ rate: selectedTax?.rate != null ? String(selectedTax.rate) : ""
30127
31621
  });
30128
31622
  }, "onChange"),
30129
31623
  className: inputCls3
@@ -31948,6 +33442,35 @@ function EventEditPage({ eventId }) {
31948
33442
  const [saving, setSaving] = React25.useState(false);
31949
33443
  const [errors, setErrors] = React25.useState([]);
31950
33444
  const [activeTab, setActiveTab] = React25.useState("basic");
33445
+ const eventTabs = [
33446
+ {
33447
+ key: "basic",
33448
+ label: "Basic info"
33449
+ },
33450
+ {
33451
+ key: "venue",
33452
+ label: "Venue"
33453
+ },
33454
+ {
33455
+ key: "datetime",
33456
+ label: "Date & time"
33457
+ },
33458
+ {
33459
+ key: "settings",
33460
+ label: "Settings"
33461
+ }
33462
+ ];
33463
+ const activeTabIndex = eventTabs.findIndex((tab) => tab.key === activeTab);
33464
+ const goToPreviousTab = /* @__PURE__ */ __name(() => {
33465
+ if (activeTabIndex > 0) {
33466
+ setActiveTab(eventTabs[activeTabIndex - 1].key);
33467
+ }
33468
+ }, "goToPreviousTab");
33469
+ const goToNextTab = /* @__PURE__ */ __name(() => {
33470
+ if (activeTabIndex < eventTabs.length - 1) {
33471
+ setActiveTab(eventTabs[activeTabIndex + 1].key);
33472
+ }
33473
+ }, "goToNextTab");
31951
33474
  const [name, setName] = React25.useState("");
31952
33475
  const [slug, setSlug] = React25.useState("");
31953
33476
  const [description, setDescription] = React25.useState("");
@@ -32767,7 +34290,19 @@ function EventEditPage({ eventId }) {
32767
34290
  [key]: value
32768
34291
  })), "onChange")
32769
34292
  }))))
32770
- }), showEventProducts ? /* @__PURE__ */ React.createElement("div", {
34293
+ }), /* @__PURE__ */ React.createElement("div", {
34294
+ className: "flex items-center justify-between border-t border-gray-200 px-4 py-4 sm:px-6"
34295
+ }, /* @__PURE__ */ React.createElement("button", {
34296
+ type: "button",
34297
+ onClick: goToPreviousTab,
34298
+ disabled: activeTabIndex === 0,
34299
+ className: "rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
34300
+ }, "Previous"), /* @__PURE__ */ React.createElement("button", {
34301
+ type: "button",
34302
+ onClick: goToNextTab,
34303
+ disabled: activeTabIndex === eventTabs.length - 1,
34304
+ className: "rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-40"
34305
+ }, "Next")), showEventProducts ? /* @__PURE__ */ React.createElement("div", {
32771
34306
  className: "border-t border-gray-200 px-4 py-4 sm:px-6"
32772
34307
  }, /* @__PURE__ */ React.createElement(EventProductsSection, {
32773
34308
  eventId: eventRecordId,
@@ -33112,6 +34647,18 @@ function ComboEditPage({ comboId }) {
33112
34647
  ]);
33113
34648
  return;
33114
34649
  }
34650
+ if (!startsAt) {
34651
+ setErrors([
34652
+ "Starts at is required"
34653
+ ]);
34654
+ return;
34655
+ }
34656
+ if (!endsAt) {
34657
+ setErrors([
34658
+ "Ends at is required"
34659
+ ]);
34660
+ return;
34661
+ }
33115
34662
  if (!priceStr.trim()) {
33116
34663
  setErrors([
33117
34664
  `Price (${defaultCurrency}) is required`
@@ -33401,18 +34948,20 @@ function ComboEditPage({ comboId }) {
33401
34948
  className: "grid grid-cols-2 gap-4"
33402
34949
  }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
33403
34950
  className: "block text-xs font-medium text-gray-600 mb-1"
33404
- }, "Starts at"), /* @__PURE__ */ React.createElement("input", {
34951
+ }, "Starts at *"), /* @__PURE__ */ React.createElement("input", {
33405
34952
  type: "datetime-local",
33406
34953
  value: startsAt,
33407
34954
  onChange: /* @__PURE__ */ __name((e) => setStartsAt(e.target.value), "onChange"),
33408
- className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
34955
+ className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
34956
+ required: true
33409
34957
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
33410
34958
  className: "block text-xs font-medium text-gray-600 mb-1"
33411
- }, "Ends at"), /* @__PURE__ */ React.createElement("input", {
34959
+ }, "Ends at *"), /* @__PURE__ */ React.createElement("input", {
33412
34960
  type: "datetime-local",
33413
34961
  value: endsAt,
33414
34962
  onChange: /* @__PURE__ */ __name((e) => setEndsAt(e.target.value), "onChange"),
33415
- className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
34963
+ className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
34964
+ required: true
33416
34965
  })))))),
33417
34966
  sidebar: /* @__PURE__ */ React.createElement(React.Fragment, null)
33418
34967
  }));
@@ -33498,7 +35047,8 @@ function VendorEditPage({ vendorId }) {
33498
35047
  const [website, setWebsite] = React25.useState("");
33499
35048
  const [logo, setLogo] = React25.useState("");
33500
35049
  const [email, setEmail] = React25.useState("");
33501
- const [phone, setPhone] = React25.useState("");
35050
+ const [storePhoneCode, setStorePhoneCode] = React25.useState("+91");
35051
+ const [storePhoneNum, setStorePhoneNum] = React25.useState("");
33502
35052
  const [addressLine1, setAddressLine1] = React25.useState("");
33503
35053
  const [addressLine2, setAddressLine2] = React25.useState("");
33504
35054
  const [city, setCity] = React25.useState("");
@@ -33512,7 +35062,8 @@ function VendorEditPage({ vendorId }) {
33512
35062
  const [metadata, setMetadata] = React25.useState(null);
33513
35063
  const [ownerName, setOwnerName] = React25.useState("");
33514
35064
  const [ownerEmail, setOwnerEmail] = React25.useState("");
33515
- const [ownerPhone, setOwnerPhone] = React25.useState("");
35065
+ const [ownerPhoneCode, setOwnerPhoneCode] = React25.useState("+91");
35066
+ const [ownerPhoneNum, setOwnerPhoneNum] = React25.useState("");
33516
35067
  const [ownerDesignation, setOwnerDesignation] = React25.useState("");
33517
35068
  const [ownerAadhaarNo, setOwnerAadhaarNo] = React25.useState("");
33518
35069
  const [ownerPanNo, setOwnerPanNo] = React25.useState("");
@@ -33536,7 +35087,7 @@ function VendorEditPage({ vendorId }) {
33536
35087
  website: website.trim() || null,
33537
35088
  logo: logo.trim() || null,
33538
35089
  email: email.trim() || null,
33539
- phone: phone.trim() || null,
35090
+ phone: formatPhoneWithCountryCode(storePhoneCode, storePhoneNum),
33540
35091
  addressLine1: addressLine1.trim() || null,
33541
35092
  addressLine2: addressLine2.trim() || null,
33542
35093
  city: city.trim() || null,
@@ -33564,7 +35115,9 @@ function VendorEditPage({ vendorId }) {
33564
35115
  setWebsite(data.website ?? "");
33565
35116
  setLogo(data.logo ?? "");
33566
35117
  setEmail(data.email ?? "");
33567
- setPhone(data.phone ?? "");
35118
+ const sPhone = splitPhoneAndCountryCode(data.phone);
35119
+ setStorePhoneCode(sPhone.countryCode);
35120
+ setStorePhoneNum(sPhone.phoneNumber);
33568
35121
  setAddressLine1(data.addressLine1 ?? "");
33569
35122
  setAddressLine2(data.addressLine2 ?? "");
33570
35123
  setCity(data.city ?? "");
@@ -33586,7 +35139,9 @@ function VendorEditPage({ vendorId }) {
33586
35139
  if (!cancelled) {
33587
35140
  setOwnerName(user.name ?? "");
33588
35141
  setOwnerEmail(user.email ?? "");
33589
- setOwnerPhone(user.phone ?? "");
35142
+ const oPhone = splitPhoneAndCountryCode(user.phone);
35143
+ setOwnerPhoneCode(oPhone.countryCode);
35144
+ setOwnerPhoneNum(oPhone.phoneNumber);
33590
35145
  }
33591
35146
  }
33592
35147
  } else if (!cancelled) {
@@ -33733,7 +35288,10 @@ function VendorEditPage({ vendorId }) {
33733
35288
  setSaving(false);
33734
35289
  return;
33735
35290
  }
33736
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35291
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
35292
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35293
+ required: true
35294
+ });
33737
35295
  if (taxError) {
33738
35296
  setErrors([
33739
35297
  taxError
@@ -33775,7 +35333,7 @@ function VendorEditPage({ vendorId }) {
33775
35333
  user: {
33776
35334
  name: ownerName.trim(),
33777
35335
  email: ownerEmail.trim(),
33778
- phone: ownerPhone.trim() || void 0,
35336
+ phone: formattedOwnerPhone || void 0,
33779
35337
  designation: ownerDesignation.trim() || void 0,
33780
35338
  aadhaarNo: ownerAadhaarNo.trim(),
33781
35339
  panNo: ownerPanNo.trim().toUpperCase(),
@@ -33839,7 +35397,9 @@ function VendorEditPage({ vendorId }) {
33839
35397
  setSaving(false);
33840
35398
  return;
33841
35399
  }
33842
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35400
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35401
+ required: false
35402
+ });
33843
35403
  if (taxError) {
33844
35404
  setErrors([
33845
35405
  taxError
@@ -33875,6 +35435,7 @@ function VendorEditPage({ vendorId }) {
33875
35435
  return;
33876
35436
  }
33877
35437
  if (ownerUserId != null) {
35438
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
33878
35439
  const userRes = await fetch(`/api/users/${ownerUserId}`, {
33879
35440
  method: "PUT",
33880
35441
  headers: {
@@ -33883,7 +35444,7 @@ function VendorEditPage({ vendorId }) {
33883
35444
  body: JSON.stringify({
33884
35445
  name: ownerName.trim(),
33885
35446
  email: ownerEmail.trim(),
33886
- phone: ownerPhone.trim() || null
35447
+ phone: formattedOwnerPhone || null
33887
35448
  })
33888
35449
  });
33889
35450
  if (!userRes.ok) {
@@ -34017,12 +35578,23 @@ function VendorEditPage({ vendorId }) {
34017
35578
  className: `mt-1 ${fieldClass}`
34018
35579
  })), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
34019
35580
  htmlFor: "vendorPhone"
34020
- }, "Store phone"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
35581
+ }, "Store phone"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35582
+ className: "flex gap-2 mt-1"
35583
+ }, /* @__PURE__ */ React25__namespace.default.createElement("select", {
35584
+ value: storePhoneCode,
35585
+ onChange: /* @__PURE__ */ __name((e) => setStorePhoneCode(e.target.value), "onChange"),
35586
+ className: "w-36 rounded-md border border-gray-300 px-3 py-2 text-sm bg-white focus:outline-none focus:ring-1 focus:ring-black"
35587
+ }, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__namespace.default.createElement("option", {
35588
+ key: c.code,
35589
+ value: c.code
35590
+ }, c.label))), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
34021
35591
  id: "vendorPhone",
34022
- value: phone,
34023
- onChange: /* @__PURE__ */ __name((e) => setPhone(e.target.value), "onChange"),
34024
- className: `mt-1 ${fieldClass}`
34025
- })))), /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
35592
+ type: "tel",
35593
+ placeholder: "9876543210",
35594
+ value: storePhoneNum,
35595
+ onChange: /* @__PURE__ */ __name((e) => setStorePhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
35596
+ className: `flex-1 ${fieldClass}`
35597
+ }))))), /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
34026
35598
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34027
35599
  }, "Store address"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
34028
35600
  className: sectionCls5
@@ -34075,12 +35647,15 @@ function VendorEditPage({ vendorId }) {
34075
35647
  className: `mt-1 ${fieldClass}`
34076
35648
  }))))), /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
34077
35649
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34078
- }, "Tax (India)"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35650
+ }, "Tax"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
34079
35651
  className: sectionCls5
34080
35652
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
34081
- htmlFor: "gstin"
35653
+ htmlFor: "gstin",
35654
+ required: create
34082
35655
  }, "GSTIN"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
34083
35656
  id: "gstin",
35657
+ required: create,
35658
+ maxLength: 15,
34084
35659
  value: gstin,
34085
35660
  onChange: /* @__PURE__ */ __name((e) => setGstin(e.target.value.toUpperCase()), "onChange"),
34086
35661
  placeholder: "22AAAAA0000A1Z5",
@@ -34110,12 +35685,23 @@ function VendorEditPage({ vendorId }) {
34110
35685
  className: `mt-1 ${fieldClass}`
34111
35686
  })), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
34112
35687
  htmlFor: "ownerPhone"
34113
- }, "Phone"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
35688
+ }, "Phone"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35689
+ className: "flex gap-2 mt-1"
35690
+ }, /* @__PURE__ */ React25__namespace.default.createElement("select", {
35691
+ value: ownerPhoneCode,
35692
+ onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneCode(e.target.value), "onChange"),
35693
+ className: "w-36 rounded-md border border-gray-300 px-3 py-2 text-sm bg-white focus:outline-none focus:ring-1 focus:ring-black"
35694
+ }, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__namespace.default.createElement("option", {
35695
+ key: c.code,
35696
+ value: c.code
35697
+ }, c.label))), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
34114
35698
  id: "ownerPhone",
34115
- value: ownerPhone,
34116
- onChange: /* @__PURE__ */ __name((e) => setOwnerPhone(e.target.value), "onChange"),
34117
- className: `mt-1 ${fieldClass}`
34118
- })), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
35699
+ type: "tel",
35700
+ placeholder: "9876543210",
35701
+ value: ownerPhoneNum,
35702
+ onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
35703
+ className: `flex-1 ${fieldClass}`
35704
+ }))), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
34119
35705
  htmlFor: "ownerDesignation"
34120
35706
  }, "Designation"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
34121
35707
  id: "ownerDesignation",
@@ -34199,8 +35785,8 @@ function VendorEditPage({ vendorId }) {
34199
35785
  }), /* @__PURE__ */ React25__namespace.default.createElement(Label3, {
34200
35786
  htmlFor: "termsAccepted",
34201
35787
  className: "font-normal cursor-pointer text-sm leading-snug"
34202
- }, "I confirm the vendor has read and accepted the terms and conditions for selling on this platform."))))),
34203
- sidebar: /* @__PURE__ */ React25__namespace.default.createElement(React25__namespace.default.Fragment, null, !create && /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
35788
+ }, "I confirm I have read and accepted the terms and conditions for selling on this platform."))))),
35789
+ sidebar: create ? void 0 : /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
34204
35790
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34205
35791
  }, "Status"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
34206
35792
  className: sectionCls5
@@ -34235,18 +35821,7 @@ function VendorEditPage({ vendorId }) {
34235
35821
  readOnly: true,
34236
35822
  value: active ? "Yes" : "No",
34237
35823
  className: `mt-1 ${fieldClass} bg-gray-100`
34238
- })))), create ? /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
34239
- className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34240
- }, "Owner access"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
34241
- className: sectionCls5
34242
- }, /* @__PURE__ */ React25__namespace.default.createElement("p", {
34243
- className: "text-sm text-gray-600"
34244
- }, "Creates the vendor and an invite for the owner. After create, you can send the invite email or copy the invite link."), /* @__PURE__ */ React25__namespace.default.createElement(Button, {
34245
- type: "button",
34246
- disabled: saving,
34247
- onClick: handleCreate,
34248
- className: "w-full"
34249
- }, saving ? "Creating\u2026" : "Create vendor"))) : void 0)
35824
+ }))))
34250
35825
  }), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
34251
35826
  open: inviteDialog != null,
34252
35827
  onOpenChange: /* @__PURE__ */ __name((open) => {
@@ -35841,7 +37416,7 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
35841
37416
  value: "minAmount"
35842
37417
  }, "Minimum order amount"), /* @__PURE__ */ React.createElement(SelectItem, {
35843
37418
  value: "minQuantity"
35844
- }, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
37419
+ }, "Minimum Cart Quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
35845
37420
  value: "productMinQuantity"
35846
37421
  }, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
35847
37422
  value: "events"
@@ -37296,8 +38871,8 @@ function ContactDetailPage({ contactId }) {
37296
38871
  setAddAddressError("Select country, state, and city from the lists.");
37297
38872
  return;
37298
38873
  }
37299
- const { Country: Country2, State: State2 } = await import('country-state-city');
37300
- const countryName = Country2.getCountryByCode(addressGeo.countryIso)?.name ?? "";
38874
+ const { Country: Country3, State: State2 } = await import('country-state-city');
38875
+ const countryName = Country3.getCountryByCode(addressGeo.countryIso)?.name ?? "";
37301
38876
  const stateName = State2.getStatesOfCountry(addressGeo.countryIso).find((s) => s.isoCode === addressGeo.stateIso)?.name ?? "";
37302
38877
  const payload = {
37303
38878
  contactId: Number(contactId),