@infuro/cms-core 1.0.50 → 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.
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.50" ;
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();
@@ -1514,7 +1934,15 @@ function useEventsSettings() {
1514
1934
  requireEventApproval
1515
1935
  };
1516
1936
  }
1517
- 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
+ }, []);
1518
1946
  const resolvedTheme = useResolvedTheme(theme, themeRegistry);
1519
1947
  const { storeEnabled, currency } = useStoreEnabled();
1520
1948
  const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
@@ -1535,6 +1963,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1535
1963
  customCrudConfigs,
1536
1964
  categoryRelatedProductLabels,
1537
1965
  renderOrderDetailFooter,
1966
+ extraNotificationVariables,
1538
1967
  theme: resolvedTheme,
1539
1968
  themeRegistry,
1540
1969
  pluginDescriptors: mergedPluginDescriptors,
@@ -1553,6 +1982,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1553
1982
  customCrudConfigs,
1554
1983
  categoryRelatedProductLabels,
1555
1984
  renderOrderDetailFooter,
1985
+ extraNotificationVariables,
1556
1986
  resolvedTheme,
1557
1987
  themeRegistry,
1558
1988
  mergedPluginDescriptors,
@@ -4409,6 +4839,8 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4409
4839
  const hasLoadedRef = React25.useRef(false);
4410
4840
  const isMobile = useIsMobile();
4411
4841
  const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
4842
+ const { data: session } = react.useSession();
4843
+ const sessionUser = session?.user;
4412
4844
  const listColumns = React25.useMemo(() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [], [
4413
4845
  columns
4414
4846
  ]);
@@ -4949,6 +5381,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4949
5381
  withListFrom
4950
5382
  ]);
4951
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");
4952
5387
  const dedicatedRecordEditHref = /* @__PURE__ */ __name((recordId) => addEditPage && (resourceName === "orders" || resourceName === "events") ? withListFrom(`${addEditPageUrl}/${recordId}/edit`) : addEditPage ? withListFrom(`${addEditPageUrl}/${recordId}`) : "", "dedicatedRecordEditHref");
4953
5388
  const showDedicatedDuplicate = addEditPage && !CRUD_NO_DUPLICATE_RESOURCES.has(resourceName);
4954
5389
  const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
@@ -5108,15 +5543,15 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5108
5543
  onClick: /* @__PURE__ */ __name(() => setBulkDialogOpen(true), "onClick"),
5109
5544
  variant: "outline",
5110
5545
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5111
- }, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
5546
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Download, {
5112
5547
  className: "h-3.5 w-3.5 mr-1"
5113
5548
  }), "Import"), /* @__PURE__ */ React.createElement(Button, {
5114
5549
  onClick: handleExport,
5115
5550
  variant: "outline",
5116
5551
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5117
- }, /* @__PURE__ */ React.createElement(LucideIcons.Download, {
5552
+ }, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
5118
5553
  className: "h-3.5 w-3.5 mr-1"
5119
- }), "Export"), !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5554
+ }), "Export"), canCreate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5120
5555
  onClick: /* @__PURE__ */ __name(() => {
5121
5556
  setEditingItem(null);
5122
5557
  setDuplicateSeed(null);
@@ -5125,7 +5560,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5125
5560
  className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8"
5126
5561
  }, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
5127
5562
  className: "h-3.5 w-3.5"
5128
- }), "Add"), addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2__default.default, {
5563
+ }), "Add"), canCreate && addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2__default.default, {
5129
5564
  href: dedicatedCreateHref,
5130
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"
5131
5566
  }, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
@@ -5284,7 +5719,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5284
5719
  onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
5285
5720
  }, /* @__PURE__ */ React.createElement("div", {
5286
5721
  className: "flex items-center justify-center gap-1"
5287
- }, 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, {
5288
5723
  variant: "outline",
5289
5724
  size: "icon",
5290
5725
  className: "h-7 w-7",
@@ -5294,7 +5729,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5294
5729
  className: "h-3.5 w-3.5"
5295
5730
  }), /* @__PURE__ */ React.createElement("span", {
5296
5731
  className: "sr-only"
5297
- }, "Edit")), addEditPage && /* @__PURE__ */ React.createElement(Button, {
5732
+ }, "Edit")), canUpdate && addEditPage && /* @__PURE__ */ React.createElement(Button, {
5298
5733
  variant: "outline",
5299
5734
  size: "icon",
5300
5735
  className: "h-7 w-7",
@@ -5304,7 +5739,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5304
5739
  className: "h-3.5 w-3.5"
5305
5740
  }), /* @__PURE__ */ React.createElement("span", {
5306
5741
  className: "sr-only"
5307
- }, "Edit")), showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5742
+ }, "Edit")), canCreate && showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5308
5743
  variant: "outline",
5309
5744
  size: "icon",
5310
5745
  className: "h-7 w-7",
@@ -5330,7 +5765,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5330
5765
  className: "h-3.5 w-3.5"
