@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.js CHANGED
@@ -26,10 +26,10 @@ import 'jodit/es2021/jodit.min.css';
26
26
  import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, PointElement, LineElement, Title, Filler, BarElement } from 'chart.js';
27
27
  import { Line, Doughnut, Bar } from 'react-chartjs-2';
28
28
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
29
+ import { Country, State, City } from 'country-state-city';
29
30
  import { QRCode } from 'react-qr-code';
30
31
  import 'typeorm';
31
32
  import { Editor, Frame, Element as Element$1, useEditor, useNode } from '@craftjs/core';
32
- import { Country, State, City } from 'country-state-city';
33
33
  import { ThemeProvider } from 'next-themes';
34
34
 
35
35
  var __defProp = Object.defineProperty;
@@ -230,11 +230,20 @@ var init_infuro_favicon = __esm({
230
230
  function isSuperAdminGroupName(name) {
231
231
  return name === ADMIN_GROUP_NAME;
232
232
  }
233
+ function hasEntityPermission(record, entity, action) {
234
+ const p = record?.[entity];
235
+ if (!p) return false;
236
+ if (action === "create") return p.c;
237
+ if (action === "read") return p.r;
238
+ if (action === "update") return p.u;
239
+ return p.d;
240
+ }
233
241
  var ADMIN_GROUP_NAME;
234
242
  var init_permission_entities = __esm({
235
243
  "src/auth/permission-entities.ts"() {
236
244
  ADMIN_GROUP_NAME = "Administrator";
237
245
  __name(isSuperAdminGroupName, "isSuperAdminGroupName");
246
+ __name(hasEntityPermission, "hasEntityPermission");
238
247
  }
239
248
  });
240
249
 
@@ -436,6 +445,395 @@ var init_Header = __esm({
436
445
  __name(AdminHeader, "AdminHeader");
437
446
  }
438
447
  });
448
+
449
+ // src/lib/vendor-role-defaults.ts
450
+ function full() {
451
+ return {
452
+ canCreate: true,
453
+ canRead: true,
454
+ canUpdate: true,
455
+ canDelete: true
456
+ };
457
+ }
458
+ function read() {
459
+ return {
460
+ canCreate: false,
461
+ canRead: true,
462
+ canUpdate: false,
463
+ canDelete: false
464
+ };
465
+ }
466
+ function none() {
467
+ return {
468
+ canCreate: false,
469
+ canRead: false,
470
+ canUpdate: false,
471
+ canDelete: false
472
+ };
473
+ }
474
+ function readUpdate() {
475
+ return {
476
+ canCreate: false,
477
+ canRead: true,
478
+ canUpdate: true,
479
+ canDelete: false
480
+ };
481
+ }
482
+ function storePerms(spec) {
483
+ const resolve = /* @__PURE__ */ __name((v) => {
484
+ if (v === "full") return full();
485
+ if (v === "read") return read();
486
+ if (v === "none") return none();
487
+ return v;
488
+ }, "resolve");
489
+ const out = {};
490
+ for (const entity of STORE_ENTITIES) {
491
+ out[entity] = resolve(spec[entity] ?? "none");
492
+ }
493
+ return out;
494
+ }
495
+ var VENDOR_RBAC_ENTITIES, STORE_ENTITIES;
496
+ var init_vendor_role_defaults = __esm({
497
+ "src/lib/vendor-role-defaults.ts"() {
498
+ init_vendor_scope();
499
+ VENDOR_RBAC_ENTITIES = [
500
+ ...VENDOR_STORE_RBAC_ENTITIES,
501
+ "dashboard",
502
+ "settings",
503
+ "team",
504
+ "roles"
505
+ ];
506
+ STORE_ENTITIES = [
507
+ ...VENDOR_STORE_RBAC_ENTITIES
508
+ ];
509
+ __name(full, "full");
510
+ __name(read, "read");
511
+ __name(none, "none");
512
+ __name(readUpdate, "readUpdate");
513
+ __name(storePerms, "storePerms");
514
+ [
515
+ {
516
+ name: "Owner",
517
+ description: "Full store access and team/role management",
518
+ isSystem: true,
519
+ isOwnerRole: true,
520
+ permissions: {
521
+ ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
522
+ e,
523
+ "full"
524
+ ]))),
525
+ dashboard: read(),
526
+ settings: readUpdate(),
527
+ team: full(),
528
+ roles: full()
529
+ }
530
+ },
531
+ {
532
+ name: "Manager",
533
+ description: "Day-to-day store operations",
534
+ isSystem: true,
535
+ isOwnerRole: false,
536
+ permissions: {
537
+ ...storePerms({
538
+ products: "full",
539
+ collections: "full",
540
+ brands: "full",
541
+ product_categories: "full",
542
+ orders: "full",
543
+ vendor_customers: "full",
544
+ discounts: "full",
545
+ order_discounts: "full",
546
+ order_addresses: "full",
547
+ payments: "read",
548
+ taxes: "read",
549
+ attributes: "read"
550
+ }),
551
+ dashboard: read(),
552
+ settings: read(),
553
+ team: none(),
554
+ roles: none()
555
+ }
556
+ },
557
+ {
558
+ name: "Catalog Staff",
559
+ description: "Manage products and catalog content",
560
+ isSystem: true,
561
+ isOwnerRole: false,
562
+ permissions: {
563
+ ...storePerms({
564
+ products: "full",
565
+ collections: "full",
566
+ brands: "full",
567
+ product_categories: "full",
568
+ attributes: "full",
569
+ orders: "read",
570
+ vendor_customers: "read"
571
+ }),
572
+ dashboard: read(),
573
+ settings: none(),
574
+ team: none(),
575
+ roles: none()
576
+ }
577
+ },
578
+ {
579
+ name: "Fulfillment",
580
+ description: "Orders and fulfillment",
581
+ isSystem: true,
582
+ isOwnerRole: false,
583
+ permissions: {
584
+ ...storePerms({
585
+ products: "read",
586
+ orders: "full",
587
+ order_addresses: "full",
588
+ order_discounts: "read",
589
+ vendor_customers: "read"
590
+ }),
591
+ dashboard: read(),
592
+ settings: none(),
593
+ team: none(),
594
+ roles: none()
595
+ }
596
+ },
597
+ {
598
+ name: "Viewer",
599
+ description: "Read-only access to store data",
600
+ isSystem: true,
601
+ isOwnerRole: false,
602
+ permissions: {
603
+ ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
604
+ e,
605
+ "read"
606
+ ]))),
607
+ dashboard: read(),
608
+ settings: none(),
609
+ team: none(),
610
+ roles: none()
611
+ }
612
+ }
613
+ ];
614
+ }
615
+ });
616
+
617
+ // src/auth/rbac-debug.ts
618
+ function summarizeEntityPerms(entityPerms) {
619
+ const out = {};
620
+ if (!entityPerms) return out;
621
+ for (const [entity, p] of Object.entries(entityPerms)) {
622
+ out[entity] = {
623
+ c: p.c,
624
+ r: p.r,
625
+ u: p.u,
626
+ d: p.d
627
+ };
628
+ }
629
+ return out;
630
+ }
631
+ function vendorPortalFallbackReason(user, entity, action) {
632
+ if (!isVendorPortalUser(user)) return null;
633
+ if (entity === "dashboard" && action === "read") return "vendor_portal_dashboard_fallback";
634
+ if (entity === "analytics" && action === "read") return "vendor_portal_analytics_fallback";
635
+ if (entity === "settings" && action === "read") return "vendor_portal_settings_read_fallback";
636
+ if (entity === "upload" && (action === "create" || action === "read")) {
637
+ return "vendor_portal_upload_fallback";
638
+ }
639
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity) && action === "read") return "vendor_portal_store_read_fallback";
640
+ return null;
641
+ }
642
+ function explainSessionEntityAccess(user, entity, action) {
643
+ const userSnapshot = {
644
+ email: user?.email ?? null,
645
+ id: user?.id ?? null,
646
+ groupId: user?.groupId ?? null,
647
+ groupName: user?.groupName ?? null,
648
+ isRBACAdmin: user?.isRBACAdmin ?? false,
649
+ adminAccess: user?.adminAccess ?? null,
650
+ vendorIds: user?.vendorIds ?? [],
651
+ activeVendorId: user?.activeVendorId ?? null,
652
+ vendorRole: user?.vendorRole ?? null,
653
+ vendorRoleId: user?.vendorRoleId ?? null,
654
+ vendorRoleName: user?.vendorRoleName ?? null,
655
+ isVendorRoleOwner: user?.isVendorRoleOwner ?? false,
656
+ vendorEntityPerms: summarizeEntityPerms(user?.vendorEntityPerms),
657
+ isVendorPortal: user?.isVendorPortal ?? false,
658
+ isVendorOwner: user?.isVendorOwner ?? false,
659
+ isVendorPortalComputed: isVendorPortalUser(user),
660
+ isVendorOwnerComputed: isVendorOwner(user),
661
+ jwtEntityPerms: summarizeEntityPerms(user?.entityPerms),
662
+ jwtEntityPermForTarget: user?.entityPerms?.[entity] ?? null
663
+ };
664
+ if (!user?.email) {
665
+ return {
666
+ allowed: false,
667
+ reason: "no_session_email",
668
+ userSnapshot
669
+ };
670
+ }
671
+ if (user.isRBACAdmin) {
672
+ return {
673
+ allowed: true,
674
+ reason: "platform_administrator",
675
+ userSnapshot
676
+ };
677
+ }
678
+ if ((isVendorPortalUser(user) || (user.vendorIds?.length ?? 0) > 0) && entity === "upload" && (action === "create" || action === "read")) {
679
+ return {
680
+ allowed: true,
681
+ reason: "vendor_portal_upload",
682
+ userSnapshot
683
+ };
684
+ }
685
+ const vendorIds = user.vendorIds ?? [];
686
+ const isStaff = user.vendorRole === "staff";
687
+ const vendorPerms = user.vendorEntityPerms;
688
+ const hasVendorRolePerms = vendorPerms != null && Object.keys(vendorPerms).length > 0;
689
+ const enforceVendorRoleMatrix = vendorIds.length > 0 && (hasVendorRolePerms || user.vendorRoleId != null);
690
+ if (enforceVendorRoleMatrix) {
691
+ const vendorRbacSet = new Set(VENDOR_RBAC_ENTITIES);
692
+ if (vendorRbacSet.has(entity) || entity === "analytics") {
693
+ if (entity === "analytics" && action === "read") {
694
+ const allowed3 = hasEntityPermission(vendorPerms, "dashboard", "read");
695
+ return {
696
+ allowed: allowed3,
697
+ reason: allowed3 ? "vendor_role_analytics_via_dashboard" : "vendor_role_analytics_denied",
698
+ userSnapshot
699
+ };
700
+ }
701
+ if (user.isVendorRoleOwner === true) {
702
+ return {
703
+ allowed: true,
704
+ reason: "vendor_owner_role_full",
705
+ userSnapshot
706
+ };
707
+ }
708
+ const allowed2 = hasEntityPermission(vendorPerms, entity, action);
709
+ return {
710
+ allowed: allowed2,
711
+ reason: allowed2 ? "vendor_role_permissions_allow" : "vendor_role_permissions_deny",
712
+ userSnapshot
713
+ };
714
+ }
715
+ }
716
+ if (vendorIds.length > 0) {
717
+ if (entity === "dashboard" && action === "read") {
718
+ return {
719
+ allowed: true,
720
+ reason: "vendor_link_dashboard_read",
721
+ userSnapshot
722
+ };
723
+ }
724
+ if (entity === "analytics" && action === "read") {
725
+ return {
726
+ allowed: true,
727
+ reason: "vendor_link_analytics_read",
728
+ userSnapshot
729
+ };
730
+ }
731
+ if (entity === "settings") {
732
+ if (action === "read") {
733
+ return {
734
+ allowed: true,
735
+ reason: "vendor_link_settings_read",
736
+ userSnapshot
737
+ };
738
+ }
739
+ if (action === "update" && !isStaff) {
740
+ return {
741
+ allowed: true,
742
+ reason: "vendor_link_settings_update",
743
+ userSnapshot
744
+ };
745
+ }
746
+ }
747
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
748
+ if (isStaff && action === "read") {
749
+ return {
750
+ allowed: true,
751
+ reason: "vendor_staff_store_read",
752
+ userSnapshot
753
+ };
754
+ }
755
+ if (!isStaff) {
756
+ return {
757
+ allowed: true,
758
+ reason: "vendor_link_store_full",
759
+ userSnapshot
760
+ };
761
+ }
762
+ return {
763
+ allowed: false,
764
+ reason: "vendor_staff_store_write_denied",
765
+ userSnapshot
766
+ };
767
+ }
768
+ }
769
+ if (isVendorPortalUser(user) && isVendorOwner(user)) {
770
+ if (entity === "dashboard" && action === "read") {
771
+ return {
772
+ allowed: true,
773
+ reason: "vendor_owner_group_dashboard",
774
+ userSnapshot
775
+ };
776
+ }
777
+ if (entity === "analytics" && action === "read") {
778
+ return {
779
+ allowed: true,
780
+ reason: "vendor_owner_group_analytics",
781
+ userSnapshot
782
+ };
783
+ }
784
+ if (entity === "settings" && (action === "read" || action === "update")) {
785
+ return {
786
+ allowed: true,
787
+ reason: "vendor_owner_group_settings",
788
+ userSnapshot
789
+ };
790
+ }
791
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
792
+ return {
793
+ allowed: true,
794
+ reason: "vendor_owner_group_store",
795
+ userSnapshot
796
+ };
797
+ }
798
+ }
799
+ const vendorPortalReason = vendorPortalFallbackReason(user, entity, action);
800
+ if (vendorPortalReason) {
801
+ return {
802
+ allowed: true,
803
+ reason: vendorPortalReason,
804
+ userSnapshot
805
+ };
806
+ }
807
+ const explicit = user.entityPerms?.[entity];
808
+ if (explicit !== void 0) {
809
+ const allowed2 = hasEntityPermission(user.entityPerms, entity, action);
810
+ return {
811
+ allowed: allowed2,
812
+ reason: allowed2 ? "jwt_entity_perms_allow" : "jwt_entity_perms_deny",
813
+ userSnapshot
814
+ };
815
+ }
816
+ const allowed = hasEntityPermission(user.entityPerms, entity, action);
817
+ return {
818
+ allowed,
819
+ reason: allowed ? "entity_perms_fallback_allow" : "no_matching_rule",
820
+ userSnapshot
821
+ };
822
+ }
823
+ function sessionHasEntityAccessFromExplanation(user, entity, action) {
824
+ return explainSessionEntityAccess(user, entity, action).allowed;
825
+ }
826
+ var init_rbac_debug = __esm({
827
+ "src/auth/rbac-debug.ts"() {
828
+ init_permission_entities();
829
+ init_vendor_role_defaults();
830
+ init_vendor_scope();
831
+ __name(summarizeEntityPerms, "summarizeEntityPerms");
832
+ __name(vendorPortalFallbackReason, "vendorPortalFallbackReason");
833
+ __name(explainSessionEntityAccess, "explainSessionEntityAccess");
834
+ __name(sessionHasEntityAccessFromExplanation, "sessionHasEntityAccessFromExplanation");
835
+ }
836
+ });
439
837
  var defaultValue, AdminConfigContext;
