@infuro/cms-core 1.0.47 → 1.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +30 -16
  2. package/dist/admin.cjs +2012 -437
  3. package/dist/admin.d.cts +13 -1
  4. package/dist/admin.d.ts +13 -1
  5. package/dist/admin.js +2013 -438
  6. package/dist/api.cjs +37 -37
  7. package/dist/api.js +5 -5
  8. package/dist/auth.cjs +52 -23
  9. package/dist/auth.d.cts +101 -2
  10. package/dist/auth.d.ts +101 -2
  11. package/dist/auth.js +3 -2
  12. package/dist/chunk-3EOM4V2M.cjs +452 -0
  13. package/dist/{chunk-2HU5R2JE.js → chunk-AYERBA7I.js} +1 -7
  14. package/dist/{chunk-YXH2UUEZ.js → chunk-CTXBYO2J.js} +290 -59
  15. package/dist/{chunk-LMJ7RKPF.cjs → chunk-JN4AFHZ4.cjs} +311 -59
  16. package/dist/{chunk-JE22VP6S.js → chunk-MXIWUFBP.js} +1 -1
  17. package/dist/{chunk-W42UZLQO.js → chunk-NUXR7BUG.js} +2819 -2597
  18. package/dist/chunk-RK5ETF2I.js +434 -0
  19. package/dist/{chunk-4PMK3RNA.cjs → chunk-TJ2MIQUT.cjs} +1 -7
  20. package/dist/{chunk-KOTXDSQB.cjs → chunk-UQWZUZ5X.cjs} +1 -1
  21. package/dist/{chunk-YC4NUZCS.js → chunk-WRKV6MHB.js} +414 -2
  22. package/dist/{chunk-XUCKZPML.cjs → chunk-YOW457RA.cjs} +2872 -2657
  23. package/dist/{chunk-Q3HQEM4R.cjs → chunk-YV5PK4JW.cjs} +421 -1
  24. package/dist/cli.cjs +13 -22
  25. package/dist/cli.js +13 -22
  26. package/dist/{emit-order-notification-trigger-6XX7LSC4.js → emit-order-notification-trigger-HZQPPSFB.js} +1 -1
  27. package/dist/{emit-order-notification-trigger-NASG7ZEO.cjs → emit-order-notification-trigger-NR3VN6C6.cjs} +3 -3
  28. package/dist/{event-order-defaults-7Z3UZOEH.cjs → event-order-defaults-DU7V3YND.cjs} +9 -9
  29. package/dist/{event-order-defaults-XQ3IDPD7.js → event-order-defaults-H4RT7NMR.js} +1 -1
  30. package/dist/index.cjs +379 -309
  31. package/dist/index.d.cts +136 -6
  32. package/dist/index.d.ts +136 -6
  33. package/dist/index.js +58 -24
  34. package/dist/migrations/1782400000000-CreateUserDeviceTokens.ts +36 -0
  35. package/dist/migrations/1782500000000-AddPushToOrderNotificationBindingsChannelEnum.ts +15 -0
  36. package/dist/{order-notification-dispatcher-6WNG24NY.js → order-notification-dispatcher-AT4IFAGL.js} +1 -1
  37. package/dist/{order-notification-dispatcher-6XSU3AR7.cjs → order-notification-dispatcher-YBAWDOIO.cjs} +7 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-VUQFARRT.cjs +0 -182
  40. package/dist/chunk-ZF2RQWXB.js +0 -171
package/dist/admin.js CHANGED
@@ -10,7 +10,7 @@ import { Slot } from '@radix-ui/react-slot';
10
10
  import { cva } from 'class-variance-authority';
11
11
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
12
12
  import * as LucideIcons from 'lucide-react';
13
- import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, ShieldCheck, QrCode, Car, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, Smartphone, HardDrive, Folder, Film } from 'lucide-react';
13
+ import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, Smartphone, ShieldCheck, QrCode, Car, Send, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, HardDrive, Folder, Film } from 'lucide-react';
14
14
  import Image from 'next/image';
15
15
  import Link2 from 'next/link';
16
16
  import { usePathname, useSearchParams, useRouter } from 'next/navigation';
@@ -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.47" ;
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();
@@ -1146,6 +1566,15 @@ var init_builtin_plugin_descriptors = __esm({
1146
1566
  enabled: true,
1147
1567
  icon: "event_notifications",
1148
1568
  settingsGroup: "event_notifications"
1569
+ },
1570
+ {
1571
+ name: "auth_providers",
1572
+ label: "Authentication Provider",
1573
+ version: "1.0.0",
1574
+ description: "Enable Google sign-in for admin and vendor users (existing accounts only).",
1575
+ enabled: true,
1576
+ icon: "auth_providers",
1577
+ settingsGroup: "auth_providers"
1149
1578
  }
1150
1579
  ];
1151
1580
  }
@@ -1471,7 +1900,15 @@ function useEventsSettings() {
1471
1900
  requireEventApproval
1472
1901
  };
1473
1902
  }
1474
- 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
+ }, []);
1475
1912
  const resolvedTheme = useResolvedTheme(theme, themeRegistry);
1476
1913
  const { storeEnabled, currency } = useStoreEnabled();
1477
1914
  const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
@@ -1492,6 +1929,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1492
1929
  customCrudConfigs,
1493
1930
  categoryRelatedProductLabels,
1494
1931
  renderOrderDetailFooter,
1932
+ extraNotificationVariables,
1495
1933
  theme: resolvedTheme,
1496
1934
  themeRegistry,
1497
1935
  pluginDescriptors: mergedPluginDescriptors,
@@ -1510,6 +1948,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
1510
1948
  customCrudConfigs,
1511
1949
  categoryRelatedProductLabels,
1512
1950
  renderOrderDetailFooter,
1951
+ extraNotificationVariables,
1513
1952
  resolvedTheme,
1514
1953
  themeRegistry,
1515
1954
  mergedPluginDescriptors,
@@ -4366,6 +4805,8 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4366
4805
  const hasLoadedRef = useRef(false);
4367
4806
  const isMobile = useIsMobile();
4368
4807
  const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
4808
+ const { data: session } = useSession();
4809
+ const sessionUser = session?.user;
4369
4810
  const listColumns = useMemo(() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [], [
4370
4811
  columns
4371
4812
  ]);
@@ -4906,6 +5347,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
4906
5347
  withListFrom
4907
5348
  ]);
4908
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");
4909
5353
  const dedicatedRecordEditHref = /* @__PURE__ */ __name((recordId) => addEditPage && (resourceName === "orders" || resourceName === "events") ? withListFrom(`${addEditPageUrl}/${recordId}/edit`) : addEditPage ? withListFrom(`${addEditPageUrl}/${recordId}`) : "", "dedicatedRecordEditHref");
4910
5354
  const showDedicatedDuplicate = addEditPage && !CRUD_NO_DUPLICATE_RESOURCES.has(resourceName);
4911
5355
  const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
@@ -5065,15 +5509,15 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5065
5509
  onClick: /* @__PURE__ */ __name(() => setBulkDialogOpen(true), "onClick"),
5066
5510
  variant: "outline",
5067
5511
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5068
- }, /* @__PURE__ */ React.createElement(Upload, {
5512
+ }, /* @__PURE__ */ React.createElement(Download, {
5069
5513
  className: "h-3.5 w-3.5 mr-1"
5070
5514
  }), "Import"), /* @__PURE__ */ React.createElement(Button, {
5071
5515
  onClick: handleExport,
5072
5516
  variant: "outline",
5073
5517
  className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
5074
- }, /* @__PURE__ */ React.createElement(Download, {
5518
+ }, /* @__PURE__ */ React.createElement(Upload, {
5075
5519
  className: "h-3.5 w-3.5 mr-1"
5076
- }), "Export"), !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5520
+ }), "Export"), canCreate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
5077
5521
  onClick: /* @__PURE__ */ __name(() => {
5078
5522
  setEditingItem(null);
5079
5523
  setDuplicateSeed(null);
@@ -5082,7 +5526,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5082
5526
  className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8"
5083
5527
  }, /* @__PURE__ */ React.createElement(Plus, {
5084
5528
  className: "h-3.5 w-3.5"
5085
- }), "Add"), addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
5529
+ }), "Add"), canCreate && addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
5086
5530
  href: dedicatedCreateHref,
5087
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"
5088
5532
  }, /* @__PURE__ */ React.createElement(Plus, {
@@ -5241,7 +5685,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5241
5685
  onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
5242
5686
  }, /* @__PURE__ */ React.createElement("div", {
5243
5687
  className: "flex items-center justify-center gap-1"
5244
- }, 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, {
5245
5689
  variant: "outline",
5246
5690
  size: "icon",
5247
5691
  className: "h-7 w-7",
@@ -5251,7 +5695,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5251
5695
  className: "h-3.5 w-3.5"
5252
5696
  }), /* @__PURE__ */ React.createElement("span", {
5253
5697
  className: "sr-only"
5254
- }, "Edit")), addEditPage && /* @__PURE__ */ React.createElement(Button, {
5698
+ }, "Edit")), canUpdate && addEditPage && /* @__PURE__ */ React.createElement(Button, {
5255
5699
  variant: "outline",
5256
5700
  size: "icon",
5257
5701
  className: "h-7 w-7",
@@ -5261,7 +5705,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5261
5705
  className: "h-3.5 w-3.5"
5262
5706
  }), /* @__PURE__ */ React.createElement("span", {
5263
5707
  className: "sr-only"
5264
- }, "Edit")), showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5708
+ }, "Edit")), canCreate && showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
5265
5709
  variant: "outline",
5266
5710
  size: "icon",
5267
5711
  className: "h-7 w-7",
@@ -5287,7 +5731,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
5287
5731
  className: "h-3.5 w-3.5"