5331
5766
  }), /* @__PURE__ */ React.createElement("span", {
5332
5767
  className: "sr-only"
5333
- }, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
5768
+ }, "Resend invite")), canDelete && /* @__PURE__ */ React.createElement(Button, {
5334
5769
  variant: "outline",
5335
5770
  size: "icon",
5336
5771
  className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
@@ -5468,6 +5903,7 @@ var init_CRUD = __esm({
5468
5903
  init_dropdown_menu();
5469
5904
  init_utils();
5470
5905
  init_use_mobile();
5906
+ init_rbac_debug();
5471
5907
  init_BulkUploadDialog();
5472
5908
  init_admin_list_return_url();
5473
5909
  init_build_crud_list_filters_from_columns();
@@ -15536,6 +15972,18 @@ var init_email_recipients = __esm({
15536
15972
  __name(serializeEmailRecipients, "serializeEmailRecipients");
15537
15973
  }
15538
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
+ }
15539
15987
  function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15540
15988
  if (audience === "vendor") {
15541
15989
  return VENDOR_EMAIL_TRIGGER_DEFAULTS[triggerKey] ?? {
@@ -15679,6 +16127,7 @@ var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMA
15679
16127
  var init_order_notification_bindings_shared = __esm({
15680
16128
  "src/admin/components/order-notification-bindings-shared.ts"() {
15681
16129
  "use client";
16130
+ init_admin_config_context();
15682
16131
  KNOWN_VARS = [
15683
16132
  "orderId",
15684
16133
  "orderNumber",
@@ -15693,6 +16142,7 @@ var init_order_notification_bindings_shared = __esm({
15693
16142
  "invoiceNumber",
15694
16143
  "invoiceUrl"
15695
16144
  ];
16145
+ __name(useNotificationVariables, "useNotificationVariables");
15696
16146
  EMAIL_TRIGGER_DEFAULTS = {
15697
16147
  order_placed: {
15698
16148
  subject: "Your order {{orderNumber}} is confirmed",
@@ -16396,16 +16846,23 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16396
16846
  cancelled = true;
16397
16847
  };
16398
16848
  }, []);
16849
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16399
16850
  const visibleVars = React25.useMemo(() => {
16400
- return KNOWN_VARS.filter((v) => {
16851
+ return availableVars.filter((v) => {
16401
16852
  if (v === "vendorName" || v === "vendorId") return multiVendorOn;
16402
16853
  if (v === "eventName") return eventsOn;
16403
16854
  return true;
16404
16855
  });
16405
16856
  }, [
16857
+ availableVars,
16406
16858
  multiVendorOn,
16407
16859
  eventsOn
16408
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");
16409
16866
  return /* @__PURE__ */ React.createElement("div", {
16410
16867
  className: "space-y-3 border-t border-gray-100 pt-4 first:border-t-0 first:pt-0 dark:border-gray-700"
16411
16868
  }, /* @__PURE__ */ React.createElement("div", {
@@ -16468,7 +16925,7 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16468
16925
  }, visibleVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16469
16926
  key: v,
16470
16927
  type: "button",
16471
- title: VAR_HINTS[v],
16928
+ title: getVarHint(v),
16472
16929
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16473
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"
16474
16931
  }, `{{${v}}}`))), /* @__PURE__ */ React.createElement("p", {
@@ -16514,6 +16971,12 @@ function PushTriggerBindings({ audience = "customer" }) {
16514
16971
  const bodyRef = React25.useRef(null);
16515
16972
  const subjectRef = React25.useRef(null);
16516
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");
16517
16980
  React25.useEffect(() => {
16518
16981
  let cancelled = false;
16519
16982
  (async () => {
@@ -16735,11 +17198,11 @@ function PushTriggerBindings({ audience = "customer" }) {
16735
17198
  className: "text-xs font-medium text-gray-700 dark:text-gray-300"
16736
17199
  }, "Insert Variables"), /* @__PURE__ */ React.createElement("div", {
16737
17200
  className: "flex flex-wrap gap-1.5"
16738
- }, KNOWN_VARS.map((v) => /* @__PURE__ */ React.createElement("button", {
17201
+ }, availableVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16739
17202
  key: v,
16740
17203
  type: "button",
16741
17204
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16742
- title: VAR_HINTS2[v],
17205
+ title: getVarHint(v),
16743
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"
16744
17207
  }, `{{${v}}}`)))), /* @__PURE__ */ React.createElement("div", {
16745
17208
  className: "space-y-1.5"
@@ -20845,183 +21308,6 @@ var init_PluginsPage = __esm({
20845
21308
  }
20846
21309
  });
20847
21310
 
20848
- // src/lib/vendor-role-defaults.ts
20849
- function full() {
20850
- return {
20851
- canCreate: true,
20852
- canRead: true,
20853
- canUpdate: true,
20854
- canDelete: true
20855
- };
20856
- }
20857
- function read() {
20858
- return {
20859
- canCreate: false,
20860
- canRead: true,
20861
- canUpdate: false,
20862
- canDelete: false
20863
- };
20864
- }
20865
- function none() {
20866
- return {
20867
- canCreate: false,
20868
- canRead: false,
20869
- canUpdate: false,
20870
- canDelete: false
20871
- };
20872
- }
20873
- function readUpdate() {
20874
- return {
20875
- canCreate: false,
20876
- canRead: true,
20877
- canUpdate: true,
20878
- canDelete: false
20879
- };
20880
- }
20881
- function storePerms(spec) {
20882
- const resolve = /* @__PURE__ */ __name((v) => {
20883
- if (v === "full") return full();
20884
- if (v === "read") return read();
20885
- if (v === "none") return none();
20886
- return v;
20887
- }, "resolve");
20888
- const out = {};
20889
- for (const entity of STORE_ENTITIES) {
20890
- out[entity] = resolve(spec[entity] ?? "none");
20891
- }
20892
- return out;
20893
- }
20894
- var STORE_ENTITIES;
20895
- var init_vendor_role_defaults = __esm({
20896
- "src/lib/vendor-role-defaults.ts"() {
20897
- init_vendor_scope();
20898
- [
20899
- ...VENDOR_STORE_RBAC_ENTITIES,
20900
- "dashboard",
20901
- "settings",
20902
- "team",
20903
- "roles"
20904
- ];
20905
- STORE_ENTITIES = [
20906
- ...VENDOR_STORE_RBAC_ENTITIES
20907
- ];
20908
- __name(full, "full");
20909
- __name(read, "read");
20910
- __name(none, "none");
20911
- __name(readUpdate, "readUpdate");
20912
- __name(storePerms, "storePerms");
20913
- [
20914
- {
20915
- name: "Owner",
20916
- description: "Full store access and team/role management",
20917
- isSystem: true,
20918
- isOwnerRole: true,
20919
- permissions: {
20920
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
20921
- e,
20922
- "full"
20923
- ]))),
20924
- dashboard: read(),
20925
- settings: readUpdate(),
20926
- team: full(),
20927
- roles: full()
20928
- }
20929
- },
20930
- {
20931
- name: "Manager",
20932
- description: "Day-to-day store operations",
20933
- isSystem: true,
20934
- isOwnerRole: false,
20935
- permissions: {
20936
- ...storePerms({
20937
- products: "full",
20938
- collections: "full",
20939
- brands: "full",
20940
- product_categories: "full",
20941
- orders: "full",
20942
- vendor_customers: "full",
20943
- discounts: "full",
20944
- order_discounts: "full",
20945
- order_addresses: "full",
20946
- payments: "read",
20947
- taxes: "read",
20948
- attributes: "read"
20949
- }),
20950
- dashboard: read(),
20951
- settings: read(),
20952
- team: none(),
20953
- roles: none()
20954
- }
20955
- },
20956
- {
20957
- name: "Catalog Staff",
20958
- description: "Manage products and catalog content",
20959
- isSystem: true,
20960
- isOwnerRole: false,
20961
- permissions: {
20962
- ...storePerms({
20963
- products: "full",
20964
- collections: "full",
20965
- brands: "full",
20966
- product_categories: "full",
20967
- attributes: "full",
20968
- orders: "read",
20969
- vendor_customers: "read"
20970
- }),
20971
- dashboard: read(),
20972
- settings: none(),
20973
- team: none(),
20974
- roles: none()
20975
- }
20976
- },
20977
- {
20978
- name: "Fulfillment",
20979
- description: "Orders and fulfillment",
20980
- isSystem: true,
20981
- isOwnerRole: false,
20982
- permissions: {
20983
- ...storePerms({
20984
- products: "read",
20985
- orders: "full",
20986
- order_addresses: "full",
20987
- order_discounts: "read",
20988
- vendor_customers: "read"
20989
- }),
20990
- dashboard: read(),
20991
- settings: none(),
20992
- team: none(),
20993
- roles: none()
20994
- }
20995
- },
20996
- {
20997
- name: "Viewer",
20998
- description: "Read-only access to store data",
20999
- isSystem: true,
21000
- isOwnerRole: false,
21001
- permissions: {
21002
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
21003
- e,
21004
- "read"
21005
- ]))),
21006
- dashboard: read(),
21007
- settings: none(),
21008
- team: none(),
21009
- roles: none()
21010
- }
21011
- }
21012
- ];
21013
- }
21014
- });
21015
-
21016
- // src/auth/rbac-debug.ts
21017
- var init_rbac_debug = __esm({
21018
- "src/auth/rbac-debug.ts"() {
21019
- init_permission_entities();
21020
- init_vendor_role_defaults();
21021
- init_vendor_scope();
21022
- }
21023
- });
21024
-
21025
21311
  // src/auth/helpers.ts
21026
21312
  function canManageRoles(user) {
21027
21313
  return !!(user?.email && user.isRBACAdmin);
@@ -21179,23 +21465,6 @@ function VendorRolesPage() {
21179
21465
  return next;
21180
21466
  });
21181
21467
  }, "setAllRows");
21182
- const toggleEntityRow = /* @__PURE__ */ __name((entity) => {
21183
- const isOwnerOnlyEntity = entity === "team" || entity === "roles";
21184
- setMatrix((prev) => {
21185
- const current = prev[entity];
21186
- const isAllChecked = current?.canRead && (isOwnerOnlyEntity || current.canCreate && current.canUpdate && current.canDelete);
21187
- return {
21188
- ...prev,
21189
- [entity]: {
21190
- entity,
21191
- canRead: !isAllChecked,
21192
- canCreate: isOwnerOnlyEntity ? false : !isAllChecked,
21193
- canUpdate: isOwnerOnlyEntity ? false : !isAllChecked,
21194
- canDelete: isOwnerOnlyEntity ? false : !isAllChecked
21195
- }
21196
- };
21197
- });
21198
- }, "toggleEntityRow");
21199
21468
  const saveMatrix = /* @__PURE__ */ __name(async () => {
21200
21469
  if (!selectedId) return;
21201
21470
  setSaving(true);
@@ -21389,31 +21658,35 @@ function VendorRolesPage() {
21389
21658
  }), " Clear All"))), /* @__PURE__ */ React25__namespace.default.createElement("div", {
21390
21659
  className: "overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700"
21391
21660
  }, /* @__PURE__ */ React25__namespace.default.createElement("table", {
21392
- className: "min-w-full text-xs"
21661
+ className: "min-w-full text-sm"
21393
21662
  }, /* @__PURE__ */ React25__namespace.default.createElement("thead", {
21394
- className: "bg-gray-100 text-left text-gray-700 dark:bg-gray-900 dark:text-gray-300"
21395
- }, /* @__PURE__ */ React25__namespace.default.createElement("tr", null, /* @__PURE__ */ React25__namespace.default.createElement("th", {
21396
- className: "px-3 py-2 font-medium"
21397
- }, "Entity"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21398
- 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"
21399
21672
  }, "Create"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21400
- 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"
21401
21674
  }, "Read"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21402
- 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"
21403
21676
  }, "Update"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21404
- className: "px-3 py-2 font-medium"
21405
- }, "Delete"), /* @__PURE__ */ React25__namespace.default.createElement("th", {
21406
- className: "px-3 py-2 font-medium text-right"
21407
- }, "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) => {
21408
21679
  const isOwnerOnlyEntity = entity === "team" || entity === "roles";
21409
21680
  return /* @__PURE__ */ React25__namespace.default.createElement("tr", {
21410
21681
  key: entity,
21411
- 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"
21412
21683
  }, /* @__PURE__ */ React25__namespace.default.createElement("td", {
21413
- className: "px-3 py-2 font-mono text-gray-800 dark:text-gray-200 font-medium"
21414
- }, entity, isOwnerOnlyEntity && /* @__PURE__ */ React25__namespace.default.createElement("span", {
21415
- className: "ml-2 text-[10px] text-amber-600 dark:text-amber-400 font-normal"
21416
- }, "(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)"))), [
21417
21690
  "canCreate",
21418
21691
  "canRead",
21419
21692
  "canUpdate",
@@ -21422,21 +21695,15 @@ function VendorRolesPage() {
21422
21695
  const disabled = isOwnerOnlyEntity && key !== "canRead";
21423
21696
  return /* @__PURE__ */ React25__namespace.default.createElement("td", {
21424
21697
  key,
21425
- className: "px-3 py-2"
21698
+ className: "px-3 py-2.5 text-center"
21426
21699
  }, /* @__PURE__ */ React25__namespace.default.createElement("input", {
21427
21700
  type: "checkbox",
21428
21701
  checked: disabled ? false : !!matrix[entity]?.[key],
21429
21702
  disabled,
21430
21703
  onChange: /* @__PURE__ */ __name(() => toggle(entity, key), "onChange"),
21431
- 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"
21432
21705
  }));
21433
- }), /* @__PURE__ */ React25__namespace.default.createElement("td", {
21434
- className: "px-3 py-2 text-right"
21435
- }, /* @__PURE__ */ React25__namespace.default.createElement("button", {
21436
- type: "button",
21437
- onClick: /* @__PURE__ */ __name(() => toggleEntityRow(entity), "onClick"),
21438
- className: "text-[11px] text-blue-600 hover:underline dark:text-blue-400"
21439
- }, "Toggle")));
21706
+ }));
21440
21707
  })))))))), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