440
838
  var init_admin_config_context = __esm({
441
839
  "src/admin/admin-config-context.tsx"() {
@@ -455,7 +853,7 @@ var init_admin_config_context = __esm({
455
853
  var CMS_VERSION;
456
854
  var init_cms_version = __esm({
457
855
  "src/lib/cms-version.ts"() {
458
- CMS_VERSION = "1.0.50" ;
856
+ CMS_VERSION = "1.0.51" ;
459
857
  }
460
858
  });
461
859
  function useCatalogCategories(enabled = true) {
@@ -590,6 +988,10 @@ function AdminSidebar({ variant = "sidebar" }) {
590
988
  const sectionCls7 = "mb-5";
591
989
  const headingCls = "text-[11px] font-semibold text-gray-400 uppercase tracking-wider px-2.5 mb-1.5";
592
990
  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";
991
+ const canReadEntity = /* @__PURE__ */ __name((entity) => {
992
+ if (!sessionUser) return true;
993
+ return sessionHasEntityAccessFromExplanation(sessionUser, entity, "read");
994
+ }, "canReadEntity");
593
995
  return /* @__PURE__ */ React.createElement("aside", {
594
996
  className: asideCls
595
997
  }, /* @__PURE__ */ React.createElement("div", {
@@ -607,30 +1009,40 @@ function AdminSidebar({ variant = "sidebar" }) {
607
1009
  className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
608
1010
  }, /* @__PURE__ */ React.createElement(LayoutDashboard, {
609
1011
  className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
610
- }), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) => /* @__PURE__ */ React.createElement("div", {
611
- key: section.title,
612
- className: sectionCls7
613
- }, /* @__PURE__ */ React.createElement("h3", {
614
- className: headingCls
615
- }, section.title), /* @__PURE__ */ React.createElement("ul", {
616
- className: "space-y-0.5"
617
- }, section.items.map((item) => {
618
- const Icon2 = getIconForItem(item.icon);
619
- return /* @__PURE__ */ React.createElement("li", {
620
- key: item.href
621
- }, /* @__PURE__ */ React.createElement(Link2, {
622
- href: item.href,
623
- className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
624
- }, /* @__PURE__ */ React.createElement(Icon2, {
625
- className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
626
- }), item.label));
627
- })))), showPlatformNav && customNavSections.length === 0 && customNavItems.length > 0 && /* @__PURE__ */ React.createElement("div", {
1012
+ }), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) => {
1013
+ const filteredItems = section.items.filter((item) => {
1014
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1015
+ return canReadEntity(entity);
1016
+ });
1017
+ if (filteredItems.length === 0) return null;
1018
+ return /* @__PURE__ */ React.createElement("div", {
1019
+ key: section.title,
1020
+ className: sectionCls7
1021
+ }, /* @__PURE__ */ React.createElement("h3", {
1022
+ className: headingCls
1023
+ }, section.title), /* @__PURE__ */ React.createElement("ul", {
1024
+ className: "space-y-0.5"
1025
+ }, filteredItems.map((item) => {
1026
+ const Icon2 = getIconForItem(item.icon);
1027
+ return /* @__PURE__ */ React.createElement("li", {
1028
+ key: item.href
1029
+ }, /* @__PURE__ */ React.createElement(Link2, {
1030
+ href: item.href,
1031
+ className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
1032
+ }, /* @__PURE__ */ React.createElement(Icon2, {
1033
+ className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
1034
+ }), item.label));
1035
+ })));
1036
+ }), showPlatformNav && customNavSections.length === 0 && customNavItems.length > 0 && /* @__PURE__ */ React.createElement("div", {
628
1037
  className: sectionCls7
629
1038
  }, /* @__PURE__ */ React.createElement("h3", {
630
1039
  className: headingCls
631
1040
  }, "Custom"), /* @__PURE__ */ React.createElement("ul", {
632
1041
  className: "space-y-0.5"
633
- }, customNavItems.map((item) => {
1042
+ }, customNavItems.filter((item) => {
1043
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1044
+ return canReadEntity(entity);
1045
+ }).map((item) => {
634
1046
  const Icon2 = getIconForItem(item.icon);
635
1047
  return /* @__PURE__ */ React.createElement("li", {
636
1048
  key: item.href
@@ -651,85 +1063,92 @@ function AdminSidebar({ variant = "sidebar" }) {
651
1063
  className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
652
1064
  }, /* @__PURE__ */ React.createElement(LayoutDashboard, {
653
1065
  className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
654
- }), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) => /* @__PURE__ */ React.createElement("div", {
655
- key: section.title,
656
- className: sectionCls7
657
- }, /* @__PURE__ */ React.createElement("h3", {
658
- className: headingCls
659
- }, section.title), /* @__PURE__ */ React.createElement("ul", {
660
- className: "space-y-0.5"
661
- }, section.items.map((item) => {
662
- const Icon2 = getIconForItem(item.icon);
663
- return /* @__PURE__ */ React.createElement("li", {
664
- key: item.href
665
- }, /* @__PURE__ */ React.createElement(Link2, {
666
- href: item.href,
667
- className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
668
- }, /* @__PURE__ */ React.createElement(Icon2, {
669
- className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
670
- }), item.label));
671
- })))), showStoreNav && /* @__PURE__ */ React.createElement("div", {
1066
+ }), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) => {
1067
+ const filteredItems = section.items.filter((item) => {
1068
+ const entity = item.href.replace("/admin/", "").split("/")[0];
1069
+ return canReadEntity(entity);
1070
+ });
1071
+ if (filteredItems.length === 0) return null;
1072
+ return /* @__PURE__ */ React.createElement("div", {
1073
+ key: section.title,
1074
+ className: sectionCls7
1075
+ }, /* @__PURE__ */ React.createElement("h3", {
1076
+ className: headingCls
1077
+ }, section.title), /* @__PURE__ */ React.createElement("ul", {
1078
+ className: "space-y-0.5"
1079
+ }, filteredItems.map((item) => {
1080
+ const Icon2 = getIconForItem(item.icon);
1081
+ return /* @__PURE__ */ React.createElement("li", {
1082
+ key: item.href
1083
+ }, /* @__PURE__ */ React.createElement(Link2, {
1084
+ href: item.href,
1085
+ className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
1086
+ }, /* @__PURE__ */ React.createElement(Icon2, {
1087
+ className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
1088
+ }), item.label));
1089
+ })));
1090
+ }), showStoreNav && /* @__PURE__ */ React.createElement("div", {
672
1091
  className: sectionCls7
673
1092
  }, /* @__PURE__ */ React.createElement("h3", {
674
1093
  className: headingCls
675
1094
  }, "Store"), /* @__PURE__ */ React.createElement("ul", {
676
1095
  className: "space-y-0.5"
677
- }, showVendorOnboard && multiVendorEnabled !== false && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1096
+ }, showVendorOnboard && multiVendorEnabled !== false && canReadEntity("vendors") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
678
1097
  href: "/admin/vendors",
679
1098
  className: `${linkCls} ${isActive("/admin/vendors") ? linkActive : linkInactive}`
680
1099
  }, /* @__PURE__ */ React.createElement(Store, {
681
1100
  className: `h-4 w-4 mr-2 ${isActive("/admin/vendors") ? iconActive : iconInactive}`
682
- }), "Vendors")), showVendorCategories && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1101
+ }), "Vendors")), showVendorCategories && canReadEntity("product_categories") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
683
1102
  href: "/admin/product_categories",
684
1103
  className: `${linkCls} ${isActive("/admin/product_categories") ? linkActive : linkInactive}`
685
1104
  }, /* @__PURE__ */ React.createElement(FolderTree, {
686
1105
  className: `h-4 w-4 mr-2 ${isActive("/admin/product_categories") ? iconActive : iconInactive}`
687
- }), "Categories")), showVendorCollections && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1106
+ }), "Categories")), showVendorCollections && canReadEntity("collections") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
688
1107
  href: "/admin/collections",
689
1108
  className: `${linkCls} ${isActive("/admin/collections") ? linkActive : linkInactive}`