5288
5732
  }), /* @__PURE__ */ React.createElement("span", {
5289
5733
  className: "sr-only"
5290
- }, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
5734
+ }, "Resend invite")), canDelete && /* @__PURE__ */ React.createElement(Button, {
5291
5735
  variant: "outline",
5292
5736
  size: "icon",
5293
5737
  className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
@@ -5425,6 +5869,7 @@ var init_CRUD = __esm({
5425
5869
  init_dropdown_menu();
5426
5870
  init_utils();
5427
5871
  init_use_mobile();
5872
+ init_rbac_debug();
5428
5873
  init_BulkUploadDialog();
5429
5874
  init_admin_list_return_url();
5430
5875
  init_build_crud_list_filters_from_columns();
@@ -9140,28 +9585,75 @@ var init_AuthPageLayout = __esm({
9140
9585
  }
9141
9586
  });
9142
9587
 
9588
+ // src/auth/auth-providers-settings.ts
9589
+ function parseEnabled(raw) {
9590
+ if (typeof raw === "boolean") return raw;
9591
+ if (typeof raw === "number") return raw !== 0;
9592
+ if (typeof raw === "string") {
9593
+ const n = raw.trim().toLowerCase();
9594
+ return n === "true" || n === "1" || n === "yes" || n === "on";
9595
+ }
9596
+ return false;
9597
+ }
9598
+ function isGoogleAuthPubliclyEnabled(settings) {
9599
+ if (!settings) return false;
9600
+ if (settings.googleEnabled !== void 0 && settings.googleEnabled !== "") {
9601
+ return parseEnabled(settings.googleEnabled);
9602
+ }
9603
+ return parseEnabled(settings.enabled);
9604
+ }
9605
+ var init_auth_providers_settings = __esm({
9606
+ "src/auth/auth-providers-settings.ts"() {
9607
+ init_auth_debug();
9608
+ __name(parseEnabled, "parseEnabled");
9609
+ __name(isGoogleAuthPubliclyEnabled, "isGoogleAuthPubliclyEnabled");
9610
+ }
9611
+ });
9612
+
9143
9613
  // src/admin/pages/SignInPage.tsx
9144
9614
  var SignInPage_exports = {};
9145
9615
  __export(SignInPage_exports, {
9146
9616
  default: () => SignInPage_default
9147
9617
  });
9618
+ function oauthErrorMessage(code) {
9619
+ if (!code) return "";
9620
+ const c = code.toLowerCase();
9621
+ if (c === "accessdenied" || c === "access_denied") {
9622
+ return "No account for this Google email. Ask an admin to invite you first.";
9623
+ }
9624
+ if (c === "oauthaccountnotlinked") {
9625
+ return "This Google account is not linked to an existing user. Sign in with email/password or ask an admin to invite you.";
9626
+ }
9627
+ if (c === "oauthcallback" || c === "oauthsignin" || c === "oauthcreateaccount") {
9628
+ return "Google sign-in failed. Check Authentication Provider settings and the redirect URI in Google Cloud Console.";
9629
+ }
9630
+ if (c === "configuration") {
9631
+ return "Google sign-in is not configured correctly.";
9632
+ }
9633
+ return "Invalid email or password";
9634
+ }
9148
9635
  var SigninPage, SignInPage_default;
9149
9636
  var init_SignInPage = __esm({
9150
9637
  "src/admin/pages/SignInPage.tsx"() {
9151
9638
  "use client";
9152
9639
  init_AuthPageLayout();
9153
9640
  init_auth_debug();
9641
+ init_auth_providers_settings();
9642
+ __name(oauthErrorMessage, "oauthErrorMessage");
9154
9643
  SigninPage = /* @__PURE__ */ __name(() => {
9155
9644
  const [email, setEmail] = useState("");
9156
9645
  const [password, setPassword] = useState("");
9157
9646
  const [error, setError] = useState("");
9158
9647
  const [success, setSuccess] = useState("");
9159
9648
  const [loading, setLoading] = useState(false);
9649
+ const [googleLoading, setGoogleLoading] = useState(false);
9650
+ const [googleEnabled, setGoogleEnabled] = useState(false);
9160
9651
  const { status } = useSession();
9161
9652
  const searchParams = useSearchParams();
9162
9653
  useEffect(() => {
9163
- if (searchParams.get("error")) {
9164
- setError("Invalid email or password");
9654
+ const err = searchParams.get("error");
9655
+ if (err) {
9656
+ setError(oauthErrorMessage(err));
9165
9657
  }
9166
9658
  if (searchParams.get("activated") === "1") {
9167
9659
  setSuccess("Account activated. Sign in with your new password.");
@@ -9172,6 +9664,23 @@ var init_SignInPage = __esm({
9172
9664
  }, [
9173
9665
  searchParams
9174
9666
  ]);
9667
+ useEffect(() => {
9668
+ let cancelled = false;
9669
+ (async () => {
9670
+ try {
9671
+ const res = await fetch("/api/settings/auth_providers", {
9672
+ cache: "no-store"
9673
+ });
9674
+ if (!res.ok) return;
9675
+ const data = await res.json();
9676
+ if (!cancelled) setGoogleEnabled(isGoogleAuthPubliclyEnabled(data));
9677
+ } catch {
9678
+ }
9679
+ })();
9680
+ return () => {
9681
+ cancelled = true;
9682
+ };
9683
+ }, []);
9175
9684
  useEffect(() => {
9176
9685
  logAuthClient("SignInPage session status", {
9177
9686
  status
@@ -9229,6 +9738,19 @@ var init_SignInPage = __esm({
9229
9738
  setLoading(false);
9230
9739
  }
9231
9740
  }, "handleSubmit");
9741
+ const handleGoogle = /* @__PURE__ */ __name(async () => {
9742
+ setGoogleLoading(true);
9743
+ setError("");
9744
+ try {
9745
+ logAuthClient("SignInPage Google signIn start");
9746
+ await signIn("google", {
9747
+ callbackUrl: "/admin/dashboard"
9748
+ });
9749
+ } catch {
9750
+ setError("Google sign-in failed. Please try again.");
9751
+ setGoogleLoading(false);
9752
+ }
9753
+ }, "handleGoogle");
9232
9754
  return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
9233
9755
  className: "w-full shadow-md rounded-lg bg-white px-4 py-6 sm:px-6 sm:py-8"
9234
9756
  }, /* @__PURE__ */ React.createElement("h3", {
@@ -9281,9 +9803,42 @@ var init_SignInPage = __esm({
9281
9803
  className: "text-xs font-medium text-gray-600 hover:underline"
9282
9804
  }, "Forgot password?")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", {
9283
9805
  type: "submit",
9284
- disabled: loading,
9806
+ disabled: loading || googleLoading,
9285
9807
  className: "w-full rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
9286
- }, loading ? "Signing in..." : "Sign in")))));
9808
+ }, loading ? "Signing in..." : "Sign in"))), googleEnabled && /* @__PURE__ */ React.createElement("div", {
9809
+ className: "mt-6"
9810
+ }, /* @__PURE__ */ React.createElement("div", {
9811
+ className: "relative mb-4"
9812
+ }, /* @__PURE__ */ React.createElement("div", {
9813
+ className: "absolute inset-0 flex items-center"
9814
+ }, /* @__PURE__ */ React.createElement("div", {
9815
+ className: "w-full border-t border-gray-200"
9816
+ })), /* @__PURE__ */ React.createElement("div", {
9817
+ className: "relative flex justify-center text-xs"
9818
+ }, /* @__PURE__ */ React.createElement("span", {
9819
+ className: "bg-white px-2 text-gray-500"
9820
+ }, "or"))), /* @__PURE__ */ React.createElement("button", {
9821
+ type: "button",
9822
+ onClick: handleGoogle,
9823
+ disabled: loading || googleLoading,
9824
+ className: "flex w-full items-center justify-center gap-2 rounded border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-800 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
9825
+ }, /* @__PURE__ */ React.createElement("svg", {
9826
+ className: "h-4 w-4",
9827
+ viewBox: "0 0 24 24",
9828
+ "aria-hidden": "true"
9829
+ }, /* @__PURE__ */ React.createElement("path", {
9830
+ fill: "#4285F4",
9831
+ d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
9832
+ }), /* @__PURE__ */ React.createElement("path", {
9833
+ fill: "#34A853",
9834
+ d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
9835
+ }), /* @__PURE__ */ React.createElement("path", {
9836
+ fill: "#FBBC05",
9837
+ d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
9838
+ }), /* @__PURE__ */ React.createElement("path", {
9839
+ fill: "#EA4335",
9840
+ d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
9841
+ })), googleLoading ? "Redirecting\u2026" : "Continue with Google"))));
9287
9842
  }, "SigninPage");
9288
9843
  SignInPage_default = SigninPage;
9289
9844
  }
@@ -15383,6 +15938,18 @@ var init_email_recipients = __esm({
15383
15938
  __name(serializeEmailRecipients, "serializeEmailRecipients");
15384
15939
  }
15385
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
+ }
15386
15953
  function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15387
15954
  if (audience === "vendor") {
15388
15955
  return VENDOR_EMAIL_TRIGGER_DEFAULTS[triggerKey] ?? {
@@ -15407,6 +15974,30 @@ function getEmailTriggerDefaults(triggerKey, audience = "customer") {
15407
15974
  bodyPlaceholder: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been updated."
15408
15975
  };
15409
15976
  }
15977
+ function getPushTriggerDefaults(triggerKey, audience = "customer") {
15978
+ if (audience === "vendor") {
15979
+ return VENDOR_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
15980
+ subject: "Vendor Push Update",
15981
+ body: "Hi {{vendorName}}, order {{orderNumber}} update.",
15982
+ subjectPlaceholder: "e.g. Vendor Push Update",
15983
+ bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} update."
15984
+ };
15985
+ }
15986
+ if (audience === "admin") {
15987
+ return ADMIN_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
15988
+ subject: "Admin Push Alert",
15989
+ body: "Admin Alert: Order {{orderNumber}} update.",
15990
+ subjectPlaceholder: "e.g. Admin Push Alert",
15991
+ bodyPlaceholder: "Admin Alert: Order {{orderNumber}} update."
15992
+ };
15993
+ }
15994
+ return PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
15995
+ subject: "Order Update",
15996
+ body: "Hi {{customerName}}, order {{orderNumber}} update.",
15997
+ subjectPlaceholder: "e.g. Order Update",
15998
+ bodyPlaceholder: "Hi {{customerName}}, order {{orderNumber}} update."
15999
+ };
16000
+ }
15410
16001
  async function jsonFetch(url, init) {
15411
16002
  const res = await fetch(url, {
15412
16003
  credentials: "include",
@@ -15498,10 +16089,11 @@ function useOrderNotificationTriggerBindings(channel, audienceType = "customers"
15498
16089
  rebind
15499
16090
  };
15500
16091
  }
15501
- var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS;
16092
+ var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS, PUSH_TRIGGER_DEFAULTS, VENDOR_PUSH_TRIGGER_DEFAULTS, ADMIN_PUSH_TRIGGER_DEFAULTS;
15502
16093
  var init_order_notification_bindings_shared = __esm({
15503
16094
  "src/admin/components/order-notification-bindings-shared.ts"() {
15504
16095
  "use client";
16096
+ init_admin_config_context();
15505
16097
  KNOWN_VARS = [
15506
16098
  "orderId",
15507
16099
  "orderNumber",
@@ -15514,10 +16106,9 @@ var init_order_notification_bindings_shared = __esm({
15514
16106
  "vendorId",
15515
16107
  "refundAmount",
15516
16108
  "invoiceNumber",
15517
- "invoiceUrl",
15518
- "ticketQr",
15519
- "qrImageUrl"
16109
+ "invoiceUrl"
15520
16110
  ];
16111
+ __name(useNotificationVariables, "useNotificationVariables");
15521
16112
  EMAIL_TRIGGER_DEFAULTS = {
15522
16113
  order_placed: {
15523
16114
  subject: "Your order {{orderNumber}} is confirmed",
@@ -15595,7 +16186,7 @@ Order Summary:
15595
16186
  Customer Name: {{customerName}}
15596
16187
  Customer Email: {{customerEmail}}
15597
16188
  Customer Phone: {{customerPhone}}`,
15598
- bodyPlaceholder: "Admin Notification:\n\nNew order {{orderNumber}} placed by {{customerName}}."
16189
+ bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
15599
16190
  },
15600
16191
  order_cancelled: {
15601
16192
  subject: "Order {{orderNumber}} cancelled",
@@ -15611,6 +16202,66 @@ Refund Due: {{refundAmount}} {{currency}}`,
15611
16202
  }
15612
16203
  };
15613
16204
  __name(getEmailTriggerDefaults, "getEmailTriggerDefaults");
16205
+ PUSH_TRIGGER_DEFAULTS = {
16206
+ order_placed: {
16207
+ subject: "Order {{orderNumber}} Confirmed!",
16208
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Confirmed!",
16209
+ body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} is confirmed.`,
16210
+ bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} is confirmed."
16211
+ },
16212
+ order_cancelled: {
16213
+ subject: "Order {{orderNumber}} Cancelled",
16214
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16215
+ body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} has been cancelled.`,
16216
+ bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} has been cancelled."
16217
+ }
16218
+ };
16219
+ VENDOR_PUSH_TRIGGER_DEFAULTS = {
16220
+ order_placed: {
16221
+ subject: "New Order Received: {{orderNumber}}",
16222
+ subjectPlaceholder: "e.g. New Order Received: {{orderNumber}}",
16223
+ body: `Hi {{vendorName}}, you received a new order {{orderNumber}} for {{total}} {{currency}} from {{customerName}}.`,
16224
+ bodyPlaceholder: "Hi {{vendorName}}, you received a new order {{orderNumber}}."
16225
+ },
16226
+ order_cancelled: {
16227
+ subject: "Order {{orderNumber}} Cancelled",
16228
+ subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
16229
+ body: `Hi {{vendorName}}, order {{orderNumber}} from {{customerName}} has been cancelled.`,
16230
+ bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} was cancelled."
16231
+ }
16232
+ };
16233
+ ADMIN_PUSH_TRIGGER_DEFAULTS = {
16234
+ order_placed: {
16235
+ subject: "New Order {{orderNumber}} ({{total}} {{currency}})",
16236
+ subjectPlaceholder: "e.g. New Order {{orderNumber}} placed",
16237
+ body: `Hi Admin,
16238
+
16239
+ A new order {{orderNumber}} of {{total}} {{currency}} was placed.
16240
+
16241
+ Vendor: {{vendorName}}
16242
+
16243
+ Order Summary:
16244
+ {{orderDetails}}
16245
+
16246
+ Customer Name: {{customerName}}
16247
+ Customer Email: {{customerEmail}}
16248
+ Customer Phone: {{customerPhone}}`,
16249
+ bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
16250
+ },
16251
+ order_cancelled: {
16252
+ subject: "Order {{orderNumber}} Cancelled",
16253
+ subjectPlaceholder: "e.g. Order {{orderNumber}} cancelled",
16254
+ body: `Hi Admin,
16255
+
16256
+ Order {{orderNumber}} has been cancelled.
16257
+
16258
+ Customer: {{customerName}}
16259
+ Vendor: {{vendorName}}
16260
+ Refund Due: {{refundAmount}} {{currency}}`,
16261
+ bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
16262
+ }
16263
+ };
16264
+ __name(getPushTriggerDefaults, "getPushTriggerDefaults");
15614
16265
  __name(jsonFetch, "jsonFetch");
15615
16266
  __name(useOrderNotificationTriggerBindings, "useOrderNotificationTriggerBindings");
15616
16267
  }
@@ -16161,16 +16812,23 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16161
16812
  cancelled = true;
16162
16813
  };
16163
16814
  }, []);
