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