690
1109
  }, /* @__PURE__ */ React.createElement(Layers, {
691
1110
  className: `h-4 w-4 mr-2 ${isActive("/admin/collections") ? iconActive : iconInactive}`
692
- }), "Collections")), showVendorBrands && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1111
+ }), "Collections")), showVendorBrands && canReadEntity("brands") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
693
1112
  href: "/admin/brands",
694
1113
  className: `${linkCls} ${isActive("/admin/brands") ? linkActive : linkInactive}`
695
1114
  }, /* @__PURE__ */ React.createElement(Building2, {
696
1115
  className: `h-4 w-4 mr-2 ${isActive("/admin/brands") ? iconActive : iconInactive}`
697
- }), "Brands")), !hasCustomEventsNav && eventsEnabled !== false ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1116
+ }), "Brands")), !hasCustomEventsNav && eventsEnabled !== false && canReadEntity("events") ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
698
1117
  href: "/admin/events",
699
1118
  className: `${linkCls} ${isActive("/admin/events") ? linkActive : linkInactive}`
700
1119
  }, /* @__PURE__ */ React.createElement(CalendarDays, {
701
1120
  className: `h-4 w-4 mr-2 ${isActive("/admin/events") ? iconActive : iconInactive}`
702
- }), "Events")) : null, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1121
+ }), "Events")) : null, canReadEntity("products") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
703
1122
  href: "/admin/products",
704
1123
  className: `${linkCls} ${isActive("/admin/products") ? linkActive : linkInactive}`
705
1124
  }, /* @__PURE__ */ React.createElement(ShoppingBag, {
706
1125
  className: `h-4 w-4 mr-2 ${isActive("/admin/products") ? iconActive : iconInactive}`
707
- }), "Products")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1126
+ }), "Products")), canReadEntity("combos") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
708
1127
  href: "/admin/combos",
709
1128
  className: `${linkCls} ${isActive("/admin/combos") ? linkActive : linkInactive}`
710
1129
  }, /* @__PURE__ */ React.createElement(Package, {
711
1130
  className: `h-4 w-4 mr-2 ${isActive("/admin/combos") ? iconActive : iconInactive}`
712
- }), "Combos")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1131
+ }), "Combos")), canReadEntity("taxes") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
713
1132
  href: "/admin/taxes",
714
1133
  className: `${linkCls} ${isActive("/admin/taxes") ? linkActive : linkInactive}`
715
1134
  }, /* @__PURE__ */ React.createElement(Receipt, {
716
1135
  className: `h-4 w-4 mr-2 ${isActive("/admin/taxes") ? iconActive : iconInactive}`
717
- }), "Taxes")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1136
+ }), "Taxes")), canReadEntity("discounts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
718
1137
  href: "/admin/discounts",
719
1138
  className: `${linkCls} ${isActive("/admin/discounts") ? linkActive : linkInactive}`
720
1139
  }, /* @__PURE__ */ React.createElement(BadgePercent, {
721
1140
  className: `h-4 w-4 mr-2 ${isActive("/admin/discounts") ? iconActive : iconInactive}`
722
- }), "Discounts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1141
+ }), "Discounts")), canReadEntity("orders") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
723
1142
  href: "/admin/orders",
724
1143
  className: `${linkCls} ${isActive("/admin/orders") ? linkActive : linkInactive}`
725
1144
  }, /* @__PURE__ */ React.createElement(ShoppingCart, {
726
1145
  className: `h-4 w-4 mr-2 ${isActive("/admin/orders") ? iconActive : iconInactive}`
727
- }), "Orders")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1146
+ }), "Orders")), canReadEntity("payments") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
728
1147
  href: "/admin/payments",
729
1148
  className: `${linkCls} ${isActive("/admin/payments") ? linkActive : linkInactive}`
730
1149
  }, /* @__PURE__ */ React.createElement(CreditCard, {
731
1150
  className: `h-4 w-4 mr-2 ${isActive("/admin/payments") ? iconActive : iconInactive}`
732
- }), "Payments")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1151
+ }), "Payments")), canReadEntity("vendor_customers") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
733
1152
  href: "/admin/vendor_customers",
734
1153
  className: `${linkCls} ${isActive("/admin/vendor_customers") ? linkActive : linkInactive}`
735
1154
  }, /* @__PURE__ */ React.createElement(Users, {
@@ -740,32 +1159,32 @@ function AdminSidebar({ variant = "sidebar" }) {
740
1159
  className: headingCls
741
1160
  }, "Management"), /* @__PURE__ */ React.createElement("ul", {
742
1161
  className: "space-y-0.5"
743
- }, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1162
+ }, canReadEntity("contacts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
744
1163
  href: "/admin/contacts",
745
1164
  className: `${linkCls} ${isActive("/admin/contacts") ? linkActive : linkInactive}`
746
1165
  }, /* @__PURE__ */ React.createElement(Inbox, {
747
1166
  className: `h-4 w-4 mr-2 ${isActive("/admin/contacts") ? iconActive : iconInactive}`
748
- }), "Contacts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1167
+ }), "Contacts")), canReadEntity("blogs") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
749
1168
  href: "/admin/blogs",
750
1169
  className: `${linkCls} ${isActive("/admin/blogs") ? linkActive : linkInactive}`
751
1170
  }, /* @__PURE__ */ React.createElement(File, {
752
1171
  className: `h-4 w-4 mr-2 ${isActive("/admin/blogs") ? iconActive : iconInactive}`
753
- }), "Blogs")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1172
+ }), "Blogs")), canReadEntity("pages") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
754
1173
  href: "/admin/pages",
755
1174
  className: `${linkCls} ${isActive("/admin/pages") ? linkActive : linkInactive}`
756
1175
  }, /* @__PURE__ */ React.createElement(LinkIcon, {
757
1176
  className: `h-4 w-4 mr-2 ${isActive("/admin/pages") ? iconActive : iconInactive}`
758
- }), "Pages")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1177
+ }), "Pages")), canReadEntity("forms") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
759
1178
  href: "/admin/forms",
760
1179
  className: `${linkCls} ${isActive("/admin/forms") ? linkActive : linkInactive}`
761
1180
  }, /* @__PURE__ */ React.createElement(ClipboardList, {
762
1181
  className: `h-4 w-4 mr-2 ${isActive("/admin/forms") ? iconActive : iconInactive}`
763
- }), "Forms")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1182
+ }), "Forms")), canReadEntity("form_submissions") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
764
1183
  href: "/admin/submissions",
765
1184
  className: `${linkCls} ${isActive("/admin/submissions") ? linkActive : linkInactive}`
766
1185
  }, /* @__PURE__ */ React.createElement(MessageSquare, {
767
1186
  className: `h-4 w-4 mr-2 ${isActive("/admin/submissions") ? iconActive : iconInactive}`
768
- }), "Submissions")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1187
+ }), "Submissions")), canReadEntity("upload") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
769
1188
  href: "/admin/media",
770
1189
  className: `${linkCls} ${isActive("/admin/media") ? linkActive : linkInactive}`
771
1190
  }, /* @__PURE__ */ React.createElement(Image$1, {
@@ -802,12 +1221,12 @@ function AdminSidebar({ variant = "sidebar" }) {
802
1221
  className: headingCls
803
1222
  }, "System"), /* @__PURE__ */ React.createElement("ul", {
804
1223
  className: "space-y-0.5"
805
- }, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1224
+ }, canReadEntity("users") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
806
1225
  href: "/admin/users",
807
1226
  className: `${linkCls} ${isActive("/admin/users") ? linkActive : linkInactive}`
808
1227
  }, /* @__PURE__ */ React.createElement(Users, {
809
1228
  className: `h-4 w-4 mr-2 ${isActive("/admin/users") ? iconActive : iconInactive}`
810
- }), "Users")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
1229
+ }), "Users")), canReadEntity("roles") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
811
1230
  href: "/admin/roles",
812
1231
  className: `${linkCls} ${isActive("/admin/roles") ? linkActive : linkInactive}`
813
1232
  }, /* @__PURE__ */ React.createElement(Shield, {
@@ -854,6 +1273,7 @@ var init_Sidebar = __esm({
854
1273
  "src/components/Admin/Sidebar.tsx"() {
855
1274
  "use client";
856
1275
  init_vendor_scope();
1276
+ init_rbac_debug();
857
1277
  init_admin_config_context();
858
1278
  init_infuro_favicon();
859
1279
  init_cms_version();
@@ -1480,7 +1900,15 @@ function useEventsSettings() {
1480
1900
  requireEventApproval
1481
1901
  };
1482
1902
  }
1483
- function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, theme, themeRegistry, pluginDescriptors = [] }) {
1903
+ function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, extraNotificationVariables, theme, themeRegistry, pluginDescriptors = [] }) {
1904
+ useEffect(() => {
1905
+ document.documentElement.style.overflow = "hidden";
1906
+ document.body.style.overflow = "hidden";
1907
+ return () => {
1908
+ document.documentElement.style.overflow = "";
1909
+ document.body.style.overflow = "";
1910
+ };
1911
+ }, []);
1484
1912
  const resolvedTheme = useResolvedTheme(theme, themeRegistry);
1485
1913
  const { storeEnabled, currency } = useStoreEnabled();
1486
1914
  const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
@@ -1501,6 +1929,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1501
1929
  customCrudConfigs,
1502
1930
  categoryRelatedProductLabels,
1503
1931
  renderOrderDetailFooter,
1932
+ extraNotificationVariables,
1504
1933
  theme: resolvedTheme,
1505
1934
  themeRegistry,
1506
1935
  pluginDescriptors: mergedPluginDescriptors,
@@ -1519,6 +1948,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1519
1948
  customCrudConfigs,
1520
1949
  categoryRelatedProductLabels,
1521
1950
  renderOrderDetailFooter,
1951
+ extraNotificationVariables,
1522
1952
  resolvedTheme,
1523
1953
  themeRegistry,
1524
1954
  mergedPluginDescriptors,
@@ -4375,6 +4805,8 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4375
4805
  const hasLoadedRef = useRef(false);
4376
4806
  const isMobile = useIsMobile();
4377
4807
  const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
4808
+ const { data: session } = useSession();
4809
+ const sessionUser = session?.user;
4378
4810
  const listColumns = useMemo(() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [], [
4379
4811
  columns
4380
4812
  ]);
@@ -4915,6 +5347,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4915
5347
  withListFrom
4916
5348
  ]);
4917
5349
  const resourceName = apiEndpoint.replace("/api/", "");
5350
+ const canCreate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "create");
5351
+ const canUpdate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "update");
5352
+ const canDelete = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "delete");
4918
5353
  const dedicatedRecordEditHref = /* @__PURE__ */ __name((recordId) => addEditPage && (resourceName === "orders" || resourceName === "events") ? withListFrom(`${addEditPageUrl}/${recordId}/edit`) : addEditPage ? withListFrom(`${addEditPageUrl}/${recordId}`) : "", "dedicatedRecordEditHref");
4919
5354
  const showDedicatedDuplicate = addEditPage && !CRUD_NO_DUPLICATE_RESOURCES.has(resourceName);
4920
5355
  const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
@@ -5074,15 +5509,15 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5074
5509
  onClick: /* @__PURE__ */ __name(() => setBulkDialogOpen(true), "onClick"),
5075
5510
  variant: "outline",
5076
5511
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5077
- }, /* @__PURE__ */ React.createElement(Upload, {
5512
+ }, /* @__PURE__ */ React.createElement(Download, {
5078
5513
  className: "h-3.5 w-3.5 mr-1"
5079
5514
  }), "Import"), /* @__PURE__ */ React.createElement(Button, {
5080
5515
  onClick: handleExport,
5081
5516
  variant: "outline",
5082
5517
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5083
- }, /* @__PURE__ */ React.createElement(Download, {
5518
+ }, /* @__PURE__ */ React.createElement(Upload, {
5084
5519
  className: "h-3.5 w-3.5 mr-1"
5085
- }), "Export"), !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5520
+ }), "Export"), canCreate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5086
5521
  onClick: /* @__PURE__ */ __name(() => {
5087
5522
  setEditingItem(null);
5088
5523
  setDuplicateSeed(null);
@@ -5091,7 +5526,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5091
5526
  className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8"
5092
5527
  }, /* @__PURE__ */ React.createElement(Plus, {
5093
5528
  className: "h-3.5 w-3.5"
5094
- }), "Add"), addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
5529
+ }), "Add"), canCreate && addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
5095
5530
  href: dedicatedCreateHref,