16815
+ const { vars: availableVars, extra: extraVars } = useNotificationVariables();
16164
16816
  const visibleVars = useMemo(() => {
16165
- return KNOWN_VARS.filter((v) => {
16817
+ return availableVars.filter((v) => {
16166
16818
  if (v === "vendorName" || v === "vendorId") return multiVendorOn;
16167
16819
  if (v === "eventName") return eventsOn;
16168
16820
  return true;
16169
16821
  });
16170
16822
  }, [
16823
+ availableVars,
16171
16824
  multiVendorOn,
16172
16825
  eventsOn
16173
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");
16174
16832
  return /* @__PURE__ */ React.createElement("div", {
16175
16833
  className: "space-y-3 border-t border-gray-100 pt-4 first:border-t-0 first:pt-0 dark:border-gray-700"
16176
16834
  }, /* @__PURE__ */ React.createElement("div", {
@@ -16233,7 +16891,7 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
16233
16891
  }, visibleVars.map((v) => /* @__PURE__ */ React.createElement("button", {
16234
16892
  key: v,
16235
16893
  type: "button",
16236
- title: VAR_HINTS[v],
16894
+ title: getVarHint(v),
16237
16895
  onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
16238
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"
16239
16897
  }, `{{${v}}}`))), /* @__PURE__ */ React.createElement("p", {
@@ -16261,14 +16919,588 @@ var init_emailTriggersBindingsPage = __esm({
16261
16919
  vendorId: "Vendor / store ID",
16262
16920
  refundAmount: "Refund amount (refund triggers)",
16263
16921
  invoiceNumber: "Generated invoice number",
16264
- invoiceUrl: "Link to download invoice PDF",
16265
- ticketQr: "Embedded ticket QR code image",
16266
- qrImageUrl: "Direct QR code image URL"
16922
+ invoiceUrl: "Link to download invoice PDF"
16267
16923
  };
16268
16924
  __name(EmailTriggerBindings, "EmailTriggerBindings");
16269
16925
  __name(TriggerEmailEditor, "TriggerEmailEditor");
16270
16926
  }
16271
16927
  });
16928
+ function PushTriggerBindings({ audience = "customer" }) {
16929
+ const audienceType = audience === "vendor" ? "vendors" : audience === "admin" ? "admin" : "customers";
16930
+ const { triggers, bindings, loading, reload, rebind } = useOrderNotificationTriggerBindings("mobile", audienceType);
16931
+ const [selectedKey, setSelectedKey] = useState("");
16932
+ const [templates, setTemplates] = useState([]);
16933
+ const [loadingTpl, setLoadingTpl] = useState(true);
16934
+ const [title, setTitle] = useState("");
16935
+ const [body, setBody] = useState("");
16936
+ const [saving, setSaving] = useState(false);
16937
+ const bodyRef = useRef(null);
16938
+ const subjectRef = useRef(null);
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");
16946
+ useEffect(() => {
16947
+ let cancelled = false;
16948
+ (async () => {
16949
+ try {
16950
+ setLoadingTpl(true);
16951
+ const res = await fetch("/api/message-templates/email", {
16952
+ cache: "no-store"
16953
+ });
16954
+ if (!res.ok) throw new Error("Failed to load message templates");
16955
+ const data = await res.json();
16956
+ if (!cancelled) setTemplates(Array.isArray(data.items) ? data.items : []);
16957
+ } catch {
16958
+ if (!cancelled) toast.error("Could not load message templates");
16959
+ } finally {
16960
+ if (!cancelled) setLoadingTpl(false);
16961
+ }
16962
+ })();
16963
+ return () => {
16964
+ cancelled = true;
16965
+ };
16966
+ }, []);
16967
+ useEffect(() => {
16968
+ if (triggers.length > 0 && !selectedKey) {
16969
+ setSelectedKey(triggers[0].triggerKey);
16970
+ }
16971
+ }, [
16972
+ triggers,
16973
+ selectedKey
16974
+ ]);
16975
+ const activeTrigger = useMemo(() => triggers.find((t) => t.triggerKey === selectedKey), [
16976
+ triggers,
16977
+ selectedKey
16978
+ ]);
16979
+ const bindingMap = useMemo(() => {
16980
+ const m = /* @__PURE__ */ new Map();
16981
+ for (const b of bindings) {
16982
+ if (b.messageTemplateId != null) m.set(b.triggerKey, b.messageTemplateId);
16983
+ }
16984
+ return m;
16985
+ }, [
16986
+ bindings
16987
+ ]);
16988
+ const activeBindingTplId = selectedKey ? bindingMap.get(selectedKey) ?? null : null;
16989
+ const activeTemplate = useMemo(() => {
16990
+ if (!activeBindingTplId) return null;
16991
+ return templates.find((t) => t.id === activeBindingTplId) ?? null;
16992
+ }, [
16993
+ activeBindingTplId,
16994
+ templates
16995
+ ]);
16996
+ const defaults = useMemo(() => {
16997
+ if (!selectedKey) return {
16998
+ subject: "",
16999
+ body: "",
17000
+ subjectPlaceholder: "",
17001
+ bodyPlaceholder: ""
17002
+ };
17003
+ return getPushTriggerDefaults(selectedKey, audience);
17004
+ }, [
17005
+ selectedKey,
17006
+ audience
17007
+ ]);
17008
+ useEffect(() => {
17009
+ if (activeTemplate) {
17010
+ setTitle(activeTemplate.subject ?? "");
17011
+ setBody(activeTemplate.body ?? "");
17012
+ } else {
17013
+ setTitle(defaults.subject);
17014
+ setBody(defaults.body);
17015
+ }
17016
+ }, [
17017
+ activeTemplate,
17018
+ defaults,
17019
+ selectedKey
17020
+ ]);
17021
+ const insertVariable = /* @__PURE__ */ __name((varName) => {
17022
+ const token = `{{${varName}}}`;
17023
+ if (lastFocused.current === "subject" && subjectRef.current) {
17024
+ const el = subjectRef.current;
17025
+ const start = el.selectionStart ?? title.length;
17026
+ const end = el.selectionEnd ?? title.length;
17027
+ const next = title.slice(0, start) + token + title.slice(end);
17028
+ setTitle(next);
17029
+ setTimeout(() => {
17030
+ el.focus();
17031
+ el.setSelectionRange(start + token.length, start + token.length);
17032
+ }, 0);
17033
+ return;
17034
+ }
17035
+ if (bodyRef.current) {
17036
+ const el = bodyRef.current;
17037
+ const start = el.selectionStart ?? body.length;
17038
+ const end = el.selectionEnd ?? body.length;
17039
+ const next = body.slice(0, start) + token + body.slice(end);
17040
+ setBody(next);
17041
+ setTimeout(() => {
17042
+ el.focus();
17043
+ el.setSelectionRange(start + token.length, start + token.length);
17044
+ }, 0);
17045
+ } else {
17046
+ setBody((prev) => prev + token);
17047
+ }
17048
+ }, "insertVariable");
17049
+ const handleSave = /* @__PURE__ */ __name(async () => {
17050
+ if (!selectedKey) return;
17051
+ setSaving(true);
17052
+ try {
17053
+ let tplId = activeBindingTplId;
17054
+ if (tplId) {
17055
+ const res = await jsonFetch(`/api/message-templates/email/${tplId}`, {
17056
+ method: "PATCH",
17057
+ headers: {
17058
+ "Content-Type": "application/json"
17059
+ },
17060
+ body: JSON.stringify({
17061
+ name: `${audienceType}_push_${selectedKey}`,
17062
+ subject: title,
17063
+ body
17064
+ })
17065
+ });
17066
+ if (res.item) {
17067
+ setTemplates((prev) => prev.map((t) => t.id === res.item.id ? res.item : t));
17068
+ }
17069
+ } else {
17070
+ const res = await jsonFetch("/api/message-templates/email", {
17071
+ method: "POST",
17072
+ headers: {
17073
+ "Content-Type": "application/json"
17074
+ },
17075
+ body: JSON.stringify({
17076
+ name: `${audienceType}_push_${selectedKey}`,
17077
+ subject: title,
17078
+ body
17079
+ })
17080
+ });
17081
+ const createdItem = res.item || res;
17082
+ tplId = createdItem.id;
17083
+ if (createdItem) {
17084
+ setTemplates((prev) => [
17085
+ createdItem,
17086
+ ...prev
17087
+ ]);
17088
+ }
17089
+ }
17090
+ await rebind(selectedKey, tplId, `${audienceType}_push_${selectedKey}`);
17091
+ toast.success("Push notification template saved");
17092
+ await reload();
17093
+ } catch (e) {
17094
+ const msg = e instanceof Error ? e.message : "Save failed";
17095
+ toast.error(msg);
17096
+ } finally {
17097
+ setSaving(false);
17098
+ }
17099
+ }, "handleSave");
17100
+ if (loading || loadingTpl) {
17101
+ return /* @__PURE__ */ React.createElement("div", {
17102
+ className: "flex items-center gap-2 p-6 text-sm text-gray-500"
17103
+ }, /* @__PURE__ */ React.createElement(Loader2, {
17104
+ className: "h-4 w-4 animate-spin"
17105
+ }), " Loading push notification triggers\u2026");
17106
+ }
17107
+ return /* @__PURE__ */ React.createElement("div", {
17108
+ className: "space-y-6"
17109
+ }, /* @__PURE__ */ React.createElement("div", {
17110
+ className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4"
17111
+ }, /* @__PURE__ */ React.createElement("div", {
17112
+ className: "space-y-1"
17113
+ }, /* @__PURE__ */ React.createElement("h3", {
17114
+ className: "text-base font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
17115
+ }, /* @__PURE__ */ React.createElement(Smartphone, {
17116
+ className: "h-5 w-5 text-gray-700 dark:text-gray-300"
17117
+ }), "Mobile Push Notification Templates"), /* @__PURE__ */ React.createElement("p", {
17118
+ className: "text-xs text-gray-500"
17119
+ }, "Configure push notification title and message body per trigger for", " ", /* @__PURE__ */ React.createElement("span", {
17120
+ className: "font-semibold capitalize text-gray-700 dark:text-gray-300"
17121
+ }, audience, "s"), ".")), /* @__PURE__ */ React.createElement(Button, {
17122
+ type: "button",
17123
+ onClick: handleSave,
17124
+ disabled: saving || !selectedKey,
17125
+ className: "gap-2 shrink-0"
17126
+ }, saving ? /* @__PURE__ */ React.createElement(Loader2, {
17127
+ className: "h-4 w-4 animate-spin"
17128
+ }) : /* @__PURE__ */ React.createElement(Save, {
17129
+ className: "h-4 w-4"
17130
+ }), "Save Template")), /* @__PURE__ */ React.createElement("div", {
17131
+ className: "grid grid-cols-1 md:grid-cols-4 gap-6"
17132
+ }, /* @__PURE__ */ React.createElement("div", {
17133
+ className: "md:col-span-1 space-y-2"
17134
+ }, /* @__PURE__ */ React.createElement(Label3, {
17135
+ className: "text-xs font-semibold text-gray-500 uppercase tracking-wider"
17136
+ }, "Triggers"), /* @__PURE__ */ React.createElement("div", {
17137
+ className: "flex flex-col gap-1"
17138
+ }, triggers.map((t) => {
17139
+ const isBound = bindingMap.has(t.triggerKey);
17140
+ const isSelected = t.triggerKey === selectedKey;
17141
+ return /* @__PURE__ */ React.createElement("button", {
17142
+ key: t.triggerKey,
17143
+ type: "button",
17144
+ onClick: /* @__PURE__ */ __name(() => setSelectedKey(t.triggerKey), "onClick"),
17145
+ className: `flex flex-col items-start p-3 rounded-lg border text-left transition-all ${isSelected ? "border-gray-900 bg-gray-900 text-white dark:border-gray-100 dark:bg-gray-100 dark:text-gray-900 shadow-sm" : "border-gray-200 bg-white hover:bg-gray-50 text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300"}`
17146
+ }, /* @__PURE__ */ React.createElement("div", {
17147
+ className: "flex items-center justify-between w-full"
17148
+ }, /* @__PURE__ */ React.createElement("span", {
17149
+ className: "font-medium text-sm"
17150
+ }, t.label), isBound && /* @__PURE__ */ React.createElement("span", {
17151
+ className: `text-[10px] px-1.5 py-0.5 rounded font-mono ${isSelected ? "bg-white/20 text-white dark:bg-black/20 dark:text-gray-900" : "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300"}`
17152
+ }, "Configured")), /* @__PURE__ */ React.createElement("span", {
17153
+ className: `text-xs mt-1 ${isSelected ? "text-gray-300 dark:text-gray-600" : "text-gray-500 dark:text-gray-400"}`
17154
+ }, t.triggerKey));
17155
+ }))), /* @__PURE__ */ React.createElement("div", {
17156
+ className: "md:col-span-3 space-y-5 bg-white dark:bg-gray-900 p-5 rounded-xl border border-gray-200 dark:border-gray-800 shadow-sm"
17157
+ }, activeTrigger ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h4", {
17158
+ className: "text-sm font-semibold text-gray-900 dark:text-gray-100"
17159
+ }, activeTrigger.label, " (", activeTrigger.triggerKey, ")"), activeTrigger.description && /* @__PURE__ */ React.createElement("p", {
17160
+ className: "text-xs text-gray-500 mt-0.5"
17161
+ }, activeTrigger.description)), /* @__PURE__ */ React.createElement("div", {
17162
+ className: "space-y-1.5"
17163
+ }, /* @__PURE__ */ React.createElement(Label3, {
17164
+ className: "text-xs font-medium text-gray-700 dark:text-gray-300"
17165
+ }, "Insert Variables"), /* @__PURE__ */ React.createElement("div", {
17166
+ className: "flex flex-wrap gap-1.5"
17167
+ }, availableVars.map((v) => /* @__PURE__ */ React.createElement("button", {
17168
+ key: v,
17169
+ type: "button",
17170
+ onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
17171
+ title: getVarHint(v),
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"
17173
+ }, `{{${v}}}`)))), /* @__PURE__ */ React.createElement("div", {
17174
+ className: "space-y-1.5"
17175
+ }, /* @__PURE__ */ React.createElement(Label3, {
17176
+ htmlFor: "push-title",
17177
+ className: "text-xs font-medium"
17178
+ }, "Notification Title"), /* @__PURE__ */ React.createElement(Input, {
17179
+ id: "push-title",
17180
+ ref: subjectRef,
17181
+ value: title,
17182
+ onFocus: /* @__PURE__ */ __name(() => {
17183
+ lastFocused.current = "subject";
17184
+ }, "onFocus"),
17185
+ onChange: /* @__PURE__ */ __name((e) => setTitle(e.target.value), "onChange"),
17186
+ placeholder: defaults.subjectPlaceholder
17187
+ })), /* @__PURE__ */ React.createElement("div", {
17188
+ className: "space-y-1.5"
17189
+ }, /* @__PURE__ */ React.createElement(Label3, {
17190
+ htmlFor: "push-body",
17191
+ className: "text-xs font-medium"
17192
+ }, "Notification Body"), /* @__PURE__ */ React.createElement(Textarea, {
17193
+ id: "push-body",
17194
+ ref: bodyRef,
17195
+ value: body,
17196
+ onFocus: /* @__PURE__ */ __name(() => {
17197
+ lastFocused.current = "body";
17198
+ }, "onFocus"),
17199
+ onChange: /* @__PURE__ */ __name((e) => setBody(e.target.value), "onChange"),
17200
+ placeholder: defaults.bodyPlaceholder,
17201
+ className: "h-36 font-mono text-xs"
17202
+ }))) : /* @__PURE__ */ React.createElement("div", {
17203
+ className: "p-12 text-center text-sm text-gray-500"
17204
+ }, "Select a trigger from the left to configure push notification template."))));
17205
+ }
17206
+ var VAR_HINTS2;
17207
+ var init_pushTriggersBindingsPage = __esm({
17208
+ "src/admin/pages/pushTriggersBindingsPage.tsx"() {
17209
+ "use client";
17210
+ init_button();
17211
+ init_input();
17212
+ init_textarea();
17213
+ init_label();
17214
+ init_order_notification_bindings_shared();
17215
+ VAR_HINTS2 = {
17216
+ orderId: "Internal order ID",
17217
+ orderNumber: "Display order number",
17218
+ customerName: "Customer full name",
17219
+ customerPhone: "Customer phone",
17220
+ eventName: "Event title / product name",
17221
+ vendorName: "Vendor / store name",
17222
+ total: "Order total amount",
17223
+ currency: "Currency code (e.g. INR)",
17224
+ vendorId: "Vendor / store ID",
17225
+ refundAmount: "Refund amount (refund triggers)",
17226
+ invoiceNumber: "Generated invoice number",
17227
+ invoiceUrl: "Link to download invoice PDF"
17228
+ };
17229
+ __name(PushTriggerBindings, "PushTriggerBindings");
17230
+ }
17231
+ });
17232
+ function PushConfigModal({ open, onOpenChange }) {
17233
+ const [loading, setLoading] = useState(false);
17234
+ const [saving, setSaving] = useState(false);
17235
+ const [testing, setTesting] = useState(false);
17236
+ const [serviceAccountJson, setServiceAccountJson] = useState("");
17237
+ const [projectId, setProjectId] = useState("");
17238
+ const [clientEmail, setClientEmail] = useState("");
17239
+ const [privateKey, setPrivateKey] = useState("");
17240
+ const [isValidJson, setIsValidJson] = useState(false);
17241
+ const [testToken, setTestToken] = useState("");
17242
+ const [testTitle, setTestTitle] = useState("Test Push Notification");
17243
+ const [testBody, setTestBody] = useState("Firebase Push Notification setup is working!");
17244
+ useEffect(() => {
17245
+ if (!open) return;
17246
+ let cancelled = false;
17247
+ (async () => {
17248
+ try {
17249
+ setLoading(true);
17250
+ const res = await fetch("/api/settings/push", {
17251
+ cache: "no-store"
17252
+ });
17253
+ if (res.ok) {
17254
+ const data = await res.json();
17255
+ if (!cancelled && data) {
17256
+ const rawJson = data.firebase_service_account_json || "";
17257
+ setServiceAccountJson(rawJson);
17258
+ if (rawJson.trim()) {
17259
+ parseAndExtract(rawJson, false);
17260
+ } else {
17261
+ setProjectId(data.firebase_project_id || "");
17262
+ setClientEmail(data.firebase_client_email || "");
17263
+ setPrivateKey(data.firebase_private_key || "");
17264
+ }
17265
+ }
17266
+ }
17267
+ } catch {
17268
+ toast.error("Failed to load push configuration");
17269
+ } finally {
17270
+ if (!cancelled) setLoading(false);
17271
+ }
17272
+ })();
17273
+ return () => {
17274
+ cancelled = true;
17275
+ };
17276
+ }, [
17277
+ open
17278
+ ]);
17279
+ const parseAndExtract = /* @__PURE__ */ __name((input, showToast = true) => {
17280
+ if (!input.trim()) {
17281
+ setIsValidJson(false);
17282
+ return;
17283
+ }
17284
+ try {
17285
+ const parsed = JSON.parse(input);
17286
+ if (parsed.project_id) setProjectId(parsed.project_id);
17287
+ if (parsed.client_email) setClientEmail(parsed.client_email);
17288
+ if (parsed.private_key) setPrivateKey(parsed.private_key);
17289
+ setIsValidJson(true);
17290
+ if (showToast) {
17291
+ toast.success("Valid Service Account JSON loaded");
17292
+ }
17293
+ } catch {
17294
+ setIsValidJson(false);
17295
+ if (showToast) {
17296
+ toast.error("Invalid Service Account JSON format");
17297
+ }
17298
+ }
17299
+ }, "parseAndExtract");
17300
+ const handleJsonChange = /* @__PURE__ */ __name((val) => {
17301
+ setServiceAccountJson(val);
17302
+ if (val.trim()) {
17303
+ parseAndExtract(val, false);
17304
+ } else {
17305
+ setIsValidJson(false);
17306
+ }
17307
+ }, "handleJsonChange");
17308
+ const handleSaveConfig = /* @__PURE__ */ __name(async () => {
17309
+ if (serviceAccountJson.trim() && !isValidJson) {
17310
+ toast.error("Please enter a valid Firebase Service Account JSON file");
17311
+ return;
17312
+ }
17313
+ setSaving(true);
17314
+ try {
17315
+ const res = await fetch("/api/settings/push", {
17316
+ method: "PUT",
17317
+ headers: {
17318
+ "Content-Type": "application/json"
17319
+ },
17320
+ body: JSON.stringify({
17321
+ firebase_service_account_json: {
17322
+ value: serviceAccountJson.trim(),
17323
+ type: "secret"
17324
+ },
17325
+ firebase_project_id: {
17326
+ value: projectId.trim(),
17327
+ type: "secret"
17328
+ },
17329
+ firebase_client_email: {
17330
+ value: clientEmail.trim(),
17331
+ type: "secret"
17332
+ },
17333
+ firebase_private_key: {
17334
+ value: privateKey.trim(),
17335
+ type: "secret"
17336
+ }
17337
+ })
17338
+ });
17339
+ if (!res.ok) {
17340
+ toast.error("Failed to save push configuration");
17341
+ return;
17342
+ }
17343
+ toast.success("Firebase Service Account JSON saved successfully");
17344
+ onOpenChange(false);
17345
+ } catch {
17346
+ toast.error("Failed to save push configuration");
17347
+ } finally {
17348
+ setSaving(false);
17349
+ }
17350
+ }, "handleSaveConfig");
17351
+ const handleSendTestPush = /* @__PURE__ */ __name(async () => {
17352
+ if (!testToken.trim()) {
17353
+ toast.error("Please enter a target FCM device token to test");
17354
+ return;
17355
+ }
17356
+ setTesting(true);
17357
+ try {
17358
+ const res = await fetch("/api/settings/push/test", {
17359
+ method: "POST",
17360
+ headers: {
17361
+ "Content-Type": "application/json"
17362
+ },
17363
+ body: JSON.stringify({
17364
+ token: testToken.trim(),
17365
+ title: testTitle.trim(),
17366
+ body: testBody.trim()
17367
+ })
17368
+ });
17369
+ const data = await res.json();
17370
+ if (!res.ok || !data.success) {
17371
+ toast.error(data.error || "Failed to send test push notification");
17372
+ return;
17373
+ }
17374
+ toast.success("Test push notification sent successfully!");
17375
+ } catch {
17376
+ toast.error("Failed to send test push notification");
17377
+ } finally {
17378
+ setTesting(false);
17379
+ }
17380
+ }, "handleSendTestPush");
17381
+ if (!open) return null;
17382
+ return /* @__PURE__ */ React.createElement("div", {
17383
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm animate-in fade-in duration-150"
17384
+ }, /* @__PURE__ */ React.createElement("div", {
17385
+ className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-xl bg-white p-6 shadow-2xl dark:bg-gray-900 border border-gray-200 dark:border-gray-800 space-y-6"
17386
+ }, /* @__PURE__ */ React.createElement("div", {
17387
+ className: "flex items-center justify-between border-b border-gray-200 pb-4 dark:border-gray-800"
17388
+ }, /* @__PURE__ */ React.createElement("div", {
17389
+ className: "flex items-center gap-2"
17390
+ }, /* @__PURE__ */ React.createElement(Smartphone, {
17391
+ className: "h-5 w-5 text-gray-800 dark:text-gray-200"
17392
+ }), /* @__PURE__ */ React.createElement("h2", {
17393
+ className: "text-lg font-semibold text-gray-900 dark:text-gray-100"
17394
+ }, "Firebase Service Account JSON Configuration")), /* @__PURE__ */ React.createElement("button", {
17395
+ type: "button",
17396
+ onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick"),
17397
+ className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
17398
+ }, "\u2715")), loading ? /* @__PURE__ */ React.createElement("div", {
17399
+ className: "flex items-center justify-center py-12"
17400
+ }, /* @__PURE__ */ React.createElement(Loader2, {
17401
+ className: "h-6 w-6 animate-spin text-gray-500"
17402
+ })) : /* @__PURE__ */ React.createElement("div", {
17403
+ className: "space-y-6"
17404
+ }, /* @__PURE__ */ React.createElement("div", {
17405
+ className: "space-y-2"
17406
+ }, /* @__PURE__ */ React.createElement(Label3, {
17407
+ htmlFor: "service-account-json",
17408
+ className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center justify-between"
17409
+ }, /* @__PURE__ */ React.createElement("span", {
17410
+ className: "flex items-center gap-1.5"
17411
+ }, /* @__PURE__ */ React.createElement(KeyRound, {
17412
+ className: "h-4 w-4 text-blue-500"
17413
+ }), "Firebase Service Account JSON File"), isValidJson && /* @__PURE__ */ React.createElement("span", {
17414
+ className: "text-xs font-normal text-green-600 flex items-center gap-1"
17415
+ }, /* @__PURE__ */ React.createElement(CheckCircle2, {
17416
+ className: "h-3.5 w-3.5"
17417
+ }), " Valid JSON")), /* @__PURE__ */ React.createElement(Textarea, {
17418
+ id: "service-account-json",
17419
+ value: serviceAccountJson,
17420
+ onChange: /* @__PURE__ */ __name((e) => handleJsonChange(e.target.value), "onChange"),
17421
+ placeholder: `Paste your entire service-account.json content here...
17422
+ {
17423
+ "type": "service_account",
17424
+ "project_id": "...",
17425
+ "private_key": "...",
17426
+ "client_email": "..."
17427
+ }`,
17428
+ className: "h-44 font-mono text-xs border-gray-300 dark:border-gray-700"
17429
+ })), isValidJson && /* @__PURE__ */ React.createElement("div", {
17430
+ className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
17431
+ }, /* @__PURE__ */ React.createElement(Label3, {
17432
+ className: "text-xs font-semibold text-gray-700 dark:text-gray-300"
17433
+ }, "Extracted Firebase Credentials Preview"), /* @__PURE__ */ React.createElement("div", {
17434
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs"
17435
+ }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
17436
+ className: "font-medium text-gray-500"
17437
+ }, "Project ID:"), /* @__PURE__ */ React.createElement("p", {
17438
+ className: "font-mono text-gray-900 dark:text-gray-100 truncate"
17439
+ }, projectId || "\u2014")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
17440
+ className: "font-medium text-gray-500"
17441
+ }, "Client Email:"), /* @__PURE__ */ React.createElement("p", {
17442
+ className: "font-mono text-gray-900 dark:text-gray-100 truncate"
17443
+ }, clientEmail || "\u2014")))), /* @__PURE__ */ React.createElement("div", {
17444
+ className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
17445
+ }, /* @__PURE__ */ React.createElement(Label3, {
17446
+ className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center gap-1.5"
17447
+ }, /* @__PURE__ */ React.createElement(Send, {
17448
+ className: "h-3.5 w-3.5 text-green-500"
17449
+ }), "Test Push Notification (Target Device Token)"), /* @__PURE__ */ React.createElement("div", {
17450
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-2"
17451
+ }, /* @__PURE__ */ React.createElement(Input, {
17452
+ value: testTitle,
17453
+ onChange: /* @__PURE__ */ __name((e) => setTestTitle(e.target.value), "onChange"),
17454
+ placeholder: "Test Notification Title",
17455
+ className: "text-xs"
17456
+ }), /* @__PURE__ */ React.createElement(Input, {
17457
+ value: testBody,
17458
+ onChange: /* @__PURE__ */ __name((e) => setTestBody(e.target.value), "onChange"),
17459
+ placeholder: "Test Notification Body",
17460
+ className: "text-xs"
17461
+ })), /* @__PURE__ */ React.createElement("div", {
17462
+ className: "flex gap-2"
17463
+ }, /* @__PURE__ */ React.createElement(Input, {
17464
+ value: testToken,
17465
+ onChange: /* @__PURE__ */ __name((e) => setTestToken(e.target.value), "onChange"),
17466
+ placeholder: "Target FCM Device Token",
17467
+ className: "text-xs font-mono"
17468
+ }), /* @__PURE__ */ React.createElement(Button, {
17469
+ type: "button",
17470
+ variant: "secondary",
17471
+ size: "sm",
17472
+ disabled: testing,
17473
+ onClick: handleSendTestPush,
17474
+ className: "shrink-0 gap-1.5"
17475
+ }, testing ? /* @__PURE__ */ React.createElement(Loader2, {
17476
+ className: "h-3.5 w-3.5 animate-spin"
17477
+ }) : /* @__PURE__ */ React.createElement(Send, {
17478
+ className: "h-3.5 w-3.5"
17479
+ }), "Send Test"))), /* @__PURE__ */ React.createElement("div", {
17480
+ className: "flex justify-end gap-2 border-t border-gray-200 pt-4 dark:border-gray-800"
17481
+ }, /* @__PURE__ */ React.createElement(Button, {
17482
+ type: "button",
17483
+ variant: "outline",
17484
+ onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick")
17485
+ }, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
17486
+ type: "button",
17487
+ disabled: saving,
17488
+ onClick: handleSaveConfig,
17489
+ className: "gap-1.5"
17490
+ }, saving && /* @__PURE__ */ React.createElement(Loader2, {
17491
+ className: "h-4 w-4 animate-spin"
17492
+ }), "Save Service Account JSON")))));
17493
+ }
17494
+ var init_PushConfigModal = __esm({
17495
+ "src/admin/components/PushConfigModal.tsx"() {
17496
+ "use client";
17497
+ init_button();
17498
+ init_input();
17499
+ init_textarea();
17500
+ init_label();
17501
+ __name(PushConfigModal, "PushConfigModal");
17502
+ }
17503
+ });
16272
17504
  function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledChange, onSaveSettings, saving }) {
16273
17505
  const [mainTab, setMainTab] = useState("email");
16274
17506
  const [audienceTab, setAudienceTab] = useState("customer");
@@ -16276,6 +17508,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16276
17508
  const [emailVendorEnabled, setEmailVendorEnabled] = useState(true);
16277
17509
  const [emailAdminEnabled, setEmailAdminEnabled] = useState(true);
16278
17510
  const [whatsappEnabled, setWhatsappEnabled] = useState(true);
17511
+ const [pushCustomerEnabled, setPushCustomerEnabled] = useState(true);
17512
+ const [pushVendorEnabled, setPushVendorEnabled] = useState(true);
17513
+ const [pushAdminEnabled, setPushAdminEnabled] = useState(true);
17514
+ const [showPushConfigModal, setShowPushConfigModal] = useState(false);
16279
17515
  const [multiVendorOn, setMultiVendorOn] = useState(true);
16280
17516
  const [loading, setLoading] = useState(true);
16281
17517
  const [savingChannel, setSavingChannel] = useState(false);
@@ -16306,6 +17542,9 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16306
17542
  setEmailVendorEnabled(data.email_vendor_enabled !== "false" && data.email_vendor_enabled !== "0");
16307
17543
  setEmailAdminEnabled(data.email_admin_enabled !== "false" && data.email_admin_enabled !== "0");
16308
17544
  setWhatsappEnabled(data.whatsapp_enabled !== "false" && data.whatsapp_enabled !== "0");
17545
+ setPushCustomerEnabled(data.push_customer_enabled !== "false" && data.push_customer_enabled !== "0");
17546
+ setPushVendorEnabled(data.push_vendor_enabled !== "false" && data.push_vendor_enabled !== "0");
17547
+ setPushAdminEnabled(data.push_admin_enabled !== "false" && data.push_admin_enabled !== "0");
16309
17548
  }