21441
21708
  open: deleteOpen,
21442
21709
  onOpenChange: setDeleteOpen
@@ -22314,39 +22581,90 @@ var init_VendorTeamPage = __esm({
22314
22581
  __name(VendorTeamPage, "VendorTeamPage");
22315
22582
  }
22316
22583
  });
22317
-
22318
- // src/lib/vendor-profile.ts
22319
- function validateIndiaTaxIds(gstin, pan) {
22320
- if (gstin && !/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin)) {
22321
- 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
+ };
22322
22590
  }
22323
- if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan)) {
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;
22624
+ }
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())) {
22324
22639
  return "Invalid PAN format (e.g. ABCDE1234F)";
22325
22640
  }
22326
22641
  return null;
22327
22642
  }
22328
22643
  function validateAadhaar(aadhaar) {
22329
22644
  if (!aadhaar) return null;
22330
- if (!/^[0-9]{12}$/.test(aadhaar)) {
22645
+ if (!/^[0-9]{12}$/.test(aadhaar.replace(/\s+/g, ""))) {
22331
22646
  return "Invalid Aadhaar number (12 digits)";
22332
22647
  }
22333
22648
  return null;
22334
22649
  }
22335
22650
  function validatePersonKyc(person, options) {
22336
22651
  const required = options?.required === true;
22337
- if (required && !person.aadhaarNo) return "Aadhaar number is required";
22338
- 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";
22339
22654
  const aadhaarErr = validateAadhaar(person.aadhaarNo ?? null);
22340
22655
  if (aadhaarErr) return aadhaarErr;
22341
- const panErr = validateIndiaTaxIds(null, person.panNo ?? null);
22656
+ const panErr = validateIndiaTaxIds(null, person.panNo ?? null, {
22657
+ requiredPan: required
22658
+ });
22342
22659
  if (panErr) return panErr;
22343
22660
  return null;
22344
22661
  }
22345
22662
  function readOwnerDesignation(metadata) {
22346
- const v = metadata?.ownerDesignation;
22347
- return typeof v === "string" ? v : "";
22663
+ if (!metadata || typeof metadata !== "object") return "";
22664
+ const d = metadata.ownerDesignation;
22665
+ return typeof d === "string" ? d.trim() : "";
22348
22666
  }
22349
- var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES;
22667
+ var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES, COUNTRY_PHONE_CODES;
22350
22668
  var init_vendor_profile = __esm({
22351
22669
  "src/lib/vendor-profile.ts"() {
22352
22670
  VENDOR_REGISTRATION_STATUSES = [
@@ -22393,7 +22711,34 @@ var init_vendor_profile = __esm({
22393
22711
  label: "Other"
22394
22712
  }
22395
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");
22396
22740
  new Set(VENDOR_REGISTRATION_STATUSES.map((s) => s.value));
22741
+ __name(validateGstin, "validateGstin");
22397
22742
  __name(validateIndiaTaxIds, "validateIndiaTaxIds");
22398
22743
  __name(validateAadhaar, "validateAadhaar");
22399
22744
  __name(validatePersonKyc, "validatePersonKyc");
@@ -22996,10 +23341,26 @@ function SubmissionDetailPage({ submissionId }) {
22996
23341
  }
22997
23342
  const formName = submission.form?.name ?? `Form #${submission.formId}`;
22998
23343
  const contact = submission.contact;
22999
- const fieldLabel = /* @__PURE__ */ __name((key) => {
23000
- const field = submission.form?.fields?.find((f) => String(f.id) === key);
23001
- return field?.label ?? key;
23002
- }, "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");
23003
23364
  return /* @__PURE__ */ React.createElement("div", {
23004
23365
  className: "rounded-lg bg-white shadow-md"
23005
23366
  }, /* @__PURE__ */ React.createElement(DetailPageHeader, {
@@ -23058,7 +23419,7 @@ function SubmissionDetailPage({ submissionId }) {
23058
23419
  key
23059
23420
  }, /* @__PURE__ */ React.createElement("td", {
23060
23421
  className: "py-2 px-3 text-gray-600 font-medium"
23061
- }, fieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23422
+ }, resolveFieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23062
23423
  className: "py-2 px-3 text-gray-900 break-words min-w-0"
23063
23424
  }, value === null || value === void 0 ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value)))))))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
23064
23425
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
@@ -24713,7 +25074,7 @@ function OrderPlacementPage({ editOrderId }) {
24713
25074
  productQuery
24714
25075
  ]);