5096
5531
  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"
5097
5532
  }, /* @__PURE__ */ React.createElement(Plus, {
@@ -5250,7 +5685,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5250
5685
  onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
5251
5686
  }, /* @__PURE__ */ React.createElement("div", {
5252
5687
  className: "flex items-center justify-center gap-1"
5253
- }, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5688
+ }, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, canUpdate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5254
5689
  variant: "outline",
5255
5690
  size: "icon",
5256
5691
  className: "h-7 w-7",
@@ -5260,7 +5695,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5260
5695
  className: "h-3.5 w-3.5"
5261
5696
  }), /* @__PURE__ */ React.createElement("span", {
5262
5697
  className: "sr-only"
5263
- }, "Edit")), addEditPage && /* @__PURE__ */ React.createElement(Button, {
5698
+ }, "Edit")), canUpdate && addEditPage && /* @__PURE__ */ React.createElement(Button, {
5264
5699
  variant: "outline",
5265
5700
  size: "icon",
5266
5701
  className: "h-7 w-7",
@@ -5270,7 +5705,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5270
5705
  className: "h-3.5 w-3.5"
5271
5706
  }), /* @__PURE__ */ React.createElement("span", {
5272
5707
  className: "sr-only"
5273
- }, "Edit")), showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5708
+ }, "Edit")), canCreate && showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5274
5709
  variant: "outline",
5275
5710
  size: "icon",
5276
5711
  className: "h-7 w-7",
@@ -5296,7 +5731,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5296
5731
  className: "h-3.5 w-3.5"