16310
17549
  }
16311
17550
  } catch {
@@ -16323,7 +17562,8 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16323
17562
  setSavingChannel(true);
16324
17563
  try {
16325
17564
  const isAnyEmailEnabled = emailCustomerEnabled || emailVendorEnabled || emailAdminEnabled;
16326
- const isAnyChannelEnabled = isAnyEmailEnabled || whatsappEnabled;
17565
+ const isAnyPushEnabled = pushCustomerEnabled || pushVendorEnabled || pushAdminEnabled;
17566
+ const isAnyChannelEnabled = isAnyEmailEnabled || whatsappEnabled || isAnyPushEnabled;
16327
17567
  const res = await fetch(`/api/settings/${settingsGroup}`, {
16328
17568
  method: "PUT",
16329
17569
  headers: {
@@ -16350,6 +17590,22 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16350
17590
  value: whatsappEnabled ? "true" : "false",
16351
17591
  type: "public"
16352
17592
  },
17593
+ push_enabled: {
17594
+ value: isAnyPushEnabled ? "true" : "false",
17595
+ type: "public"
17596
+ },
17597
+ push_customer_enabled: {
17598
+ value: pushCustomerEnabled ? "true" : "false",
17599
+ type: "public"
17600
+ },
17601
+ push_vendor_enabled: {
17602
+ value: pushVendorEnabled ? "true" : "false",
17603
+ type: "public"
17604
+ },
17605
+ push_admin_enabled: {
17606
+ value: pushAdminEnabled ? "true" : "false",
17607
+ type: "public"
17608
+ },
16353
17609
  enabled: {
16354
17610
  value: isAnyChannelEnabled ? "true" : "false",
16355
17611
  type: "public"
@@ -16371,7 +17627,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16371
17627
  }, "handleSaveChannels");
16372
17628
  return /* @__PURE__ */ React.createElement("div", {
16373
17629
  className: "space-y-5"
16374
- }, /* @__PURE__ */ React.createElement("div", {
17630
+ }, /* @__PURE__ */ React.createElement(PushConfigModal, {
17631
+ open: showPushConfigModal,
17632
+ onOpenChange: setShowPushConfigModal
17633
+ }), /* @__PURE__ */ React.createElement("div", {
16375
17634
  className: "border-b border-gray-200 dark:border-gray-700"
16376
17635
  }, /* @__PURE__ */ React.createElement("div", {
16377
17636
  className: "flex gap-1"
@@ -16387,7 +17646,13 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16387
17646
  className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "whatsapp" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
16388
17647
  }, /* @__PURE__ */ React.createElement(MessageCircle, {
16389
17648
  className: "h-4 w-4"
16390
- }), "WhatsApp template"))), mainTab === "email" && /* @__PURE__ */ React.createElement("div", {
17649
+ }), "WhatsApp template"), /* @__PURE__ */ React.createElement("button", {
17650
+ type: "button",
17651
+ onClick: /* @__PURE__ */ __name(() => setMainTab("push"), "onClick"),
17652
+ className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "push" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
17653
+ }, /* @__PURE__ */ React.createElement(Smartphone, {
17654
+ className: "h-4 w-4"
17655
+ }), "Mobile notification template"))), mainTab === "email" && /* @__PURE__ */ React.createElement("div", {
16391
17656
  className: "space-y-5"
16392
17657
  }, /* @__PURE__ */ React.createElement("div", {
16393
17658
  className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
@@ -16421,7 +17686,7 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16421
17686
  className: "h-4 w-4 text-blue-600 dark:text-blue-400"
16422
17687
  }), "Enable Customer Email Notifications"), /* @__PURE__ */ React.createElement("p", {
16423
17688
  className: "text-xs text-gray-500 dark:text-gray-400"
16424
- }, "When enabled, order confirmations, tickets, and status updates are emailed to customers.")), /* @__PURE__ */ React.createElement(Switch, {
17689
+ }, "When enabled, order updates, invoices, and ticket details are emailed to customers.")), /* @__PURE__ */ React.createElement(Switch, {
16425
17690
  id: "email-customer-notifications-enabled",
16426
17691
  checked: emailCustomerEnabled,
16427
17692
  onCheckedChange: setEmailCustomerEnabled,
@@ -16437,10 +17702,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16437
17702
  }, /* @__PURE__ */ React.createElement("div", {
16438
17703
  className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
16439
17704
  }, /* @__PURE__ */ React.createElement(Store, {
16440
- className: "h-4 w-4 text-purple-600 dark:text-purple-400"
17705
+ className: "h-4 w-4 text-purple-600 dark:purple-400"
16441
17706
  }), "Enable Vendor Email Notifications"), /* @__PURE__ */ React.createElement("p", {
16442
17707
  className: "text-xs text-gray-500 dark:text-gray-400"
16443
- }, "When enabled, vendors are emailed automatically when order is placed/cancelled for their products.")), /* @__PURE__ */ React.createElement(Switch, {
17708
+ }, "When enabled, vendors receive email notifications for new orders assigned to them.")), /* @__PURE__ */ React.createElement(Switch, {
16444
17709
  id: "email-vendor-notifications-enabled",
16445
17710
  checked: emailVendorEnabled,
16446
17711
  onCheckedChange: setEmailVendorEnabled,
@@ -16483,7 +17748,104 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
16483
17748
  checked: whatsappEnabled,
16484
17749
  onCheckedChange: setWhatsappEnabled,
16485
17750
  disabled: loading
16486
- })), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), /* @__PURE__ */ React.createElement(Button, {
17751
+ })), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), mainTab === "push" && /* @__PURE__ */ React.createElement("div", {
17752
+ className: "space-y-5"
17753
+ }, /* @__PURE__ */ React.createElement("div", {
17754
+ className: "flex items-center justify-between gap-4 rounded-lg border border-blue-100 bg-blue-50/50 p-4 dark:border-blue-900/40 dark:bg-blue-950/20"
17755
+ }, /* @__PURE__ */ React.createElement("div", {
17756
+ className: "space-y-0.5"
17757
+ }, /* @__PURE__ */ React.createElement("div", {
17758
+ className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
17759
+ }, /* @__PURE__ */ React.createElement(Smartphone, {
17760
+ className: "h-4 w-4 text-blue-600 dark:text-blue-400"
17761
+ }), "Firebase Cloud Messaging (FCM) Setup"), /* @__PURE__ */ React.createElement("p", {
17762
+ className: "text-xs text-gray-500 dark:text-gray-400"
17763
+ }, "Set up your Firebase project credentials to send mobile push notifications to iOS and Android apps.")), /* @__PURE__ */ React.createElement(Button, {
17764
+ type: "button",
17765
+ variant: "outline",
17766
+ size: "sm",
17767
+ onClick: /* @__PURE__ */ __name(() => setShowPushConfigModal(true), "onClick"),
17768
+ className: "gap-1.5 bg-white dark:bg-gray-900 shrink-0"
17769
+ }, /* @__PURE__ */ React.createElement(Settings, {
17770
+ className: "h-4 w-4 text-gray-600"
17771
+ }), "Set Configuration")), /* @__PURE__ */ React.createElement("div", {
17772
+ className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
17773
+ }, /* @__PURE__ */ React.createElement("button", {
17774
+ type: "button",
17775
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("customer"), "onClick"),
17776
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "customer" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17777
+ }, /* @__PURE__ */ React.createElement(User, {
17778
+ className: "h-3.5 w-3.5 text-blue-500"
17779
+ }), "Customers"), multiVendorOn && /* @__PURE__ */ React.createElement("button", {
17780
+ type: "button",
17781
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("vendor"), "onClick"),
17782
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "vendor" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17783
+ }, /* @__PURE__ */ React.createElement(Store, {
17784
+ className: "h-3.5 w-3.5 text-purple-500"
17785
+ }), "Vendors"), /* @__PURE__ */ React.createElement("button", {
17786
+ type: "button",
17787
+ onClick: /* @__PURE__ */ __name(() => setAudienceTab("admin"), "onClick"),
17788
+ className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "admin" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
17789
+ }, /* @__PURE__ */ React.createElement(ShieldCheck, {
17790
+ className: "h-3.5 w-3.5 text-amber-500"
17791
+ }), "Admin")), audienceTab === "customer" && /* @__PURE__ */ React.createElement("div", {
17792
+ className: "space-y-4"
17793
+ }, /* @__PURE__ */ React.createElement("div", {
17794
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17795
+ }, /* @__PURE__ */ React.createElement("div", {
17796
+ className: "space-y-0.5"
17797
+ }, /* @__PURE__ */ React.createElement("div", {
17798
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17799
+ }, /* @__PURE__ */ React.createElement(User, {
17800
+ className: "h-4 w-4 text-blue-600 dark:text-blue-400"
17801
+ }), "Enable Customer Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17802
+ className: "text-xs text-gray-500 dark:text-gray-400"
17803
+ }, "When enabled, order confirmation alerts and updates are pushed directly to customer mobile apps.")), /* @__PURE__ */ React.createElement(Switch, {
17804
+ id: "push-customer-notifications-enabled",
17805
+ checked: pushCustomerEnabled,
17806
+ onCheckedChange: setPushCustomerEnabled,
17807
+ disabled: loading
17808
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17809
+ audience: "customer"
17810
+ })), audienceTab === "vendor" && multiVendorOn && /* @__PURE__ */ React.createElement("div", {
17811
+ className: "space-y-4"
17812
+ }, /* @__PURE__ */ React.createElement("div", {
17813
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17814
+ }, /* @__PURE__ */ React.createElement("div", {
17815
+ className: "space-y-0.5"
17816
+ }, /* @__PURE__ */ React.createElement("div", {
17817
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17818
+ }, /* @__PURE__ */ React.createElement(Store, {
17819
+ className: "h-4 w-4 text-purple-600 dark:text-purple-400"
17820
+ }), "Enable Vendor Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17821
+ className: "text-xs text-gray-500 dark:text-gray-400"
17822
+ }, "When enabled, vendor mobile app receives instant push alerts for assigned orders.")), /* @__PURE__ */ React.createElement(Switch, {
17823
+ id: "push-vendor-notifications-enabled",
17824
+ checked: pushVendorEnabled,
17825
+ onCheckedChange: setPushVendorEnabled,
17826
+ disabled: loading
17827
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17828
+ audience: "vendor"
17829
+ })), audienceTab === "admin" && /* @__PURE__ */ React.createElement("div", {
17830
+ className: "space-y-4"
17831
+ }, /* @__PURE__ */ React.createElement("div", {
17832
+ className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
17833
+ }, /* @__PURE__ */ React.createElement("div", {
17834
+ className: "space-y-0.5"
17835
+ }, /* @__PURE__ */ React.createElement("div", {
17836
+ className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
17837
+ }, /* @__PURE__ */ React.createElement(ShieldCheck, {
17838
+ className: "h-4 w-4 text-amber-600 dark:text-amber-400"
17839
+ }), "Enable Admin Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
17840
+ className: "text-xs text-gray-500 dark:text-gray-400"
17841
+ }, "When enabled, store administrators receive mobile push alerts for all orders placed/cancelled.")), /* @__PURE__ */ React.createElement(Switch, {
17842
+ id: "push-admin-notifications-enabled",
17843
+ checked: pushAdminEnabled,
17844
+ onCheckedChange: setPushAdminEnabled,
17845
+ disabled: loading
17846
+ })), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
17847
+ audience: "admin"
17848
+ }))), /* @__PURE__ */ React.createElement(Button, {
16487
17849
  size: "sm",
16488
17850
  onClick: /* @__PURE__ */ __name(() => void handleSaveChannels(), "onClick"),
16489
17851
  disabled: saving || savingChannel || loading,
@@ -16499,9 +17861,207 @@ var init_EventNotificationsPluginSettings = __esm({
16499
17861
  init_switch();
16500
17862
  init_whatsappTriggerBindingsPage();
16501
17863
  init_emailTriggersBindingsPage();
17864
+ init_pushTriggersBindingsPage();
17865
+ init_PushConfigModal();
16502
17866
  __name(EventNotificationsPluginSettings, "EventNotificationsPluginSettings");
16503
17867
  }
16504
17868
  });
17869
+ function googleRedirectUri() {
17870
+ if (typeof window === "undefined") {
17871
+ const path2 = typeof process !== "undefined" && process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim() || "/api/auth/callback/google";
17872
+ return path2.startsWith("http") ? path2 : path2;
17873
+ }
17874
+ const configured = process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim();
17875
+ if (configured?.startsWith("http")) return configured;
17876
+ const path = configured || "/api/auth/callback/google";
17877
+ return `${window.location.origin}${path.startsWith("/") ? path : `/${path}`}`;
17878
+ }
17879
+ function AuthProvidersPluginSettings({ settingsGroup, onEnabledChange }) {
17880
+ const [loading, setLoading] = useState(true);
17881
+ const [saving, setSaving] = useState(false);
17882
+ const [googleEnabled, setGoogleEnabled] = useState(false);
17883
+ const [googleClientId, setGoogleClientId] = useState("");
17884
+ const [googleClientSecret, setGoogleClientSecret] = useState("");
17885
+ const [hasSavedSecret, setHasSavedSecret] = useState(false);
17886
+ const [copied, setCopied] = useState(false);
17887
+ const onEnabledChangeRef = useRef(onEnabledChange);
17888
+ onEnabledChangeRef.current = onEnabledChange;
17889
+ const redirectUri = useMemo(() => googleRedirectUri(), []);
17890
+ useEffect(() => {
17891
+ let cancelled = false;
17892
+ (async () => {
17893
+ setLoading(true);
17894
+ try {
17895
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17896
+ cache: "no-store"
17897
+ });
17898
+ const data = res.ok ? await res.json() : {};
17899
+ if (cancelled) return;
17900
+ const enabled = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
17901
+ setGoogleEnabled(enabled);
17902
+ setGoogleClientId(String(data.googleClientId ?? "").trim());
17903
+ const secret = String(data.googleClientSecret ?? "").trim();
17904
+ setGoogleClientSecret("");
17905
+ setHasSavedSecret(Boolean(secret));
17906
+ } catch {
17907
+ } finally {
17908
+ if (!cancelled) setLoading(false);
17909
+ }
17910
+ })();
17911
+ return () => {
17912
+ cancelled = true;
17913
+ };
17914
+ }, [
17915
+ settingsGroup
17916
+ ]);
17917
+ const handleCopyRedirect = /* @__PURE__ */ __name(async () => {
17918
+ try {
17919
+ await navigator.clipboard.writeText(redirectUri);
17920
+ setCopied(true);
17921
+ toast.success("Redirect URI copied");
17922
+ setTimeout(() => setCopied(false), 2e3);
17923
+ } catch {
17924
+ toast.error("Could not copy redirect URI");
17925
+ }
17926
+ }, "handleCopyRedirect");
17927
+ const handleSave = /* @__PURE__ */ __name(async () => {
17928
+ setSaving(true);
17929
+ try {
17930
+ const payload = {
17931
+ enabled: {
17932
+ value: googleEnabled ? "true" : "false",
17933
+ type: "public"
17934
+ },
17935
+ googleEnabled: {
17936
+ value: googleEnabled ? "true" : "false",
17937
+ type: "public"
17938
+ },
17939
+ googleClientId: {
17940
+ value: googleClientId.trim(),
17941
+ type: "private"
17942
+ }
17943
+ };
17944
+ if (googleClientSecret.trim()) {
17945
+ payload.googleClientSecret = {
17946
+ value: googleClientSecret.trim(),
17947
+ type: "private",
17948
+ encrypted: true
17949
+ };
17950
+ }
17951
+ const res = await fetch(`/api/settings/${settingsGroup}`, {
17952
+ method: "PUT",
17953
+ headers: {
17954
+ "Content-Type": "application/json"
17955
+ },
17956
+ body: JSON.stringify(payload)
17957
+ });
17958
+ if (!res.ok) {
17959
+ toast.error("Failed to save Google auth settings");
17960
+ return;
17961
+ }
17962
+ if (googleClientSecret.trim()) {
17963
+ setHasSavedSecret(true);
17964
+ setGoogleClientSecret("");
17965
+ }
17966
+ onEnabledChangeRef.current?.(googleEnabled);
17967
+ toast.success("Authentication provider settings saved");
17968
+ } catch {
17969
+ toast.error("Failed to save Google auth settings");
17970
+ } finally {
17971
+ setSaving(false);
17972
+ }
17973
+ }, "handleSave");
17974
+ if (loading) {
17975
+ return /* @__PURE__ */ React.createElement("p", {
17976
+ className: "text-sm text-gray-500 dark:text-gray-400"
17977
+ }, "Loading\u2026");
17978
+ }
17979
+ return /* @__PURE__ */ React.createElement("div", {
17980
+ className: "space-y-4"
17981
+ }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", {
17982
+ className: "text-sm font-semibold text-gray-900 dark:text-white"
17983
+ }, "Google (Web)"), /* @__PURE__ */ React.createElement("p", {
17984
+ className: "mt-1 text-xs text-gray-500 dark:text-gray-400"
17985
+ }, "Create a Web OAuth client in Google Cloud Console, add the redirect URI below, then paste Client ID and Client Secret here. Only existing admin/vendor users (matching Google email) can sign in.")), /* @__PURE__ */ React.createElement("div", {
17986
+ className: "flex items-center justify-between gap-2"
17987
+ }, /* @__PURE__ */ React.createElement(Label3, {
17988
+ htmlFor: `${settingsGroup}-googleEnabled`,
17989
+ className: "text-sm"
17990
+ }, "Enable Google sign-in"), /* @__PURE__ */ React.createElement(Switch, {
17991
+ id: `${settingsGroup}-googleEnabled`,
17992
+ checked: googleEnabled,
17993
+ onCheckedChange: setGoogleEnabled
17994
+ })), /* @__PURE__ */ React.createElement("div", {
17995
+ className: "space-y-1"
17996
+ }, /* @__PURE__ */ React.createElement(Label3, {
17997
+ htmlFor: `${settingsGroup}-redirectUri`,
17998
+ className: "text-sm"
17999
+ }, "Redirect URI (add in Google Console)"), /* @__PURE__ */ React.createElement("div", {
18000
+ className: "flex gap-2"
18001
+ }, /* @__PURE__ */ React.createElement(Input, {
18002
+ id: `${settingsGroup}-redirectUri`,
18003
+ value: redirectUri,
18004
+ readOnly: true,
18005
+ className: "h-9 flex-1 font-mono text-xs"
18006
+ }), /* @__PURE__ */ React.createElement(Button, {
18007
+ type: "button",
18008
+ size: "sm",
18009
+ variant: "outline",
18010
+ onClick: handleCopyRedirect,
18011
+ className: "shrink-0 gap-1"
18012
+ }, copied ? /* @__PURE__ */ React.createElement(Check, {
18013
+ className: "h-3.5 w-3.5"
18014
+ }) : /* @__PURE__ */ React.createElement(Copy, {
18015
+ className: "h-3.5 w-3.5"
18016
+ }), copied ? "Copied" : "Copy")), /* @__PURE__ */ React.createElement("p", {
18017
+ className: "text-xs text-gray-500 dark:text-gray-400"
18018
+ }, "Must match ", /* @__PURE__ */ React.createElement("code", {
18019
+ className: "text-[11px]"
18020
+ }, "NEXTAUTH_URL"), " for this environment.")), /* @__PURE__ */ React.createElement("div", {
18021
+ className: "space-y-1"
18022
+ }, /* @__PURE__ */ React.createElement(Label3, {
18023
+ htmlFor: `${settingsGroup}-googleClientId`,
18024
+ className: "text-sm"
18025
+ }, "Client ID"), /* @__PURE__ */ React.createElement(Input, {
18026
+ id: `${settingsGroup}-googleClientId`,
18027
+ value: googleClientId,
18028
+ onChange: /* @__PURE__ */ __name((e) => setGoogleClientId(e.target.value), "onChange"),
18029
+ placeholder: "xxxxx.apps.googleusercontent.com",
18030
+ className: "h-9 text-sm",
18031
+ autoComplete: "off"
18032
+ })), /* @__PURE__ */ React.createElement("div", {
18033
+ className: "space-y-1"
18034
+ }, /* @__PURE__ */ React.createElement(Label3, {
18035
+ htmlFor: `${settingsGroup}-googleClientSecret`,
18036
+ className: "text-sm"
18037
+ }, "Client Secret ", hasSavedSecret && !googleClientSecret.trim() ? "(saved)" : ""), /* @__PURE__ */ React.createElement(Input, {
18038
+ id: `${settingsGroup}-googleClientSecret`,
18039
+ type: "password",
18040
+ value: googleClientSecret,
18041
+ onChange: /* @__PURE__ */ __name((e) => setGoogleClientSecret(e.target.value), "onChange"),
18042
+ placeholder: hasSavedSecret ? "Leave blank to keep saved secret" : "Paste client secret",
18043
+ className: "h-9 text-sm",
18044
+ autoComplete: "new-password"
18045
+ })), /* @__PURE__ */ React.createElement(Button, {
18046
+ size: "sm",
18047
+ onClick: handleSave,
18048
+ disabled: saving,
18049
+ className: "gap-1"
18050
+ }, /* @__PURE__ */ React.createElement(Save, {
18051
+ className: "h-3.5 w-3.5"
18052
+ }), saving ? "Saving\u2026" : "Save"));
18053
+ }
18054
+ var init_AuthProvidersPluginSettings = __esm({
18055
+ "src/admin/components/AuthProvidersPluginSettings.tsx"() {
18056
+ "use client";
18057
+ init_button();
18058
+ init_input();
18059
+ init_label();
18060
+ init_switch();
18061
+ __name(googleRedirectUri, "googleRedirectUri");
18062
+ __name(AuthProvidersPluginSettings, "AuthProvidersPluginSettings");
18063
+ }
18064
+ });
16505
18065
 