24715
25076
  const recalcPreview = React25.useCallback(async () => {
24716
- const orderLines = lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => ({
25077
+ const orderLines = lines.filter((l) => !l.key.startsWith("reward-")).map((l) => ({
24717
25078
  productId: l.productId,
24718
25079
  quantity: l.quantity
24719
25080
  }));
@@ -24737,19 +25098,22 @@ function OrderPlacementPage({ editOrderId }) {
24737
25098
  body: JSON.stringify({
24738
25099
  orderLines,
24739
25100
  currency,
24740
- discountId: coupons[0]?.discountId ?? null
25101
+ discountId: coupons[0]?.discountId ?? null,
25102
+ discountIds: coupons.map((c) => c.discountId)
24741
25103
  })
24742
25104
  });
24743
25105
  if (!res.ok) throw new Error("Calculate failed");
24744
25106
  const data = await res.json();
24745
25107
  const correctedLines = data.lines.map((cl) => {
24746
25108
  if (cl.productId == null) return cl;
24747
- const cached = productCache.current.get(cl.productId);
25109
+ const pid = Number(cl.productId);
25110
+ const cached = productCache.current.get(pid);
24748
25111
  if (!cl.found && cached) {
24749
- const unitPrice2 = cached.price;
25112
+ const unitPrice2 = Number(cached.price ?? 0);
24750
25113
  const subtotal3 = unitPrice2 * cl.quantity;
24751
25114
  return {
24752
25115
  ...cl,
25116
+ productId: pid,
24753
25117
  found: true,
24754
25118
  unitPrice: unitPrice2,
24755
25119
  subtotal: subtotal3,
@@ -24757,15 +25121,16 @@ function OrderPlacementPage({ editOrderId }) {
24757
25121
  total: subtotal3
24758
25122
  };
24759
25123
  }
24760
- const unitPrice = cached?.price ?? cl.unitPrice;
25124
+ const unitPrice = Number(cached?.price ?? cl.unitPrice ?? 0);
24761
25125
  const subtotal2 = unitPrice * cl.quantity;
24762
- const taxRate = cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal : 0);
24763
- 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);
24764
25128
  const discountedBase = Math.max(0, subtotal2 - lineDiscount);
24765
25129
  const tax2 = discountedBase * taxRate / 100;
24766
25130
  const total2 = discountedBase + tax2;
24767
25131
  return {
24768
25132
  ...cl,
25133
+ productId: pid,
24769
25134
  unitPrice,
24770
25135
  subtotal: subtotal2,
24771
25136
  taxRate,
@@ -24773,13 +25138,14 @@ function OrderPlacementPage({ editOrderId }) {
24773
25138
  total: total2
24774
25139
  };
24775
25140
  });
24776
- const rewardDisplayLines = lines.filter((l) => rewardProductIds.current.has(l.productId)).map((l) => {
24777
- 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);
24778
25144
  return {
24779
- productId: l.productId,
25145
+ productId: pid,
24780
25146
  productName: l.label,
24781
25147
  found: true,
24782
- unitPrice: cached?.price ?? 0,
25148
+ unitPrice: Number(cached?.price ?? 0),
24783
25149
  quantity: l.quantity,
24784
25150
  subtotal: 0,
24785
25151
  tax: 0,
@@ -24821,7 +25187,7 @@ function OrderPlacementPage({ editOrderId }) {
24821
25187
  React25.useEffect(() => {
24822
25188
  const automaticCoupons = coupons.filter((c) => c.isAutomatic);
24823
25189
  if (automaticCoupons.length === 0) return;
24824
- const nonRewardLines = lines.filter((l) => !rewardProductIds.current.has(l.productId));
25190
+ const nonRewardLines = lines.filter((l) => !l.key.startsWith("reward-"));
24825
25191
  if (nonRewardLines.length === 0) {
24826
25192
  const autoIds = new Set(automaticCoupons.map((c) => c.discountId));
24827
25193
  const rewardPids = new Set(automaticCoupons.flatMap((c) => c.rewardLines.map((r) => r.productId)));
@@ -24901,7 +25267,7 @@ function OrderPlacementPage({ editOrderId }) {
24901
25267
  cancelled = true;
24902
25268
  };
24903
25269
  }, [
24904
- 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(","),
24905
25271
  coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
24906
25272
  ]);
24907
25273
  function handleRemoveCoupon(couponCode) {
@@ -25023,7 +25389,10 @@ function OrderPlacementPage({ editOrderId }) {
25023
25389
  const calcByProductId = React25.useMemo(() => {
25024
25390
  const m = /* @__PURE__ */ new Map();
25025
25391
  for (const l of preview?.lines ?? []) {
25026
- 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
+ }
25027
25396
  }
25028
25397
  return m;
25029
25398
  }, [
@@ -25045,13 +25414,13 @@ function OrderPlacementPage({ editOrderId }) {
25045
25414
  }
25046
25415
  const unknown = preview?.lines.filter((l) => !l.found) ?? [];
25047
25416
  if (unknown.length > 0) {
25048
- 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(", ")}`);
25049
25418
  return;
25050
25419
  }
25051
25420
  setSubmitting(true);
25421
+ let resolvedContactId = effectiveContactId;
25052
25422
  try {
25053
- let resolvedContactId = placeForSomeoneElse ? subContactId : null;
25054
- if (!resolvedContactId && effectiveEmail) {
25423
+ if (resolvedContactId == null && effectiveEmail) {
25055
25424
  try {
25056
25425
  const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
25057
25426
  if (res2.ok) {
@@ -25073,12 +25442,12 @@ function OrderPlacementPage({ editOrderId }) {
25073
25442
  billingAddress,
25074
25443
  shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
25075
25444
  orderLines: lines.map((l) => {
25076
- const isReward = rewardProductIds.current.has(l.productId);
25077
- 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));
25078
25447
  return {
25079
25448
  productId: l.productId,
25080
25449
  quantity: l.quantity,
25081
- 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)
25082
25451
  };
25083
25452
  })
25084
25453
  };
@@ -30054,8 +30423,8 @@ function ProductEditPage({ productId }) {
30054
30423
  const [price, setPrice] = React25.useState(0);
30055
30424
  const [defaultPriceStr, setDefaultPriceStr] = React25.useState("");
30056
30425
  const [pricingConfig, setPricingConfig] = React25.useState(DEFAULT_PRICING_CONFIG);
30057
- const [compareAtPrice, setCompareAtPrice] = React25.useState(0);
30058
- const [quantity, setQuantity] = React25.useState(1);
30426
+ const [compareAtPrice, setCompareAtPrice] = React25.useState("");
30427
+ const [quantity, setQuantity] = React25.useState("1");
30059
30428
  const [status, setStatus] = React25.useState("draft");
30060
30429
  const [approvalStatus, setApprovalStatus] = React25.useState("pending");
30061
30430
  const [rejectionReason, setRejectionReason] = React25.useState("");
@@ -30276,8 +30645,8 @@ function ProductEditPage({ productId }) {
30276
30645
  setDefaultPriceStr(product.price != null && Number.isFinite(Number(product.price)) ? String(product.price) : "");
30277
30646
  setPrice(product.price != null ? Number(product.price) : 0);
30278
30647
  const rawCompare = product.compareAtPrice != null ? Number(product.compareAtPrice) : 0;
30279
- setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
30280
- setQuantity(product.quantity ?? 1);
30648
+ setCompareAtPrice(rawCompare != null && Number.isFinite(rawCompare) ? String(rawCompare) : "");
30649
+ setQuantity(product.quantity != null ? String(product.quantity) : "1");
30281
30650
  setStatus(product.status ?? "draft");
30282
30651
  setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
30283
30652
  setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
@@ -30444,22 +30813,6 @@ function ProductEditPage({ productId }) {
30444
30813
  return pricingConfig.defaultCurrency || "INR";
30445
30814
  }
30446
30815
  })();
30447
- const handleNumberChange = /* @__PURE__ */ __name((setter, options) => (e) => {
30448
- const value = e.target.value;
30449
- if (value === "") {
30450
- setter(0);
30451
- return;
30452
- }
30453
- let num = options?.integer ? Number.parseInt(value, 10) : Number(value);
30454
- if (Number.isNaN(num)) {
30455
- setter(0);
30456
- return;
30457
- }
30458
- if (options?.min !== void 0) {
30459
- num = Math.max(options.min, num);
30460
- }
30461
- setter(num);
30462
- }, "handleNumberChange");
30463
30816
  const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
30464
30817
  approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
30465
30818
  setRejectDraft(rejectionReason);
@@ -30532,12 +30885,13 @@ function ProductEditPage({ productId }) {
30532
30885
  return;
30533
30886
  }
30534
30887
  }
30535
- const quantityErrors = validateProductQuantity(quantity, hasVariants, parsedVariants);
30888
+ const quantityValue = quantity === "" ? 0 : Number(quantity);
30889
+ const quantityErrors = validateProductQuantity(quantityValue, hasVariants, parsedVariants);
30536
30890
  if (quantityErrors.length) {
30537
30891
  setErrors(quantityErrors);
30538
30892
  return;
30539
30893
  }
30540
- const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantity;
30894
+ const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantityValue;
30541
30895
  setSaving(true);
30542
30896
  try {
30543
30897
  const compareAtPriceValue = compareAtPrice ? Math.round(Number(compareAtPrice) * 100) / 100 : null;
@@ -31151,9 +31505,8 @@ function ProductEditPage({ productId }) {
31151
31505
  type: "number",
31152
31506
  value: compareAtPrice,
31153
31507
  className: inputCls3,
31154
- onChange: handleNumberChange(setCompareAtPrice, {
31155
- min: 0
31156
- })
31508
+ min: 0,
31509
+ onChange: /* @__PURE__ */ __name((e) => setCompareAtPrice(e.target.value), "onChange")
31157
31510
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
31158
31511
  className: labelCls3
31159
31512
  }, "Contact form"), /* @__PURE__ */ React.createElement("select", {
@@ -31176,10 +31529,7 @@ function ProductEditPage({ productId }) {
31176
31529
  min: 0,
31177
31530
  step: 1,
31178
31531
  value: hasVariants ? productQuantityFromVariants(variantsFromForm(variantRows, pricingConfig.defaultCurrency)) : quantity,
31179
- onChange: handleNumberChange(setQuantity, {
31180
- min: 0,
31181
- integer: true
31182
- }),
31532
+ onChange: /* @__PURE__ */ __name((e) => setQuantity(e.target.value), "onChange"),
31183
31533
  className: `${inputCls3}${hasVariants ? " bg-gray-100" : ""}`,
31184
31534
  required: !hasVariants,
31185
31535
  readOnly: hasVariants,
@@ -31256,8 +31606,18 @@ function ProductEditPage({ productId }) {
31256
31606
  value: row.taxId === "" ? "" : String(row.taxId),
31257
31607
  onChange: /* @__PURE__ */ __name((e) => {
31258
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);
31259
31618
  setTaxRow(i, {
31260
- taxId: v === "" ? "" : Number(v)
31619
+ taxId,
31620
+ rate: selectedTax?.rate != null ? String(selectedTax.rate) : ""
31261
31621
  });
31262
31622
  }, "onChange"),
31263
31623
  className: inputCls3
@@ -33082,6 +33442,35 @@ function EventEditPage({ eventId }) {
33082
33442
  const [saving, setSaving] = React25.useState(false);
33083
33443
  const [errors, setErrors] = React25.useState([]);
33084
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");
33085
33474
  const [name, setName] = React25.useState("");
33086
33475
  const [slug, setSlug] = React25.useState("");
33087
33476
  const [description, setDescription] = React25.useState("");
@@ -33901,7 +34290,19 @@ function EventEditPage({ eventId }) {
33901
34290
  [key]: value
33902
34291
  })), "onChange")
33903
34292
  }))))
33904
- }), 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", {
33905
34306
  className: "border-t border-gray-200 px-4 py-4 sm:px-6"
33906
34307
  }, /* @__PURE__ */ React.createElement(EventProductsSection, {
33907
34308
  eventId: eventRecordId,
@@ -34246,6 +34647,18 @@ function ComboEditPage({ comboId }) {
34246
34647
  ]);
34247
34648
  return;
34248
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
+ }
34249
34662
  if (!priceStr.trim()) {
34250
34663
  setErrors([
34251
34664
  `Price (${defaultCurrency}) is required`
@@ -34535,18 +34948,20 @@ function ComboEditPage({ comboId }) {
34535
34948
  className: "grid grid-cols-2 gap-4"
34536
34949
  }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
34537
34950
  className: "block text-xs font-medium text-gray-600 mb-1"
34538
- }, "Starts at"), /* @__PURE__ */ React.createElement("input", {
34951
+ }, "Starts at *"), /* @__PURE__ */ React.createElement("input", {
34539
34952
  type: "datetime-local",
34540
34953
  value: startsAt,
34541
34954
  onChange: /* @__PURE__ */ __name((e) => setStartsAt(e.target.value), "onChange"),
34542
- 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
34543
34957
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
34544
34958
  className: "block text-xs font-medium text-gray-600 mb-1"
34545
- }, "Ends at"), /* @__PURE__ */ React.createElement("input", {
34959
+ }, "Ends at *"), /* @__PURE__ */ React.createElement("input", {
34546
34960
  type: "datetime-local",
34547
34961
  value: endsAt,
34548
34962
  onChange: /* @__PURE__ */ __name((e) => setEndsAt(e.target.value), "onChange"),
34549
- 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
34550
34965
  })))))),
34551
34966
  sidebar: /* @__PURE__ */ React.createElement(React.Fragment, null)
34552
34967
  }));
@@ -34632,7 +35047,8 @@ function VendorEditPage({ vendorId }) {
34632
35047
  const [website, setWebsite] = React25.useState("");
34633
35048
  const [logo, setLogo] = React25.useState("");
34634
35049
  const [email, setEmail] = React25.useState("");
34635
- const [phone, setPhone] = React25.useState("");
35050
+ const [storePhoneCode, setStorePhoneCode] = React25.useState("+91");
35051
+ const [storePhoneNum, setStorePhoneNum] = React25.useState("");
34636
35052
  const [addressLine1, setAddressLine1] = React25.useState("");
34637
35053
  const [addressLine2, setAddressLine2] = React25.useState("");
34638
35054
  const [city, setCity] = React25.useState("");
@@ -34646,7 +35062,8 @@ function VendorEditPage({ vendorId }) {
34646
35062
  const [metadata, setMetadata] = React25.useState(null);
34647
35063
  const [ownerName, setOwnerName] = React25.useState("");
34648
35064
  const [ownerEmail, setOwnerEmail] = React25.useState("");
34649
- const [ownerPhone, setOwnerPhone] = React25.useState("");
35065
+ const [ownerPhoneCode, setOwnerPhoneCode] = React25.useState("+91");
35066
+ const [ownerPhoneNum, setOwnerPhoneNum] = React25.useState("");
34650
35067
  const [ownerDesignation, setOwnerDesignation] = React25.useState("");
34651
35068
  const [ownerAadhaarNo, setOwnerAadhaarNo] = React25.useState("");
34652
35069
  const [ownerPanNo, setOwnerPanNo] = React25.useState("");
@@ -34670,7 +35087,7 @@ function VendorEditPage({ vendorId }) {
34670
35087
  website: website.trim() || null,
34671
35088
  logo: logo.trim() || null,
34672
35089
  email: email.trim() || null,
34673
- phone: phone.trim() || null,
35090
+ phone: formatPhoneWithCountryCode(storePhoneCode, storePhoneNum),
34674
35091
  addressLine1: addressLine1.trim() || null,
34675
35092
  addressLine2: addressLine2.trim() || null,
34676
35093
  city: city.trim() || null,
@@ -34698,7 +35115,9 @@ function VendorEditPage({ vendorId }) {
34698
35115
  setWebsite(data.website ?? "");
34699
35116
  setLogo(data.logo ?? "");
34700
35117
  setEmail(data.email ?? "");
34701
- setPhone(data.phone ?? "");
35118
+ const sPhone = splitPhoneAndCountryCode(data.phone);
35119
+ setStorePhoneCode(sPhone.countryCode);
35120
+ setStorePhoneNum(sPhone.phoneNumber);
34702
35121
  setAddressLine1(data.addressLine1 ?? "");
34703
35122
  setAddressLine2(data.addressLine2 ?? "");
34704
35123
  setCity(data.city ?? "");
@@ -34720,7 +35139,9 @@ function VendorEditPage({ vendorId }) {
34720
35139
  if (!cancelled) {
34721
35140
  setOwnerName(user.name ?? "");
34722
35141
  setOwnerEmail(user.email ?? "");
34723
- setOwnerPhone(user.phone ?? "");
35142
+ const oPhone = splitPhoneAndCountryCode(user.phone);
35143
+ setOwnerPhoneCode(oPhone.countryCode);
35144
+ setOwnerPhoneNum(oPhone.phoneNumber);
34724
35145
  }
34725
35146
  }
34726
35147
  } else if (!cancelled) {
@@ -34867,7 +35288,10 @@ function VendorEditPage({ vendorId }) {
34867
35288
  setSaving(false);
34868
35289
  return;
34869
35290
  }
34870
- 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
+ });
34871
35295
  if (taxError) {
34872
35296
  setErrors([
34873
35297
  taxError
@@ -34909,7 +35333,7 @@ function VendorEditPage({ vendorId }) {
34909
35333
  user: {
34910
35334
  name: ownerName.trim(),
34911
35335
  email: ownerEmail.trim(),
34912
- phone: ownerPhone.trim() || void 0,
35336
+ phone: formattedOwnerPhone || void 0,
34913
35337
  designation: ownerDesignation.trim() || void 0,
34914
35338
  aadhaarNo: ownerAadhaarNo.trim(),
34915
35339
  panNo: ownerPanNo.trim().toUpperCase(),
@@ -34973,7 +35397,9 @@ function VendorEditPage({ vendorId }) {
34973
35397
  setSaving(false);
34974
35398
  return;
34975
35399
  }
34976
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35400
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35401
+ required: false
35402
+ });
34977
35403
  if (taxError) {
34978
35404
  setErrors([
34979
35405
  taxError
@@ -35009,6 +35435,7 @@ function VendorEditPage({ vendorId }) {
35009
35435
  return;
35010
35436
  }
35011
35437
  if (ownerUserId != null) {
35438
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
35012
35439
  const userRes = await fetch(`/api/users/${ownerUserId}`, {
35013
35440
  method: "PUT",
35014
35441
  headers: {
@@ -35017,7 +35444,7 @@ function VendorEditPage({ vendorId }) {
35017
35444
  body: JSON.stringify({
35018
35445
  name: ownerName.trim(),
35019
35446
  email: ownerEmail.trim(),
35020
- phone: ownerPhone.trim() || null
35447
+ phone: formattedOwnerPhone || null
35021
35448
  })
35022
35449
  });
35023
35450
  if (!userRes.ok) {
@@ -35151,12 +35578,23 @@ function VendorEditPage({ vendorId }) {
35151
35578
  className: `mt-1 ${fieldClass}`
35152
35579
  })), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
35153
35580
  htmlFor: "vendorPhone"
35154
- }, "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, {
35155
35591
  id: "vendorPhone",
35156
- value: phone,
35157
- onChange: /* @__PURE__ */ __name((e) => setPhone(e.target.value), "onChange"),
35158
- className: `mt-1 ${fieldClass}`
35159
- })))), /* @__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", {
35160
35598
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35161
35599
  }, "Store address"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35162
35600
  className: sectionCls5
@@ -35209,12 +35647,15 @@ function VendorEditPage({ vendorId }) {
35209
35647
  className: `mt-1 ${fieldClass}`
35210
35648
  }))))), /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
35211
35649
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35212
- }, "Tax (India)"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35650
+ }, "Tax"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35213
35651
  className: sectionCls5
35214
35652
  }, /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
35215
- htmlFor: "gstin"
35653
+ htmlFor: "gstin",
35654
+ required: create
35216
35655
  }, "GSTIN"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
35217
35656
  id: "gstin",
35657
+ required: create,
35658
+ maxLength: 15,
35218
35659
  value: gstin,
35219
35660
  onChange: /* @__PURE__ */ __name((e) => setGstin(e.target.value.toUpperCase()), "onChange"),
35220
35661
  placeholder: "22AAAAA0000A1Z5",
@@ -35244,12 +35685,23 @@ function VendorEditPage({ vendorId }) {
35244
35685
  className: `mt-1 ${fieldClass}`
35245
35686
  })), /* @__PURE__ */ React25__namespace.default.createElement("div", null, /* @__PURE__ */ React25__namespace.default.createElement(FieldLabel, {
35246
35687
  htmlFor: "ownerPhone"
35247
- }, "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, {
35248
35698
  id: "ownerPhone",
35249
- value: ownerPhone,
35250
- onChange: /* @__PURE__ */ __name((e) => setOwnerPhone(e.target.value), "onChange"),
35251
- className: `mt-1 ${fieldClass}`
35252
- })), /* @__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, {
35253
35705
  htmlFor: "ownerDesignation"
35254
35706
  }, "Designation"), /* @__PURE__ */ React25__namespace.default.createElement(Input, {
35255
35707
  id: "ownerDesignation",
@@ -35334,7 +35786,7 @@ function VendorEditPage({ vendorId }) {
35334
35786
  htmlFor: "termsAccepted",
35335
35787
  className: "font-normal cursor-pointer text-sm leading-snug"
35336
35788
  }, "I confirm I have read and accepted the terms and conditions for selling on this platform."))))),
35337
- 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", {
35789
+ sidebar: create ? void 0 : /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
35338
35790
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35339
35791
  }, "Status"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35340
35792
  className: sectionCls5
@@ -35369,18 +35821,7 @@ function VendorEditPage({ vendorId }) {
35369
35821
  readOnly: true,
35370
35822
  value: active ? "Yes" : "No",
35371
35823
  className: `mt-1 ${fieldClass} bg-gray-100`
35372
- })))), create ? /* @__PURE__ */ React25__namespace.default.createElement("section", null, /* @__PURE__ */ React25__namespace.default.createElement("h2", {
35373
- className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35374
- }, "Owner access"), /* @__PURE__ */ React25__namespace.default.createElement("div", {
35375
- className: sectionCls5
35376
- }, /* @__PURE__ */ React25__namespace.default.createElement("p", {
35377
- className: "text-sm text-gray-600"
35378
- }, "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, {
35379
- type: "button",
35380
- disabled: saving,
35381
- onClick: handleCreate,
35382
- className: "w-full"
35383
- }, saving ? "Creating\u2026" : "Create vendor"))) : void 0)
35824
+ }))))
35384
35825
  }), /* @__PURE__ */ React25__namespace.default.createElement(Dialog, {
35385
35826
  open: inviteDialog != null,
35386
35827
  onOpenChange: /* @__PURE__ */ __name((open) => {
@@ -36975,7 +37416,7 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
36975
37416
  value: "minAmount"
36976
37417
  }, "Minimum order amount"), /* @__PURE__ */ React.createElement(SelectItem, {
36977
37418
  value: "minQuantity"
36978
- }, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
37419
+ }, "Minimum Cart Quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
36979
37420
  value: "productMinQuantity"
36980
37421
  }, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
36981
37422
  value: "events"
@@ -38430,8 +38871,8 @@ function ContactDetailPage({ contactId }) {
38430
38871
  setAddAddressError("Select country, state, and city from the lists.");
38431
38872
  return;
38432
38873
  }
38433
- const { Country: Country2, State: State2 } = await import('country-state-city');
38434
- 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 ?? "";
38435
38876
  const stateName = State2.getStatesOfCountry(addressGeo.countryIso).find((s) => s.isoCode === addressGeo.stateIso)?.name ?? "";
38436
38877
  const payload = {
38437
38878
  contactId: Number(contactId),