5297
5732
  }), /* @__PURE__ */ React.createElement("span", {
5298
5733
  className: "sr-only"
5299
- }, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
5734
+ }, "Resend invite")), canDelete && /* @__PURE__ */ React.createElement(Button, {
5300
5735
  variant: "outline",
5301
5736
  size: "icon",
5302
5737
  className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
@@ -5434,6 +5869,7 @@ var init_CRUD = __esm({
5434
5869
  init_dropdown_menu();
5435
5870
  init_utils();
5436
5871
  init_use_mobile();
5872
+ init_rbac_debug();
5437
5873
  init_BulkUploadDialog();
5438
5874
  init_admin_list_return_url();
5439
5875
  init_build_crud_list_filters_from_columns();
@@ -15502,6 +15938,18 @@ var init_email_recipients = __esm({
15502
15938
  __name(serializeEmailRecipients, "serializeEmailRecipients");
15503
15939
  }
15504
15940
  });
15941
+ function useNotificationVariables() {
15942
+ const config = useContext(AdminConfigContext);
15943
+ const extra = config?.extraNotificationVariables ?? [];
15944
+ const vars = [
15945
+ ...KNOWN_VARS,
15946
+ ...extra.map((e) => e.name)
15947
+ ];
15948
+ return {
15949
+ vars,
15950
+ extra
15951
+ };
15952
+ }
15505
15953
  function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15506
15954
  if (audience === "vendor") {
15507
15955
  return VENDOR_EMAIL_TRIGGER_DEFAULTS[triggerKey] ?? {
@@ -15645,6 +16093,7 @@ var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMA
15645
16093
  var init_order_notification_bindings_shared = __esm({
15646
16094
  "src/admin/components/order-notification-bindings-shared.ts"() {
15647
16095
  "use client";
16096
+ init_admin_config_context();
15648
16097
  KNOWN_VARS = [
15649
16098
  "orderId",
15650
16099
  "orderNumber",
@@ -15659,6 +16108,7 @@ var init_order_notification_bindings_shared = __esm({
15659
16108
  "invoiceNumber",
15660
16109
  "invoiceUrl"
15661
16110
  ];
16111
+ __name(useNotificationVariables, "useNotificationVariables");
15662
16112
  EMAIL_TRIGGER_DEFAULTS = {
15663
16113
  order_placed: {
15664
16114
  subject: "Your order {{orderNumber}} is confirmed",
@@ -16362,16 +16812,23 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16362
16812
  cancelled = true;
16363
16813
  };
16364
16814
  }, []);
16815
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16365
16816
  const visibleVars = useMemo(() => {
16366
- return KNOWN_VARS.filter((v) => {
16817
+ return availableVars.filter((v) => {
16367
16818
  if (v === "vendorName" || v === "vendorId") return multiVendorOn;
16368
16819
  if (v === "eventName") return eventsOn;
16369
16820
  return true;
16370
16821
  });
16371
16822
  }, [
16823
+ availableVars,
16372
16824
  multiVendorOn,
16373
16825
  eventsOn
16374
16826
  ]);
16827
+ const getVarHint = /* @__PURE__ */ __name((v) => {
16828
+ if (v in VAR_HINTS) return VAR_HINTS[v];
16829
+ const match = extraVars.find((e) => e.name === v);
16830
+ return match?.hint || match?.label || v;
16831
+ }, "getVarHint");
16375
16832
  return /* @__PURE__ */ React.createElement("div", {
16376
16833
  className: "space-y-3 border-t border-gray-100 pt-4 first:border-t-0 first:pt-0 dark:border-gray-700"
16377
16834
  }, /* @__PURE__ */ React.createElement("div", {
@@ -16434,7 +16891,7 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16434
16891
  }, visibleVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16435
16892
  key: v,
16436
16893
  type: "button",
16437
- title: VAR_HINTS[v],
16894
+ title: getVarHint(v),
16438
16895
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16439
16896
  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"
16440
16897
  }, `{{${v}}}`))), /* @__PURE__ */ React.createElement("p", {
@@ -16480,6 +16937,12 @@ function PushTriggerBindings({ audience = "customer" }) {
16480
16937
  const bodyRef = useRef(null);
16481
16938
  const subjectRef = useRef(null);
16482
16939
  const lastFocused = useRef("body");
16940
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16941
+ const getVarHint = /* @__PURE__ */ __name((v) => {
16942
+ if (v in VAR_HINTS2) return VAR_HINTS2[v];
16943
+ const match = extraVars.find((e) => e.name === v);
16944
+ return match?.hint || match?.label || v;
16945
+ }, "getVarHint");
16483
16946
  useEffect(() => {
16484
16947
  let cancelled = false;
16485
16948
  (async () => {
@@ -16701,11 +17164,11 @@ function PushTriggerBindings({ audience = "customer" }) {
16701
17164
  className: "text-xs font-medium text-gray-700 dark:text-gray-300"
16702
17165
  }, "Insert Variables"), /* @__PURE__ */ React.createElement("div", {
16703
17166
  className: "flex flex-wrap gap-1.5"
16704
- }, KNOWN_VARS.map((v) => /* @__PURE__ */ React.createElement("button", {
17167
+ }, availableVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16705
17168
  key: v,
16706
17169
  type: "button",
16707
17170
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16708
- title: VAR_HINTS2[v],
17171
+ title: getVarHint(v),
16709
17172
  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"
16710
17173
  }, `{{${v}}}`)))), /* @__PURE__ */ React.createElement("div", {
16711
17174
  className: "space-y-1.5"
@@ -20811,183 +21274,6 @@ var init_PluginsPage = __esm({
20811
21274
  }
20812
21275
  });
20813
21276
 
20814
- // src/lib/vendor-role-defaults.ts
20815
- function full() {
20816
- return {
20817
- canCreate: true,
20818
- canRead: true,
20819
- canUpdate: true,
20820
- canDelete: true
20821
- };
20822
- }
20823
- function read() {
20824
- return {
20825
- canCreate: false,
20826
- canRead: true,
20827
- canUpdate: false,
20828
- canDelete: false
20829
- };
20830
- }
20831
- function none() {
20832
- return {
20833
- canCreate: false,
20834
- canRead: false,
20835
- canUpdate: false,
20836
- canDelete: false
20837
- };
20838
- }
20839
- function readUpdate() {
20840
- return {
20841
- canCreate: false,
20842
- canRead: true,
20843
- canUpdate: true,
20844
- canDelete: false
20845
- };
20846
- }
20847
- function storePerms(spec) {
20848
- const resolve = /* @__PURE__ */ __name((v) => {
20849
- if (v === "full") return full();
20850
- if (v === "read") return read();
20851
- if (v === "none") return none();
20852
- return v;
20853
- }, "resolve");
20854
- const out = {};
20855
- for (const entity of STORE_ENTITIES) {
20856
- out[entity] = resolve(spec[entity] ?? "none");
20857
- }
20858
- return out;
20859
- }
20860
- var STORE_ENTITIES;
20861
- var init_vendor_role_defaults = __esm({
20862
- "src/lib/vendor-role-defaults.ts"() {
20863
- init_vendor_scope();
20864
- [
20865
- ...VENDOR_STORE_RBAC_ENTITIES,
20866
- "dashboard",
20867
- "settings",
20868
- "team",
20869
- "roles"
20870
- ];
20871
- STORE_ENTITIES = [
20872
- ...VENDOR_STORE_RBAC_ENTITIES
20873
- ];
20874
- __name(full, "full");
20875
- __name(read, "read");
20876
- __name(none, "none");
20877
- __name(readUpdate, "readUpdate");
20878
- __name(storePerms, "storePerms");
20879
- [
20880
- {
20881
- name: "Owner",
20882
- description: "Full store access and team/role management",
20883
- isSystem: true,
20884
- isOwnerRole: true,
20885
- permissions: {
20886
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
20887
- e,
20888
- "full"
20889
- ]))),
20890
- dashboard: read(),
20891
- settings: readUpdate(),
20892
- team: full(),
20893
- roles: full()
20894
- }
20895
- },
20896
- {
20897
- name: "Manager",
20898
- description: "Day-to-day store operations",
20899
- isSystem: true,
20900
- isOwnerRole: false,
20901
- permissions: {
20902
- ...storePerms({
20903
- products: "full",
20904
- collections: "full",
20905
- brands: "full",
20906
- product_categories: "full",
20907
- orders: "full",
20908
- vendor_customers: "full",
20909
- discounts: "full",
20910
- order_discounts: "full",
20911
- order_addresses: "full",
20912
- payments: "read",
20913
- taxes: "read",
20914
- attributes: "read"
20915
- }),
20916
- dashboard: read(),
20917
- settings: read(),
20918
- team: none(),
20919
- roles: none()
20920
- }
20921
- },
20922
- {
20923
- name: "Catalog Staff",
20924
- description: "Manage products and catalog content",
20925
- isSystem: true,
20926
- isOwnerRole: false,
20927
- permissions: {
20928
- ...storePerms({
20929
- products: "full",
20930
- collections: "full",
20931
- brands: "full",
20932
- product_categories: "full",
20933
- attributes: "full",
20934
- orders: "read",
20935
- vendor_customers: "read"
20936
- }),
20937
- dashboard: read(),
20938
- settings: none(),
20939
- team: none(),
20940
- roles: none()
20941
- }
20942
- },
20943
- {
20944
- name: "Fulfillment",
20945
- description: "Orders and fulfillment",
20946
- isSystem: true,
20947
- isOwnerRole: false,
20948
- permissions: {
20949
- ...storePerms({
20950
- products: "read",
20951
- orders: "full",
20952
- order_addresses: "full",
20953
- order_discounts: "read",
20954
- vendor_customers: "read"
20955
- }),
20956
- dashboard: read(),
20957
- settings: none(),
20958
- team: none(),
20959
- roles: none()
20960
- }
20961
- },
20962
- {
20963
- name: "Viewer",
20964
- description: "Read-only access to store data",
20965
- isSystem: true,
20966
- isOwnerRole: false,
20967
- permissions: {
20968
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
20969
- e,
20970
- "read"
20971
- ]))),
20972
- dashboard: read(),
20973
- settings: none(),
20974
- team: none(),
20975
- roles: none()
20976
- }
20977
- }
20978
- ];
20979
- }
20980
- });
20981
-
20982
- // src/auth/rbac-debug.ts
20983
- var init_rbac_debug = __esm({
20984
- "src/auth/rbac-debug.ts"() {
20985
- init_permission_entities();
20986
- init_vendor_role_defaults();
20987
- init_vendor_scope();
20988
- }
20989
- });
20990
-
20991
21277
  // src/auth/helpers.ts
20992
21278
  function canManageRoles(user) {
20993
21279
  return !!(user?.email && user.isRBACAdmin);
@@ -21145,23 +21431,6 @@ function VendorRolesPage() {
21145
21431
  return next;
21146
21432
  });
21147
21433
  }, "setAllRows");
21148
- const toggleEntityRow = /* @__PURE__ */ __name((entity) => {
21149
- const isOwnerOnlyEntity = entity === "team" || entity === "roles";
21150
- setMatrix((prev) => {
21151
- const current = prev[entity];
21152
- const isAllChecked = current?.canRead && (isOwnerOnlyEntity || current.canCreate && current.canUpdate && current.canDelete);
21153
- return {
21154
- ...prev,
21155
- [entity]: {
21156
- entity,
21157
- canRead: !isAllChecked,
21158
- canCreate: isOwnerOnlyEntity ? false : !isAllChecked,
21159
- canUpdate: isOwnerOnlyEntity ? false : !isAllChecked,
21160
- canDelete: isOwnerOnlyEntity ? false : !isAllChecked
21161
- }
21162
- };
21163
- });
21164
- }, "toggleEntityRow");
21165
21434
  const saveMatrix = /* @__PURE__ */ __name(async () => {
21166
21435
  if (!selectedId) return;
21167
21436
  setSaving(true);
@@ -21355,31 +21624,35 @@ function VendorRolesPage() {
21355
21624
  }), " Clear All"))), /* @__PURE__ */ React25__default.createElement("div", {
21356
21625
  className: "overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700"
21357
21626
  }, /* @__PURE__ */ React25__default.createElement("table", {
21358
- className: "min-w-full text-xs"
21627
+ className: "min-w-full text-sm"
21359
21628
  }, /* @__PURE__ */ React25__default.createElement("thead", {
21360
- className: "bg-gray-100 text-left text-gray-700 dark:bg-gray-900 dark:text-gray-300"
21361
- }, /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("th", {
21362
- className: "px-3 py-2 font-medium"
21363
- }, "Entity"), /* @__PURE__ */ React25__default.createElement("th", {
21364
- className: "px-3 py-2 font-medium"
21629
+ className: "bg-white dark:bg-gray-800"
21630
+ }, /* @__PURE__ */ React25__default.createElement("tr", {
21631
+ className: "border-b border-gray-200 dark:border-gray-700"
21632
+ }, /* @__PURE__ */ React25__default.createElement("th", {
21633
+ className: "px-3 py-2.5 text-left font-semibold text-gray-700 dark:text-gray-300"
21634
+ }, /* @__PURE__ */ React25__default.createElement("div", {
21635
+ className: "flex items-center gap-2"
21636
+ }, /* @__PURE__ */ React25__default.createElement("span", null, "Entity"))), /* @__PURE__ */ React25__default.createElement("th", {
21637
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
21365
21638
  }, "Create"), /* @__PURE__ */ React25__default.createElement("th", {
21366
- className: "px-3 py-2 font-medium"
21639
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
21367
21640
  }, "Read"), /* @__PURE__ */ React25__default.createElement("th", {
21368
- className: "px-3 py-2 font-medium"
21641
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
21369
21642
  }, "Update"), /* @__PURE__ */ React25__default.createElement("th", {
21370
- className: "px-3 py-2 font-medium"
21371
- }, "Delete"), /* @__PURE__ */ React25__default.createElement("th", {
21372
- className: "px-3 py-2 font-medium text-right"
21373
- }, "Row Actions"))), /* @__PURE__ */ React25__default.createElement("tbody", null, entities.map((entity) => {
21643
+ className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
21644
+ }, "Delete"))), /* @__PURE__ */ React25__default.createElement("tbody", null, entities.map((entity) => {
21374
21645
  const isOwnerOnlyEntity = entity === "team" || entity === "roles";
21375
21646
  return /* @__PURE__ */ React25__default.createElement("tr", {
21376
21647
  key: entity,
21377
- className: "border-t border-gray-100 dark:border-gray-700 hover:bg-gray-50/50 dark:hover:bg-gray-800/50"
21648
+ className: "border-b border-gray-100 last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800/50"
21378
21649
  }, /* @__PURE__ */ React25__default.createElement("td", {
21379
- className: "px-3 py-2 font-mono text-gray-800 dark:text-gray-200 font-medium"
21380
- }, entity, isOwnerOnlyEntity && /* @__PURE__ */ React25__default.createElement("span", {
21381
- className: "ml-2 text-[10px] text-amber-600 dark:text-amber-400 font-normal"
21382
- }, "(Owner managed)")), [
21650
+ className: "px-3 py-2.5 font-mono text-sm text-gray-800 dark:text-gray-200"
21651
+ }, /* @__PURE__ */ React25__default.createElement("div", {
21652
+ className: "flex items-center gap-2"
21653
+ }, /* @__PURE__ */ React25__default.createElement("span", null, entity), isOwnerOnlyEntity && /* @__PURE__ */ React25__default.createElement("span", {
21654
+ className: "text-[10px] text-amber-600 dark:text-amber-400"
21655
+ }, "(Owner managed)"))), [
21383
21656
  "canCreate",
21384
21657
  "canRead",
21385
21658
  "canUpdate",
@@ -21388,21 +21661,15 @@ function VendorRolesPage() {
21388
21661
  const disabled = isOwnerOnlyEntity && key !== "canRead";
21389
21662
  return /* @__PURE__ */ React25__default.createElement("td", {
21390
21663
  key,
21391
- className: "px-3 py-2"
21664
+ className: "px-3 py-2.5 text-center"
21392
21665
  }, /* @__PURE__ */ React25__default.createElement("input", {
21393
21666
  type: "checkbox",
21394
21667
  checked: disabled ? false : !!matrix[entity]?.[key],
21395
21668
  disabled,
21396
21669
  onChange: /* @__PURE__ */ __name(() => toggle(entity, key), "onChange"),
21397
- className: disabled ? "cursor-not-allowed opacity-40" : "cursor-pointer"
21670
+ className: disabled ? "h-4 w-4 cursor-not-allowed opacity-40" : "h-4 w-4 cursor-pointer"
21398
21671
  }));
21399
- }), /* @__PURE__ */ React25__default.createElement("td", {
21400
- className: "px-3 py-2 text-right"
21401
- }, /* @__PURE__ */ React25__default.createElement("button", {
21402
- type: "button",
21403
- onClick: /* @__PURE__ */ __name(() => toggleEntityRow(entity), "onClick"),
21404
- className: "text-[11px] text-blue-600 hover:underline dark:text-blue-400"
21405
- }, "Toggle")));
21672
+ }));
21406
21673
  })))))))), /* @__PURE__ */ React25__default.createElement(Dialog, {
21407
21674
  open: deleteOpen,
21408
21675
  onOpenChange: setDeleteOpen
@@ -22280,39 +22547,90 @@ var init_VendorTeamPage = __esm({
22280
22547
  __name(VendorTeamPage, "VendorTeamPage");
22281
22548
  }
22282
22549
  });
22283
-
22284
- // src/lib/vendor-profile.ts
22285
- function validateIndiaTaxIds(gstin, pan) {
22286
- if (gstin && !/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin)) {
22287
- return "Invalid GSTIN format (15 characters, e.g. 22AAAAA0000A1Z5)";
22550
+ function splitPhoneAndCountryCode(rawPhone, defaultCode = "+91") {
22551
+ if (!rawPhone || !rawPhone.trim()) {
22552
+ return {
22553
+ countryCode: defaultCode,
22554
+ phoneNumber: ""
22555
+ };
22288
22556
  }
22289
- if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan)) {
22557
+ const trimmed = rawPhone.trim();
22558
+ const match = COUNTRY_PHONE_CODES.find((c) => trimmed.startsWith(c.code));
22559
+ if (match) {
22560
+ return {
22561
+ countryCode: match.code,
22562
+ phoneNumber: trimmed.slice(match.code.length).replace(/\s+/g, "")
22563
+ };
22564
+ }
22565
+ if (trimmed.startsWith("+")) {
22566
+ const spaceIdx = trimmed.indexOf(" ");
22567
+ if (spaceIdx > 0) {
22568
+ return {
22569
+ countryCode: trimmed.slice(0, spaceIdx),
22570
+ phoneNumber: trimmed.slice(spaceIdx + 1).replace(/\s+/g, "")
22571
+ };
22572
+ }
22573
+ }
22574
+ return {
22575
+ countryCode: defaultCode,
22576
+ phoneNumber: trimmed.replace(/\D/g, "")
22577
+ };
22578
+ }
22579
+ function formatPhoneWithCountryCode(countryCode, phoneNumber) {
22580
+ const cleanNum = phoneNumber.replace(/\D/g, "");
22581
+ if (!cleanNum) return null;
22582
+ const cleanCode = countryCode.trim() || "+91";
22583
+ return `${cleanCode}${cleanNum}`;
22584
+ }
22585
+ function validateGstin(gstin, options) {
22586
+ const required = options?.required === true;
22587
+ if (!gstin || !gstin.trim()) {
22588
+ if (required) return "GSTIN number is required";
22589
+ return null;
22590
+ }
22591
+ if (!/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin.trim())) {
22592
+ return "Invalid GSTIN number (15 characters, e.g. 22AAAAA0000A1Z5)";
22593
+ }
22594
+ return null;
22595
+ }
22596
+ function validateIndiaTaxIds(gstin, pan, options) {
22597
+ const gstinErr = validateGstin(gstin, {
22598
+ required: options?.requiredGstin
22599
+ });
22600
+ if (gstinErr) return gstinErr;
22601
+ if (options?.requiredPan && (!pan || !pan.trim())) {
22602
+ return "PAN is required";
22603
+ }
22604
+ if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan.trim())) {
22290
22605
  return "Invalid PAN format (e.g. ABCDE1234F)";
22291
22606
  }
22292
22607
  return null;
22293
22608
  }