16506
18066
  // src/plugins/llm/chat-email-intent.ts
16507
18067
  function normalizeIntentKey(raw) {
@@ -16879,6 +18439,7 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
16879
18439
  const isEventNotifications = settingsGroup === "event_notifications";
16880
18440
  const isSocialMedia = settingsGroup === "social_media";
16881
18441
  const isMultiVendor = settingsGroup === "multi_vendor";
18442
+ const isAuthProviders = settingsGroup === "auth_providers";
16882
18443
  const [enabled, setEnabled] = useState(true);
16883
18444
  const [botName, setBotName] = useState("");
16884
18445
  const [icon, setIcon] = useState("");
@@ -17228,6 +18789,10 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17228
18789
  setWhatsappPhoneNumberId(String(data.phoneNumberId ?? data.WHATSAPP_PHONE_NUMBER_ID ?? "").trim());
17229
18790
  setWhatsappWabaId(String(data.wabaId ?? data.WHATSAPP_WABA_ID ?? "").trim());
17230
18791
  }
18792
+ if (isAuthProviders) {
18793
+ const googleOn = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
18794
+ setEnabled(googleOn);
18795
+ }
17231
18796
  if (isSocialMedia) {
17232
18797
  const savedLiToken = String(data.linkedin_access_token ?? "").trim();
17233
18798
  setLinkedinAccessToken(savedLiToken);
@@ -17265,7 +18830,8 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17265
18830
  isWhatsapp,
17266
18831
  isEventNotifications,
17267
18832
  isSocialMedia,
17268
- isMultiVendor
18833
+ isMultiVendor,
18834
+ isAuthProviders
17269
18835
  ]);