22294
22609
  function validateAadhaar(aadhaar) {
22295
22610
  if (!aadhaar) return null;
22296
- if (!/^[0-9]{12}$/.test(aadhaar)) {
22611
+ if (!/^[0-9]{12}$/.test(aadhaar.replace(/\s+/g, ""))) {
22297
22612
  return "Invalid Aadhaar number (12 digits)";
22298
22613
  }
22299
22614
  return null;
22300
22615
  }
22301
22616
  function validatePersonKyc(person, options) {
22302
22617
  const required = options?.required === true;
22303
- if (required && !person.aadhaarNo) return "Aadhaar number is required";
22304
- if (required && !person.panNo) return "PAN is required";
22618
+ if (required && (!person.aadhaarNo || !person.aadhaarNo.trim())) return "Aadhaar number is required";
22619
+ if (required && (!person.panNo || !person.panNo.trim())) return "PAN is required";
22305
22620
  const aadhaarErr = validateAadhaar(person.aadhaarNo ?? null);
22306
22621
  if (aadhaarErr) return aadhaarErr;
22307
- const panErr = validateIndiaTaxIds(null, person.panNo ?? null);
22622
+ const panErr = validateIndiaTaxIds(null, person.panNo ?? null, {
22623
+ requiredPan: required
22624
+ });
22308
22625
  if (panErr) return panErr;
22309
22626
  return null;
22310
22627
  }
22311
22628
  function readOwnerDesignation(metadata) {
22312
- const v = metadata?.ownerDesignation;
22313
- return typeof v === "string" ? v : "";
22629
+ if (!metadata || typeof metadata !== "object") return "";
22630
+ const d = metadata.ownerDesignation;
22631
+ return typeof d === "string" ? d.trim() : "";
22314
22632
  }
22315
- var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES;
22633
+ var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES, COUNTRY_PHONE_CODES;
22316
22634
  var init_vendor_profile = __esm({
22317
22635
  "src/lib/vendor-profile.ts"() {
22318
22636
  VENDOR_REGISTRATION_STATUSES = [
@@ -22359,7 +22677,34 @@ var init_vendor_profile = __esm({
22359
22677
  label: "Other"
22360
22678
  }
22361
22679
  ];
22680
+ COUNTRY_PHONE_CODES = (() => {
22681
+ const list = [];
22682
+ const seenCodes = /* @__PURE__ */ new Set();
22683
+ for (const c of Country.getAllCountries()) {
22684
+ if (!c.phonecode) continue;
22685
+ const rawCode = c.phonecode.replace(/^\+/, "").trim();
22686
+ if (!rawCode) continue;
22687
+ const code = `+${rawCode}`;
22688
+ const key = `${code}-${c.name}`;
22689
+ if (seenCodes.has(key)) continue;
22690
+ seenCodes.add(key);
22691
+ list.push({
22692
+ code,
22693
+ country: c.name,
22694
+ isoCode: c.isoCode,
22695
+ label: `${code} (${c.name})`
22696
+ });
22697
+ }
22698
+ return list.sort((a, b) => {
22699
+ if (a.code === "+91" && b.code !== "+91") return -1;
22700
+ if (b.code === "+91" && a.code !== "+91") return 1;
22701
+ return a.country.localeCompare(b.country);
22702
+ });
22703
+ })();
22704
+ __name(splitPhoneAndCountryCode, "splitPhoneAndCountryCode");
22705
+ __name(formatPhoneWithCountryCode, "formatPhoneWithCountryCode");
22362
22706
  new Set(VENDOR_REGISTRATION_STATUSES.map((s) => s.value));
22707
+ __name(validateGstin, "validateGstin");
22363
22708
  __name(validateIndiaTaxIds, "validateIndiaTaxIds");
22364
22709
  __name(validateAadhaar, "validateAadhaar");
22365
22710
  __name(validatePersonKyc, "validatePersonKyc");
@@ -22962,10 +23307,26 @@ function SubmissionDetailPage({ submissionId }) {
22962
23307
  }
22963
23308
  const formName = submission.form?.name ?? `Form #${submission.formId}`;
22964
23309
  const contact = submission.contact;
22965
- const fieldLabel = /* @__PURE__ */ __name((key) => {
22966
- const field = submission.form?.fields?.find((f) => String(f.id) === key);
22967
- return field?.label ?? key;
22968
- }, "fieldLabel");
23310
+ const resolveFieldLabel = /* @__PURE__ */ __name((key) => {
23311
+ if (!key) return "\u2014";
23312
+ const rawKey = key.trim();
23313
+ const numericIdMatch = rawKey.match(/^(?:field_)?(\d+)$/i);
23314
+ const fieldIdStr = numericIdMatch ? numericIdMatch[1] : null;
23315
+ const fields = submission?.form?.fields ?? [];
23316
+ 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());
23317
+ if (field) {
23318
+ if (field.label && field.label.trim() && !/^\d+$/.test(field.label.trim())) {
23319
+ return field.label.trim();
23320
+ }
23321
+ if (field.name && field.name.trim() && !/^\d+$/.test(field.name.trim())) {
23322
+ return field.name.trim().replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
23323
+ }
23324
+ }
23325
+ if (fieldIdStr !== null) {
23326
+ return `Field #${fieldIdStr}`;
23327
+ }
23328
+ return rawKey.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
23329
+ }, "resolveFieldLabel");
22969
23330
  return /* @__PURE__ */ React.createElement("div", {
22970
23331
  className: "rounded-lg bg-white shadow-md"
22971
23332
  }, /* @__PURE__ */ React.createElement(DetailPageHeader, {
@@ -23024,7 +23385,7 @@ function SubmissionDetailPage({ submissionId }) {
23024
23385
  key
23025
23386
  }, /* @__PURE__ */ React.createElement("td", {
23026
23387
  className: "py-2 px-3 text-gray-600 font-medium"
23027
- }, fieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23388
+ }, resolveFieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23028
23389
  className: "py-2 px-3 text-gray-900 break-words min-w-0"
23029
23390
  }, value === null || value === void 0 ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value)))))))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
23030
23391
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
@@ -24679,7 +25040,7 @@ function OrderPlacementPage({ editOrderId }) {
24679
25040
  productQuery
24680
25041
  ]);
24681
25042
  const recalcPreview = useCallback(async () => {
24682
- const orderLines = lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => ({
25043
+ const orderLines = lines.filter((l) => !l.key.startsWith("reward-")).map((l) => ({
24683
25044
  productId: l.productId,
24684
25045
  quantity: l.quantity
24685
25046
  }));
@@ -24703,19 +25064,22 @@ function OrderPlacementPage({ editOrderId }) {
24703
25064
  body: JSON.stringify({
24704
25065
  orderLines,
24705
25066
  currency,
24706
- discountId: coupons[0]?.discountId ?? null
25067
+ discountId: coupons[0]?.discountId ?? null,
25068
+ discountIds: coupons.map((c) => c.discountId)
24707
25069
  })
24708
25070
  });
24709
25071
  if (!res.ok) throw new Error("Calculate failed");
24710
25072
  const data = await res.json();
24711
25073
  const correctedLines = data.lines.map((cl) => {
24712
25074
  if (cl.productId == null) return cl;
24713
- const cached = productCache.current.get(cl.productId);
25075
+ const pid = Number(cl.productId);
25076
+ const cached = productCache.current.get(pid);
24714
25077
  if (!cl.found && cached) {
24715
- const unitPrice2 = cached.price;
25078
+ const unitPrice2 = Number(cached.price ?? 0);
24716
25079
  const subtotal3 = unitPrice2 * cl.quantity;
24717
25080
  return {
24718
25081
  ...cl,
25082
+ productId: pid,
24719
25083
  found: true,
24720
25084
  unitPrice: unitPrice2,
24721
25085
  subtotal: subtotal3,
@@ -24723,15 +25087,16 @@ function OrderPlacementPage({ editOrderId }) {
24723
25087
  total: subtotal3
24724
25088
  };
24725
25089
  }
24726
- const unitPrice = cached?.price ?? cl.unitPrice;
25090
+ const unitPrice = Number(cached?.price ?? cl.unitPrice ?? 0);
24727
25091
  const subtotal2 = unitPrice * cl.quantity;
24728
- const taxRate = cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal : 0);
24729
- const lineDiscount = cl.subtotal > 0 && subtotal2 > 0 ? cl.discount ?? 0 : 0;
25092
+ const taxRate = Number(cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal * 100 : 0));
25093
+ const lineDiscount = Number(cl.discount ?? 0);
24730
25094
  const discountedBase = Math.max(0, subtotal2 - lineDiscount);
24731
25095
  const tax2 = discountedBase * taxRate / 100;
24732
25096
  const total2 = discountedBase + tax2;
24733
25097
  return {
24734
25098
  ...cl,
25099
+ productId: pid,
24735
25100
  unitPrice,
24736
25101
  subtotal: subtotal2,
24737
25102
  taxRate,
@@ -24739,13 +25104,14 @@ function OrderPlacementPage({ editOrderId }) {
24739
25104
  total: total2
24740
25105
  };
24741
25106
  });
24742
- const rewardDisplayLines = lines.filter((l) => rewardProductIds.current.has(l.productId)).map((l) => {
24743
- const cached = productCache.current.get(l.productId);
25107
+ const rewardDisplayLines = lines.filter((l) => l.key.startsWith("reward-")).map((l) => {
25108
+ const pid = Number(l.productId);
25109
+ const cached = productCache.current.get(pid);
24744
25110
  return {
24745
- productId: l.productId,
25111
+ productId: pid,
24746
25112
  productName: l.label,
24747
25113
  found: true,
24748
- unitPrice: cached?.price ?? 0,
25114
+ unitPrice: Number(cached?.price ?? 0),
24749
25115
  quantity: l.quantity,
24750
25116
  subtotal: 0,
24751
25117
  tax: 0,
@@ -24787,7 +25153,7 @@ function OrderPlacementPage({ editOrderId }) {
24787
25153
  useEffect(() => {
24788
25154
  const automaticCoupons = coupons.filter((c) => c.isAutomatic);
24789
25155
  if (automaticCoupons.length === 0) return;
24790
- const nonRewardLines = lines.filter((l) => !rewardProductIds.current.has(l.productId));
25156
+ const nonRewardLines = lines.filter((l) => !l.key.startsWith("reward-"));
24791
25157
  if (nonRewardLines.length === 0) {
24792
25158
  const autoIds = new Set(automaticCoupons.map((c) => c.discountId));
24793
25159
  const rewardPids = new Set(automaticCoupons.flatMap((c) => c.rewardLines.map((r) => r.productId)));
@@ -24867,7 +25233,7 @@ function OrderPlacementPage({ editOrderId }) {
24867
25233
  cancelled = true;
24868
25234
  };
24869
25235
  }, [
24870
- lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => `${l.productId}:${l.quantity}`).join(","),
25236
+ lines.filter((l) => !l.key.startsWith("reward-")).map((l) => `${l.productId}:${l.quantity}`).join(","),
24871
25237
  coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
24872
25238
  ]);
24873
25239
  function handleRemoveCoupon(couponCode) {
@@ -24989,7 +25355,10 @@ function OrderPlacementPage({ editOrderId }) {
24989
25355
  const calcByProductId = useMemo(() => {
24990
25356
  const m = /* @__PURE__ */ new Map();
24991
25357
  for (const l of preview?.lines ?? []) {
24992
- if (l.productId != null && l.found) m.set(l.productId, l);
25358
+ if (l.productId != null && l.found) {
25359
+ const pid = Number(l.productId);
25360
+ if (Number.isFinite(pid)) m.set(pid, l);
25361
+ }
24993
25362
  }
24994
25363
  return m;
24995
25364
  }, [
@@ -25011,13 +25380,13 @@ function OrderPlacementPage({ editOrderId }) {
25011
25380
  }
25012
25381
  const unknown = preview?.lines.filter((l) => !l.found) ?? [];
25013
25382
  if (unknown.length > 0) {
25014
- toast.error("Some products are missing or unavailable. Remove invalid lines and try again.");
25383
+ toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
25015
25384
  return;
25016
25385
  }
25017
25386
  setSubmitting(true);
25387
+ let resolvedContactId = effectiveContactId;
25018
25388
  try {
25019
- let resolvedContactId = placeForSomeoneElse ? subContactId : null;
25020
- if (!resolvedContactId && effectiveEmail) {
25389
+ if (resolvedContactId == null && effectiveEmail) {
25021
25390
  try {
25022
25391
  const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
25023
25392
  if (res2.ok) {
@@ -25039,12 +25408,12 @@ function OrderPlacementPage({ editOrderId }) {
25039
25408
  billingAddress,
25040
25409
  shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
25041
25410
  orderLines: lines.map((l) => {
25042
- const isReward = rewardProductIds.current.has(l.productId);
25043
- const calc = preview?.lines.find((pl) => pl.productId === l.productId);
25411
+ const isReward = l.key.startsWith("reward-");
25412
+ const calc = preview?.lines.find((pl) => Number(pl.productId) === Number(l.productId));
25044
25413
  return {
25045
25414
  productId: l.productId,
25046
25415
  quantity: l.quantity,
25047
- unitPrice: isReward ? 0 : calc?.unitPrice ?? productCache.current.get(l.productId)?.price ?? 0
25416
+ unitPrice: isReward ? 0 : calc?.unitPrice ?? Number(productCache.current.get(l.productId)?.price ?? 0)
25048
25417
  };
25049
25418
  })
25050
25419
  };
@@ -30020,8 +30389,8 @@ function ProductEditPage({ productId }) {
30020
30389
  const [price, setPrice] = useState(0);
30021
30390
  const [defaultPriceStr, setDefaultPriceStr] = useState("");
30022
30391
  const [pricingConfig, setPricingConfig] = useState(DEFAULT_PRICING_CONFIG);
30023
- const [compareAtPrice, setCompareAtPrice] = useState(0);
30024
- const [quantity, setQuantity] = useState(1);
30392
+ const [compareAtPrice, setCompareAtPrice] = useState("");
30393
+ const [quantity, setQuantity] = useState("1");
30025
30394
  const [status, setStatus] = useState("draft");
30026
30395
  const [approvalStatus, setApprovalStatus] = useState("pending");
30027
30396
  const [rejectionReason, setRejectionReason] = useState("");
@@ -30242,8 +30611,8 @@ function ProductEditPage({ productId }) {
30242
30611
  setDefaultPriceStr(product.price != null && Number.isFinite(Number(product.price)) ? String(product.price) : "");
30243
30612
  setPrice(product.price != null ? Number(product.price) : 0);
30244
30613
  const rawCompare = product.compareAtPrice != null ? Number(product.compareAtPrice) : 0;
30245
- setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
30246
- setQuantity(product.quantity ?? 1);
30614
+ setCompareAtPrice(rawCompare != null && Number.isFinite(rawCompare) ? String(rawCompare) : "");
30615
+ setQuantity(product.quantity != null ? String(product.quantity) : "1");
30247
30616
  setStatus(product.status ?? "draft");
30248
30617
  setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
30249
30618
  setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
@@ -30410,22 +30779,6 @@ function ProductEditPage({ productId }) {
30410
30779
  return pricingConfig.defaultCurrency || "INR";
30411
30780
  }
30412
30781
  })();
30413
- const handleNumberChange = /* @__PURE__ */ __name((setter, options) => (e) => {
30414
- const value = e.target.value;
30415
- if (value === "") {
30416
- setter(0);
30417
- return;
30418
- }
30419
- let num = options?.integer ? Number.parseInt(value, 10) : Number(value);
30420
- if (Number.isNaN(num)) {
30421
- setter(0);
30422
- return;
30423
- }
30424
- if (options?.min !== void 0) {
30425
- num = Math.max(options.min, num);
30426
- }
30427
- setter(num);
30428
- }, "handleNumberChange");
30429
30782
  const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
30430
30783
  approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
30431
30784
  setRejectDraft(rejectionReason);
@@ -30498,12 +30851,13 @@ function ProductEditPage({ productId }) {
30498
30851
  return;
30499
30852
  }
30500
30853
  }
30501
- const quantityErrors = validateProductQuantity(quantity, hasVariants, parsedVariants);
30854
+ const quantityValue = quantity === "" ? 0 : Number(quantity);
30855
+ const quantityErrors = validateProductQuantity(quantityValue, hasVariants, parsedVariants);
30502
30856
  if (quantityErrors.length) {
30503
30857
  setErrors(quantityErrors);
30504
30858
  return;
30505
30859
  }
30506
- const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantity;
30860
+ const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantityValue;
30507
30861
  setSaving(true);
30508
30862
  try {
30509
30863
  const compareAtPriceValue = compareAtPrice ? Math.round(Number(compareAtPrice) * 100) / 100 : null;
@@ -31117,9 +31471,8 @@ function ProductEditPage({ productId }) {
31117
31471
  type: "number",
31118
31472
  value: compareAtPrice,
31119
31473
  className: inputCls3,
31120
- onChange: handleNumberChange(setCompareAtPrice, {
31121
- min: 0
31122
- })
31474
+ min: 0,
31475
+ onChange: /* @__PURE__ */ __name((e) => setCompareAtPrice(e.target.value), "onChange")
31123
31476
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
31124
31477
  className: labelCls3
31125
31478
  }, "Contact form"), /* @__PURE__ */ React.createElement("select", {
@@ -31142,10 +31495,7 @@ function ProductEditPage({ productId }) {
31142
31495
  min: 0,
31143
31496
  step: 1,
31144
31497
  value: hasVariants ? productQuantityFromVariants(variantsFromForm(variantRows, pricingConfig.defaultCurrency)) : quantity,
31145
- onChange: handleNumberChange(setQuantity, {
31146
- min: 0,
31147
- integer: true
31148
- }),
31498
+ onChange: /* @__PURE__ */ __name((e) => setQuantity(e.target.value), "onChange"),
31149
31499
  className: `${inputCls3}${hasVariants ? " bg-gray-100" : ""}`,
31150
31500
  required: !hasVariants,
31151
31501
  readOnly: hasVariants,
@@ -31222,8 +31572,18 @@ function ProductEditPage({ productId }) {
31222
31572
  value: row.taxId === "" ? "" : String(row.taxId),
31223
31573
  onChange: /* @__PURE__ */ __name((e) => {
31224
31574
  const v = e.target.value;
31575
+ if (v === "") {
31576
+ setTaxRow(i, {
31577
+ taxId: "",
31578
+ rate: ""
31579
+ });
31580
+ return;
31581
+ }
31582
+ const taxId = Number(v);
31583
+ const selectedTax = taxMasterList.find((t) => t.id === taxId);
31225
31584
  setTaxRow(i, {
31226
- taxId: v === "" ? "" : Number(v)
31585
+ taxId,
31586
+ rate: selectedTax?.rate != null ? String(selectedTax.rate) : ""
31227
31587
  });
31228
31588
  }, "onChange"),
31229
31589
  className: inputCls3
@@ -33048,6 +33408,35 @@ function EventEditPage({ eventId }) {
33048
33408
  const [saving, setSaving] = useState(false);
33049
33409
  const [errors, setErrors] = useState([]);
33050
33410
  const [activeTab, setActiveTab] = useState("basic");
33411
+ const eventTabs = [
33412
+ {
33413
+ key: "basic",
33414
+ label: "Basic info"
33415
+ },
33416
+ {
33417
+ key: "venue",
33418
+ label: "Venue"
33419
+ },
33420
+ {
33421
+ key: "datetime",
33422
+ label: "Date & time"
33423
+ },
33424
+ {
33425
+ key: "settings",
33426
+ label: "Settings"
33427
+ }
33428
+ ];
33429
+ const activeTabIndex = eventTabs.findIndex((tab) => tab.key === activeTab);
33430
+ const goToPreviousTab = /* @__PURE__ */ __name(() => {
33431
+ if (activeTabIndex > 0) {
33432
+ setActiveTab(eventTabs[activeTabIndex - 1].key);
33433
+ }
33434
+ }, "goToPreviousTab");
33435
+ const goToNextTab = /* @__PURE__ */ __name(() => {
33436
+ if (activeTabIndex < eventTabs.length - 1) {
33437
+ setActiveTab(eventTabs[activeTabIndex + 1].key);
33438
+ }
33439
+ }, "goToNextTab");
33051
33440
  const [name, setName] = useState("");
33052
33441
  const [slug, setSlug] = useState("");
33053
33442
  const [description, setDescription] = useState("");
@@ -33867,7 +34256,19 @@ function EventEditPage({ eventId }) {
33867
34256
  [key]: value
33868
34257
  })), "onChange")
33869
34258
  }))))
33870
- }), showEventProducts ? /* @__PURE__ */ React.createElement("div", {
34259
+ }), /* @__PURE__ */ React.createElement("div", {
34260
+ className: "flex items-center justify-between border-t border-gray-200 px-4 py-4 sm:px-6"
34261
+ }, /* @__PURE__ */ React.createElement("button", {
34262
+ type: "button",
34263
+ onClick: goToPreviousTab,
34264
+ disabled: activeTabIndex === 0,
34265
+ 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"
34266
+ }, "Previous"), /* @__PURE__ */ React.createElement("button", {
34267
+ type: "button",
34268
+ onClick: goToNextTab,
34269
+ disabled: activeTabIndex === eventTabs.length - 1,
34270
+ 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"
34271
+ }, "Next")), showEventProducts ? /* @__PURE__ */ React.createElement("div", {
33871
34272
  className: "border-t border-gray-200 px-4 py-4 sm:px-6"
33872
34273
  }, /* @__PURE__ */ React.createElement(EventProductsSection, {
33873
34274
  eventId: eventRecordId,
@@ -34212,6 +34613,18 @@ function ComboEditPage({ comboId }) {
34212
34613
  ]);
34213
34614
  return;
34214
34615
  }
34616
+ if (!startsAt) {
34617
+ setErrors([
34618
+ "Starts at is required"
34619
+ ]);
34620
+ return;
34621
+ }
34622
+ if (!endsAt) {
34623
+ setErrors([
34624
+ "Ends at is required"
34625
+ ]);
34626
+ return;
34627
+ }
34215
34628
  if (!priceStr.trim()) {
34216
34629
  setErrors([
34217
34630
  `Price (${defaultCurrency}) is required`
@@ -34501,18 +34914,20 @@ function ComboEditPage({ comboId }) {
34501
34914
  className: "grid grid-cols-2 gap-4"
34502
34915
  }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
34503
34916
  className: "block text-xs font-medium text-gray-600 mb-1"
34504
- }, "Starts at"), /* @__PURE__ */ React.createElement("input", {
34917
+ }, "Starts at *"), /* @__PURE__ */ React.createElement("input", {
34505
34918
  type: "datetime-local",
34506
34919
  value: startsAt,
34507
34920
  onChange: /* @__PURE__ */ __name((e) => setStartsAt(e.target.value), "onChange"),
34508
- className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
34921
+ className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
34922
+ required: true
34509
34923
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
34510
34924
  className: "block text-xs font-medium text-gray-600 mb-1"
34511
- }, "Ends at"), /* @__PURE__ */ React.createElement("input", {
34925
+ }, "Ends at *"), /* @__PURE__ */ React.createElement("input", {
34512
34926
  type: "datetime-local",
34513
34927
  value: endsAt,
34514
34928
  onChange: /* @__PURE__ */ __name((e) => setEndsAt(e.target.value), "onChange"),
34515
- className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
34929
+ className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
34930
+ required: true
34516
34931
  })))))),
34517
34932
  sidebar: /* @__PURE__ */ React.createElement(React.Fragment, null)
34518
34933
  }));
@@ -34598,7 +35013,8 @@ function VendorEditPage({ vendorId }) {
34598
35013
  const [website, setWebsite] = useState("");
34599
35014
  const [logo, setLogo] = useState("");
34600
35015
  const [email, setEmail] = useState("");
34601
- const [phone, setPhone] = useState("");
35016
+ const [storePhoneCode, setStorePhoneCode] = useState("+91");
35017
+ const [storePhoneNum, setStorePhoneNum] = useState("");
34602
35018
  const [addressLine1, setAddressLine1] = useState("");
34603
35019
  const [addressLine2, setAddressLine2] = useState("");
34604
35020
  const [city, setCity] = useState("");
@@ -34612,7 +35028,8 @@ function VendorEditPage({ vendorId }) {
34612
35028
  const [metadata, setMetadata] = useState(null);
34613
35029
  const [ownerName, setOwnerName] = useState("");
34614
35030
  const [ownerEmail, setOwnerEmail] = useState("");
34615
- const [ownerPhone, setOwnerPhone] = useState("");
35031
+ const [ownerPhoneCode, setOwnerPhoneCode] = useState("+91");
35032
+ const [ownerPhoneNum, setOwnerPhoneNum] = useState("");
34616
35033
  const [ownerDesignation, setOwnerDesignation] = useState("");
34617
35034
  const [ownerAadhaarNo, setOwnerAadhaarNo] = useState("");
34618
35035
  const [ownerPanNo, setOwnerPanNo] = useState("");
@@ -34636,7 +35053,7 @@ function VendorEditPage({ vendorId }) {
34636
35053
  website: website.trim() || null,
34637
35054
  logo: logo.trim() || null,
34638
35055
  email: email.trim() || null,
34639
- phone: phone.trim() || null,
35056
+ phone: formatPhoneWithCountryCode(storePhoneCode, storePhoneNum),
34640
35057
  addressLine1: addressLine1.trim() || null,
34641
35058
  addressLine2: addressLine2.trim() || null,
34642
35059
  city: city.trim() || null,
@@ -34664,7 +35081,9 @@ function VendorEditPage({ vendorId }) {
34664
35081
  setWebsite(data.website ?? "");
34665
35082
  setLogo(data.logo ?? "");
34666
35083
  setEmail(data.email ?? "");
34667
- setPhone(data.phone ?? "");
35084
+ const sPhone = splitPhoneAndCountryCode(data.phone);
35085
+ setStorePhoneCode(sPhone.countryCode);
35086
+ setStorePhoneNum(sPhone.phoneNumber);
34668
35087
  setAddressLine1(data.addressLine1 ?? "");
34669
35088
  setAddressLine2(data.addressLine2 ?? "");
34670
35089
  setCity(data.city ?? "");
@@ -34686,7 +35105,9 @@ function VendorEditPage({ vendorId }) {
34686
35105
  if (!cancelled) {
34687
35106
  setOwnerName(user.name ?? "");
34688
35107
  setOwnerEmail(user.email ?? "");
34689
- setOwnerPhone(user.phone ?? "");
35108
+ const oPhone = splitPhoneAndCountryCode(user.phone);
35109
+ setOwnerPhoneCode(oPhone.countryCode);
35110
+ setOwnerPhoneNum(oPhone.phoneNumber);
34690
35111
  }
34691
35112
  }
34692
35113
  } else if (!cancelled) {
@@ -34833,7 +35254,10 @@ function VendorEditPage({ vendorId }) {
34833
35254
  setSaving(false);
34834
35255
  return;
34835
35256
  }
34836
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35257
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
35258
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35259
+ required: true
35260
+ });
34837
35261
  if (taxError) {
34838
35262
  setErrors([
34839
35263
  taxError
@@ -34875,7 +35299,7 @@ function VendorEditPage({ vendorId }) {
34875
35299
  user: {
34876
35300
  name: ownerName.trim(),
34877
35301
  email: ownerEmail.trim(),
34878
- phone: ownerPhone.trim() || void 0,
35302
+ phone: formattedOwnerPhone || void 0,
34879
35303
  designation: ownerDesignation.trim() || void 0,
34880
35304
  aadhaarNo: ownerAadhaarNo.trim(),
34881
35305
  panNo: ownerPanNo.trim().toUpperCase(),
@@ -34939,7 +35363,9 @@ function VendorEditPage({ vendorId }) {
34939
35363
  setSaving(false);
34940
35364
  return;
34941
35365
  }
34942
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35366
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35367
+ required: false
35368
+ });
34943
35369
  if (taxError) {
34944
35370
  setErrors([
34945
35371
  taxError
@@ -34975,6 +35401,7 @@ function VendorEditPage({ vendorId }) {
34975
35401
  return;
34976
35402
  }
34977
35403
  if (ownerUserId != null) {
35404
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
34978
35405
  const userRes = await fetch(`/api/users/${ownerUserId}`, {
34979
35406
  method: "PUT",
34980
35407
  headers: {
@@ -34983,7 +35410,7 @@ function VendorEditPage({ vendorId }) {
34983
35410
  body: JSON.stringify({
34984
35411
  name: ownerName.trim(),
34985
35412
  email: ownerEmail.trim(),
34986
- phone: ownerPhone.trim() || null
35413
+ phone: formattedOwnerPhone || null
34987
35414
  })
34988
35415
  });
34989
35416
  if (!userRes.ok) {
@@ -35117,12 +35544,23 @@ function VendorEditPage({ vendorId }) {
35117
35544
  className: `mt-1 ${fieldClass}`
35118
35545
  })), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
35119
35546
  htmlFor: "vendorPhone"
35120
- }, "Store phone"), /* @__PURE__ */ React25__default.createElement(Input, {
35547
+ }, "Store phone"), /* @__PURE__ */ React25__default.createElement("div", {
35548
+ className: "flex gap-2 mt-1"
35549
+ }, /* @__PURE__ */ React25__default.createElement("select", {
35550
+ value: storePhoneCode,
35551
+ onChange: /* @__PURE__ */ __name((e) => setStorePhoneCode(e.target.value), "onChange"),
35552
+ 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"
35553
+ }, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__default.createElement("option", {
35554
+ key: c.code,
35555
+ value: c.code
35556
+ }, c.label))), /* @__PURE__ */ React25__default.createElement(Input, {
35121
35557
  id: "vendorPhone",
35122
- value: phone,
35123
- onChange: /* @__PURE__ */ __name((e) => setPhone(e.target.value), "onChange"),
35124
- className: `mt-1 ${fieldClass}`
35125
- })))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35558
+ type: "tel",
35559
+ placeholder: "9876543210",
35560
+ value: storePhoneNum,
35561
+ onChange: /* @__PURE__ */ __name((e) => setStorePhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
35562
+ className: `flex-1 ${fieldClass}`
35563
+ }))))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35126
35564
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35127
35565
  }, "Store address"), /* @__PURE__ */ React25__default.createElement("div", {
35128
35566
  className: sectionCls5
@@ -35175,12 +35613,15 @@ function VendorEditPage({ vendorId }) {
35175
35613
  className: `mt-1 ${fieldClass}`
35176
35614
  }))))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35177
35615
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35178
- }, "Tax (India)"), /* @__PURE__ */ React25__default.createElement("div", {
35616
+ }, "Tax"), /* @__PURE__ */ React25__default.createElement("div", {
35179
35617
  className: sectionCls5
35180
35618
  }, /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
35181
- htmlFor: "gstin"
35619
+ htmlFor: "gstin",
35620
+ required: create
35182
35621
  }, "GSTIN"), /* @__PURE__ */ React25__default.createElement(Input, {
35183
35622
  id: "gstin",
35623
+ required: create,
35624
+ maxLength: 15,
35184
35625
  value: gstin,
35185
35626
  onChange: /* @__PURE__ */ __name((e) => setGstin(e.target.value.toUpperCase()), "onChange"),
35186
35627
  placeholder: "22AAAAA0000A1Z5",
@@ -35210,12 +35651,23 @@ function VendorEditPage({ vendorId }) {
35210
35651
  className: `mt-1 ${fieldClass}`
35211
35652
  })), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
35212
35653
  htmlFor: "ownerPhone"
35213
- }, "Phone"), /* @__PURE__ */ React25__default.createElement(Input, {
35654
+ }, "Phone"), /* @__PURE__ */ React25__default.createElement("div", {
35655
+ className: "flex gap-2 mt-1"
35656
+ }, /* @__PURE__ */ React25__default.createElement("select", {
35657
+ value: ownerPhoneCode,
35658
+ onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneCode(e.target.value), "onChange"),
35659
+ 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"
35660
+ }, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__default.createElement("option", {
35661
+ key: c.code,
35662
+ value: c.code
35663
+ }, c.label))), /* @__PURE__ */ React25__default.createElement(Input, {
35214
35664
  id: "ownerPhone",
35215
- value: ownerPhone,
35216
- onChange: /* @__PURE__ */ __name((e) => setOwnerPhone(e.target.value), "onChange"),
35217
- className: `mt-1 ${fieldClass}`
35218
- })), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
35665
+ type: "tel",
35666
+ placeholder: "9876543210",
35667
+ value: ownerPhoneNum,
35668
+ onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
35669
+ className: `flex-1 ${fieldClass}`
35670
+ }))), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
35219
35671
  htmlFor: "ownerDesignation"