17270
18836
  useEffect(() => {
17271
18837
  if (!isSocialMedia || loading || linkedInOrgsPrefetchedRef.current) return;
@@ -17711,6 +19277,18 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
17711
19277
  }
17712
19278
  };
17713
19279
  }
19280
+ if (isAuthProviders) {
19281
+ return {
19282
+ enabled: {
19283
+ value: enabled ? "true" : "false",
19284
+ type: "public"
19285
+ },
19286
+ googleEnabled: {
19287
+ value: enabled ? "true" : "false",
19288
+ type: "public"
19289
+ }
19290
+ };
19291
+ }
17714
19292
  if (isSocialMedia) {
17715
19293
  const payload2 = {
17716
19294
  enabled: {
@@ -18196,6 +19774,15 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
18196
19774
  toast.success("Removed from agent");
18197
19775
  await fetchAttachedKnowledge(slug);
18198
19776
  }, "handleUnlinkKbDoc");
19777
+ if (isAuthProviders) {
19778
+ return /* @__PURE__ */ React.createElement(AuthProvidersPluginSettings, {
19779
+ settingsGroup,
19780
+ onEnabledChange: /* @__PURE__ */ __name((value) => {
19781
+ setEnabled(value);
19782
+ onSaved?.();
19783
+ }, "onEnabledChange")
19784
+ });
19785
+ }
18199
19786
  if (loading) return /* @__PURE__ */ React.createElement("div", {
18200
19787
  className: "text-sm text-gray-500 dark:text-gray-400"
18201
19788
  }, "Loading...");
@@ -19532,6 +21119,10 @@ function PluginsPage() {
19532
21119
  const en = pluginDescriptors.find((p) => p.name === "event_notifications");
19533
21120
  if (en) setSelectedName(en.name);
19534
21121
  }
21122
+ if (q === "auth_providers" || q === "auth-providers" || q === "authentication") {
21123
+ const ap = pluginDescriptors.find((p) => p.name === "auth_providers");
21124
+ if (ap) setSelectedName(ap.name);
21125
+ }
19535
21126
  }, [
19536
21127
  searchParams,
19537
21128
  pluginDescriptors
@@ -19539,10 +21130,13 @@ function PluginsPage() {
19539
21130
  useEffect(() => {
19540
21131
  pluginDescriptors.forEach((p) => {
19541
21132
  if (!p.settingsGroup) return;
19542
- fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => setEnabledMap((prev) => ({
19543
- ...prev,
19544
- [p.name]: data.enabled !== "false"
19545
- }))).catch(() => {
21133
+ fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
21134
+ const on = p.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled !== "false" && data.googleEnabled !== "false" && data.enabled === "true" : data.enabled !== "false";
21135
+ setEnabledMap((prev) => ({
21136
+ ...prev,
21137
+ [p.name]: on
21138
+ }));
21139
+ }).catch(() => {
19546
21140
  });
19547
21141
  });
19548
21142
  }, [
@@ -19552,10 +21146,13 @@ function PluginsPage() {
19552
21146
  const refreshEnabled = /* @__PURE__ */ __name((name) => {
19553
21147
  const d = pluginDescriptors.find((p) => p.name === name);
19554
21148
  if (!d?.settingsGroup) return;
19555
- fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => setEnabledMap((prev) => ({
19556
- ...prev,
19557
- [name]: data.enabled !== "false"
19558
- }))).catch(() => {
21149
+ fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
21150
+ const on = d.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false" : data.enabled !== "false";
21151
+ setEnabledMap((prev) => ({
21152
+ ...prev,
21153
+ [name]: on
21154
+ }));
21155
+ }).catch(() => {
19559
21156
  });
19560
21157
  }, "refreshEnabled");
19561
21158
  return /* @__PURE__ */ React.createElement("div", {
@@ -19637,6 +21234,7 @@ var init_PluginsPage = __esm({
19637
21234
  init_checkbox();
19638
21235
  init_select();
19639
21236
  init_EventNotificationsPluginSettings();
21237
+ init_AuthProvidersPluginSettings();
19640
21238
  init_ImageOrUrlField();
19641
21239
  init_chat_email_intent();
19642
21240
  init_llm_agent_scope();
@@ -19655,7 +21253,8 @@ var init_PluginsPage = __esm({
19655
21253
  pg_boss: CalendarClock,
19656
21254
  share: Share2,
19657
21255
  whatsapp: MessageCircle,
19658
- event_notifications: Bell
21256
+ event_notifications: Bell,
21257
+ auth_providers: KeyRound
19659
21258
  };
19660
21259
  __name(normalizeSmsProviderChoice, "normalizeSmsProviderChoice");
19661
21260
  __name(normalizeMsg91ApiMode, "normalizeMsg91ApiMode");
@@ -19675,183 +21274,6 @@ var init_PluginsPage = __esm({
19675
21274
  }
19676
21275
  });
19677
21276
 
19678
- // src/lib/vendor-role-defaults.ts
19679
- function full() {
19680
- return {
19681
- canCreate: true,
19682
- canRead: true,
19683
- canUpdate: true,
19684
- canDelete: true
19685
- };
19686
- }
19687
- function read() {
19688
- return {
19689
- canCreate: false,
19690
- canRead: true,
19691
- canUpdate: false,
19692
- canDelete: false
19693
- };
19694
- }
19695
- function none() {
19696
- return {
19697
- canCreate: false,
19698
- canRead: false,
19699
- canUpdate: false,
19700
- canDelete: false
19701
- };
19702
- }
19703
- function readUpdate() {
19704
- return {
19705
- canCreate: false,
19706
- canRead: true,
19707
- canUpdate: true,
19708
- canDelete: false
19709
- };
19710
- }
19711
- function storePerms(spec) {
19712
- const resolve = /* @__PURE__ */ __name((v) => {
19713
- if (v === "full") return full();
19714
- if (v === "read") return read();
19715
- if (v === "none") return none();
19716
- return v;
19717
- }, "resolve");
19718
- const out = {};
19719
- for (const entity of STORE_ENTITIES) {
19720
- out[entity] = resolve(spec[entity] ?? "none");
19721
- }
19722
- return out;
19723
- }
19724
- var STORE_ENTITIES;
19725
- var init_vendor_role_defaults = __esm({
19726
- "src/lib/vendor-role-defaults.ts"() {
19727
- init_vendor_scope();
19728
- [
19729
- ...VENDOR_STORE_RBAC_ENTITIES,
19730
- "dashboard",
19731
- "settings",
19732
- "team",
19733
- "roles"
19734
- ];
19735
- STORE_ENTITIES = [
19736
- ...VENDOR_STORE_RBAC_ENTITIES
19737
- ];
19738
- __name(full, "full");
19739
- __name(read, "read");
19740
- __name(none, "none");
19741
- __name(readUpdate, "readUpdate");
19742
- __name(storePerms, "storePerms");
19743
- [
19744
- {
19745
- name: "Owner",
19746
- description: "Full store access and team/role management",
19747
- isSystem: true,
19748
- isOwnerRole: true,
19749
- permissions: {
19750
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
19751
- e,
19752
- "full"
19753
- ]))),
19754
- dashboard: read(),
19755
- settings: readUpdate(),
19756
- team: full(),
19757
- roles: full()
19758
- }
19759
- },
19760
- {
19761
- name: "Manager",
19762
- description: "Day-to-day store operations",
19763
- isSystem: true,
19764
- isOwnerRole: false,
19765
- permissions: {
19766
- ...storePerms({
19767
- products: "full",
19768
- collections: "full",
19769
- brands: "full",
19770
- product_categories: "full",
19771
- orders: "full",
19772
- vendor_customers: "full",
19773
- discounts: "full",
19774
- order_discounts: "full",
19775
- order_addresses: "full",
19776
- payments: "read",
19777
- taxes: "read",
19778
- attributes: "read"
19779
- }),
19780
- dashboard: read(),
19781
- settings: read(),
19782
- team: none(),
19783
- roles: none()
19784
- }
19785
- },
19786
- {
19787
- name: "Catalog Staff",
19788
- description: "Manage products and catalog content",
19789
- isSystem: true,
19790
- isOwnerRole: false,
19791
- permissions: {
19792
- ...storePerms({
19793
- products: "full",
19794
- collections: "full",
19795
- brands: "full",
19796
- product_categories: "full",
19797
- attributes: "full",
19798
- orders: "read",
19799
- vendor_customers: "read"
19800
- }),
19801
- dashboard: read(),
19802
- settings: none(),
19803
- team: none(),
19804
- roles: none()
19805
- }
19806
- },
19807
- {
19808
- name: "Fulfillment",
19809
- description: "Orders and fulfillment",
19810
- isSystem: true,
19811
- isOwnerRole: false,
19812
- permissions: {
19813
- ...storePerms({
19814
- products: "read",
19815
- orders: "full",
19816
- order_addresses: "full",
19817
- order_discounts: "read",
19818
- vendor_customers: "read"
19819
- }),
19820
- dashboard: read(),
19821
- settings: none(),
19822
- team: none(),
19823
- roles: none()
19824
- }
19825
- },
19826
- {
19827
- name: "Viewer",
19828
- description: "Read-only access to store data",
19829
- isSystem: true,
19830
- isOwnerRole: false,
19831
- permissions: {
19832
- ...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
19833
- e,
19834
- "read"
19835
- ]))),
19836
- dashboard: read(),
19837
- settings: none(),
19838
- team: none(),
19839
- roles: none()
19840
- }
19841
- }
19842
- ];
19843
- }
19844
- });
19845
-
19846
- // src/auth/rbac-debug.ts
19847
- var init_rbac_debug = __esm({
19848
- "src/auth/rbac-debug.ts"() {
19849
- init_permission_entities();
19850
- init_vendor_role_defaults();
19851
- init_vendor_scope();
19852
- }
19853
- });
19854
-
19855
21277
  // src/auth/helpers.ts
19856
21278
  function canManageRoles(user) {
19857
21279
  return !!(user?.email && user.isRBACAdmin);
@@ -20009,23 +21431,6 @@ function VendorRolesPage() {
20009
21431
  return next;
20010
21432
  });
20011
21433
  }, "setAllRows");
20012
- const toggleEntityRow = /* @__PURE__ */ __name((entity) => {
20013
- const isOwnerOnlyEntity = entity === "team" || entity === "roles";
20014
- setMatrix((prev) => {
20015
- const current = prev[entity];
20016
- const isAllChecked = current?.canRead && (isOwnerOnlyEntity || current.canCreate && current.canUpdate && current.canDelete);
20017
- return {
20018
- ...prev,
20019
- [entity]: {
20020
- entity,
20021
- canRead: !isAllChecked,
20022
- canCreate: isOwnerOnlyEntity ? false : !isAllChecked,
20023
- canUpdate: isOwnerOnlyEntity ? false : !isAllChecked,
20024
- canDelete: isOwnerOnlyEntity ? false : !isAllChecked
20025
- }
20026
- };
20027
- });
20028
- }, "toggleEntityRow");
20029
21434
  const saveMatrix = /* @__PURE__ */ __name(async () => {
20030
21435
  if (!selectedId) return;
20031
21436
  setSaving(true);
@@ -20128,9 +21533,7 @@ function VendorRolesPage() {
20128
21533
  className: "text-base font-semibold text-white"
20129
21534
  }, "Vendor Store Roles & Permissions"), /* @__PURE__ */ React25__default.createElement("p", {
20130
21535
  className: "mt-0.5 text-xs text-gray-300"
20131
- }, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles). Permissions are stored in the ", /* @__PURE__ */ React25__default.createElement("code", {
20132
- className: "font-mono bg-gray-700 px-1 py-0.5 rounded text-gray-200"
20133
- }, "vendor_role_permissions"), " table and apply directly to team members assigned to that role.")), /* @__PURE__ */ React25__default.createElement("div", {
21536
+ }, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles).")), /* @__PURE__ */ React25__default.createElement("div", {
20134
21537
  className: "min-w-0 p-6"