35220
35672
  }, "Designation"), /* @__PURE__ */ React25__default.createElement(Input, {
35221
35673
  id: "ownerDesignation",
@@ -35300,7 +35752,7 @@ function VendorEditPage({ vendorId }) {
35300
35752
  htmlFor: "termsAccepted",
35301
35753
  className: "font-normal cursor-pointer text-sm leading-snug"
35302
35754
  }, "I confirm I have read and accepted the terms and conditions for selling on this platform."))))),
35303
- sidebar: /* @__PURE__ */ React25__default.createElement(React25__default.Fragment, null, !create && /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35755
+ sidebar: create ? void 0 : /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35304
35756
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35305
35757
  }, "Status"), /* @__PURE__ */ React25__default.createElement("div", {
35306
35758
  className: sectionCls5
@@ -35335,18 +35787,7 @@ function VendorEditPage({ vendorId }) {
35335
35787
  readOnly: true,
35336
35788
  value: active ? "Yes" : "No",
35337
35789
  className: `mt-1 ${fieldClass} bg-gray-100`
35338
- })))), create ? /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35339
- className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
35340
- }, "Owner access"), /* @__PURE__ */ React25__default.createElement("div", {
35341
- className: sectionCls5
35342
- }, /* @__PURE__ */ React25__default.createElement("p", {
35343
- className: "text-sm text-gray-600"
35344
- }, "Creates the vendor and an invite for the owner. After create, you can send the invite email or copy the invite link."), /* @__PURE__ */ React25__default.createElement(Button, {
35345
- type: "button",
35346
- disabled: saving,
35347
- onClick: handleCreate,
35348
- className: "w-full"
35349
- }, saving ? "Creating\u2026" : "Create vendor"))) : void 0)
35790
+ }))))
35350
35791
  }), /* @__PURE__ */ React25__default.createElement(Dialog, {
35351
35792
  open: inviteDialog != null,
35352
35793
  onOpenChange: /* @__PURE__ */ __name((open) => {
@@ -36941,7 +37382,7 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
36941
37382
  value: "minAmount"
36942
37383
  }, "Minimum order amount"), /* @__PURE__ */ React.createElement(SelectItem, {
36943
37384
  value: "minQuantity"
36944
- }, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
37385
+ }, "Minimum Cart Quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
36945
37386
  value: "productMinQuantity"
36946
37387
  }, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
36947
37388
  value: "events"
@@ -38396,8 +38837,8 @@ function ContactDetailPage({ contactId }) {
38396
38837
  setAddAddressError("Select country, state, and city from the lists.");
38397
38838
  return;
38398
38839
  }
38399
- const { Country: Country2, State: State2 } = await import('country-state-city');
38400
- const countryName = Country2.getCountryByCode(addressGeo.countryIso)?.name ?? "";
38840
+ const { Country: Country3, State: State2 } = await import('country-state-city');
38841
+ const countryName = Country3.getCountryByCode(addressGeo.countryIso)?.name ?? "";
38401
38842
  const stateName = State2.getStatesOfCountry(addressGeo.countryIso).find((s) => s.isoCode === addressGeo.stateIso)?.name ?? "";
38402
38843
  const payload = {
38403
38844
  contactId: Number(contactId),