20135
21538
  }, error ? /* @__PURE__ */ React25__default.createElement("p", {
20136
21539
  className: "mb-4 text-sm text-red-600 dark:text-red-400"
@@ -20221,31 +21624,35 @@ function VendorRolesPage() {
20221
21624
  }), " Clear All"))), /* @__PURE__ */ React25__default.createElement("div", {
20222
21625
  className: "overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700"
20223
21626
  }, /* @__PURE__ */ React25__default.createElement("table", {
20224
- className: "min-w-full text-xs"
21627
+ className: "min-w-full text-sm"
20225
21628
  }, /* @__PURE__ */ React25__default.createElement("thead", {
20226
- className: "bg-gray-100 text-left text-gray-700 dark:bg-gray-900 dark:text-gray-300"
20227
- }, /* @__PURE__ */ React25__default.createElement("tr", null, /* @__PURE__ */ React25__default.createElement("th", {
20228
- className: "px-3 py-2 font-medium"
20229
- }, "Entity"), /* @__PURE__ */ React25__default.createElement("th", {
20230
- 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"
20231
21638
  }, "Create"), /* @__PURE__ */ React25__default.createElement("th", {
20232
- 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"
20233
21640
  }, "Read"), /* @__PURE__ */ React25__default.createElement("th", {
20234
- 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"
20235
21642
  }, "Update"), /* @__PURE__ */ React25__default.createElement("th", {
20236
- className: "px-3 py-2 font-medium"
20237
- }, "Delete"), /* @__PURE__ */ React25__default.createElement("th", {
20238
- className: "px-3 py-2 font-medium text-right"
20239
- }, "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) => {
20240
21645
  const isOwnerOnlyEntity = entity === "team" || entity === "roles";
20241
21646
  return /* @__PURE__ */ React25__default.createElement("tr", {
20242
21647
  key: entity,
20243
- 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"
20244
21649
  }, /* @__PURE__ */ React25__default.createElement("td", {
20245
- className: "px-3 py-2 font-mono text-gray-800 dark:text-gray-200 font-medium"
20246
- }, entity, isOwnerOnlyEntity && /* @__PURE__ */ React25__default.createElement("span", {
20247
- className: "ml-2 text-[10px] text-amber-600 dark:text-amber-400 font-normal"
20248
- }, "(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)"))), [
20249
21656
  "canCreate",
20250
21657
  "canRead",
20251
21658
  "canUpdate",
@@ -20254,21 +21661,15 @@ function VendorRolesPage() {
20254
21661
  const disabled = isOwnerOnlyEntity && key !== "canRead";
20255
21662
  return /* @__PURE__ */ React25__default.createElement("td", {
20256
21663
  key,
20257
- className: "px-3 py-2"
21664
+ className: "px-3 py-2.5 text-center"
20258
21665
  }, /* @__PURE__ */ React25__default.createElement("input", {
20259
21666
  type: "checkbox",
20260
21667
  checked: disabled ? false : !!matrix[entity]?.[key],
20261
21668
  disabled,
20262
21669
  onChange: /* @__PURE__ */ __name(() => toggle(entity, key), "onChange"),
20263
- 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"
20264
21671
  }));
20265
- }), /* @__PURE__ */ React25__default.createElement("td", {
20266
- className: "px-3 py-2 text-right"
20267
- }, /* @__PURE__ */ React25__default.createElement("button", {
20268
- type: "button",
20269
- onClick: /* @__PURE__ */ __name(() => toggleEntityRow(entity), "onClick"),
20270
- className: "text-[11px] text-blue-600 hover:underline dark:text-blue-400"
20271
- }, "Toggle")));
21672
+ }));
20272
21673
  })))))))), /* @__PURE__ */ React25__default.createElement(Dialog, {
20273
21674
  open: deleteOpen,
20274
21675
  onOpenChange: setDeleteOpen
@@ -20629,7 +22030,7 @@ function PlatformRolesPage() {
20629
22030
  className: "p-2"
20630
22031
  }, /* @__PURE__ */ React25__default.createElement("div", {
20631
22032
  className: "flex items-center justify-center gap-1"
20632
- }, /* @__PURE__ */ React25__default.createElement("span", null, "C"), /* @__PURE__ */ React25__default.createElement("input", {
22033
+ }, /* @__PURE__ */ React25__default.createElement("span", null, "Create"), /* @__PURE__ */ React25__default.createElement("input", {
20633
22034
  type: "checkbox",
20634
22035
  checked: isColumnAllChecked("canCreate"),
20635
22036
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canCreate"), "onChange")
@@ -20637,7 +22038,7 @@ function PlatformRolesPage() {
20637
22038
  className: "p-2"
20638
22039
  }, /* @__PURE__ */ React25__default.createElement("div", {
20639
22040
  className: "flex items-center justify-center gap-1"
20640
- }, /* @__PURE__ */ React25__default.createElement("span", null, "R"), /* @__PURE__ */ React25__default.createElement("input", {
22041
+ }, /* @__PURE__ */ React25__default.createElement("span", null, "Read"), /* @__PURE__ */ React25__default.createElement("input", {
20641
22042
  type: "checkbox",
20642
22043
  checked: isColumnAllChecked("canRead"),
20643
22044
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canRead"), "onChange")
@@ -20645,7 +22046,7 @@ function PlatformRolesPage() {
20645
22046
  className: "p-2"
20646
22047
  }, /* @__PURE__ */ React25__default.createElement("div", {
20647
22048
  className: "flex items-center justify-center gap-1"
20648
- }, /* @__PURE__ */ React25__default.createElement("span", null, "U"), /* @__PURE__ */ React25__default.createElement("input", {
22049
+ }, /* @__PURE__ */ React25__default.createElement("span", null, "Update"), /* @__PURE__ */ React25__default.createElement("input", {
20649
22050
  type: "checkbox",
20650
22051
  checked: isColumnAllChecked("canUpdate"),
20651
22052
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canUpdate"), "onChange")
@@ -20653,7 +22054,7 @@ function PlatformRolesPage() {
20653
22054
  className: "p-2"
20654
22055
  }, /* @__PURE__ */ React25__default.createElement("div", {
20655
22056
  className: "flex items-center justify-center gap-1"
20656
- }, /* @__PURE__ */ React25__default.createElement("span", null, "D"), /* @__PURE__ */ React25__default.createElement("input", {
22057
+ }, /* @__PURE__ */ React25__default.createElement("span", null, "Delete"), /* @__PURE__ */ React25__default.createElement("input", {
20657
22058
  type: "checkbox",
20658
22059
  checked: isColumnAllChecked("canDelete"),
20659
22060
  onChange: /* @__PURE__ */ __name(() => toggleColumn("canDelete"), "onChange")
@@ -21146,39 +22547,90 @@ var init_VendorTeamPage = __esm({
21146
22547
  __name(VendorTeamPage, "VendorTeamPage");
21147
22548
  }
21148
22549
  });
21149
-
21150
- // src/lib/vendor-profile.ts
21151
- function validateIndiaTaxIds(gstin, pan) {
21152
- if (gstin && !/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin)) {
21153
- 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
+ };
22556
+ }
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;
21154
22590
  }
21155
- if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan)) {
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())) {
21156
22605
  return "Invalid PAN format (e.g. ABCDE1234F)";
21157
22606
  }
21158
22607
  return null;
21159
22608
  }
21160
22609
  function validateAadhaar(aadhaar) {
21161
22610
  if (!aadhaar) return null;
21162
- if (!/^[0-9]{12}$/.test(aadhaar)) {
22611
+ if (!/^[0-9]{12}$/.test(aadhaar.replace(/\s+/g, ""))) {
21163
22612
  return "Invalid Aadhaar number (12 digits)";
21164
22613
  }
21165
22614
  return null;
21166
22615
  }
21167
22616
  function validatePersonKyc(person, options) {
21168
22617
  const required = options?.required === true;
21169
- if (required && !person.aadhaarNo) return "Aadhaar number is required";
21170
- 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";
21171
22620
  const aadhaarErr = validateAadhaar(person.aadhaarNo ?? null);
21172
22621
  if (aadhaarErr) return aadhaarErr;
21173
- const panErr = validateIndiaTaxIds(null, person.panNo ?? null);
22622
+ const panErr = validateIndiaTaxIds(null, person.panNo ?? null, {
22623
+ requiredPan: required
22624
+ });
21174
22625
  if (panErr) return panErr;
21175
22626
  return null;
21176
22627
  }
21177
22628
  function readOwnerDesignation(metadata) {
21178
- const v = metadata?.ownerDesignation;
21179
- return typeof v === "string" ? v : "";
22629
+ if (!metadata || typeof metadata !== "object") return "";
22630
+ const d = metadata.ownerDesignation;
22631
+ return typeof d === "string" ? d.trim() : "";
21180
22632
  }
21181
- var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES;
22633
+ var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES, COUNTRY_PHONE_CODES;
21182
22634
  var init_vendor_profile = __esm({
21183
22635
  "src/lib/vendor-profile.ts"() {
21184
22636
  VENDOR_REGISTRATION_STATUSES = [
@@ -21225,7 +22677,34 @@ var init_vendor_profile = __esm({
21225
22677
  label: "Other"
21226
22678
  }
21227
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");
21228
22706
  new Set(VENDOR_REGISTRATION_STATUSES.map((s) => s.value));
22707
+ __name(validateGstin, "validateGstin");
21229
22708
  __name(validateIndiaTaxIds, "validateIndiaTaxIds");
21230
22709
  __name(validateAadhaar, "validateAadhaar");
21231
22710
  __name(validatePersonKyc, "validatePersonKyc");
@@ -21828,10 +23307,26 @@ function SubmissionDetailPage({ submissionId }) {
21828
23307
  }
21829
23308
  const formName = submission.form?.name ?? `Form #${submission.formId}`;
21830
23309
  const contact = submission.contact;
21831
- const fieldLabel = /* @__PURE__ */ __name((key) => {
21832
- const field = submission.form?.fields?.find((f) => String(f.id) === key);
21833
- return field?.label ?? key;
21834
- }, "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");
21835
23330
  return /* @__PURE__ */ React.createElement("div", {
21836
23331
  className: "rounded-lg bg-white shadow-md"
21837
23332
  }, /* @__PURE__ */ React.createElement(DetailPageHeader, {
@@ -21890,7 +23385,7 @@ function SubmissionDetailPage({ submissionId }) {
21890
23385
  key
21891
23386
  }, /* @__PURE__ */ React.createElement("td", {
21892
23387
  className: "py-2 px-3 text-gray-600 font-medium"
21893
- }, fieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
23388
+ }, resolveFieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
21894
23389
  className: "py-2 px-3 text-gray-900 break-words min-w-0"
21895
23390
  }, value === null || value === void 0 ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value)))))))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
21896
23391
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
@@ -23545,7 +25040,7 @@ function OrderPlacementPage({ editOrderId }) {
23545
25040
  productQuery
23546
25041
  ]);
23547
25042
  const recalcPreview = useCallback(async () => {
23548
- const orderLines = lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => ({
25043
+ const orderLines = lines.filter((l) => !l.key.startsWith("reward-")).map((l) => ({
23549
25044
  productId: l.productId,
23550
25045
  quantity: l.quantity
23551
25046
  }));
@@ -23569,19 +25064,22 @@ function OrderPlacementPage({ editOrderId }) {
23569
25064
  body: JSON.stringify({
23570
25065
  orderLines,
23571
25066
  currency,
23572
- discountId: coupons[0]?.discountId ?? null
25067
+ discountId: coupons[0]?.discountId ?? null,
25068
+ discountIds: coupons.map((c) => c.discountId)
23573
25069
  })
23574
25070
  });
23575
25071
  if (!res.ok) throw new Error("Calculate failed");
23576
25072
  const data = await res.json();
23577
25073
  const correctedLines = data.lines.map((cl) => {
23578
25074
  if (cl.productId == null) return cl;
23579
- const cached = productCache.current.get(cl.productId);
25075
+ const pid = Number(cl.productId);
25076
+ const cached = productCache.current.get(pid);
23580
25077
  if (!cl.found && cached) {
23581
- const unitPrice2 = cached.price;
25078
+ const unitPrice2 = Number(cached.price ?? 0);
23582
25079
  const subtotal3 = unitPrice2 * cl.quantity;
23583
25080
  return {
23584
25081
  ...cl,
25082
+ productId: pid,
23585
25083
  found: true,
23586
25084
  unitPrice: unitPrice2,
23587
25085
  subtotal: subtotal3,
@@ -23589,15 +25087,16 @@ function OrderPlacementPage({ editOrderId }) {
23589
25087
  total: subtotal3
23590
25088
  };
23591
25089
  }
23592
- const unitPrice = cached?.price ?? cl.unitPrice;
25090
+ const unitPrice = Number(cached?.price ?? cl.unitPrice ?? 0);
23593
25091
  const subtotal2 = unitPrice * cl.quantity;
23594
- const taxRate = cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal : 0);
23595
- 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);
23596
25094
  const discountedBase = Math.max(0, subtotal2 - lineDiscount);
23597
25095
  const tax2 = discountedBase * taxRate / 100;
23598
25096
  const total2 = discountedBase + tax2;
23599
25097
  return {
23600
25098
  ...cl,
25099
+ productId: pid,
23601
25100
  unitPrice,
23602
25101
  subtotal: subtotal2,
23603
25102
  taxRate,
@@ -23605,13 +25104,14 @@ function OrderPlacementPage({ editOrderId }) {
23605
25104
  total: total2
23606
25105
  };
23607
25106
  });
23608
- const rewardDisplayLines = lines.filter((l) => rewardProductIds.current.has(l.productId)).map((l) => {
23609
- 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);
23610
25110
  return {
23611
- productId: l.productId,
25111
+ productId: pid,
23612
25112
  productName: l.label,
23613
25113
  found: true,
23614
- unitPrice: cached?.price ?? 0,
25114
+ unitPrice: Number(cached?.price ?? 0),
23615
25115
  quantity: l.quantity,
23616
25116
  subtotal: 0,
23617
25117
  tax: 0,
@@ -23653,7 +25153,7 @@ function OrderPlacementPage({ editOrderId }) {
23653
25153
  useEffect(() => {
23654
25154
  const automaticCoupons = coupons.filter((c) => c.isAutomatic);
23655
25155
  if (automaticCoupons.length === 0) return;
23656
- const nonRewardLines = lines.filter((l) => !rewardProductIds.current.has(l.productId));
25156
+ const nonRewardLines = lines.filter((l) => !l.key.startsWith("reward-"));
23657
25157
  if (nonRewardLines.length === 0) {
23658
25158
  const autoIds = new Set(automaticCoupons.map((c) => c.discountId));
23659
25159
  const rewardPids = new Set(automaticCoupons.flatMap((c) => c.rewardLines.map((r) => r.productId)));
@@ -23733,7 +25233,7 @@ function OrderPlacementPage({ editOrderId }) {
23733
25233
  cancelled = true;
23734
25234
  };
23735
25235
  }, [
23736
- 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(","),
23737
25237
  coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
23738
25238
  ]);
23739
25239
  function handleRemoveCoupon(couponCode) {
@@ -23855,7 +25355,10 @@ function OrderPlacementPage({ editOrderId }) {
23855
25355
  const calcByProductId = useMemo(() => {
23856
25356
  const m = /* @__PURE__ */ new Map();
23857
25357
  for (const l of preview?.lines ?? []) {
23858
- 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
+ }
23859
25362
  }
23860
25363
  return m;
23861
25364
  }, [
@@ -23877,13 +25380,13 @@ function OrderPlacementPage({ editOrderId }) {
23877
25380
  }
23878
25381
  const unknown = preview?.lines.filter((l) => !l.found) ?? [];
23879
25382
  if (unknown.length > 0) {
23880
- 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(", ")}`);
23881
25384
  return;
23882
25385
  }
23883
25386
  setSubmitting(true);
25387
+ let resolvedContactId = effectiveContactId;
23884
25388
  try {
23885
- let resolvedContactId = placeForSomeoneElse ? subContactId : null;
23886
- if (!resolvedContactId && effectiveEmail) {
25389
+ if (resolvedContactId == null && effectiveEmail) {
23887
25390
  try {
23888
25391
  const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
23889
25392
  if (res2.ok) {
@@ -23905,12 +25408,12 @@ function OrderPlacementPage({ editOrderId }) {
23905
25408
  billingAddress,
23906
25409
  shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
23907
25410
  orderLines: lines.map((l) => {
23908
- const isReward = rewardProductIds.current.has(l.productId);
23909
- 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));
23910
25413
  return {
23911
25414
  productId: l.productId,
23912
25415
  quantity: l.quantity,
23913
- 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)
23914
25417
  };
23915
25418
  })
23916
25419
  };
@@ -28886,8 +30389,8 @@ function ProductEditPage({ productId }) {
28886
30389
  const [price, setPrice] = useState(0);
28887
30390
  const [defaultPriceStr, setDefaultPriceStr] = useState("");
28888
30391
  const [pricingConfig, setPricingConfig] = useState(DEFAULT_PRICING_CONFIG);
28889
- const [compareAtPrice, setCompareAtPrice] = useState(0);
28890
- const [quantity, setQuantity] = useState(1);
30392
+ const [compareAtPrice, setCompareAtPrice] = useState("");
30393
+ const [quantity, setQuantity] = useState("1");
28891
30394
  const [status, setStatus] = useState("draft");
28892
30395
  const [approvalStatus, setApprovalStatus] = useState("pending");
28893
30396
  const [rejectionReason, setRejectionReason] = useState("");
@@ -29108,8 +30611,8 @@ function ProductEditPage({ productId }) {
29108
30611
  setDefaultPriceStr(product.price != null && Number.isFinite(Number(product.price)) ? String(product.price) : "");
29109
30612
  setPrice(product.price != null ? Number(product.price) : 0);
29110
30613
  const rawCompare = product.compareAtPrice != null ? Number(product.compareAtPrice) : 0;
29111
- setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
29112
- setQuantity(product.quantity ?? 1);
30614
+ setCompareAtPrice(rawCompare != null && Number.isFinite(rawCompare) ? String(rawCompare) : "");
30615
+ setQuantity(product.quantity != null ? String(product.quantity) : "1");
29113
30616
  setStatus(product.status ?? "draft");
29114
30617
  setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
29115
30618
  setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
@@ -29276,22 +30779,6 @@ function ProductEditPage({ productId }) {
29276
30779
  return pricingConfig.defaultCurrency || "INR";
29277
30780
  }
29278
30781
  })();
29279
- const handleNumberChange = /* @__PURE__ */ __name((setter, options) => (e) => {
29280
- const value = e.target.value;
29281
- if (value === "") {
29282
- setter(0);
29283
- return;
29284
- }
29285
- let num = options?.integer ? Number.parseInt(value, 10) : Number(value);
29286
- if (Number.isNaN(num)) {
29287
- setter(0);
29288
- return;
29289
- }
29290
- if (options?.min !== void 0) {
29291
- num = Math.max(options.min, num);
29292
- }
29293
- setter(num);
29294
- }, "handleNumberChange");
29295
30782
  const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
29296
30783
  approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
29297
30784
  setRejectDraft(rejectionReason);
@@ -29364,12 +30851,13 @@ function ProductEditPage({ productId }) {
29364
30851
  return;
29365
30852
  }
29366
30853
  }
29367
- const quantityErrors = validateProductQuantity(quantity, hasVariants, parsedVariants);
30854
+ const quantityValue = quantity === "" ? 0 : Number(quantity);
30855
+ const quantityErrors = validateProductQuantity(quantityValue, hasVariants, parsedVariants);
29368
30856
  if (quantityErrors.length) {
29369
30857
  setErrors(quantityErrors);
29370
30858
  return;
29371
30859
  }
29372
- const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantity;
30860
+ const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantityValue;
29373
30861
  setSaving(true);
29374
30862
  try {
29375
30863
  const compareAtPriceValue = compareAtPrice ? Math.round(Number(compareAtPrice) * 100) / 100 : null;
@@ -29983,9 +31471,8 @@ function ProductEditPage({ productId }) {
29983
31471
  type: "number",
29984
31472
  value: compareAtPrice,
29985
31473
  className: inputCls3,
29986
- onChange: handleNumberChange(setCompareAtPrice, {
29987
- min: 0
29988
- })
31474
+ min: 0,
31475
+ onChange: /* @__PURE__ */ __name((e) => setCompareAtPrice(e.target.value), "onChange")
29989
31476
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
29990
31477
  className: labelCls3
29991
31478
  }, "Contact form"), /* @__PURE__ */ React.createElement("select", {
@@ -30008,10 +31495,7 @@ function ProductEditPage({ productId }) {
30008
31495
  min: 0,
30009
31496
  step: 1,
30010
31497
  value: hasVariants ? productQuantityFromVariants(variantsFromForm(variantRows, pricingConfig.defaultCurrency)) : quantity,
30011
- onChange: handleNumberChange(setQuantity, {
30012
- min: 0,
30013
- integer: true
30014
- }),
31498
+ onChange: /* @__PURE__ */ __name((e) => setQuantity(e.target.value), "onChange"),
30015
31499
  className: `${inputCls3}${hasVariants ? " bg-gray-100" : ""}`,
30016
31500
  required: !hasVariants,
30017
31501
  readOnly: hasVariants,
@@ -30088,8 +31572,18 @@ function ProductEditPage({ productId }) {
30088
31572
  value: row.taxId === "" ? "" : String(row.taxId),
30089
31573
  onChange: /* @__PURE__ */ __name((e) => {
30090
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);
30091
31584
  setTaxRow(i, {
30092
- taxId: v === "" ? "" : Number(v)
31585
+ taxId,
31586
+ rate: selectedTax?.rate != null ? String(selectedTax.rate) : ""
30093
31587
  });
30094
31588
  }, "onChange"),
30095
31589
  className: inputCls3
@@ -31914,6 +33408,35 @@ function EventEditPage({ eventId }) {
31914
33408
  const [saving, setSaving] = useState(false);
31915
33409
  const [errors, setErrors] = useState([]);
31916
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");
31917
33440
  const [name, setName] = useState("");
31918
33441
  const [slug, setSlug] = useState("");
31919
33442
  const [description, setDescription] = useState("");
@@ -32733,7 +34256,19 @@ function EventEditPage({ eventId }) {
32733
34256
  [key]: value
32734
34257
  })), "onChange")
32735
34258
  }))))
32736
- }), 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", {
32737
34272
  className: "border-t border-gray-200 px-4 py-4 sm:px-6"
32738
34273
  }, /* @__PURE__ */ React.createElement(EventProductsSection, {
32739
34274
  eventId: eventRecordId,
@@ -33078,6 +34613,18 @@ function ComboEditPage({ comboId }) {
33078
34613
  ]);
33079
34614
  return;
33080
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
+ }
33081
34628
  if (!priceStr.trim()) {
33082
34629
  setErrors([
33083
34630
  `Price (${defaultCurrency}) is required`
@@ -33367,18 +34914,20 @@ function ComboEditPage({ comboId }) {
33367
34914
  className: "grid grid-cols-2 gap-4"
33368
34915
  }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
33369
34916
  className: "block text-xs font-medium text-gray-600 mb-1"
33370
- }, "Starts at"), /* @__PURE__ */ React.createElement("input", {
34917
+ }, "Starts at *"), /* @__PURE__ */ React.createElement("input", {
33371
34918
  type: "datetime-local",
33372
34919
  value: startsAt,
33373
34920
  onChange: /* @__PURE__ */ __name((e) => setStartsAt(e.target.value), "onChange"),
33374
- 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
33375
34923
  })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
33376
34924
  className: "block text-xs font-medium text-gray-600 mb-1"
33377
- }, "Ends at"), /* @__PURE__ */ React.createElement("input", {
34925
+ }, "Ends at *"), /* @__PURE__ */ React.createElement("input", {
33378
34926
  type: "datetime-local",
33379
34927
  value: endsAt,
33380
34928
  onChange: /* @__PURE__ */ __name((e) => setEndsAt(e.target.value), "onChange"),
33381
- 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
33382
34931
  })))))),
33383
34932
  sidebar: /* @__PURE__ */ React.createElement(React.Fragment, null)
33384
34933
  }));
@@ -33464,7 +35013,8 @@ function VendorEditPage({ vendorId }) {
33464
35013
  const [website, setWebsite] = useState("");
33465
35014
  const [logo, setLogo] = useState("");
33466
35015
  const [email, setEmail] = useState("");
33467
- const [phone, setPhone] = useState("");
35016
+ const [storePhoneCode, setStorePhoneCode] = useState("+91");
35017
+ const [storePhoneNum, setStorePhoneNum] = useState("");
33468
35018
  const [addressLine1, setAddressLine1] = useState("");
33469
35019
  const [addressLine2, setAddressLine2] = useState("");
33470
35020
  const [city, setCity] = useState("");
@@ -33478,7 +35028,8 @@ function VendorEditPage({ vendorId }) {
33478
35028
  const [metadata, setMetadata] = useState(null);
33479
35029
  const [ownerName, setOwnerName] = useState("");
33480
35030
  const [ownerEmail, setOwnerEmail] = useState("");
33481
- const [ownerPhone, setOwnerPhone] = useState("");
35031
+ const [ownerPhoneCode, setOwnerPhoneCode] = useState("+91");
35032
+ const [ownerPhoneNum, setOwnerPhoneNum] = useState("");
33482
35033
  const [ownerDesignation, setOwnerDesignation] = useState("");
33483
35034
  const [ownerAadhaarNo, setOwnerAadhaarNo] = useState("");
33484
35035
  const [ownerPanNo, setOwnerPanNo] = useState("");
@@ -33502,7 +35053,7 @@ function VendorEditPage({ vendorId }) {
33502
35053
  website: website.trim() || null,
33503
35054
  logo: logo.trim() || null,
33504
35055
  email: email.trim() || null,
33505
- phone: phone.trim() || null,
35056
+ phone: formatPhoneWithCountryCode(storePhoneCode, storePhoneNum),
33506
35057
  addressLine1: addressLine1.trim() || null,
33507
35058
  addressLine2: addressLine2.trim() || null,
33508
35059
  city: city.trim() || null,
@@ -33530,7 +35081,9 @@ function VendorEditPage({ vendorId }) {
33530
35081
  setWebsite(data.website ?? "");
33531
35082
  setLogo(data.logo ?? "");
33532
35083
  setEmail(data.email ?? "");
33533
- setPhone(data.phone ?? "");
35084
+ const sPhone = splitPhoneAndCountryCode(data.phone);
35085
+ setStorePhoneCode(sPhone.countryCode);
35086
+ setStorePhoneNum(sPhone.phoneNumber);
33534
35087
  setAddressLine1(data.addressLine1 ?? "");
33535
35088
  setAddressLine2(data.addressLine2 ?? "");
33536
35089
  setCity(data.city ?? "");
@@ -33552,7 +35105,9 @@ function VendorEditPage({ vendorId }) {
33552
35105
  if (!cancelled) {
33553
35106
  setOwnerName(user.name ?? "");
33554
35107
  setOwnerEmail(user.email ?? "");
33555
- setOwnerPhone(user.phone ?? "");
35108
+ const oPhone = splitPhoneAndCountryCode(user.phone);
35109
+ setOwnerPhoneCode(oPhone.countryCode);
35110
+ setOwnerPhoneNum(oPhone.phoneNumber);
33556
35111
  }
33557
35112
  }
33558
35113
  } else if (!cancelled) {
@@ -33699,7 +35254,10 @@ function VendorEditPage({ vendorId }) {
33699
35254
  setSaving(false);
33700
35255
  return;
33701
35256
  }
33702
- 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
+ });
33703
35261
  if (taxError) {
33704
35262
  setErrors([
33705
35263
  taxError
@@ -33741,7 +35299,7 @@ function VendorEditPage({ vendorId }) {
33741
35299
  user: {
33742
35300
  name: ownerName.trim(),
33743
35301
  email: ownerEmail.trim(),
33744
- phone: ownerPhone.trim() || void 0,
35302
+ phone: formattedOwnerPhone || void 0,
33745
35303
  designation: ownerDesignation.trim() || void 0,
33746
35304
  aadhaarNo: ownerAadhaarNo.trim(),
33747
35305
  panNo: ownerPanNo.trim().toUpperCase(),
@@ -33805,7 +35363,9 @@ function VendorEditPage({ vendorId }) {
33805
35363
  setSaving(false);
33806
35364
  return;
33807
35365
  }
33808
- const taxError = validateIndiaTaxIds(gstin.trim().toUpperCase() || null, null);
35366
+ const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
35367
+ required: false
35368
+ });
33809
35369
  if (taxError) {
33810
35370
  setErrors([
33811
35371
  taxError
@@ -33841,6 +35401,7 @@ function VendorEditPage({ vendorId }) {
33841
35401
  return;
33842
35402
  }
33843
35403
  if (ownerUserId != null) {
35404
+ const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
33844
35405
  const userRes = await fetch(`/api/users/${ownerUserId}`, {
33845
35406
  method: "PUT",
33846
35407
  headers: {
@@ -33849,7 +35410,7 @@ function VendorEditPage({ vendorId }) {
33849
35410
  body: JSON.stringify({
33850
35411
  name: ownerName.trim(),
33851
35412
  email: ownerEmail.trim(),
33852
- phone: ownerPhone.trim() || null
35413
+ phone: formattedOwnerPhone || null
33853
35414
  })
33854
35415
  });
33855
35416
  if (!userRes.ok) {
@@ -33983,12 +35544,23 @@ function VendorEditPage({ vendorId }) {
33983
35544
  className: `mt-1 ${fieldClass}`
33984
35545
  })), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
33985
35546
  htmlFor: "vendorPhone"
33986
- }, "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, {
33987
35557
  id: "vendorPhone",
33988
- value: phone,
33989
- onChange: /* @__PURE__ */ __name((e) => setPhone(e.target.value), "onChange"),
33990
- className: `mt-1 ${fieldClass}`
33991
- })))), /* @__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", {
33992
35564
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
33993
35565
  }, "Store address"), /* @__PURE__ */ React25__default.createElement("div", {
33994
35566
  className: sectionCls5
@@ -34041,12 +35613,15 @@ function VendorEditPage({ vendorId }) {
34041
35613
  className: `mt-1 ${fieldClass}`
34042
35614
  }))))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
34043
35615
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34044
- }, "Tax (India)"), /* @__PURE__ */ React25__default.createElement("div", {
35616
+ }, "Tax"), /* @__PURE__ */ React25__default.createElement("div", {
34045
35617
  className: sectionCls5
34046
35618
  }, /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
34047
- htmlFor: "gstin"
35619
+ htmlFor: "gstin",
35620
+ required: create
34048
35621
  }, "GSTIN"), /* @__PURE__ */ React25__default.createElement(Input, {
34049
35622
  id: "gstin",
35623
+ required: create,
35624
+ maxLength: 15,
34050
35625
  value: gstin,
34051
35626
  onChange: /* @__PURE__ */ __name((e) => setGstin(e.target.value.toUpperCase()), "onChange"),
34052
35627
  placeholder: "22AAAAA0000A1Z5",
@@ -34076,12 +35651,23 @@ function VendorEditPage({ vendorId }) {
34076
35651
  className: `mt-1 ${fieldClass}`
34077
35652
  })), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
34078
35653
  htmlFor: "ownerPhone"
34079
- }, "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, {
34080
35664
  id: "ownerPhone",
34081
- value: ownerPhone,
34082
- onChange: /* @__PURE__ */ __name((e) => setOwnerPhone(e.target.value), "onChange"),
34083
- className: `mt-1 ${fieldClass}`
34084
- })), /* @__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, {
34085
35671
  htmlFor: "ownerDesignation"
34086
35672
  }, "Designation"), /* @__PURE__ */ React25__default.createElement(Input, {
34087
35673
  id: "ownerDesignation",
@@ -34165,8 +35751,8 @@ function VendorEditPage({ vendorId }) {
34165
35751
  }), /* @__PURE__ */ React25__default.createElement(Label3, {
34166
35752
  htmlFor: "termsAccepted",
34167
35753
  className: "font-normal cursor-pointer text-sm leading-snug"
34168
- }, "I confirm the vendor has read and accepted the terms and conditions for selling on this platform."))))),
34169
- sidebar: /* @__PURE__ */ React25__default.createElement(React25__default.Fragment, null, !create && /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
35754
+ }, "I confirm I have read and accepted the terms and conditions for selling on this platform."))))),
35755
+ sidebar: create ? void 0 : /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
34170
35756
  className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34171
35757
  }, "Status"), /* @__PURE__ */ React25__default.createElement("div", {
34172
35758
  className: sectionCls5
@@ -34201,18 +35787,7 @@ function VendorEditPage({ vendorId }) {
34201
35787
  readOnly: true,
34202
35788
  value: active ? "Yes" : "No",
34203
35789
  className: `mt-1 ${fieldClass} bg-gray-100`
34204
- })))), create ? /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
34205
- className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
34206
- }, "Owner access"), /* @__PURE__ */ React25__default.createElement("div", {
34207
- className: sectionCls5
34208
- }, /* @__PURE__ */ React25__default.createElement("p", {
34209
- className: "text-sm text-gray-600"
34210
- }, "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, {
34211
- type: "button",
34212
- disabled: saving,
34213
- onClick: handleCreate,
34214
- className: "w-full"
34215
- }, saving ? "Creating\u2026" : "Create vendor"))) : void 0)
35790
+ }))))
34216
35791
  }), /* @__PURE__ */ React25__default.createElement(Dialog, {
34217
35792
  open: inviteDialog != null,
34218
35793
  onOpenChange: /* @__PURE__ */ __name((open) => {
@@ -35807,7 +37382,7 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
35807
37382
  value: "minAmount"
35808
37383
  }, "Minimum order amount"), /* @__PURE__ */ React.createElement(SelectItem, {
35809
37384
  value: "minQuantity"
35810
- }, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
37385
+ }, "Minimum Cart Quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
35811
37386
  value: "productMinQuantity"
35812
37387
  }, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
35813
37388
  value: "events"
@@ -37262,8 +38837,8 @@ function ContactDetailPage({ contactId }) {
37262
38837
  setAddAddressError("Select country, state, and city from the lists.");
37263
38838
  return;
37264
38839
  }
37265
- const { Country: Country2, State: State2 } = await import('country-state-city');
37266
- 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 ?? "";
37267
38842
  const stateName = State2.getStatesOfCountry(addressGeo.countryIso).find((s) => s.isoCode === addressGeo.stateIso)?.name ?? "";
37268
38843
  const payload = {
37269
38844
  contactId: Number(contactId),