@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.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Slot } from '@radix-ui/react-slot';
|
|
|
10
10
|
import { cva } from 'class-variance-authority';
|
|
11
11
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
12
12
|
import * as LucideIcons from 'lucide-react';
|
|
13
|
-
import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, ShieldCheck, QrCode, Car, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2,
|
|
13
|
+
import { ChevronDown, User, Settings, LogOut, LayoutDashboard, Store, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Check, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, Loader2, CalendarClock, CheckSquare, Square, XSquare, Rss, ArrowLeft, Pencil, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, CheckCircle2, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, XCircle, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, Smartphone, ShieldCheck, QrCode, Car, Send, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, HardDrive, Folder, Film } from 'lucide-react';
|
|
14
14
|
import Image from 'next/image';
|
|
15
15
|
import Link2 from 'next/link';
|
|
16
16
|
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
|
@@ -26,10 +26,10 @@ import 'jodit/es2021/jodit.min.css';
|
|
|
26
26
|
import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, PointElement, LineElement, Title, Filler, BarElement } from 'chart.js';
|
|
27
27
|
import { Line, Doughnut, Bar } from 'react-chartjs-2';
|
|
28
28
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
29
|
+
import { Country, State, City } from 'country-state-city';
|
|
29
30
|
import { QRCode } from 'react-qr-code';
|
|
30
31
|
import 'typeorm';
|
|
31
32
|
import { Editor, Frame, Element as Element$1, useEditor, useNode } from '@craftjs/core';
|
|
32
|
-
import { Country, State, City } from 'country-state-city';
|
|
33
33
|
import { ThemeProvider } from 'next-themes';
|
|
34
34
|
|
|
35
35
|
var __defProp = Object.defineProperty;
|
|
@@ -230,11 +230,20 @@ var init_infuro_favicon = __esm({
|
|
|
230
230
|
function isSuperAdminGroupName(name) {
|
|
231
231
|
return name === ADMIN_GROUP_NAME;
|
|
232
232
|
}
|
|
233
|
+
function hasEntityPermission(record, entity, action) {
|
|
234
|
+
const p = record?.[entity];
|
|
235
|
+
if (!p) return false;
|
|
236
|
+
if (action === "create") return p.c;
|
|
237
|
+
if (action === "read") return p.r;
|
|
238
|
+
if (action === "update") return p.u;
|
|
239
|
+
return p.d;
|
|
240
|
+
}
|
|
233
241
|
var ADMIN_GROUP_NAME;
|
|
234
242
|
var init_permission_entities = __esm({
|
|
235
243
|
"src/auth/permission-entities.ts"() {
|
|
236
244
|
ADMIN_GROUP_NAME = "Administrator";
|
|
237
245
|
__name(isSuperAdminGroupName, "isSuperAdminGroupName");
|
|
246
|
+
__name(hasEntityPermission, "hasEntityPermission");
|
|
238
247
|
}
|
|
239
248
|
});
|
|
240
249
|
|
|
@@ -436,6 +445,395 @@ var init_Header = __esm({
|
|
|
436
445
|
__name(AdminHeader, "AdminHeader");
|
|
437
446
|
}
|
|
438
447
|
});
|
|
448
|
+
|
|
449
|
+
// src/lib/vendor-role-defaults.ts
|
|
450
|
+
function full() {
|
|
451
|
+
return {
|
|
452
|
+
canCreate: true,
|
|
453
|
+
canRead: true,
|
|
454
|
+
canUpdate: true,
|
|
455
|
+
canDelete: true
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function read() {
|
|
459
|
+
return {
|
|
460
|
+
canCreate: false,
|
|
461
|
+
canRead: true,
|
|
462
|
+
canUpdate: false,
|
|
463
|
+
canDelete: false
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function none() {
|
|
467
|
+
return {
|
|
468
|
+
canCreate: false,
|
|
469
|
+
canRead: false,
|
|
470
|
+
canUpdate: false,
|
|
471
|
+
canDelete: false
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function readUpdate() {
|
|
475
|
+
return {
|
|
476
|
+
canCreate: false,
|
|
477
|
+
canRead: true,
|
|
478
|
+
canUpdate: true,
|
|
479
|
+
canDelete: false
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function storePerms(spec) {
|
|
483
|
+
const resolve = /* @__PURE__ */ __name((v) => {
|
|
484
|
+
if (v === "full") return full();
|
|
485
|
+
if (v === "read") return read();
|
|
486
|
+
if (v === "none") return none();
|
|
487
|
+
return v;
|
|
488
|
+
}, "resolve");
|
|
489
|
+
const out = {};
|
|
490
|
+
for (const entity of STORE_ENTITIES) {
|
|
491
|
+
out[entity] = resolve(spec[entity] ?? "none");
|
|
492
|
+
}
|
|
493
|
+
return out;
|
|
494
|
+
}
|
|
495
|
+
var VENDOR_RBAC_ENTITIES, STORE_ENTITIES;
|
|
496
|
+
var init_vendor_role_defaults = __esm({
|
|
497
|
+
"src/lib/vendor-role-defaults.ts"() {
|
|
498
|
+
init_vendor_scope();
|
|
499
|
+
VENDOR_RBAC_ENTITIES = [
|
|
500
|
+
...VENDOR_STORE_RBAC_ENTITIES,
|
|
501
|
+
"dashboard",
|
|
502
|
+
"settings",
|
|
503
|
+
"team",
|
|
504
|
+
"roles"
|
|
505
|
+
];
|
|
506
|
+
STORE_ENTITIES = [
|
|
507
|
+
...VENDOR_STORE_RBAC_ENTITIES
|
|
508
|
+
];
|
|
509
|
+
__name(full, "full");
|
|
510
|
+
__name(read, "read");
|
|
511
|
+
__name(none, "none");
|
|
512
|
+
__name(readUpdate, "readUpdate");
|
|
513
|
+
__name(storePerms, "storePerms");
|
|
514
|
+
[
|
|
515
|
+
{
|
|
516
|
+
name: "Owner",
|
|
517
|
+
description: "Full store access and team/role management",
|
|
518
|
+
isSystem: true,
|
|
519
|
+
isOwnerRole: true,
|
|
520
|
+
permissions: {
|
|
521
|
+
...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
|
|
522
|
+
e,
|
|
523
|
+
"full"
|
|
524
|
+
]))),
|
|
525
|
+
dashboard: read(),
|
|
526
|
+
settings: readUpdate(),
|
|
527
|
+
team: full(),
|
|
528
|
+
roles: full()
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
name: "Manager",
|
|
533
|
+
description: "Day-to-day store operations",
|
|
534
|
+
isSystem: true,
|
|
535
|
+
isOwnerRole: false,
|
|
536
|
+
permissions: {
|
|
537
|
+
...storePerms({
|
|
538
|
+
products: "full",
|
|
539
|
+
collections: "full",
|
|
540
|
+
brands: "full",
|
|
541
|
+
product_categories: "full",
|
|
542
|
+
orders: "full",
|
|
543
|
+
vendor_customers: "full",
|
|
544
|
+
discounts: "full",
|
|
545
|
+
order_discounts: "full",
|
|
546
|
+
order_addresses: "full",
|
|
547
|
+
payments: "read",
|
|
548
|
+
taxes: "read",
|
|
549
|
+
attributes: "read"
|
|
550
|
+
}),
|
|
551
|
+
dashboard: read(),
|
|
552
|
+
settings: read(),
|
|
553
|
+
team: none(),
|
|
554
|
+
roles: none()
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
name: "Catalog Staff",
|
|
559
|
+
description: "Manage products and catalog content",
|
|
560
|
+
isSystem: true,
|
|
561
|
+
isOwnerRole: false,
|
|
562
|
+
permissions: {
|
|
563
|
+
...storePerms({
|
|
564
|
+
products: "full",
|
|
565
|
+
collections: "full",
|
|
566
|
+
brands: "full",
|
|
567
|
+
product_categories: "full",
|
|
568
|
+
attributes: "full",
|
|
569
|
+
orders: "read",
|
|
570
|
+
vendor_customers: "read"
|
|
571
|
+
}),
|
|
572
|
+
dashboard: read(),
|
|
573
|
+
settings: none(),
|
|
574
|
+
team: none(),
|
|
575
|
+
roles: none()
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: "Fulfillment",
|
|
580
|
+
description: "Orders and fulfillment",
|
|
581
|
+
isSystem: true,
|
|
582
|
+
isOwnerRole: false,
|
|
583
|
+
permissions: {
|
|
584
|
+
...storePerms({
|
|
585
|
+
products: "read",
|
|
586
|
+
orders: "full",
|
|
587
|
+
order_addresses: "full",
|
|
588
|
+
order_discounts: "read",
|
|
589
|
+
vendor_customers: "read"
|
|
590
|
+
}),
|
|
591
|
+
dashboard: read(),
|
|
592
|
+
settings: none(),
|
|
593
|
+
team: none(),
|
|
594
|
+
roles: none()
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: "Viewer",
|
|
599
|
+
description: "Read-only access to store data",
|
|
600
|
+
isSystem: true,
|
|
601
|
+
isOwnerRole: false,
|
|
602
|
+
permissions: {
|
|
603
|
+
...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
|
|
604
|
+
e,
|
|
605
|
+
"read"
|
|
606
|
+
]))),
|
|
607
|
+
dashboard: read(),
|
|
608
|
+
settings: none(),
|
|
609
|
+
team: none(),
|
|
610
|
+
roles: none()
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
];
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// src/auth/rbac-debug.ts
|
|
618
|
+
function summarizeEntityPerms(entityPerms) {
|
|
619
|
+
const out = {};
|
|
620
|
+
if (!entityPerms) return out;
|
|
621
|
+
for (const [entity, p] of Object.entries(entityPerms)) {
|
|
622
|
+
out[entity] = {
|
|
623
|
+
c: p.c,
|
|
624
|
+
r: p.r,
|
|
625
|
+
u: p.u,
|
|
626
|
+
d: p.d
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return out;
|
|
630
|
+
}
|
|
631
|
+
function vendorPortalFallbackReason(user, entity, action) {
|
|
632
|
+
if (!isVendorPortalUser(user)) return null;
|
|
633
|
+
if (entity === "dashboard" && action === "read") return "vendor_portal_dashboard_fallback";
|
|
634
|
+
if (entity === "analytics" && action === "read") return "vendor_portal_analytics_fallback";
|
|
635
|
+
if (entity === "settings" && action === "read") return "vendor_portal_settings_read_fallback";
|
|
636
|
+
if (entity === "upload" && (action === "create" || action === "read")) {
|
|
637
|
+
return "vendor_portal_upload_fallback";
|
|
638
|
+
}
|
|
639
|
+
if (VENDOR_STORE_RBAC_ENTITIES.has(entity) && action === "read") return "vendor_portal_store_read_fallback";
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
function explainSessionEntityAccess(user, entity, action) {
|
|
643
|
+
const userSnapshot = {
|
|
644
|
+
email: user?.email ?? null,
|
|
645
|
+
id: user?.id ?? null,
|
|
646
|
+
groupId: user?.groupId ?? null,
|
|
647
|
+
groupName: user?.groupName ?? null,
|
|
648
|
+
isRBACAdmin: user?.isRBACAdmin ?? false,
|
|
649
|
+
adminAccess: user?.adminAccess ?? null,
|
|
650
|
+
vendorIds: user?.vendorIds ?? [],
|
|
651
|
+
activeVendorId: user?.activeVendorId ?? null,
|
|
652
|
+
vendorRole: user?.vendorRole ?? null,
|
|
653
|
+
vendorRoleId: user?.vendorRoleId ?? null,
|
|
654
|
+
vendorRoleName: user?.vendorRoleName ?? null,
|
|
655
|
+
isVendorRoleOwner: user?.isVendorRoleOwner ?? false,
|
|
656
|
+
vendorEntityPerms: summarizeEntityPerms(user?.vendorEntityPerms),
|
|
657
|
+
isVendorPortal: user?.isVendorPortal ?? false,
|
|
658
|
+
isVendorOwner: user?.isVendorOwner ?? false,
|
|
659
|
+
isVendorPortalComputed: isVendorPortalUser(user),
|
|
660
|
+
isVendorOwnerComputed: isVendorOwner(user),
|
|
661
|
+
jwtEntityPerms: summarizeEntityPerms(user?.entityPerms),
|
|
662
|
+
jwtEntityPermForTarget: user?.entityPerms?.[entity] ?? null
|
|
663
|
+
};
|
|
664
|
+
if (!user?.email) {
|
|
665
|
+
return {
|
|
666
|
+
allowed: false,
|
|
667
|
+
reason: "no_session_email",
|
|
668
|
+
userSnapshot
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
if (user.isRBACAdmin) {
|
|
672
|
+
return {
|
|
673
|
+
allowed: true,
|
|
674
|
+
reason: "platform_administrator",
|
|
675
|
+
userSnapshot
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
if ((isVendorPortalUser(user) || (user.vendorIds?.length ?? 0) > 0) && entity === "upload" && (action === "create" || action === "read")) {
|
|
679
|
+
return {
|
|
680
|
+
allowed: true,
|
|
681
|
+
reason: "vendor_portal_upload",
|
|
682
|
+
userSnapshot
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
const vendorIds = user.vendorIds ?? [];
|
|
686
|
+
const isStaff = user.vendorRole === "staff";
|
|
687
|
+
const vendorPerms = user.vendorEntityPerms;
|
|
688
|
+
const hasVendorRolePerms = vendorPerms != null && Object.keys(vendorPerms).length > 0;
|
|
689
|
+
const enforceVendorRoleMatrix = vendorIds.length > 0 && (hasVendorRolePerms || user.vendorRoleId != null);
|
|
690
|
+
if (enforceVendorRoleMatrix) {
|
|
691
|
+
const vendorRbacSet = new Set(VENDOR_RBAC_ENTITIES);
|
|
692
|
+
if (vendorRbacSet.has(entity) || entity === "analytics") {
|
|
693
|
+
if (entity === "analytics" && action === "read") {
|
|
694
|
+
const allowed3 = hasEntityPermission(vendorPerms, "dashboard", "read");
|
|
695
|
+
return {
|
|
696
|
+
allowed: allowed3,
|
|
697
|
+
reason: allowed3 ? "vendor_role_analytics_via_dashboard" : "vendor_role_analytics_denied",
|
|
698
|
+
userSnapshot
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
if (user.isVendorRoleOwner === true) {
|
|
702
|
+
return {
|
|
703
|
+
allowed: true,
|
|
704
|
+
reason: "vendor_owner_role_full",
|
|
705
|
+
userSnapshot
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const allowed2 = hasEntityPermission(vendorPerms, entity, action);
|
|
709
|
+
return {
|
|
710
|
+
allowed: allowed2,
|
|
711
|
+
reason: allowed2 ? "vendor_role_permissions_allow" : "vendor_role_permissions_deny",
|
|
712
|
+
userSnapshot
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (vendorIds.length > 0) {
|
|
717
|
+
if (entity === "dashboard" && action === "read") {
|
|
718
|
+
return {
|
|
719
|
+
allowed: true,
|
|
720
|
+
reason: "vendor_link_dashboard_read",
|
|
721
|
+
userSnapshot
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
if (entity === "analytics" && action === "read") {
|
|
725
|
+
return {
|
|
726
|
+
allowed: true,
|
|
727
|
+
reason: "vendor_link_analytics_read",
|
|
728
|
+
userSnapshot
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
if (entity === "settings") {
|
|
732
|
+
if (action === "read") {
|
|
733
|
+
return {
|
|
734
|
+
allowed: true,
|
|
735
|
+
reason: "vendor_link_settings_read",
|
|
736
|
+
userSnapshot
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
if (action === "update" && !isStaff) {
|
|
740
|
+
return {
|
|
741
|
+
allowed: true,
|
|
742
|
+
reason: "vendor_link_settings_update",
|
|
743
|
+
userSnapshot
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
|
|
748
|
+
if (isStaff && action === "read") {
|
|
749
|
+
return {
|
|
750
|
+
allowed: true,
|
|
751
|
+
reason: "vendor_staff_store_read",
|
|
752
|
+
userSnapshot
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
if (!isStaff) {
|
|
756
|
+
return {
|
|
757
|
+
allowed: true,
|
|
758
|
+
reason: "vendor_link_store_full",
|
|
759
|
+
userSnapshot
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
return {
|
|
763
|
+
allowed: false,
|
|
764
|
+
reason: "vendor_staff_store_write_denied",
|
|
765
|
+
userSnapshot
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (isVendorPortalUser(user) && isVendorOwner(user)) {
|
|
770
|
+
if (entity === "dashboard" && action === "read") {
|
|
771
|
+
return {
|
|
772
|
+
allowed: true,
|
|
773
|
+
reason: "vendor_owner_group_dashboard",
|
|
774
|
+
userSnapshot
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (entity === "analytics" && action === "read") {
|
|
778
|
+
return {
|
|
779
|
+
allowed: true,
|
|
780
|
+
reason: "vendor_owner_group_analytics",
|
|
781
|
+
userSnapshot
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
if (entity === "settings" && (action === "read" || action === "update")) {
|
|
785
|
+
return {
|
|
786
|
+
allowed: true,
|
|
787
|
+
reason: "vendor_owner_group_settings",
|
|
788
|
+
userSnapshot
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
|
|
792
|
+
return {
|
|
793
|
+
allowed: true,
|
|
794
|
+
reason: "vendor_owner_group_store",
|
|
795
|
+
userSnapshot
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const vendorPortalReason = vendorPortalFallbackReason(user, entity, action);
|
|
800
|
+
if (vendorPortalReason) {
|
|
801
|
+
return {
|
|
802
|
+
allowed: true,
|
|
803
|
+
reason: vendorPortalReason,
|
|
804
|
+
userSnapshot
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const explicit = user.entityPerms?.[entity];
|
|
808
|
+
if (explicit !== void 0) {
|
|
809
|
+
const allowed2 = hasEntityPermission(user.entityPerms, entity, action);
|
|
810
|
+
return {
|
|
811
|
+
allowed: allowed2,
|
|
812
|
+
reason: allowed2 ? "jwt_entity_perms_allow" : "jwt_entity_perms_deny",
|
|
813
|
+
userSnapshot
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
const allowed = hasEntityPermission(user.entityPerms, entity, action);
|
|
817
|
+
return {
|
|
818
|
+
allowed,
|
|
819
|
+
reason: allowed ? "entity_perms_fallback_allow" : "no_matching_rule",
|
|
820
|
+
userSnapshot
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
function sessionHasEntityAccessFromExplanation(user, entity, action) {
|
|
824
|
+
return explainSessionEntityAccess(user, entity, action).allowed;
|
|
825
|
+
}
|
|
826
|
+
var init_rbac_debug = __esm({
|
|
827
|
+
"src/auth/rbac-debug.ts"() {
|
|
828
|
+
init_permission_entities();
|
|
829
|
+
init_vendor_role_defaults();
|
|
830
|
+
init_vendor_scope();
|
|
831
|
+
__name(summarizeEntityPerms, "summarizeEntityPerms");
|
|
832
|
+
__name(vendorPortalFallbackReason, "vendorPortalFallbackReason");
|
|
833
|
+
__name(explainSessionEntityAccess, "explainSessionEntityAccess");
|
|
834
|
+
__name(sessionHasEntityAccessFromExplanation, "sessionHasEntityAccessFromExplanation");
|
|
835
|
+
}
|
|
836
|
+
});
|
|
439
837
|
var defaultValue, AdminConfigContext;
|
|
440
838
|
var init_admin_config_context = __esm({
|
|
441
839
|
"src/admin/admin-config-context.tsx"() {
|
|
@@ -455,7 +853,7 @@ var init_admin_config_context = __esm({
|
|
|
455
853
|
var CMS_VERSION;
|
|
456
854
|
var init_cms_version = __esm({
|
|
457
855
|
"src/lib/cms-version.ts"() {
|
|
458
|
-
CMS_VERSION = "1.0.
|
|
856
|
+
CMS_VERSION = "1.0.51" ;
|
|
459
857
|
}
|
|
460
858
|
});
|
|
461
859
|
function useCatalogCategories(enabled = true) {
|
|
@@ -590,6 +988,10 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
590
988
|
const sectionCls7 = "mb-5";
|
|
591
989
|
const headingCls = "text-[11px] font-semibold text-gray-400 uppercase tracking-wider px-2.5 mb-1.5";
|
|
592
990
|
const asideCls = isDrawer ? "w-full h-full min-h-0 bg-white flex flex-col overflow-hidden" : "w-52 h-full min-h-0 bg-white border-r border-gray-200 flex-shrink-0 flex flex-col overflow-hidden";
|
|
991
|
+
const canReadEntity = /* @__PURE__ */ __name((entity) => {
|
|
992
|
+
if (!sessionUser) return true;
|
|
993
|
+
return sessionHasEntityAccessFromExplanation(sessionUser, entity, "read");
|
|
994
|
+
}, "canReadEntity");
|
|
593
995
|
return /* @__PURE__ */ React.createElement("aside", {
|
|
594
996
|
className: asideCls
|
|
595
997
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -607,30 +1009,40 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
607
1009
|
className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
|
|
608
1010
|
}, /* @__PURE__ */ React.createElement(LayoutDashboard, {
|
|
609
1011
|
className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
|
|
610
|
-
}), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) =>
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}, /* @__PURE__ */ React.createElement(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1012
|
+
}), "Dashboard")))), showPlatformNav && customNavSections.length > 0 && customNavSections.map((section) => {
|
|
1013
|
+
const filteredItems = section.items.filter((item) => {
|
|
1014
|
+
const entity = item.href.replace("/admin/", "").split("/")[0];
|
|
1015
|
+
return canReadEntity(entity);
|
|
1016
|
+
});
|
|
1017
|
+
if (filteredItems.length === 0) return null;
|
|
1018
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1019
|
+
key: section.title,
|
|
1020
|
+
className: sectionCls7
|
|
1021
|
+
}, /* @__PURE__ */ React.createElement("h3", {
|
|
1022
|
+
className: headingCls
|
|
1023
|
+
}, section.title), /* @__PURE__ */ React.createElement("ul", {
|
|
1024
|
+
className: "space-y-0.5"
|
|
1025
|
+
}, filteredItems.map((item) => {
|
|
1026
|
+
const Icon2 = getIconForItem(item.icon);
|
|
1027
|
+
return /* @__PURE__ */ React.createElement("li", {
|
|
1028
|
+
key: item.href
|
|
1029
|
+
}, /* @__PURE__ */ React.createElement(Link2, {
|
|
1030
|
+
href: item.href,
|
|
1031
|
+
className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
|
|
1032
|
+
}, /* @__PURE__ */ React.createElement(Icon2, {
|
|
1033
|
+
className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
|
|
1034
|
+
}), item.label));
|
|
1035
|
+
})));
|
|
1036
|
+
}), showPlatformNav && customNavSections.length === 0 && customNavItems.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
628
1037
|
className: sectionCls7
|
|
629
1038
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
630
1039
|
className: headingCls
|
|
631
1040
|
}, "Custom"), /* @__PURE__ */ React.createElement("ul", {
|
|
632
1041
|
className: "space-y-0.5"
|
|
633
|
-
}, customNavItems.
|
|
1042
|
+
}, customNavItems.filter((item) => {
|
|
1043
|
+
const entity = item.href.replace("/admin/", "").split("/")[0];
|
|
1044
|
+
return canReadEntity(entity);
|
|
1045
|
+
}).map((item) => {
|
|
634
1046
|
const Icon2 = getIconForItem(item.icon);
|
|
635
1047
|
return /* @__PURE__ */ React.createElement("li", {
|
|
636
1048
|
key: item.href
|
|
@@ -651,85 +1063,92 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
651
1063
|
className: `${linkCls} ${isActive("/admin/dashboard") ? linkActive : linkInactive}`
|
|
652
1064
|
}, /* @__PURE__ */ React.createElement(LayoutDashboard, {
|
|
653
1065
|
className: `h-4 w-4 mr-2 ${isActive("/admin/dashboard") ? iconActive : iconInactive}`
|
|
654
|
-
}), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) =>
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
}, /* @__PURE__ */ React.createElement(
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1066
|
+
}), "Dashboard")))), vendorPortal && customNavSections.length > 0 && customNavSections.map((section) => {
|
|
1067
|
+
const filteredItems = section.items.filter((item) => {
|
|
1068
|
+
const entity = item.href.replace("/admin/", "").split("/")[0];
|
|
1069
|
+
return canReadEntity(entity);
|
|
1070
|
+
});
|
|
1071
|
+
if (filteredItems.length === 0) return null;
|
|
1072
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1073
|
+
key: section.title,
|
|
1074
|
+
className: sectionCls7
|
|
1075
|
+
}, /* @__PURE__ */ React.createElement("h3", {
|
|
1076
|
+
className: headingCls
|
|
1077
|
+
}, section.title), /* @__PURE__ */ React.createElement("ul", {
|
|
1078
|
+
className: "space-y-0.5"
|
|
1079
|
+
}, filteredItems.map((item) => {
|
|
1080
|
+
const Icon2 = getIconForItem(item.icon);
|
|
1081
|
+
return /* @__PURE__ */ React.createElement("li", {
|
|
1082
|
+
key: item.href
|
|
1083
|
+
}, /* @__PURE__ */ React.createElement(Link2, {
|
|
1084
|
+
href: item.href,
|
|
1085
|
+
className: `${linkCls} ${isActive(item.href) ? linkActive : linkInactive}`
|
|
1086
|
+
}, /* @__PURE__ */ React.createElement(Icon2, {
|
|
1087
|
+
className: `h-4 w-4 mr-2 ${isActive(item.href) ? iconActive : iconInactive}`
|
|
1088
|
+
}), item.label));
|
|
1089
|
+
})));
|
|
1090
|
+
}), showStoreNav && /* @__PURE__ */ React.createElement("div", {
|
|
672
1091
|
className: sectionCls7
|
|
673
1092
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
674
1093
|
className: headingCls
|
|
675
1094
|
}, "Store"), /* @__PURE__ */ React.createElement("ul", {
|
|
676
1095
|
className: "space-y-0.5"
|
|
677
|
-
}, showVendorOnboard && multiVendorEnabled !== false && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1096
|
+
}, showVendorOnboard && multiVendorEnabled !== false && canReadEntity("vendors") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
678
1097
|
href: "/admin/vendors",
|
|
679
1098
|
className: `${linkCls} ${isActive("/admin/vendors") ? linkActive : linkInactive}`
|
|
680
1099
|
}, /* @__PURE__ */ React.createElement(Store, {
|
|
681
1100
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendors") ? iconActive : iconInactive}`
|
|
682
|
-
}), "Vendors")), showVendorCategories && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1101
|
+
}), "Vendors")), showVendorCategories && canReadEntity("product_categories") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
683
1102
|
href: "/admin/product_categories",
|
|
684
1103
|
className: `${linkCls} ${isActive("/admin/product_categories") ? linkActive : linkInactive}`
|
|
685
1104
|
}, /* @__PURE__ */ React.createElement(FolderTree, {
|
|
686
1105
|
className: `h-4 w-4 mr-2 ${isActive("/admin/product_categories") ? iconActive : iconInactive}`
|
|
687
|
-
}), "Categories")), showVendorCollections && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1106
|
+
}), "Categories")), showVendorCollections && canReadEntity("collections") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
688
1107
|
href: "/admin/collections",
|
|
689
1108
|
className: `${linkCls} ${isActive("/admin/collections") ? linkActive : linkInactive}`
|
|
690
1109
|
}, /* @__PURE__ */ React.createElement(Layers, {
|
|
691
1110
|
className: `h-4 w-4 mr-2 ${isActive("/admin/collections") ? iconActive : iconInactive}`
|
|
692
|
-
}), "Collections")), showVendorBrands && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1111
|
+
}), "Collections")), showVendorBrands && canReadEntity("brands") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
693
1112
|
href: "/admin/brands",
|
|
694
1113
|
className: `${linkCls} ${isActive("/admin/brands") ? linkActive : linkInactive}`
|
|
695
1114
|
}, /* @__PURE__ */ React.createElement(Building2, {
|
|
696
1115
|
className: `h-4 w-4 mr-2 ${isActive("/admin/brands") ? iconActive : iconInactive}`
|
|
697
|
-
}), "Brands")), !hasCustomEventsNav && eventsEnabled !== false ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1116
|
+
}), "Brands")), !hasCustomEventsNav && eventsEnabled !== false && canReadEntity("events") ? /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
698
1117
|
href: "/admin/events",
|
|
699
1118
|
className: `${linkCls} ${isActive("/admin/events") ? linkActive : linkInactive}`
|
|
700
1119
|
}, /* @__PURE__ */ React.createElement(CalendarDays, {
|
|
701
1120
|
className: `h-4 w-4 mr-2 ${isActive("/admin/events") ? iconActive : iconInactive}`
|
|
702
|
-
}), "Events")) : null, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1121
|
+
}), "Events")) : null, canReadEntity("products") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
703
1122
|
href: "/admin/products",
|
|
704
1123
|
className: `${linkCls} ${isActive("/admin/products") ? linkActive : linkInactive}`
|
|
705
1124
|
}, /* @__PURE__ */ React.createElement(ShoppingBag, {
|
|
706
1125
|
className: `h-4 w-4 mr-2 ${isActive("/admin/products") ? iconActive : iconInactive}`
|
|
707
|
-
}), "Products")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1126
|
+
}), "Products")), canReadEntity("combos") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
708
1127
|
href: "/admin/combos",
|
|
709
1128
|
className: `${linkCls} ${isActive("/admin/combos") ? linkActive : linkInactive}`
|
|
710
1129
|
}, /* @__PURE__ */ React.createElement(Package, {
|
|
711
1130
|
className: `h-4 w-4 mr-2 ${isActive("/admin/combos") ? iconActive : iconInactive}`
|
|
712
|
-
}), "Combos")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1131
|
+
}), "Combos")), canReadEntity("taxes") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
713
1132
|
href: "/admin/taxes",
|
|
714
1133
|
className: `${linkCls} ${isActive("/admin/taxes") ? linkActive : linkInactive}`
|
|
715
1134
|
}, /* @__PURE__ */ React.createElement(Receipt, {
|
|
716
1135
|
className: `h-4 w-4 mr-2 ${isActive("/admin/taxes") ? iconActive : iconInactive}`
|
|
717
|
-
}), "Taxes")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1136
|
+
}), "Taxes")), canReadEntity("discounts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
718
1137
|
href: "/admin/discounts",
|
|
719
1138
|
className: `${linkCls} ${isActive("/admin/discounts") ? linkActive : linkInactive}`
|
|
720
1139
|
}, /* @__PURE__ */ React.createElement(BadgePercent, {
|
|
721
1140
|
className: `h-4 w-4 mr-2 ${isActive("/admin/discounts") ? iconActive : iconInactive}`
|
|
722
|
-
}), "Discounts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1141
|
+
}), "Discounts")), canReadEntity("orders") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
723
1142
|
href: "/admin/orders",
|
|
724
1143
|
className: `${linkCls} ${isActive("/admin/orders") ? linkActive : linkInactive}`
|
|
725
1144
|
}, /* @__PURE__ */ React.createElement(ShoppingCart, {
|
|
726
1145
|
className: `h-4 w-4 mr-2 ${isActive("/admin/orders") ? iconActive : iconInactive}`
|
|
727
|
-
}), "Orders")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1146
|
+
}), "Orders")), canReadEntity("payments") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
728
1147
|
href: "/admin/payments",
|
|
729
1148
|
className: `${linkCls} ${isActive("/admin/payments") ? linkActive : linkInactive}`
|
|
730
1149
|
}, /* @__PURE__ */ React.createElement(CreditCard, {
|
|
731
1150
|
className: `h-4 w-4 mr-2 ${isActive("/admin/payments") ? iconActive : iconInactive}`
|
|
732
|
-
}), "Payments")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1151
|
+
}), "Payments")), canReadEntity("vendor_customers") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
733
1152
|
href: "/admin/vendor_customers",
|
|
734
1153
|
className: `${linkCls} ${isActive("/admin/vendor_customers") ? linkActive : linkInactive}`
|
|
735
1154
|
}, /* @__PURE__ */ React.createElement(Users, {
|
|
@@ -740,32 +1159,32 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
740
1159
|
className: headingCls
|
|
741
1160
|
}, "Management"), /* @__PURE__ */ React.createElement("ul", {
|
|
742
1161
|
className: "space-y-0.5"
|
|
743
|
-
}, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1162
|
+
}, canReadEntity("contacts") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
744
1163
|
href: "/admin/contacts",
|
|
745
1164
|
className: `${linkCls} ${isActive("/admin/contacts") ? linkActive : linkInactive}`
|
|
746
1165
|
}, /* @__PURE__ */ React.createElement(Inbox, {
|
|
747
1166
|
className: `h-4 w-4 mr-2 ${isActive("/admin/contacts") ? iconActive : iconInactive}`
|
|
748
|
-
}), "Contacts")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1167
|
+
}), "Contacts")), canReadEntity("blogs") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
749
1168
|
href: "/admin/blogs",
|
|
750
1169
|
className: `${linkCls} ${isActive("/admin/blogs") ? linkActive : linkInactive}`
|
|
751
1170
|
}, /* @__PURE__ */ React.createElement(File, {
|
|
752
1171
|
className: `h-4 w-4 mr-2 ${isActive("/admin/blogs") ? iconActive : iconInactive}`
|
|
753
|
-
}), "Blogs")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1172
|
+
}), "Blogs")), canReadEntity("pages") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
754
1173
|
href: "/admin/pages",
|
|
755
1174
|
className: `${linkCls} ${isActive("/admin/pages") ? linkActive : linkInactive}`
|
|
756
1175
|
}, /* @__PURE__ */ React.createElement(LinkIcon, {
|
|
757
1176
|
className: `h-4 w-4 mr-2 ${isActive("/admin/pages") ? iconActive : iconInactive}`
|
|
758
|
-
}), "Pages")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1177
|
+
}), "Pages")), canReadEntity("forms") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
759
1178
|
href: "/admin/forms",
|
|
760
1179
|
className: `${linkCls} ${isActive("/admin/forms") ? linkActive : linkInactive}`
|
|
761
1180
|
}, /* @__PURE__ */ React.createElement(ClipboardList, {
|
|
762
1181
|
className: `h-4 w-4 mr-2 ${isActive("/admin/forms") ? iconActive : iconInactive}`
|
|
763
|
-
}), "Forms")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1182
|
+
}), "Forms")), canReadEntity("form_submissions") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
764
1183
|
href: "/admin/submissions",
|
|
765
1184
|
className: `${linkCls} ${isActive("/admin/submissions") ? linkActive : linkInactive}`
|
|
766
1185
|
}, /* @__PURE__ */ React.createElement(MessageSquare, {
|
|
767
1186
|
className: `h-4 w-4 mr-2 ${isActive("/admin/submissions") ? iconActive : iconInactive}`
|
|
768
|
-
}), "Submissions")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1187
|
+
}), "Submissions")), canReadEntity("upload") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
769
1188
|
href: "/admin/media",
|
|
770
1189
|
className: `${linkCls} ${isActive("/admin/media") ? linkActive : linkInactive}`
|
|
771
1190
|
}, /* @__PURE__ */ React.createElement(Image$1, {
|
|
@@ -802,12 +1221,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
802
1221
|
className: headingCls
|
|
803
1222
|
}, "System"), /* @__PURE__ */ React.createElement("ul", {
|
|
804
1223
|
className: "space-y-0.5"
|
|
805
|
-
}, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1224
|
+
}, canReadEntity("users") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
806
1225
|
href: "/admin/users",
|
|
807
1226
|
className: `${linkCls} ${isActive("/admin/users") ? linkActive : linkInactive}`
|
|
808
1227
|
}, /* @__PURE__ */ React.createElement(Users, {
|
|
809
1228
|
className: `h-4 w-4 mr-2 ${isActive("/admin/users") ? iconActive : iconInactive}`
|
|
810
|
-
}), "Users")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1229
|
+
}), "Users")), canReadEntity("roles") && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
811
1230
|
href: "/admin/roles",
|
|
812
1231
|
className: `${linkCls} ${isActive("/admin/roles") ? linkActive : linkInactive}`
|
|
813
1232
|
}, /* @__PURE__ */ React.createElement(Shield, {
|
|
@@ -854,6 +1273,7 @@ var init_Sidebar = __esm({
|
|
|
854
1273
|
"src/components/Admin/Sidebar.tsx"() {
|
|
855
1274
|
"use client";
|
|
856
1275
|
init_vendor_scope();
|
|
1276
|
+
init_rbac_debug();
|
|
857
1277
|
init_admin_config_context();
|
|
858
1278
|
init_infuro_favicon();
|
|
859
1279
|
init_cms_version();
|
|
@@ -1146,6 +1566,15 @@ var init_builtin_plugin_descriptors = __esm({
|
|
|
1146
1566
|
enabled: true,
|
|
1147
1567
|
icon: "event_notifications",
|
|
1148
1568
|
settingsGroup: "event_notifications"
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
name: "auth_providers",
|
|
1572
|
+
label: "Authentication Provider",
|
|
1573
|
+
version: "1.0.0",
|
|
1574
|
+
description: "Enable Google sign-in for admin and vendor users (existing accounts only).",
|
|
1575
|
+
enabled: true,
|
|
1576
|
+
icon: "auth_providers",
|
|
1577
|
+
settingsGroup: "auth_providers"
|
|
1149
1578
|
}
|
|
1150
1579
|
];
|
|
1151
1580
|
}
|
|
@@ -1471,7 +1900,15 @@ function useEventsSettings() {
|
|
|
1471
1900
|
requireEventApproval
|
|
1472
1901
|
};
|
|
1473
1902
|
}
|
|
1474
|
-
function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, theme, themeRegistry, pluginDescriptors = [] }) {
|
|
1903
|
+
function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, extraNotificationVariables, theme, themeRegistry, pluginDescriptors = [] }) {
|
|
1904
|
+
useEffect(() => {
|
|
1905
|
+
document.documentElement.style.overflow = "hidden";
|
|
1906
|
+
document.body.style.overflow = "hidden";
|
|
1907
|
+
return () => {
|
|
1908
|
+
document.documentElement.style.overflow = "";
|
|
1909
|
+
document.body.style.overflow = "";
|
|
1910
|
+
};
|
|
1911
|
+
}, []);
|
|
1475
1912
|
const resolvedTheme = useResolvedTheme(theme, themeRegistry);
|
|
1476
1913
|
const { storeEnabled, currency } = useStoreEnabled();
|
|
1477
1914
|
const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
|
|
@@ -1492,6 +1929,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1492
1929
|
customCrudConfigs,
|
|
1493
1930
|
categoryRelatedProductLabels,
|
|
1494
1931
|
renderOrderDetailFooter,
|
|
1932
|
+
extraNotificationVariables,
|
|
1495
1933
|
theme: resolvedTheme,
|
|
1496
1934
|
themeRegistry,
|
|
1497
1935
|
pluginDescriptors: mergedPluginDescriptors,
|
|
@@ -1510,6 +1948,7 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1510
1948
|
customCrudConfigs,
|
|
1511
1949
|
categoryRelatedProductLabels,
|
|
1512
1950
|
renderOrderDetailFooter,
|
|
1951
|
+
extraNotificationVariables,
|
|
1513
1952
|
resolvedTheme,
|
|
1514
1953
|
themeRegistry,
|
|
1515
1954
|
mergedPluginDescriptors,
|
|
@@ -4366,6 +4805,8 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4366
4805
|
const hasLoadedRef = useRef(false);
|
|
4367
4806
|
const isMobile = useIsMobile();
|
|
4368
4807
|
const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
|
|
4808
|
+
const { data: session } = useSession();
|
|
4809
|
+
const sessionUser = session?.user;
|
|
4369
4810
|
const listColumns = useMemo(() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [], [
|
|
4370
4811
|
columns
|
|
4371
4812
|
]);
|
|
@@ -4906,6 +5347,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4906
5347
|
withListFrom
|
|
4907
5348
|
]);
|
|
4908
5349
|
const resourceName = apiEndpoint.replace("/api/", "");
|
|
5350
|
+
const canCreate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "create");
|
|
5351
|
+
const canUpdate = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "update");
|
|
5352
|
+
const canDelete = !sessionUser || sessionHasEntityAccessFromExplanation(sessionUser, resourceName, "delete");
|
|
4909
5353
|
const dedicatedRecordEditHref = /* @__PURE__ */ __name((recordId) => addEditPage && (resourceName === "orders" || resourceName === "events") ? withListFrom(`${addEditPageUrl}/${recordId}/edit`) : addEditPage ? withListFrom(`${addEditPageUrl}/${recordId}`) : "", "dedicatedRecordEditHref");
|
|
4910
5354
|
const showDedicatedDuplicate = addEditPage && !CRUD_NO_DUPLICATE_RESOURCES.has(resourceName);
|
|
4911
5355
|
const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
|
|
@@ -5065,15 +5509,15 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5065
5509
|
onClick: /* @__PURE__ */ __name(() => setBulkDialogOpen(true), "onClick"),
|
|
5066
5510
|
variant: "outline",
|
|
5067
5511
|
className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
|
|
5068
|
-
}, /* @__PURE__ */ React.createElement(
|
|
5512
|
+
}, /* @__PURE__ */ React.createElement(Download, {
|
|
5069
5513
|
className: "h-3.5 w-3.5 mr-1"
|
|
5070
5514
|
}), "Import"), /* @__PURE__ */ React.createElement(Button, {
|
|
5071
5515
|
onClick: handleExport,
|
|
5072
5516
|
variant: "outline",
|
|
5073
5517
|
className: "bg-transparent text-white border-gray-600 hover:bg-gray-700 text-xs h-8"
|
|
5074
|
-
}, /* @__PURE__ */ React.createElement(
|
|
5518
|
+
}, /* @__PURE__ */ React.createElement(Upload, {
|
|
5075
5519
|
className: "h-3.5 w-3.5 mr-1"
|
|
5076
|
-
}), "Export"), !addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5520
|
+
}), "Export"), canCreate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5077
5521
|
onClick: /* @__PURE__ */ __name(() => {
|
|
5078
5522
|
setEditingItem(null);
|
|
5079
5523
|
setDuplicateSeed(null);
|
|
@@ -5082,7 +5526,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5082
5526
|
className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8"
|
|
5083
5527
|
}, /* @__PURE__ */ React.createElement(Plus, {
|
|
5084
5528
|
className: "h-3.5 w-3.5"
|
|
5085
|
-
}), "Add"), addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
|
|
5529
|
+
}), "Add"), canCreate && addEditPage && dedicatedCreateHref && /* @__PURE__ */ React.createElement(Link2, {
|
|
5086
5530
|
href: dedicatedCreateHref,
|
|
5087
5531
|
className: "inline-flex items-center gap-1 bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs h-8 px-3 rounded-md font-medium"
|
|
5088
5532
|
}, /* @__PURE__ */ React.createElement(Plus, {
|
|
@@ -5241,7 +5685,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5241
5685
|
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
5242
5686
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
5243
5687
|
className: "flex items-center justify-center gap-1"
|
|
5244
|
-
}, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, !addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5688
|
+
}, customRowActions ? customRowActions(item) : /* @__PURE__ */ React.createElement(React.Fragment, null, canUpdate && !addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5245
5689
|
variant: "outline",
|
|
5246
5690
|
size: "icon",
|
|
5247
5691
|
className: "h-7 w-7",
|
|
@@ -5251,7 +5695,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5251
5695
|
className: "h-3.5 w-3.5"
|
|
5252
5696
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
5253
5697
|
className: "sr-only"
|
|
5254
|
-
}, "Edit")), addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5698
|
+
}, "Edit")), canUpdate && addEditPage && /* @__PURE__ */ React.createElement(Button, {
|
|
5255
5699
|
variant: "outline",
|
|
5256
5700
|
size: "icon",
|
|
5257
5701
|
className: "h-7 w-7",
|
|
@@ -5261,7 +5705,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5261
5705
|
className: "h-3.5 w-3.5"
|
|
5262
5706
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
5263
5707
|
className: "sr-only"
|
|
5264
|
-
}, "Edit")), showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
|
|
5708
|
+
}, "Edit")), canCreate && showDuplicateEntry && /* @__PURE__ */ React.createElement(Button, {
|
|
5265
5709
|
variant: "outline",
|
|
5266
5710
|
size: "icon",
|
|
5267
5711
|
className: "h-7 w-7",
|
|
@@ -5287,7 +5731,7 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5287
5731
|
className: "h-3.5 w-3.5"
|
|
5288
5732
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
5289
5733
|
className: "sr-only"
|
|
5290
|
-
}, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
|
|
5734
|
+
}, "Resend invite")), canDelete && /* @__PURE__ */ React.createElement(Button, {
|
|
5291
5735
|
variant: "outline",
|
|
5292
5736
|
size: "icon",
|
|
5293
5737
|
className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
|
|
@@ -5425,6 +5869,7 @@ var init_CRUD = __esm({
|
|
|
5425
5869
|
init_dropdown_menu();
|
|
5426
5870
|
init_utils();
|
|
5427
5871
|
init_use_mobile();
|
|
5872
|
+
init_rbac_debug();
|
|
5428
5873
|
init_BulkUploadDialog();
|
|
5429
5874
|
init_admin_list_return_url();
|
|
5430
5875
|
init_build_crud_list_filters_from_columns();
|
|
@@ -9140,28 +9585,75 @@ var init_AuthPageLayout = __esm({
|
|
|
9140
9585
|
}
|
|
9141
9586
|
});
|
|
9142
9587
|
|
|
9588
|
+
// src/auth/auth-providers-settings.ts
|
|
9589
|
+
function parseEnabled(raw) {
|
|
9590
|
+
if (typeof raw === "boolean") return raw;
|
|
9591
|
+
if (typeof raw === "number") return raw !== 0;
|
|
9592
|
+
if (typeof raw === "string") {
|
|
9593
|
+
const n = raw.trim().toLowerCase();
|
|
9594
|
+
return n === "true" || n === "1" || n === "yes" || n === "on";
|
|
9595
|
+
}
|
|
9596
|
+
return false;
|
|
9597
|
+
}
|
|
9598
|
+
function isGoogleAuthPubliclyEnabled(settings) {
|
|
9599
|
+
if (!settings) return false;
|
|
9600
|
+
if (settings.googleEnabled !== void 0 && settings.googleEnabled !== "") {
|
|
9601
|
+
return parseEnabled(settings.googleEnabled);
|
|
9602
|
+
}
|
|
9603
|
+
return parseEnabled(settings.enabled);
|
|
9604
|
+
}
|
|
9605
|
+
var init_auth_providers_settings = __esm({
|
|
9606
|
+
"src/auth/auth-providers-settings.ts"() {
|
|
9607
|
+
init_auth_debug();
|
|
9608
|
+
__name(parseEnabled, "parseEnabled");
|
|
9609
|
+
__name(isGoogleAuthPubliclyEnabled, "isGoogleAuthPubliclyEnabled");
|
|
9610
|
+
}
|
|
9611
|
+
});
|
|
9612
|
+
|
|
9143
9613
|
// src/admin/pages/SignInPage.tsx
|
|
9144
9614
|
var SignInPage_exports = {};
|
|
9145
9615
|
__export(SignInPage_exports, {
|
|
9146
9616
|
default: () => SignInPage_default
|
|
9147
9617
|
});
|
|
9618
|
+
function oauthErrorMessage(code) {
|
|
9619
|
+
if (!code) return "";
|
|
9620
|
+
const c = code.toLowerCase();
|
|
9621
|
+
if (c === "accessdenied" || c === "access_denied") {
|
|
9622
|
+
return "No account for this Google email. Ask an admin to invite you first.";
|
|
9623
|
+
}
|
|
9624
|
+
if (c === "oauthaccountnotlinked") {
|
|
9625
|
+
return "This Google account is not linked to an existing user. Sign in with email/password or ask an admin to invite you.";
|
|
9626
|
+
}
|
|
9627
|
+
if (c === "oauthcallback" || c === "oauthsignin" || c === "oauthcreateaccount") {
|
|
9628
|
+
return "Google sign-in failed. Check Authentication Provider settings and the redirect URI in Google Cloud Console.";
|
|
9629
|
+
}
|
|
9630
|
+
if (c === "configuration") {
|
|
9631
|
+
return "Google sign-in is not configured correctly.";
|
|
9632
|
+
}
|
|
9633
|
+
return "Invalid email or password";
|
|
9634
|
+
}
|
|
9148
9635
|
var SigninPage, SignInPage_default;
|
|
9149
9636
|
var init_SignInPage = __esm({
|
|
9150
9637
|
"src/admin/pages/SignInPage.tsx"() {
|
|
9151
9638
|
"use client";
|
|
9152
9639
|
init_AuthPageLayout();
|
|
9153
9640
|
init_auth_debug();
|
|
9641
|
+
init_auth_providers_settings();
|
|
9642
|
+
__name(oauthErrorMessage, "oauthErrorMessage");
|
|
9154
9643
|
SigninPage = /* @__PURE__ */ __name(() => {
|
|
9155
9644
|
const [email, setEmail] = useState("");
|
|
9156
9645
|
const [password, setPassword] = useState("");
|
|
9157
9646
|
const [error, setError] = useState("");
|
|
9158
9647
|
const [success, setSuccess] = useState("");
|
|
9159
9648
|
const [loading, setLoading] = useState(false);
|
|
9649
|
+
const [googleLoading, setGoogleLoading] = useState(false);
|
|
9650
|
+
const [googleEnabled, setGoogleEnabled] = useState(false);
|
|
9160
9651
|
const { status } = useSession();
|
|
9161
9652
|
const searchParams = useSearchParams();
|
|
9162
9653
|
useEffect(() => {
|
|
9163
|
-
|
|
9164
|
-
|
|
9654
|
+
const err = searchParams.get("error");
|
|
9655
|
+
if (err) {
|
|
9656
|
+
setError(oauthErrorMessage(err));
|
|
9165
9657
|
}
|
|
9166
9658
|
if (searchParams.get("activated") === "1") {
|
|
9167
9659
|
setSuccess("Account activated. Sign in with your new password.");
|
|
@@ -9172,6 +9664,23 @@ var init_SignInPage = __esm({
|
|
|
9172
9664
|
}, [
|
|
9173
9665
|
searchParams
|
|
9174
9666
|
]);
|
|
9667
|
+
useEffect(() => {
|
|
9668
|
+
let cancelled = false;
|
|
9669
|
+
(async () => {
|
|
9670
|
+
try {
|
|
9671
|
+
const res = await fetch("/api/settings/auth_providers", {
|
|
9672
|
+
cache: "no-store"
|
|
9673
|
+
});
|
|
9674
|
+
if (!res.ok) return;
|
|
9675
|
+
const data = await res.json();
|
|
9676
|
+
if (!cancelled) setGoogleEnabled(isGoogleAuthPubliclyEnabled(data));
|
|
9677
|
+
} catch {
|
|
9678
|
+
}
|
|
9679
|
+
})();
|
|
9680
|
+
return () => {
|
|
9681
|
+
cancelled = true;
|
|
9682
|
+
};
|
|
9683
|
+
}, []);
|
|
9175
9684
|
useEffect(() => {
|
|
9176
9685
|
logAuthClient("SignInPage session status", {
|
|
9177
9686
|
status
|
|
@@ -9229,6 +9738,19 @@ var init_SignInPage = __esm({
|
|
|
9229
9738
|
setLoading(false);
|
|
9230
9739
|
}
|
|
9231
9740
|
}, "handleSubmit");
|
|
9741
|
+
const handleGoogle = /* @__PURE__ */ __name(async () => {
|
|
9742
|
+
setGoogleLoading(true);
|
|
9743
|
+
setError("");
|
|
9744
|
+
try {
|
|
9745
|
+
logAuthClient("SignInPage Google signIn start");
|
|
9746
|
+
await signIn("google", {
|
|
9747
|
+
callbackUrl: "/admin/dashboard"
|
|
9748
|
+
});
|
|
9749
|
+
} catch {
|
|
9750
|
+
setError("Google sign-in failed. Please try again.");
|
|
9751
|
+
setGoogleLoading(false);
|
|
9752
|
+
}
|
|
9753
|
+
}, "handleGoogle");
|
|
9232
9754
|
return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
|
|
9233
9755
|
className: "w-full shadow-md rounded-lg bg-white px-4 py-6 sm:px-6 sm:py-8"
|
|
9234
9756
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
@@ -9281,9 +9803,42 @@ var init_SignInPage = __esm({
|
|
|
9281
9803
|
className: "text-xs font-medium text-gray-600 hover:underline"
|
|
9282
9804
|
}, "Forgot password?")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", {
|
|
9283
9805
|
type: "submit",
|
|
9284
|
-
disabled: loading,
|
|
9806
|
+
disabled: loading || googleLoading,
|
|
9285
9807
|
className: "w-full rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
9286
|
-
}, loading ? "Signing in..." : "Sign in")))
|
|
9808
|
+
}, loading ? "Signing in..." : "Sign in"))), googleEnabled && /* @__PURE__ */ React.createElement("div", {
|
|
9809
|
+
className: "mt-6"
|
|
9810
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
9811
|
+
className: "relative mb-4"
|
|
9812
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
9813
|
+
className: "absolute inset-0 flex items-center"
|
|
9814
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
9815
|
+
className: "w-full border-t border-gray-200"
|
|
9816
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
9817
|
+
className: "relative flex justify-center text-xs"
|
|
9818
|
+
}, /* @__PURE__ */ React.createElement("span", {
|
|
9819
|
+
className: "bg-white px-2 text-gray-500"
|
|
9820
|
+
}, "or"))), /* @__PURE__ */ React.createElement("button", {
|
|
9821
|
+
type: "button",
|
|
9822
|
+
onClick: handleGoogle,
|
|
9823
|
+
disabled: loading || googleLoading,
|
|
9824
|
+
className: "flex w-full items-center justify-center gap-2 rounded border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-800 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
9825
|
+
}, /* @__PURE__ */ React.createElement("svg", {
|
|
9826
|
+
className: "h-4 w-4",
|
|
9827
|
+
viewBox: "0 0 24 24",
|
|
9828
|
+
"aria-hidden": "true"
|
|
9829
|
+
}, /* @__PURE__ */ React.createElement("path", {
|
|
9830
|
+
fill: "#4285F4",
|
|
9831
|
+
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
9832
|
+
}), /* @__PURE__ */ React.createElement("path", {
|
|
9833
|
+
fill: "#34A853",
|
|
9834
|
+
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
9835
|
+
}), /* @__PURE__ */ React.createElement("path", {
|
|
9836
|
+
fill: "#FBBC05",
|
|
9837
|
+
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
9838
|
+
}), /* @__PURE__ */ React.createElement("path", {
|
|
9839
|
+
fill: "#EA4335",
|
|
9840
|
+
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
9841
|
+
})), googleLoading ? "Redirecting\u2026" : "Continue with Google"))));
|
|
9287
9842
|
}, "SigninPage");
|
|
9288
9843
|
SignInPage_default = SigninPage;
|
|
9289
9844
|
}
|
|
@@ -15383,6 +15938,18 @@ var init_email_recipients = __esm({
|
|
|
15383
15938
|
__name(serializeEmailRecipients, "serializeEmailRecipients");
|
|
15384
15939
|
}
|
|
15385
15940
|
});
|
|
15941
|
+
function useNotificationVariables() {
|
|
15942
|
+
const config = useContext(AdminConfigContext);
|
|
15943
|
+
const extra = config?.extraNotificationVariables ?? [];
|
|
15944
|
+
const vars = [
|
|
15945
|
+
...KNOWN_VARS,
|
|
15946
|
+
...extra.map((e) => e.name)
|
|
15947
|
+
];
|
|
15948
|
+
return {
|
|
15949
|
+
vars,
|
|
15950
|
+
extra
|
|
15951
|
+
};
|
|
15952
|
+
}
|
|
15386
15953
|
function getEmailTriggerDefaults(triggerKey, audience = "customer") {
|
|
15387
15954
|
if (audience === "vendor") {
|
|
15388
15955
|
return VENDOR_EMAIL_TRIGGER_DEFAULTS[triggerKey] ?? {
|
|
@@ -15407,6 +15974,30 @@ function getEmailTriggerDefaults(triggerKey, audience = "customer") {
|
|
|
15407
15974
|
bodyPlaceholder: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been updated."
|
|
15408
15975
|
};
|
|
15409
15976
|
}
|
|
15977
|
+
function getPushTriggerDefaults(triggerKey, audience = "customer") {
|
|
15978
|
+
if (audience === "vendor") {
|
|
15979
|
+
return VENDOR_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
|
|
15980
|
+
subject: "Vendor Push Update",
|
|
15981
|
+
body: "Hi {{vendorName}}, order {{orderNumber}} update.",
|
|
15982
|
+
subjectPlaceholder: "e.g. Vendor Push Update",
|
|
15983
|
+
bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} update."
|
|
15984
|
+
};
|
|
15985
|
+
}
|
|
15986
|
+
if (audience === "admin") {
|
|
15987
|
+
return ADMIN_PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
|
|
15988
|
+
subject: "Admin Push Alert",
|
|
15989
|
+
body: "Admin Alert: Order {{orderNumber}} update.",
|
|
15990
|
+
subjectPlaceholder: "e.g. Admin Push Alert",
|
|
15991
|
+
bodyPlaceholder: "Admin Alert: Order {{orderNumber}} update."
|
|
15992
|
+
};
|
|
15993
|
+
}
|
|
15994
|
+
return PUSH_TRIGGER_DEFAULTS[triggerKey] ?? {
|
|
15995
|
+
subject: "Order Update",
|
|
15996
|
+
body: "Hi {{customerName}}, order {{orderNumber}} update.",
|
|
15997
|
+
subjectPlaceholder: "e.g. Order Update",
|
|
15998
|
+
bodyPlaceholder: "Hi {{customerName}}, order {{orderNumber}} update."
|
|
15999
|
+
};
|
|
16000
|
+
}
|
|
15410
16001
|
async function jsonFetch(url, init) {
|
|
15411
16002
|
const res = await fetch(url, {
|
|
15412
16003
|
credentials: "include",
|
|
@@ -15498,10 +16089,11 @@ function useOrderNotificationTriggerBindings(channel, audienceType = "customers"
|
|
|
15498
16089
|
rebind
|
|
15499
16090
|
};
|
|
15500
16091
|
}
|
|
15501
|
-
var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS;
|
|
16092
|
+
var KNOWN_VARS, EMAIL_TRIGGER_DEFAULTS, VENDOR_EMAIL_TRIGGER_DEFAULTS, ADMIN_EMAIL_TRIGGER_DEFAULTS, PUSH_TRIGGER_DEFAULTS, VENDOR_PUSH_TRIGGER_DEFAULTS, ADMIN_PUSH_TRIGGER_DEFAULTS;
|
|
15502
16093
|
var init_order_notification_bindings_shared = __esm({
|
|
15503
16094
|
"src/admin/components/order-notification-bindings-shared.ts"() {
|
|
15504
16095
|
"use client";
|
|
16096
|
+
init_admin_config_context();
|
|
15505
16097
|
KNOWN_VARS = [
|
|
15506
16098
|
"orderId",
|
|
15507
16099
|
"orderNumber",
|
|
@@ -15514,10 +16106,9 @@ var init_order_notification_bindings_shared = __esm({
|
|
|
15514
16106
|
"vendorId",
|
|
15515
16107
|
"refundAmount",
|
|
15516
16108
|
"invoiceNumber",
|
|
15517
|
-
"invoiceUrl"
|
|
15518
|
-
"ticketQr",
|
|
15519
|
-
"qrImageUrl"
|
|
16109
|
+
"invoiceUrl"
|
|
15520
16110
|
];
|
|
16111
|
+
__name(useNotificationVariables, "useNotificationVariables");
|
|
15521
16112
|
EMAIL_TRIGGER_DEFAULTS = {
|
|
15522
16113
|
order_placed: {
|
|
15523
16114
|
subject: "Your order {{orderNumber}} is confirmed",
|
|
@@ -15595,7 +16186,7 @@ Order Summary:
|
|
|
15595
16186
|
Customer Name: {{customerName}}
|
|
15596
16187
|
Customer Email: {{customerEmail}}
|
|
15597
16188
|
Customer Phone: {{customerPhone}}`,
|
|
15598
|
-
bodyPlaceholder: "
|
|
16189
|
+
bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
|
|
15599
16190
|
},
|
|
15600
16191
|
order_cancelled: {
|
|
15601
16192
|
subject: "Order {{orderNumber}} cancelled",
|
|
@@ -15611,6 +16202,66 @@ Refund Due: {{refundAmount}} {{currency}}`,
|
|
|
15611
16202
|
}
|
|
15612
16203
|
};
|
|
15613
16204
|
__name(getEmailTriggerDefaults, "getEmailTriggerDefaults");
|
|
16205
|
+
PUSH_TRIGGER_DEFAULTS = {
|
|
16206
|
+
order_placed: {
|
|
16207
|
+
subject: "Order {{orderNumber}} Confirmed!",
|
|
16208
|
+
subjectPlaceholder: "e.g. Order {{orderNumber}} Confirmed!",
|
|
16209
|
+
body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} is confirmed.`,
|
|
16210
|
+
bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} is confirmed."
|
|
16211
|
+
},
|
|
16212
|
+
order_cancelled: {
|
|
16213
|
+
subject: "Order {{orderNumber}} Cancelled",
|
|
16214
|
+
subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
|
|
16215
|
+
body: `Hi {{customerName}}, your order {{orderNumber}} for {{total}} {{currency}} has been cancelled.`,
|
|
16216
|
+
bodyPlaceholder: "Hi {{customerName}}, your order {{orderNumber}} has been cancelled."
|
|
16217
|
+
}
|
|
16218
|
+
};
|
|
16219
|
+
VENDOR_PUSH_TRIGGER_DEFAULTS = {
|
|
16220
|
+
order_placed: {
|
|
16221
|
+
subject: "New Order Received: {{orderNumber}}",
|
|
16222
|
+
subjectPlaceholder: "e.g. New Order Received: {{orderNumber}}",
|
|
16223
|
+
body: `Hi {{vendorName}}, you received a new order {{orderNumber}} for {{total}} {{currency}} from {{customerName}}.`,
|
|
16224
|
+
bodyPlaceholder: "Hi {{vendorName}}, you received a new order {{orderNumber}}."
|
|
16225
|
+
},
|
|
16226
|
+
order_cancelled: {
|
|
16227
|
+
subject: "Order {{orderNumber}} Cancelled",
|
|
16228
|
+
subjectPlaceholder: "e.g. Order {{orderNumber}} Cancelled",
|
|
16229
|
+
body: `Hi {{vendorName}}, order {{orderNumber}} from {{customerName}} has been cancelled.`,
|
|
16230
|
+
bodyPlaceholder: "Hi {{vendorName}}, order {{orderNumber}} was cancelled."
|
|
16231
|
+
}
|
|
16232
|
+
};
|
|
16233
|
+
ADMIN_PUSH_TRIGGER_DEFAULTS = {
|
|
16234
|
+
order_placed: {
|
|
16235
|
+
subject: "New Order {{orderNumber}} ({{total}} {{currency}})",
|
|
16236
|
+
subjectPlaceholder: "e.g. New Order {{orderNumber}} placed",
|
|
16237
|
+
body: `Hi Admin,
|
|
16238
|
+
|
|
16239
|
+
A new order {{orderNumber}} of {{total}} {{currency}} was placed.
|
|
16240
|
+
|
|
16241
|
+
Vendor: {{vendorName}}
|
|
16242
|
+
|
|
16243
|
+
Order Summary:
|
|
16244
|
+
{{orderDetails}}
|
|
16245
|
+
|
|
16246
|
+
Customer Name: {{customerName}}
|
|
16247
|
+
Customer Email: {{customerEmail}}
|
|
16248
|
+
Customer Phone: {{customerPhone}}`,
|
|
16249
|
+
bodyPlaceholder: "New order {{orderNumber}} placed by {{customerName}}."
|
|
16250
|
+
},
|
|
16251
|
+
order_cancelled: {
|
|
16252
|
+
subject: "Order {{orderNumber}} Cancelled",
|
|
16253
|
+
subjectPlaceholder: "e.g. Order {{orderNumber}} cancelled",
|
|
16254
|
+
body: `Hi Admin,
|
|
16255
|
+
|
|
16256
|
+
Order {{orderNumber}} has been cancelled.
|
|
16257
|
+
|
|
16258
|
+
Customer: {{customerName}}
|
|
16259
|
+
Vendor: {{vendorName}}
|
|
16260
|
+
Refund Due: {{refundAmount}} {{currency}}`,
|
|
16261
|
+
bodyPlaceholder: "Hi Admin,\n\nOrder {{orderNumber}} cancelled."
|
|
16262
|
+
}
|
|
16263
|
+
};
|
|
16264
|
+
__name(getPushTriggerDefaults, "getPushTriggerDefaults");
|
|
15614
16265
|
__name(jsonFetch, "jsonFetch");
|
|
15615
16266
|
__name(useOrderNotificationTriggerBindings, "useOrderNotificationTriggerBindings");
|
|
15616
16267
|
}
|
|
@@ -16161,16 +16812,23 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
|
|
|
16161
16812
|
cancelled = true;
|
|
16162
16813
|
};
|
|
16163
16814
|
}, []);
|
|
16815
|
+
const { vars: availableVars, extra: extraVars } = useNotificationVariables();
|
|
16164
16816
|
const visibleVars = useMemo(() => {
|
|
16165
|
-
return
|
|
16817
|
+
return availableVars.filter((v) => {
|
|
16166
16818
|
if (v === "vendorName" || v === "vendorId") return multiVendorOn;
|
|
16167
16819
|
if (v === "eventName") return eventsOn;
|
|
16168
16820
|
return true;
|
|
16169
16821
|
});
|
|
16170
16822
|
}, [
|
|
16823
|
+
availableVars,
|
|
16171
16824
|
multiVendorOn,
|
|
16172
16825
|
eventsOn
|
|
16173
16826
|
]);
|
|
16827
|
+
const getVarHint = /* @__PURE__ */ __name((v) => {
|
|
16828
|
+
if (v in VAR_HINTS) return VAR_HINTS[v];
|
|
16829
|
+
const match = extraVars.find((e) => e.name === v);
|
|
16830
|
+
return match?.hint || match?.label || v;
|
|
16831
|
+
}, "getVarHint");
|
|
16174
16832
|
return /* @__PURE__ */ React.createElement("div", {
|
|
16175
16833
|
className: "space-y-3 border-t border-gray-100 pt-4 first:border-t-0 first:pt-0 dark:border-gray-700"
|
|
16176
16834
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -16233,7 +16891,7 @@ function TriggerEmailEditor({ triggerKey, label, description, audience = "custom
|
|
|
16233
16891
|
}, visibleVars.map((v) => /* @__PURE__ */ React.createElement("button", {
|
|
16234
16892
|
key: v,
|
|
16235
16893
|
type: "button",
|
|
16236
|
-
title:
|
|
16894
|
+
title: getVarHint(v),
|
|
16237
16895
|
onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
|
|
16238
16896
|
className: "rounded border border-gray-200 bg-white px-2 py-1 font-mono text-[11px] text-gray-700 hover:border-blue-300 hover:bg-blue-50 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-300 dark:hover:border-blue-700 dark:hover:bg-blue-950/40"
|
|
16239
16897
|
}, `{{${v}}}`))), /* @__PURE__ */ React.createElement("p", {
|
|
@@ -16261,14 +16919,588 @@ var init_emailTriggersBindingsPage = __esm({
|
|
|
16261
16919
|
vendorId: "Vendor / store ID",
|
|
16262
16920
|
refundAmount: "Refund amount (refund triggers)",
|
|
16263
16921
|
invoiceNumber: "Generated invoice number",
|
|
16264
|
-
invoiceUrl: "Link to download invoice PDF"
|
|
16265
|
-
ticketQr: "Embedded ticket QR code image",
|
|
16266
|
-
qrImageUrl: "Direct QR code image URL"
|
|
16922
|
+
invoiceUrl: "Link to download invoice PDF"
|
|
16267
16923
|
};
|
|
16268
16924
|
__name(EmailTriggerBindings, "EmailTriggerBindings");
|
|
16269
16925
|
__name(TriggerEmailEditor, "TriggerEmailEditor");
|
|
16270
16926
|
}
|
|
16271
16927
|
});
|
|
16928
|
+
function PushTriggerBindings({ audience = "customer" }) {
|
|
16929
|
+
const audienceType = audience === "vendor" ? "vendors" : audience === "admin" ? "admin" : "customers";
|
|
16930
|
+
const { triggers, bindings, loading, reload, rebind } = useOrderNotificationTriggerBindings("mobile", audienceType);
|
|
16931
|
+
const [selectedKey, setSelectedKey] = useState("");
|
|
16932
|
+
const [templates, setTemplates] = useState([]);
|
|
16933
|
+
const [loadingTpl, setLoadingTpl] = useState(true);
|
|
16934
|
+
const [title, setTitle] = useState("");
|
|
16935
|
+
const [body, setBody] = useState("");
|
|
16936
|
+
const [saving, setSaving] = useState(false);
|
|
16937
|
+
const bodyRef = useRef(null);
|
|
16938
|
+
const subjectRef = useRef(null);
|
|
16939
|
+
const lastFocused = useRef("body");
|
|
16940
|
+
const { vars: availableVars, extra: extraVars } = useNotificationVariables();
|
|
16941
|
+
const getVarHint = /* @__PURE__ */ __name((v) => {
|
|
16942
|
+
if (v in VAR_HINTS2) return VAR_HINTS2[v];
|
|
16943
|
+
const match = extraVars.find((e) => e.name === v);
|
|
16944
|
+
return match?.hint || match?.label || v;
|
|
16945
|
+
}, "getVarHint");
|
|
16946
|
+
useEffect(() => {
|
|
16947
|
+
let cancelled = false;
|
|
16948
|
+
(async () => {
|
|
16949
|
+
try {
|
|
16950
|
+
setLoadingTpl(true);
|
|
16951
|
+
const res = await fetch("/api/message-templates/email", {
|
|
16952
|
+
cache: "no-store"
|
|
16953
|
+
});
|
|
16954
|
+
if (!res.ok) throw new Error("Failed to load message templates");
|
|
16955
|
+
const data = await res.json();
|
|
16956
|
+
if (!cancelled) setTemplates(Array.isArray(data.items) ? data.items : []);
|
|
16957
|
+
} catch {
|
|
16958
|
+
if (!cancelled) toast.error("Could not load message templates");
|
|
16959
|
+
} finally {
|
|
16960
|
+
if (!cancelled) setLoadingTpl(false);
|
|
16961
|
+
}
|
|
16962
|
+
})();
|
|
16963
|
+
return () => {
|
|
16964
|
+
cancelled = true;
|
|
16965
|
+
};
|
|
16966
|
+
}, []);
|
|
16967
|
+
useEffect(() => {
|
|
16968
|
+
if (triggers.length > 0 && !selectedKey) {
|
|
16969
|
+
setSelectedKey(triggers[0].triggerKey);
|
|
16970
|
+
}
|
|
16971
|
+
}, [
|
|
16972
|
+
triggers,
|
|
16973
|
+
selectedKey
|
|
16974
|
+
]);
|
|
16975
|
+
const activeTrigger = useMemo(() => triggers.find((t) => t.triggerKey === selectedKey), [
|
|
16976
|
+
triggers,
|
|
16977
|
+
selectedKey
|
|
16978
|
+
]);
|
|
16979
|
+
const bindingMap = useMemo(() => {
|
|
16980
|
+
const m = /* @__PURE__ */ new Map();
|
|
16981
|
+
for (const b of bindings) {
|
|
16982
|
+
if (b.messageTemplateId != null) m.set(b.triggerKey, b.messageTemplateId);
|
|
16983
|
+
}
|
|
16984
|
+
return m;
|
|
16985
|
+
}, [
|
|
16986
|
+
bindings
|
|
16987
|
+
]);
|
|
16988
|
+
const activeBindingTplId = selectedKey ? bindingMap.get(selectedKey) ?? null : null;
|
|
16989
|
+
const activeTemplate = useMemo(() => {
|
|
16990
|
+
if (!activeBindingTplId) return null;
|
|
16991
|
+
return templates.find((t) => t.id === activeBindingTplId) ?? null;
|
|
16992
|
+
}, [
|
|
16993
|
+
activeBindingTplId,
|
|
16994
|
+
templates
|
|
16995
|
+
]);
|
|
16996
|
+
const defaults = useMemo(() => {
|
|
16997
|
+
if (!selectedKey) return {
|
|
16998
|
+
subject: "",
|
|
16999
|
+
body: "",
|
|
17000
|
+
subjectPlaceholder: "",
|
|
17001
|
+
bodyPlaceholder: ""
|
|
17002
|
+
};
|
|
17003
|
+
return getPushTriggerDefaults(selectedKey, audience);
|
|
17004
|
+
}, [
|
|
17005
|
+
selectedKey,
|
|
17006
|
+
audience
|
|
17007
|
+
]);
|
|
17008
|
+
useEffect(() => {
|
|
17009
|
+
if (activeTemplate) {
|
|
17010
|
+
setTitle(activeTemplate.subject ?? "");
|
|
17011
|
+
setBody(activeTemplate.body ?? "");
|
|
17012
|
+
} else {
|
|
17013
|
+
setTitle(defaults.subject);
|
|
17014
|
+
setBody(defaults.body);
|
|
17015
|
+
}
|
|
17016
|
+
}, [
|
|
17017
|
+
activeTemplate,
|
|
17018
|
+
defaults,
|
|
17019
|
+
selectedKey
|
|
17020
|
+
]);
|
|
17021
|
+
const insertVariable = /* @__PURE__ */ __name((varName) => {
|
|
17022
|
+
const token = `{{${varName}}}`;
|
|
17023
|
+
if (lastFocused.current === "subject" && subjectRef.current) {
|
|
17024
|
+
const el = subjectRef.current;
|
|
17025
|
+
const start = el.selectionStart ?? title.length;
|
|
17026
|
+
const end = el.selectionEnd ?? title.length;
|
|
17027
|
+
const next = title.slice(0, start) + token + title.slice(end);
|
|
17028
|
+
setTitle(next);
|
|
17029
|
+
setTimeout(() => {
|
|
17030
|
+
el.focus();
|
|
17031
|
+
el.setSelectionRange(start + token.length, start + token.length);
|
|
17032
|
+
}, 0);
|
|
17033
|
+
return;
|
|
17034
|
+
}
|
|
17035
|
+
if (bodyRef.current) {
|
|
17036
|
+
const el = bodyRef.current;
|
|
17037
|
+
const start = el.selectionStart ?? body.length;
|
|
17038
|
+
const end = el.selectionEnd ?? body.length;
|
|
17039
|
+
const next = body.slice(0, start) + token + body.slice(end);
|
|
17040
|
+
setBody(next);
|
|
17041
|
+
setTimeout(() => {
|
|
17042
|
+
el.focus();
|
|
17043
|
+
el.setSelectionRange(start + token.length, start + token.length);
|
|
17044
|
+
}, 0);
|
|
17045
|
+
} else {
|
|
17046
|
+
setBody((prev) => prev + token);
|
|
17047
|
+
}
|
|
17048
|
+
}, "insertVariable");
|
|
17049
|
+
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
17050
|
+
if (!selectedKey) return;
|
|
17051
|
+
setSaving(true);
|
|
17052
|
+
try {
|
|
17053
|
+
let tplId = activeBindingTplId;
|
|
17054
|
+
if (tplId) {
|
|
17055
|
+
const res = await jsonFetch(`/api/message-templates/email/${tplId}`, {
|
|
17056
|
+
method: "PATCH",
|
|
17057
|
+
headers: {
|
|
17058
|
+
"Content-Type": "application/json"
|
|
17059
|
+
},
|
|
17060
|
+
body: JSON.stringify({
|
|
17061
|
+
name: `${audienceType}_push_${selectedKey}`,
|
|
17062
|
+
subject: title,
|
|
17063
|
+
body
|
|
17064
|
+
})
|
|
17065
|
+
});
|
|
17066
|
+
if (res.item) {
|
|
17067
|
+
setTemplates((prev) => prev.map((t) => t.id === res.item.id ? res.item : t));
|
|
17068
|
+
}
|
|
17069
|
+
} else {
|
|
17070
|
+
const res = await jsonFetch("/api/message-templates/email", {
|
|
17071
|
+
method: "POST",
|
|
17072
|
+
headers: {
|
|
17073
|
+
"Content-Type": "application/json"
|
|
17074
|
+
},
|
|
17075
|
+
body: JSON.stringify({
|
|
17076
|
+
name: `${audienceType}_push_${selectedKey}`,
|
|
17077
|
+
subject: title,
|
|
17078
|
+
body
|
|
17079
|
+
})
|
|
17080
|
+
});
|
|
17081
|
+
const createdItem = res.item || res;
|
|
17082
|
+
tplId = createdItem.id;
|
|
17083
|
+
if (createdItem) {
|
|
17084
|
+
setTemplates((prev) => [
|
|
17085
|
+
createdItem,
|
|
17086
|
+
...prev
|
|
17087
|
+
]);
|
|
17088
|
+
}
|
|
17089
|
+
}
|
|
17090
|
+
await rebind(selectedKey, tplId, `${audienceType}_push_${selectedKey}`);
|
|
17091
|
+
toast.success("Push notification template saved");
|
|
17092
|
+
await reload();
|
|
17093
|
+
} catch (e) {
|
|
17094
|
+
const msg = e instanceof Error ? e.message : "Save failed";
|
|
17095
|
+
toast.error(msg);
|
|
17096
|
+
} finally {
|
|
17097
|
+
setSaving(false);
|
|
17098
|
+
}
|
|
17099
|
+
}, "handleSave");
|
|
17100
|
+
if (loading || loadingTpl) {
|
|
17101
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
17102
|
+
className: "flex items-center gap-2 p-6 text-sm text-gray-500"
|
|
17103
|
+
}, /* @__PURE__ */ React.createElement(Loader2, {
|
|
17104
|
+
className: "h-4 w-4 animate-spin"
|
|
17105
|
+
}), " Loading push notification triggers\u2026");
|
|
17106
|
+
}
|
|
17107
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
17108
|
+
className: "space-y-6"
|
|
17109
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17110
|
+
className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4"
|
|
17111
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17112
|
+
className: "space-y-1"
|
|
17113
|
+
}, /* @__PURE__ */ React.createElement("h3", {
|
|
17114
|
+
className: "text-base font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
17115
|
+
}, /* @__PURE__ */ React.createElement(Smartphone, {
|
|
17116
|
+
className: "h-5 w-5 text-gray-700 dark:text-gray-300"
|
|
17117
|
+
}), "Mobile Push Notification Templates"), /* @__PURE__ */ React.createElement("p", {
|
|
17118
|
+
className: "text-xs text-gray-500"
|
|
17119
|
+
}, "Configure push notification title and message body per trigger for", " ", /* @__PURE__ */ React.createElement("span", {
|
|
17120
|
+
className: "font-semibold capitalize text-gray-700 dark:text-gray-300"
|
|
17121
|
+
}, audience, "s"), ".")), /* @__PURE__ */ React.createElement(Button, {
|
|
17122
|
+
type: "button",
|
|
17123
|
+
onClick: handleSave,
|
|
17124
|
+
disabled: saving || !selectedKey,
|
|
17125
|
+
className: "gap-2 shrink-0"
|
|
17126
|
+
}, saving ? /* @__PURE__ */ React.createElement(Loader2, {
|
|
17127
|
+
className: "h-4 w-4 animate-spin"
|
|
17128
|
+
}) : /* @__PURE__ */ React.createElement(Save, {
|
|
17129
|
+
className: "h-4 w-4"
|
|
17130
|
+
}), "Save Template")), /* @__PURE__ */ React.createElement("div", {
|
|
17131
|
+
className: "grid grid-cols-1 md:grid-cols-4 gap-6"
|
|
17132
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17133
|
+
className: "md:col-span-1 space-y-2"
|
|
17134
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17135
|
+
className: "text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
|
17136
|
+
}, "Triggers"), /* @__PURE__ */ React.createElement("div", {
|
|
17137
|
+
className: "flex flex-col gap-1"
|
|
17138
|
+
}, triggers.map((t) => {
|
|
17139
|
+
const isBound = bindingMap.has(t.triggerKey);
|
|
17140
|
+
const isSelected = t.triggerKey === selectedKey;
|
|
17141
|
+
return /* @__PURE__ */ React.createElement("button", {
|
|
17142
|
+
key: t.triggerKey,
|
|
17143
|
+
type: "button",
|
|
17144
|
+
onClick: /* @__PURE__ */ __name(() => setSelectedKey(t.triggerKey), "onClick"),
|
|
17145
|
+
className: `flex flex-col items-start p-3 rounded-lg border text-left transition-all ${isSelected ? "border-gray-900 bg-gray-900 text-white dark:border-gray-100 dark:bg-gray-100 dark:text-gray-900 shadow-sm" : "border-gray-200 bg-white hover:bg-gray-50 text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300"}`
|
|
17146
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17147
|
+
className: "flex items-center justify-between w-full"
|
|
17148
|
+
}, /* @__PURE__ */ React.createElement("span", {
|
|
17149
|
+
className: "font-medium text-sm"
|
|
17150
|
+
}, t.label), isBound && /* @__PURE__ */ React.createElement("span", {
|
|
17151
|
+
className: `text-[10px] px-1.5 py-0.5 rounded font-mono ${isSelected ? "bg-white/20 text-white dark:bg-black/20 dark:text-gray-900" : "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300"}`
|
|
17152
|
+
}, "Configured")), /* @__PURE__ */ React.createElement("span", {
|
|
17153
|
+
className: `text-xs mt-1 ${isSelected ? "text-gray-300 dark:text-gray-600" : "text-gray-500 dark:text-gray-400"}`
|
|
17154
|
+
}, t.triggerKey));
|
|
17155
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
17156
|
+
className: "md:col-span-3 space-y-5 bg-white dark:bg-gray-900 p-5 rounded-xl border border-gray-200 dark:border-gray-800 shadow-sm"
|
|
17157
|
+
}, activeTrigger ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h4", {
|
|
17158
|
+
className: "text-sm font-semibold text-gray-900 dark:text-gray-100"
|
|
17159
|
+
}, activeTrigger.label, " (", activeTrigger.triggerKey, ")"), activeTrigger.description && /* @__PURE__ */ React.createElement("p", {
|
|
17160
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
17161
|
+
}, activeTrigger.description)), /* @__PURE__ */ React.createElement("div", {
|
|
17162
|
+
className: "space-y-1.5"
|
|
17163
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17164
|
+
className: "text-xs font-medium text-gray-700 dark:text-gray-300"
|
|
17165
|
+
}, "Insert Variables"), /* @__PURE__ */ React.createElement("div", {
|
|
17166
|
+
className: "flex flex-wrap gap-1.5"
|
|
17167
|
+
}, availableVars.map((v) => /* @__PURE__ */ React.createElement("button", {
|
|
17168
|
+
key: v,
|
|
17169
|
+
type: "button",
|
|
17170
|
+
onClick: /* @__PURE__ */ __name(() => insertVariable(v), "onClick"),
|
|
17171
|
+
title: getVarHint(v),
|
|
17172
|
+
className: "px-2 py-1 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-xs font-mono border border-gray-200 dark:border-gray-700 transition-colors"
|
|
17173
|
+
}, `{{${v}}}`)))), /* @__PURE__ */ React.createElement("div", {
|
|
17174
|
+
className: "space-y-1.5"
|
|
17175
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17176
|
+
htmlFor: "push-title",
|
|
17177
|
+
className: "text-xs font-medium"
|
|
17178
|
+
}, "Notification Title"), /* @__PURE__ */ React.createElement(Input, {
|
|
17179
|
+
id: "push-title",
|
|
17180
|
+
ref: subjectRef,
|
|
17181
|
+
value: title,
|
|
17182
|
+
onFocus: /* @__PURE__ */ __name(() => {
|
|
17183
|
+
lastFocused.current = "subject";
|
|
17184
|
+
}, "onFocus"),
|
|
17185
|
+
onChange: /* @__PURE__ */ __name((e) => setTitle(e.target.value), "onChange"),
|
|
17186
|
+
placeholder: defaults.subjectPlaceholder
|
|
17187
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
17188
|
+
className: "space-y-1.5"
|
|
17189
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17190
|
+
htmlFor: "push-body",
|
|
17191
|
+
className: "text-xs font-medium"
|
|
17192
|
+
}, "Notification Body"), /* @__PURE__ */ React.createElement(Textarea, {
|
|
17193
|
+
id: "push-body",
|
|
17194
|
+
ref: bodyRef,
|
|
17195
|
+
value: body,
|
|
17196
|
+
onFocus: /* @__PURE__ */ __name(() => {
|
|
17197
|
+
lastFocused.current = "body";
|
|
17198
|
+
}, "onFocus"),
|
|
17199
|
+
onChange: /* @__PURE__ */ __name((e) => setBody(e.target.value), "onChange"),
|
|
17200
|
+
placeholder: defaults.bodyPlaceholder,
|
|
17201
|
+
className: "h-36 font-mono text-xs"
|
|
17202
|
+
}))) : /* @__PURE__ */ React.createElement("div", {
|
|
17203
|
+
className: "p-12 text-center text-sm text-gray-500"
|
|
17204
|
+
}, "Select a trigger from the left to configure push notification template."))));
|
|
17205
|
+
}
|
|
17206
|
+
var VAR_HINTS2;
|
|
17207
|
+
var init_pushTriggersBindingsPage = __esm({
|
|
17208
|
+
"src/admin/pages/pushTriggersBindingsPage.tsx"() {
|
|
17209
|
+
"use client";
|
|
17210
|
+
init_button();
|
|
17211
|
+
init_input();
|
|
17212
|
+
init_textarea();
|
|
17213
|
+
init_label();
|
|
17214
|
+
init_order_notification_bindings_shared();
|
|
17215
|
+
VAR_HINTS2 = {
|
|
17216
|
+
orderId: "Internal order ID",
|
|
17217
|
+
orderNumber: "Display order number",
|
|
17218
|
+
customerName: "Customer full name",
|
|
17219
|
+
customerPhone: "Customer phone",
|
|
17220
|
+
eventName: "Event title / product name",
|
|
17221
|
+
vendorName: "Vendor / store name",
|
|
17222
|
+
total: "Order total amount",
|
|
17223
|
+
currency: "Currency code (e.g. INR)",
|
|
17224
|
+
vendorId: "Vendor / store ID",
|
|
17225
|
+
refundAmount: "Refund amount (refund triggers)",
|
|
17226
|
+
invoiceNumber: "Generated invoice number",
|
|
17227
|
+
invoiceUrl: "Link to download invoice PDF"
|
|
17228
|
+
};
|
|
17229
|
+
__name(PushTriggerBindings, "PushTriggerBindings");
|
|
17230
|
+
}
|
|
17231
|
+
});
|
|
17232
|
+
function PushConfigModal({ open, onOpenChange }) {
|
|
17233
|
+
const [loading, setLoading] = useState(false);
|
|
17234
|
+
const [saving, setSaving] = useState(false);
|
|
17235
|
+
const [testing, setTesting] = useState(false);
|
|
17236
|
+
const [serviceAccountJson, setServiceAccountJson] = useState("");
|
|
17237
|
+
const [projectId, setProjectId] = useState("");
|
|
17238
|
+
const [clientEmail, setClientEmail] = useState("");
|
|
17239
|
+
const [privateKey, setPrivateKey] = useState("");
|
|
17240
|
+
const [isValidJson, setIsValidJson] = useState(false);
|
|
17241
|
+
const [testToken, setTestToken] = useState("");
|
|
17242
|
+
const [testTitle, setTestTitle] = useState("Test Push Notification");
|
|
17243
|
+
const [testBody, setTestBody] = useState("Firebase Push Notification setup is working!");
|
|
17244
|
+
useEffect(() => {
|
|
17245
|
+
if (!open) return;
|
|
17246
|
+
let cancelled = false;
|
|
17247
|
+
(async () => {
|
|
17248
|
+
try {
|
|
17249
|
+
setLoading(true);
|
|
17250
|
+
const res = await fetch("/api/settings/push", {
|
|
17251
|
+
cache: "no-store"
|
|
17252
|
+
});
|
|
17253
|
+
if (res.ok) {
|
|
17254
|
+
const data = await res.json();
|
|
17255
|
+
if (!cancelled && data) {
|
|
17256
|
+
const rawJson = data.firebase_service_account_json || "";
|
|
17257
|
+
setServiceAccountJson(rawJson);
|
|
17258
|
+
if (rawJson.trim()) {
|
|
17259
|
+
parseAndExtract(rawJson, false);
|
|
17260
|
+
} else {
|
|
17261
|
+
setProjectId(data.firebase_project_id || "");
|
|
17262
|
+
setClientEmail(data.firebase_client_email || "");
|
|
17263
|
+
setPrivateKey(data.firebase_private_key || "");
|
|
17264
|
+
}
|
|
17265
|
+
}
|
|
17266
|
+
}
|
|
17267
|
+
} catch {
|
|
17268
|
+
toast.error("Failed to load push configuration");
|
|
17269
|
+
} finally {
|
|
17270
|
+
if (!cancelled) setLoading(false);
|
|
17271
|
+
}
|
|
17272
|
+
})();
|
|
17273
|
+
return () => {
|
|
17274
|
+
cancelled = true;
|
|
17275
|
+
};
|
|
17276
|
+
}, [
|
|
17277
|
+
open
|
|
17278
|
+
]);
|
|
17279
|
+
const parseAndExtract = /* @__PURE__ */ __name((input, showToast = true) => {
|
|
17280
|
+
if (!input.trim()) {
|
|
17281
|
+
setIsValidJson(false);
|
|
17282
|
+
return;
|
|
17283
|
+
}
|
|
17284
|
+
try {
|
|
17285
|
+
const parsed = JSON.parse(input);
|
|
17286
|
+
if (parsed.project_id) setProjectId(parsed.project_id);
|
|
17287
|
+
if (parsed.client_email) setClientEmail(parsed.client_email);
|
|
17288
|
+
if (parsed.private_key) setPrivateKey(parsed.private_key);
|
|
17289
|
+
setIsValidJson(true);
|
|
17290
|
+
if (showToast) {
|
|
17291
|
+
toast.success("Valid Service Account JSON loaded");
|
|
17292
|
+
}
|
|
17293
|
+
} catch {
|
|
17294
|
+
setIsValidJson(false);
|
|
17295
|
+
if (showToast) {
|
|
17296
|
+
toast.error("Invalid Service Account JSON format");
|
|
17297
|
+
}
|
|
17298
|
+
}
|
|
17299
|
+
}, "parseAndExtract");
|
|
17300
|
+
const handleJsonChange = /* @__PURE__ */ __name((val) => {
|
|
17301
|
+
setServiceAccountJson(val);
|
|
17302
|
+
if (val.trim()) {
|
|
17303
|
+
parseAndExtract(val, false);
|
|
17304
|
+
} else {
|
|
17305
|
+
setIsValidJson(false);
|
|
17306
|
+
}
|
|
17307
|
+
}, "handleJsonChange");
|
|
17308
|
+
const handleSaveConfig = /* @__PURE__ */ __name(async () => {
|
|
17309
|
+
if (serviceAccountJson.trim() && !isValidJson) {
|
|
17310
|
+
toast.error("Please enter a valid Firebase Service Account JSON file");
|
|
17311
|
+
return;
|
|
17312
|
+
}
|
|
17313
|
+
setSaving(true);
|
|
17314
|
+
try {
|
|
17315
|
+
const res = await fetch("/api/settings/push", {
|
|
17316
|
+
method: "PUT",
|
|
17317
|
+
headers: {
|
|
17318
|
+
"Content-Type": "application/json"
|
|
17319
|
+
},
|
|
17320
|
+
body: JSON.stringify({
|
|
17321
|
+
firebase_service_account_json: {
|
|
17322
|
+
value: serviceAccountJson.trim(),
|
|
17323
|
+
type: "secret"
|
|
17324
|
+
},
|
|
17325
|
+
firebase_project_id: {
|
|
17326
|
+
value: projectId.trim(),
|
|
17327
|
+
type: "secret"
|
|
17328
|
+
},
|
|
17329
|
+
firebase_client_email: {
|
|
17330
|
+
value: clientEmail.trim(),
|
|
17331
|
+
type: "secret"
|
|
17332
|
+
},
|
|
17333
|
+
firebase_private_key: {
|
|
17334
|
+
value: privateKey.trim(),
|
|
17335
|
+
type: "secret"
|
|
17336
|
+
}
|
|
17337
|
+
})
|
|
17338
|
+
});
|
|
17339
|
+
if (!res.ok) {
|
|
17340
|
+
toast.error("Failed to save push configuration");
|
|
17341
|
+
return;
|
|
17342
|
+
}
|
|
17343
|
+
toast.success("Firebase Service Account JSON saved successfully");
|
|
17344
|
+
onOpenChange(false);
|
|
17345
|
+
} catch {
|
|
17346
|
+
toast.error("Failed to save push configuration");
|
|
17347
|
+
} finally {
|
|
17348
|
+
setSaving(false);
|
|
17349
|
+
}
|
|
17350
|
+
}, "handleSaveConfig");
|
|
17351
|
+
const handleSendTestPush = /* @__PURE__ */ __name(async () => {
|
|
17352
|
+
if (!testToken.trim()) {
|
|
17353
|
+
toast.error("Please enter a target FCM device token to test");
|
|
17354
|
+
return;
|
|
17355
|
+
}
|
|
17356
|
+
setTesting(true);
|
|
17357
|
+
try {
|
|
17358
|
+
const res = await fetch("/api/settings/push/test", {
|
|
17359
|
+
method: "POST",
|
|
17360
|
+
headers: {
|
|
17361
|
+
"Content-Type": "application/json"
|
|
17362
|
+
},
|
|
17363
|
+
body: JSON.stringify({
|
|
17364
|
+
token: testToken.trim(),
|
|
17365
|
+
title: testTitle.trim(),
|
|
17366
|
+
body: testBody.trim()
|
|
17367
|
+
})
|
|
17368
|
+
});
|
|
17369
|
+
const data = await res.json();
|
|
17370
|
+
if (!res.ok || !data.success) {
|
|
17371
|
+
toast.error(data.error || "Failed to send test push notification");
|
|
17372
|
+
return;
|
|
17373
|
+
}
|
|
17374
|
+
toast.success("Test push notification sent successfully!");
|
|
17375
|
+
} catch {
|
|
17376
|
+
toast.error("Failed to send test push notification");
|
|
17377
|
+
} finally {
|
|
17378
|
+
setTesting(false);
|
|
17379
|
+
}
|
|
17380
|
+
}, "handleSendTestPush");
|
|
17381
|
+
if (!open) return null;
|
|
17382
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
17383
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm animate-in fade-in duration-150"
|
|
17384
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17385
|
+
className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-xl bg-white p-6 shadow-2xl dark:bg-gray-900 border border-gray-200 dark:border-gray-800 space-y-6"
|
|
17386
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17387
|
+
className: "flex items-center justify-between border-b border-gray-200 pb-4 dark:border-gray-800"
|
|
17388
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17389
|
+
className: "flex items-center gap-2"
|
|
17390
|
+
}, /* @__PURE__ */ React.createElement(Smartphone, {
|
|
17391
|
+
className: "h-5 w-5 text-gray-800 dark:text-gray-200"
|
|
17392
|
+
}), /* @__PURE__ */ React.createElement("h2", {
|
|
17393
|
+
className: "text-lg font-semibold text-gray-900 dark:text-gray-100"
|
|
17394
|
+
}, "Firebase Service Account JSON Configuration")), /* @__PURE__ */ React.createElement("button", {
|
|
17395
|
+
type: "button",
|
|
17396
|
+
onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick"),
|
|
17397
|
+
className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
17398
|
+
}, "\u2715")), loading ? /* @__PURE__ */ React.createElement("div", {
|
|
17399
|
+
className: "flex items-center justify-center py-12"
|
|
17400
|
+
}, /* @__PURE__ */ React.createElement(Loader2, {
|
|
17401
|
+
className: "h-6 w-6 animate-spin text-gray-500"
|
|
17402
|
+
})) : /* @__PURE__ */ React.createElement("div", {
|
|
17403
|
+
className: "space-y-6"
|
|
17404
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17405
|
+
className: "space-y-2"
|
|
17406
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17407
|
+
htmlFor: "service-account-json",
|
|
17408
|
+
className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center justify-between"
|
|
17409
|
+
}, /* @__PURE__ */ React.createElement("span", {
|
|
17410
|
+
className: "flex items-center gap-1.5"
|
|
17411
|
+
}, /* @__PURE__ */ React.createElement(KeyRound, {
|
|
17412
|
+
className: "h-4 w-4 text-blue-500"
|
|
17413
|
+
}), "Firebase Service Account JSON File"), isValidJson && /* @__PURE__ */ React.createElement("span", {
|
|
17414
|
+
className: "text-xs font-normal text-green-600 flex items-center gap-1"
|
|
17415
|
+
}, /* @__PURE__ */ React.createElement(CheckCircle2, {
|
|
17416
|
+
className: "h-3.5 w-3.5"
|
|
17417
|
+
}), " Valid JSON")), /* @__PURE__ */ React.createElement(Textarea, {
|
|
17418
|
+
id: "service-account-json",
|
|
17419
|
+
value: serviceAccountJson,
|
|
17420
|
+
onChange: /* @__PURE__ */ __name((e) => handleJsonChange(e.target.value), "onChange"),
|
|
17421
|
+
placeholder: `Paste your entire service-account.json content here...
|
|
17422
|
+
{
|
|
17423
|
+
"type": "service_account",
|
|
17424
|
+
"project_id": "...",
|
|
17425
|
+
"private_key": "...",
|
|
17426
|
+
"client_email": "..."
|
|
17427
|
+
}`,
|
|
17428
|
+
className: "h-44 font-mono text-xs border-gray-300 dark:border-gray-700"
|
|
17429
|
+
})), isValidJson && /* @__PURE__ */ React.createElement("div", {
|
|
17430
|
+
className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
|
|
17431
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17432
|
+
className: "text-xs font-semibold text-gray-700 dark:text-gray-300"
|
|
17433
|
+
}, "Extracted Firebase Credentials Preview"), /* @__PURE__ */ React.createElement("div", {
|
|
17434
|
+
className: "grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs"
|
|
17435
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
17436
|
+
className: "font-medium text-gray-500"
|
|
17437
|
+
}, "Project ID:"), /* @__PURE__ */ React.createElement("p", {
|
|
17438
|
+
className: "font-mono text-gray-900 dark:text-gray-100 truncate"
|
|
17439
|
+
}, projectId || "\u2014")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
17440
|
+
className: "font-medium text-gray-500"
|
|
17441
|
+
}, "Client Email:"), /* @__PURE__ */ React.createElement("p", {
|
|
17442
|
+
className: "font-mono text-gray-900 dark:text-gray-100 truncate"
|
|
17443
|
+
}, clientEmail || "\u2014")))), /* @__PURE__ */ React.createElement("div", {
|
|
17444
|
+
className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-800/40 space-y-3"
|
|
17445
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17446
|
+
className: "text-xs font-semibold text-gray-800 dark:text-gray-200 flex items-center gap-1.5"
|
|
17447
|
+
}, /* @__PURE__ */ React.createElement(Send, {
|
|
17448
|
+
className: "h-3.5 w-3.5 text-green-500"
|
|
17449
|
+
}), "Test Push Notification (Target Device Token)"), /* @__PURE__ */ React.createElement("div", {
|
|
17450
|
+
className: "grid grid-cols-1 sm:grid-cols-2 gap-2"
|
|
17451
|
+
}, /* @__PURE__ */ React.createElement(Input, {
|
|
17452
|
+
value: testTitle,
|
|
17453
|
+
onChange: /* @__PURE__ */ __name((e) => setTestTitle(e.target.value), "onChange"),
|
|
17454
|
+
placeholder: "Test Notification Title",
|
|
17455
|
+
className: "text-xs"
|
|
17456
|
+
}), /* @__PURE__ */ React.createElement(Input, {
|
|
17457
|
+
value: testBody,
|
|
17458
|
+
onChange: /* @__PURE__ */ __name((e) => setTestBody(e.target.value), "onChange"),
|
|
17459
|
+
placeholder: "Test Notification Body",
|
|
17460
|
+
className: "text-xs"
|
|
17461
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
17462
|
+
className: "flex gap-2"
|
|
17463
|
+
}, /* @__PURE__ */ React.createElement(Input, {
|
|
17464
|
+
value: testToken,
|
|
17465
|
+
onChange: /* @__PURE__ */ __name((e) => setTestToken(e.target.value), "onChange"),
|
|
17466
|
+
placeholder: "Target FCM Device Token",
|
|
17467
|
+
className: "text-xs font-mono"
|
|
17468
|
+
}), /* @__PURE__ */ React.createElement(Button, {
|
|
17469
|
+
type: "button",
|
|
17470
|
+
variant: "secondary",
|
|
17471
|
+
size: "sm",
|
|
17472
|
+
disabled: testing,
|
|
17473
|
+
onClick: handleSendTestPush,
|
|
17474
|
+
className: "shrink-0 gap-1.5"
|
|
17475
|
+
}, testing ? /* @__PURE__ */ React.createElement(Loader2, {
|
|
17476
|
+
className: "h-3.5 w-3.5 animate-spin"
|
|
17477
|
+
}) : /* @__PURE__ */ React.createElement(Send, {
|
|
17478
|
+
className: "h-3.5 w-3.5"
|
|
17479
|
+
}), "Send Test"))), /* @__PURE__ */ React.createElement("div", {
|
|
17480
|
+
className: "flex justify-end gap-2 border-t border-gray-200 pt-4 dark:border-gray-800"
|
|
17481
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
17482
|
+
type: "button",
|
|
17483
|
+
variant: "outline",
|
|
17484
|
+
onClick: /* @__PURE__ */ __name(() => onOpenChange(false), "onClick")
|
|
17485
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
17486
|
+
type: "button",
|
|
17487
|
+
disabled: saving,
|
|
17488
|
+
onClick: handleSaveConfig,
|
|
17489
|
+
className: "gap-1.5"
|
|
17490
|
+
}, saving && /* @__PURE__ */ React.createElement(Loader2, {
|
|
17491
|
+
className: "h-4 w-4 animate-spin"
|
|
17492
|
+
}), "Save Service Account JSON")))));
|
|
17493
|
+
}
|
|
17494
|
+
var init_PushConfigModal = __esm({
|
|
17495
|
+
"src/admin/components/PushConfigModal.tsx"() {
|
|
17496
|
+
"use client";
|
|
17497
|
+
init_button();
|
|
17498
|
+
init_input();
|
|
17499
|
+
init_textarea();
|
|
17500
|
+
init_label();
|
|
17501
|
+
__name(PushConfigModal, "PushConfigModal");
|
|
17502
|
+
}
|
|
17503
|
+
});
|
|
16272
17504
|
function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledChange, onSaveSettings, saving }) {
|
|
16273
17505
|
const [mainTab, setMainTab] = useState("email");
|
|
16274
17506
|
const [audienceTab, setAudienceTab] = useState("customer");
|
|
@@ -16276,6 +17508,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16276
17508
|
const [emailVendorEnabled, setEmailVendorEnabled] = useState(true);
|
|
16277
17509
|
const [emailAdminEnabled, setEmailAdminEnabled] = useState(true);
|
|
16278
17510
|
const [whatsappEnabled, setWhatsappEnabled] = useState(true);
|
|
17511
|
+
const [pushCustomerEnabled, setPushCustomerEnabled] = useState(true);
|
|
17512
|
+
const [pushVendorEnabled, setPushVendorEnabled] = useState(true);
|
|
17513
|
+
const [pushAdminEnabled, setPushAdminEnabled] = useState(true);
|
|
17514
|
+
const [showPushConfigModal, setShowPushConfigModal] = useState(false);
|
|
16279
17515
|
const [multiVendorOn, setMultiVendorOn] = useState(true);
|
|
16280
17516
|
const [loading, setLoading] = useState(true);
|
|
16281
17517
|
const [savingChannel, setSavingChannel] = useState(false);
|
|
@@ -16306,6 +17542,9 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16306
17542
|
setEmailVendorEnabled(data.email_vendor_enabled !== "false" && data.email_vendor_enabled !== "0");
|
|
16307
17543
|
setEmailAdminEnabled(data.email_admin_enabled !== "false" && data.email_admin_enabled !== "0");
|
|
16308
17544
|
setWhatsappEnabled(data.whatsapp_enabled !== "false" && data.whatsapp_enabled !== "0");
|
|
17545
|
+
setPushCustomerEnabled(data.push_customer_enabled !== "false" && data.push_customer_enabled !== "0");
|
|
17546
|
+
setPushVendorEnabled(data.push_vendor_enabled !== "false" && data.push_vendor_enabled !== "0");
|
|
17547
|
+
setPushAdminEnabled(data.push_admin_enabled !== "false" && data.push_admin_enabled !== "0");
|
|
16309
17548
|
}
|
|
16310
17549
|
}
|
|
16311
17550
|
} catch {
|
|
@@ -16323,7 +17562,8 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16323
17562
|
setSavingChannel(true);
|
|
16324
17563
|
try {
|
|
16325
17564
|
const isAnyEmailEnabled = emailCustomerEnabled || emailVendorEnabled || emailAdminEnabled;
|
|
16326
|
-
const
|
|
17565
|
+
const isAnyPushEnabled = pushCustomerEnabled || pushVendorEnabled || pushAdminEnabled;
|
|
17566
|
+
const isAnyChannelEnabled = isAnyEmailEnabled || whatsappEnabled || isAnyPushEnabled;
|
|
16327
17567
|
const res = await fetch(`/api/settings/${settingsGroup}`, {
|
|
16328
17568
|
method: "PUT",
|
|
16329
17569
|
headers: {
|
|
@@ -16350,6 +17590,22 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16350
17590
|
value: whatsappEnabled ? "true" : "false",
|
|
16351
17591
|
type: "public"
|
|
16352
17592
|
},
|
|
17593
|
+
push_enabled: {
|
|
17594
|
+
value: isAnyPushEnabled ? "true" : "false",
|
|
17595
|
+
type: "public"
|
|
17596
|
+
},
|
|
17597
|
+
push_customer_enabled: {
|
|
17598
|
+
value: pushCustomerEnabled ? "true" : "false",
|
|
17599
|
+
type: "public"
|
|
17600
|
+
},
|
|
17601
|
+
push_vendor_enabled: {
|
|
17602
|
+
value: pushVendorEnabled ? "true" : "false",
|
|
17603
|
+
type: "public"
|
|
17604
|
+
},
|
|
17605
|
+
push_admin_enabled: {
|
|
17606
|
+
value: pushAdminEnabled ? "true" : "false",
|
|
17607
|
+
type: "public"
|
|
17608
|
+
},
|
|
16353
17609
|
enabled: {
|
|
16354
17610
|
value: isAnyChannelEnabled ? "true" : "false",
|
|
16355
17611
|
type: "public"
|
|
@@ -16371,7 +17627,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16371
17627
|
}, "handleSaveChannels");
|
|
16372
17628
|
return /* @__PURE__ */ React.createElement("div", {
|
|
16373
17629
|
className: "space-y-5"
|
|
16374
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17630
|
+
}, /* @__PURE__ */ React.createElement(PushConfigModal, {
|
|
17631
|
+
open: showPushConfigModal,
|
|
17632
|
+
onOpenChange: setShowPushConfigModal
|
|
17633
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
16375
17634
|
className: "border-b border-gray-200 dark:border-gray-700"
|
|
16376
17635
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
16377
17636
|
className: "flex gap-1"
|
|
@@ -16387,7 +17646,13 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16387
17646
|
className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "whatsapp" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
|
|
16388
17647
|
}, /* @__PURE__ */ React.createElement(MessageCircle, {
|
|
16389
17648
|
className: "h-4 w-4"
|
|
16390
|
-
}), "WhatsApp template")
|
|
17649
|
+
}), "WhatsApp template"), /* @__PURE__ */ React.createElement("button", {
|
|
17650
|
+
type: "button",
|
|
17651
|
+
onClick: /* @__PURE__ */ __name(() => setMainTab("push"), "onClick"),
|
|
17652
|
+
className: `inline-flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 ${mainTab === "push" ? "border-gray-800 text-gray-800 dark:border-gray-200 dark:text-gray-100" : "border-transparent text-gray-500 hover:text-gray-700"}`
|
|
17653
|
+
}, /* @__PURE__ */ React.createElement(Smartphone, {
|
|
17654
|
+
className: "h-4 w-4"
|
|
17655
|
+
}), "Mobile notification template"))), mainTab === "email" && /* @__PURE__ */ React.createElement("div", {
|
|
16391
17656
|
className: "space-y-5"
|
|
16392
17657
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
16393
17658
|
className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
|
|
@@ -16421,7 +17686,7 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16421
17686
|
className: "h-4 w-4 text-blue-600 dark:text-blue-400"
|
|
16422
17687
|
}), "Enable Customer Email Notifications"), /* @__PURE__ */ React.createElement("p", {
|
|
16423
17688
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
16424
|
-
}, "When enabled, order
|
|
17689
|
+
}, "When enabled, order updates, invoices, and ticket details are emailed to customers.")), /* @__PURE__ */ React.createElement(Switch, {
|
|
16425
17690
|
id: "email-customer-notifications-enabled",
|
|
16426
17691
|
checked: emailCustomerEnabled,
|
|
16427
17692
|
onCheckedChange: setEmailCustomerEnabled,
|
|
@@ -16437,10 +17702,10 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16437
17702
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
16438
17703
|
className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
16439
17704
|
}, /* @__PURE__ */ React.createElement(Store, {
|
|
16440
|
-
className: "h-4 w-4 text-purple-600 dark:
|
|
17705
|
+
className: "h-4 w-4 text-purple-600 dark:purple-400"
|
|
16441
17706
|
}), "Enable Vendor Email Notifications"), /* @__PURE__ */ React.createElement("p", {
|
|
16442
17707
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
16443
|
-
}, "When enabled, vendors
|
|
17708
|
+
}, "When enabled, vendors receive email notifications for new orders assigned to them.")), /* @__PURE__ */ React.createElement(Switch, {
|
|
16444
17709
|
id: "email-vendor-notifications-enabled",
|
|
16445
17710
|
checked: emailVendorEnabled,
|
|
16446
17711
|
onCheckedChange: setEmailVendorEnabled,
|
|
@@ -16483,7 +17748,104 @@ function EventNotificationsPluginSettings({ settingsGroup, enabled, onEnabledCha
|
|
|
16483
17748
|
checked: whatsappEnabled,
|
|
16484
17749
|
onCheckedChange: setWhatsappEnabled,
|
|
16485
17750
|
disabled: loading
|
|
16486
|
-
})), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), /* @__PURE__ */ React.createElement(
|
|
17751
|
+
})), /* @__PURE__ */ React.createElement(WhatsAppTriggerBindings, null)), mainTab === "push" && /* @__PURE__ */ React.createElement("div", {
|
|
17752
|
+
className: "space-y-5"
|
|
17753
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17754
|
+
className: "flex items-center justify-between gap-4 rounded-lg border border-blue-100 bg-blue-50/50 p-4 dark:border-blue-900/40 dark:bg-blue-950/20"
|
|
17755
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17756
|
+
className: "space-y-0.5"
|
|
17757
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17758
|
+
className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
17759
|
+
}, /* @__PURE__ */ React.createElement(Smartphone, {
|
|
17760
|
+
className: "h-4 w-4 text-blue-600 dark:text-blue-400"
|
|
17761
|
+
}), "Firebase Cloud Messaging (FCM) Setup"), /* @__PURE__ */ React.createElement("p", {
|
|
17762
|
+
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17763
|
+
}, "Set up your Firebase project credentials to send mobile push notifications to iOS and Android apps.")), /* @__PURE__ */ React.createElement(Button, {
|
|
17764
|
+
type: "button",
|
|
17765
|
+
variant: "outline",
|
|
17766
|
+
size: "sm",
|
|
17767
|
+
onClick: /* @__PURE__ */ __name(() => setShowPushConfigModal(true), "onClick"),
|
|
17768
|
+
className: "gap-1.5 bg-white dark:bg-gray-900 shrink-0"
|
|
17769
|
+
}, /* @__PURE__ */ React.createElement(Settings, {
|
|
17770
|
+
className: "h-4 w-4 text-gray-600"
|
|
17771
|
+
}), "Set Configuration")), /* @__PURE__ */ React.createElement("div", {
|
|
17772
|
+
className: "flex rounded-md bg-gray-100 p-1 dark:bg-gray-800/60 w-fit"
|
|
17773
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
17774
|
+
type: "button",
|
|
17775
|
+
onClick: /* @__PURE__ */ __name(() => setAudienceTab("customer"), "onClick"),
|
|
17776
|
+
className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "customer" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
|
|
17777
|
+
}, /* @__PURE__ */ React.createElement(User, {
|
|
17778
|
+
className: "h-3.5 w-3.5 text-blue-500"
|
|
17779
|
+
}), "Customers"), multiVendorOn && /* @__PURE__ */ React.createElement("button", {
|
|
17780
|
+
type: "button",
|
|
17781
|
+
onClick: /* @__PURE__ */ __name(() => setAudienceTab("vendor"), "onClick"),
|
|
17782
|
+
className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "vendor" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
|
|
17783
|
+
}, /* @__PURE__ */ React.createElement(Store, {
|
|
17784
|
+
className: "h-3.5 w-3.5 text-purple-500"
|
|
17785
|
+
}), "Vendors"), /* @__PURE__ */ React.createElement("button", {
|
|
17786
|
+
type: "button",
|
|
17787
|
+
onClick: /* @__PURE__ */ __name(() => setAudienceTab("admin"), "onClick"),
|
|
17788
|
+
className: `inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium transition-all ${audienceTab === "admin" ? "bg-white text-gray-900 shadow-sm dark:bg-gray-900 dark:text-white" : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"}`
|
|
17789
|
+
}, /* @__PURE__ */ React.createElement(ShieldCheck, {
|
|
17790
|
+
className: "h-3.5 w-3.5 text-amber-500"
|
|
17791
|
+
}), "Admin")), audienceTab === "customer" && /* @__PURE__ */ React.createElement("div", {
|
|
17792
|
+
className: "space-y-4"
|
|
17793
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17794
|
+
className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
|
|
17795
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17796
|
+
className: "space-y-0.5"
|
|
17797
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17798
|
+
className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
17799
|
+
}, /* @__PURE__ */ React.createElement(User, {
|
|
17800
|
+
className: "h-4 w-4 text-blue-600 dark:text-blue-400"
|
|
17801
|
+
}), "Enable Customer Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
|
|
17802
|
+
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17803
|
+
}, "When enabled, order confirmation alerts and updates are pushed directly to customer mobile apps.")), /* @__PURE__ */ React.createElement(Switch, {
|
|
17804
|
+
id: "push-customer-notifications-enabled",
|
|
17805
|
+
checked: pushCustomerEnabled,
|
|
17806
|
+
onCheckedChange: setPushCustomerEnabled,
|
|
17807
|
+
disabled: loading
|
|
17808
|
+
})), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
|
|
17809
|
+
audience: "customer"
|
|
17810
|
+
})), audienceTab === "vendor" && multiVendorOn && /* @__PURE__ */ React.createElement("div", {
|
|
17811
|
+
className: "space-y-4"
|
|
17812
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17813
|
+
className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
|
|
17814
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17815
|
+
className: "space-y-0.5"
|
|
17816
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17817
|
+
className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
17818
|
+
}, /* @__PURE__ */ React.createElement(Store, {
|
|
17819
|
+
className: "h-4 w-4 text-purple-600 dark:text-purple-400"
|
|
17820
|
+
}), "Enable Vendor Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
|
|
17821
|
+
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17822
|
+
}, "When enabled, vendor mobile app receives instant push alerts for assigned orders.")), /* @__PURE__ */ React.createElement(Switch, {
|
|
17823
|
+
id: "push-vendor-notifications-enabled",
|
|
17824
|
+
checked: pushVendorEnabled,
|
|
17825
|
+
onCheckedChange: setPushVendorEnabled,
|
|
17826
|
+
disabled: loading
|
|
17827
|
+
})), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
|
|
17828
|
+
audience: "vendor"
|
|
17829
|
+
})), audienceTab === "admin" && /* @__PURE__ */ React.createElement("div", {
|
|
17830
|
+
className: "space-y-4"
|
|
17831
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17832
|
+
className: "flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
|
|
17833
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17834
|
+
className: "space-y-0.5"
|
|
17835
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
17836
|
+
className: "text-sm font-medium text-gray-900 dark:text-gray-100 flex items-center gap-2"
|
|
17837
|
+
}, /* @__PURE__ */ React.createElement(ShieldCheck, {
|
|
17838
|
+
className: "h-4 w-4 text-amber-600 dark:text-amber-400"
|
|
17839
|
+
}), "Enable Admin Mobile Push Notifications"), /* @__PURE__ */ React.createElement("p", {
|
|
17840
|
+
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17841
|
+
}, "When enabled, store administrators receive mobile push alerts for all orders placed/cancelled.")), /* @__PURE__ */ React.createElement(Switch, {
|
|
17842
|
+
id: "push-admin-notifications-enabled",
|
|
17843
|
+
checked: pushAdminEnabled,
|
|
17844
|
+
onCheckedChange: setPushAdminEnabled,
|
|
17845
|
+
disabled: loading
|
|
17846
|
+
})), /* @__PURE__ */ React.createElement(PushTriggerBindings, {
|
|
17847
|
+
audience: "admin"
|
|
17848
|
+
}))), /* @__PURE__ */ React.createElement(Button, {
|
|
16487
17849
|
size: "sm",
|
|
16488
17850
|
onClick: /* @__PURE__ */ __name(() => void handleSaveChannels(), "onClick"),
|
|
16489
17851
|
disabled: saving || savingChannel || loading,
|
|
@@ -16499,9 +17861,207 @@ var init_EventNotificationsPluginSettings = __esm({
|
|
|
16499
17861
|
init_switch();
|
|
16500
17862
|
init_whatsappTriggerBindingsPage();
|
|
16501
17863
|
init_emailTriggersBindingsPage();
|
|
17864
|
+
init_pushTriggersBindingsPage();
|
|
17865
|
+
init_PushConfigModal();
|
|
16502
17866
|
__name(EventNotificationsPluginSettings, "EventNotificationsPluginSettings");
|
|
16503
17867
|
}
|
|
16504
17868
|
});
|
|
17869
|
+
function googleRedirectUri() {
|
|
17870
|
+
if (typeof window === "undefined") {
|
|
17871
|
+
const path2 = typeof process !== "undefined" && process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim() || "/api/auth/callback/google";
|
|
17872
|
+
return path2.startsWith("http") ? path2 : path2;
|
|
17873
|
+
}
|
|
17874
|
+
const configured = process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_PATH?.trim();
|
|
17875
|
+
if (configured?.startsWith("http")) return configured;
|
|
17876
|
+
const path = configured || "/api/auth/callback/google";
|
|
17877
|
+
return `${window.location.origin}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17878
|
+
}
|
|
17879
|
+
function AuthProvidersPluginSettings({ settingsGroup, onEnabledChange }) {
|
|
17880
|
+
const [loading, setLoading] = useState(true);
|
|
17881
|
+
const [saving, setSaving] = useState(false);
|
|
17882
|
+
const [googleEnabled, setGoogleEnabled] = useState(false);
|
|
17883
|
+
const [googleClientId, setGoogleClientId] = useState("");
|
|
17884
|
+
const [googleClientSecret, setGoogleClientSecret] = useState("");
|
|
17885
|
+
const [hasSavedSecret, setHasSavedSecret] = useState(false);
|
|
17886
|
+
const [copied, setCopied] = useState(false);
|
|
17887
|
+
const onEnabledChangeRef = useRef(onEnabledChange);
|
|
17888
|
+
onEnabledChangeRef.current = onEnabledChange;
|
|
17889
|
+
const redirectUri = useMemo(() => googleRedirectUri(), []);
|
|
17890
|
+
useEffect(() => {
|
|
17891
|
+
let cancelled = false;
|
|
17892
|
+
(async () => {
|
|
17893
|
+
setLoading(true);
|
|
17894
|
+
try {
|
|
17895
|
+
const res = await fetch(`/api/settings/${settingsGroup}`, {
|
|
17896
|
+
cache: "no-store"
|
|
17897
|
+
});
|
|
17898
|
+
const data = res.ok ? await res.json() : {};
|
|
17899
|
+
if (cancelled) return;
|
|
17900
|
+
const enabled = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
|
|
17901
|
+
setGoogleEnabled(enabled);
|
|
17902
|
+
setGoogleClientId(String(data.googleClientId ?? "").trim());
|
|
17903
|
+
const secret = String(data.googleClientSecret ?? "").trim();
|
|
17904
|
+
setGoogleClientSecret("");
|
|
17905
|
+
setHasSavedSecret(Boolean(secret));
|
|
17906
|
+
} catch {
|
|
17907
|
+
} finally {
|
|
17908
|
+
if (!cancelled) setLoading(false);
|
|
17909
|
+
}
|
|
17910
|
+
})();
|
|
17911
|
+
return () => {
|
|
17912
|
+
cancelled = true;
|
|
17913
|
+
};
|
|
17914
|
+
}, [
|
|
17915
|
+
settingsGroup
|
|
17916
|
+
]);
|
|
17917
|
+
const handleCopyRedirect = /* @__PURE__ */ __name(async () => {
|
|
17918
|
+
try {
|
|
17919
|
+
await navigator.clipboard.writeText(redirectUri);
|
|
17920
|
+
setCopied(true);
|
|
17921
|
+
toast.success("Redirect URI copied");
|
|
17922
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
17923
|
+
} catch {
|
|
17924
|
+
toast.error("Could not copy redirect URI");
|
|
17925
|
+
}
|
|
17926
|
+
}, "handleCopyRedirect");
|
|
17927
|
+
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
17928
|
+
setSaving(true);
|
|
17929
|
+
try {
|
|
17930
|
+
const payload = {
|
|
17931
|
+
enabled: {
|
|
17932
|
+
value: googleEnabled ? "true" : "false",
|
|
17933
|
+
type: "public"
|
|
17934
|
+
},
|
|
17935
|
+
googleEnabled: {
|
|
17936
|
+
value: googleEnabled ? "true" : "false",
|
|
17937
|
+
type: "public"
|
|
17938
|
+
},
|
|
17939
|
+
googleClientId: {
|
|
17940
|
+
value: googleClientId.trim(),
|
|
17941
|
+
type: "private"
|
|
17942
|
+
}
|
|
17943
|
+
};
|
|
17944
|
+
if (googleClientSecret.trim()) {
|
|
17945
|
+
payload.googleClientSecret = {
|
|
17946
|
+
value: googleClientSecret.trim(),
|
|
17947
|
+
type: "private",
|
|
17948
|
+
encrypted: true
|
|
17949
|
+
};
|
|
17950
|
+
}
|
|
17951
|
+
const res = await fetch(`/api/settings/${settingsGroup}`, {
|
|
17952
|
+
method: "PUT",
|
|
17953
|
+
headers: {
|
|
17954
|
+
"Content-Type": "application/json"
|
|
17955
|
+
},
|
|
17956
|
+
body: JSON.stringify(payload)
|
|
17957
|
+
});
|
|
17958
|
+
if (!res.ok) {
|
|
17959
|
+
toast.error("Failed to save Google auth settings");
|
|
17960
|
+
return;
|
|
17961
|
+
}
|
|
17962
|
+
if (googleClientSecret.trim()) {
|
|
17963
|
+
setHasSavedSecret(true);
|
|
17964
|
+
setGoogleClientSecret("");
|
|
17965
|
+
}
|
|
17966
|
+
onEnabledChangeRef.current?.(googleEnabled);
|
|
17967
|
+
toast.success("Authentication provider settings saved");
|
|
17968
|
+
} catch {
|
|
17969
|
+
toast.error("Failed to save Google auth settings");
|
|
17970
|
+
} finally {
|
|
17971
|
+
setSaving(false);
|
|
17972
|
+
}
|
|
17973
|
+
}, "handleSave");
|
|
17974
|
+
if (loading) {
|
|
17975
|
+
return /* @__PURE__ */ React.createElement("p", {
|
|
17976
|
+
className: "text-sm text-gray-500 dark:text-gray-400"
|
|
17977
|
+
}, "Loading\u2026");
|
|
17978
|
+
}
|
|
17979
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
17980
|
+
className: "space-y-4"
|
|
17981
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", {
|
|
17982
|
+
className: "text-sm font-semibold text-gray-900 dark:text-white"
|
|
17983
|
+
}, "Google (Web)"), /* @__PURE__ */ React.createElement("p", {
|
|
17984
|
+
className: "mt-1 text-xs text-gray-500 dark:text-gray-400"
|
|
17985
|
+
}, "Create a Web OAuth client in Google Cloud Console, add the redirect URI below, then paste Client ID and Client Secret here. Only existing admin/vendor users (matching Google email) can sign in.")), /* @__PURE__ */ React.createElement("div", {
|
|
17986
|
+
className: "flex items-center justify-between gap-2"
|
|
17987
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17988
|
+
htmlFor: `${settingsGroup}-googleEnabled`,
|
|
17989
|
+
className: "text-sm"
|
|
17990
|
+
}, "Enable Google sign-in"), /* @__PURE__ */ React.createElement(Switch, {
|
|
17991
|
+
id: `${settingsGroup}-googleEnabled`,
|
|
17992
|
+
checked: googleEnabled,
|
|
17993
|
+
onCheckedChange: setGoogleEnabled
|
|
17994
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
17995
|
+
className: "space-y-1"
|
|
17996
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
17997
|
+
htmlFor: `${settingsGroup}-redirectUri`,
|
|
17998
|
+
className: "text-sm"
|
|
17999
|
+
}, "Redirect URI (add in Google Console)"), /* @__PURE__ */ React.createElement("div", {
|
|
18000
|
+
className: "flex gap-2"
|
|
18001
|
+
}, /* @__PURE__ */ React.createElement(Input, {
|
|
18002
|
+
id: `${settingsGroup}-redirectUri`,
|
|
18003
|
+
value: redirectUri,
|
|
18004
|
+
readOnly: true,
|
|
18005
|
+
className: "h-9 flex-1 font-mono text-xs"
|
|
18006
|
+
}), /* @__PURE__ */ React.createElement(Button, {
|
|
18007
|
+
type: "button",
|
|
18008
|
+
size: "sm",
|
|
18009
|
+
variant: "outline",
|
|
18010
|
+
onClick: handleCopyRedirect,
|
|
18011
|
+
className: "shrink-0 gap-1"
|
|
18012
|
+
}, copied ? /* @__PURE__ */ React.createElement(Check, {
|
|
18013
|
+
className: "h-3.5 w-3.5"
|
|
18014
|
+
}) : /* @__PURE__ */ React.createElement(Copy, {
|
|
18015
|
+
className: "h-3.5 w-3.5"
|
|
18016
|
+
}), copied ? "Copied" : "Copy")), /* @__PURE__ */ React.createElement("p", {
|
|
18017
|
+
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
18018
|
+
}, "Must match ", /* @__PURE__ */ React.createElement("code", {
|
|
18019
|
+
className: "text-[11px]"
|
|
18020
|
+
}, "NEXTAUTH_URL"), " for this environment.")), /* @__PURE__ */ React.createElement("div", {
|
|
18021
|
+
className: "space-y-1"
|
|
18022
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
18023
|
+
htmlFor: `${settingsGroup}-googleClientId`,
|
|
18024
|
+
className: "text-sm"
|
|
18025
|
+
}, "Client ID"), /* @__PURE__ */ React.createElement(Input, {
|
|
18026
|
+
id: `${settingsGroup}-googleClientId`,
|
|
18027
|
+
value: googleClientId,
|
|
18028
|
+
onChange: /* @__PURE__ */ __name((e) => setGoogleClientId(e.target.value), "onChange"),
|
|
18029
|
+
placeholder: "xxxxx.apps.googleusercontent.com",
|
|
18030
|
+
className: "h-9 text-sm",
|
|
18031
|
+
autoComplete: "off"
|
|
18032
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
18033
|
+
className: "space-y-1"
|
|
18034
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
18035
|
+
htmlFor: `${settingsGroup}-googleClientSecret`,
|
|
18036
|
+
className: "text-sm"
|
|
18037
|
+
}, "Client Secret ", hasSavedSecret && !googleClientSecret.trim() ? "(saved)" : ""), /* @__PURE__ */ React.createElement(Input, {
|
|
18038
|
+
id: `${settingsGroup}-googleClientSecret`,
|
|
18039
|
+
type: "password",
|
|
18040
|
+
value: googleClientSecret,
|
|
18041
|
+
onChange: /* @__PURE__ */ __name((e) => setGoogleClientSecret(e.target.value), "onChange"),
|
|
18042
|
+
placeholder: hasSavedSecret ? "Leave blank to keep saved secret" : "Paste client secret",
|
|
18043
|
+
className: "h-9 text-sm",
|
|
18044
|
+
autoComplete: "new-password"
|
|
18045
|
+
})), /* @__PURE__ */ React.createElement(Button, {
|
|
18046
|
+
size: "sm",
|
|
18047
|
+
onClick: handleSave,
|
|
18048
|
+
disabled: saving,
|
|
18049
|
+
className: "gap-1"
|
|
18050
|
+
}, /* @__PURE__ */ React.createElement(Save, {
|
|
18051
|
+
className: "h-3.5 w-3.5"
|
|
18052
|
+
}), saving ? "Saving\u2026" : "Save"));
|
|
18053
|
+
}
|
|
18054
|
+
var init_AuthProvidersPluginSettings = __esm({
|
|
18055
|
+
"src/admin/components/AuthProvidersPluginSettings.tsx"() {
|
|
18056
|
+
"use client";
|
|
18057
|
+
init_button();
|
|
18058
|
+
init_input();
|
|
18059
|
+
init_label();
|
|
18060
|
+
init_switch();
|
|
18061
|
+
__name(googleRedirectUri, "googleRedirectUri");
|
|
18062
|
+
__name(AuthProvidersPluginSettings, "AuthProvidersPluginSettings");
|
|
18063
|
+
}
|
|
18064
|
+
});
|
|
16505
18065
|
|
|
16506
18066
|
// src/plugins/llm/chat-email-intent.ts
|
|
16507
18067
|
function normalizeIntentKey(raw) {
|
|
@@ -16879,6 +18439,7 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
16879
18439
|
const isEventNotifications = settingsGroup === "event_notifications";
|
|
16880
18440
|
const isSocialMedia = settingsGroup === "social_media";
|
|
16881
18441
|
const isMultiVendor = settingsGroup === "multi_vendor";
|
|
18442
|
+
const isAuthProviders = settingsGroup === "auth_providers";
|
|
16882
18443
|
const [enabled, setEnabled] = useState(true);
|
|
16883
18444
|
const [botName, setBotName] = useState("");
|
|
16884
18445
|
const [icon, setIcon] = useState("");
|
|
@@ -17228,6 +18789,10 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17228
18789
|
setWhatsappPhoneNumberId(String(data.phoneNumberId ?? data.WHATSAPP_PHONE_NUMBER_ID ?? "").trim());
|
|
17229
18790
|
setWhatsappWabaId(String(data.wabaId ?? data.WHATSAPP_WABA_ID ?? "").trim());
|
|
17230
18791
|
}
|
|
18792
|
+
if (isAuthProviders) {
|
|
18793
|
+
const googleOn = data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false";
|
|
18794
|
+
setEnabled(googleOn);
|
|
18795
|
+
}
|
|
17231
18796
|
if (isSocialMedia) {
|
|
17232
18797
|
const savedLiToken = String(data.linkedin_access_token ?? "").trim();
|
|
17233
18798
|
setLinkedinAccessToken(savedLiToken);
|
|
@@ -17265,7 +18830,8 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17265
18830
|
isWhatsapp,
|
|
17266
18831
|
isEventNotifications,
|
|
17267
18832
|
isSocialMedia,
|
|
17268
|
-
isMultiVendor
|
|
18833
|
+
isMultiVendor,
|
|
18834
|
+
isAuthProviders
|
|
17269
18835
|
]);
|
|
17270
18836
|
useEffect(() => {
|
|
17271
18837
|
if (!isSocialMedia || loading || linkedInOrgsPrefetchedRef.current) return;
|
|
@@ -17711,6 +19277,18 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17711
19277
|
}
|
|
17712
19278
|
};
|
|
17713
19279
|
}
|
|
19280
|
+
if (isAuthProviders) {
|
|
19281
|
+
return {
|
|
19282
|
+
enabled: {
|
|
19283
|
+
value: enabled ? "true" : "false",
|
|
19284
|
+
type: "public"
|
|
19285
|
+
},
|
|
19286
|
+
googleEnabled: {
|
|
19287
|
+
value: enabled ? "true" : "false",
|
|
19288
|
+
type: "public"
|
|
19289
|
+
}
|
|
19290
|
+
};
|
|
19291
|
+
}
|
|
17714
19292
|
if (isSocialMedia) {
|
|
17715
19293
|
const payload2 = {
|
|
17716
19294
|
enabled: {
|
|
@@ -18196,6 +19774,15 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
18196
19774
|
toast.success("Removed from agent");
|
|
18197
19775
|
await fetchAttachedKnowledge(slug);
|
|
18198
19776
|
}, "handleUnlinkKbDoc");
|
|
19777
|
+
if (isAuthProviders) {
|
|
19778
|
+
return /* @__PURE__ */ React.createElement(AuthProvidersPluginSettings, {
|
|
19779
|
+
settingsGroup,
|
|
19780
|
+
onEnabledChange: /* @__PURE__ */ __name((value) => {
|
|
19781
|
+
setEnabled(value);
|
|
19782
|
+
onSaved?.();
|
|
19783
|
+
}, "onEnabledChange")
|
|
19784
|
+
});
|
|
19785
|
+
}
|
|
18199
19786
|
if (loading) return /* @__PURE__ */ React.createElement("div", {
|
|
18200
19787
|
className: "text-sm text-gray-500 dark:text-gray-400"
|
|
18201
19788
|
}, "Loading...");
|
|
@@ -19532,6 +21119,10 @@ function PluginsPage() {
|
|
|
19532
21119
|
const en = pluginDescriptors.find((p) => p.name === "event_notifications");
|
|
19533
21120
|
if (en) setSelectedName(en.name);
|
|
19534
21121
|
}
|
|
21122
|
+
if (q === "auth_providers" || q === "auth-providers" || q === "authentication") {
|
|
21123
|
+
const ap = pluginDescriptors.find((p) => p.name === "auth_providers");
|
|
21124
|
+
if (ap) setSelectedName(ap.name);
|
|
21125
|
+
}
|
|
19535
21126
|
}, [
|
|
19536
21127
|
searchParams,
|
|
19537
21128
|
pluginDescriptors
|
|
@@ -19539,10 +21130,13 @@ function PluginsPage() {
|
|
|
19539
21130
|
useEffect(() => {
|
|
19540
21131
|
pluginDescriptors.forEach((p) => {
|
|
19541
21132
|
if (!p.settingsGroup) return;
|
|
19542
|
-
fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) =>
|
|
19543
|
-
|
|
19544
|
-
|
|
19545
|
-
|
|
21133
|
+
fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
21134
|
+
const on = p.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled !== "false" && data.googleEnabled !== "false" && data.enabled === "true" : data.enabled !== "false";
|
|
21135
|
+
setEnabledMap((prev) => ({
|
|
21136
|
+
...prev,
|
|
21137
|
+
[p.name]: on
|
|
21138
|
+
}));
|
|
21139
|
+
}).catch(() => {
|
|
19546
21140
|
});
|
|
19547
21141
|
});
|
|
19548
21142
|
}, [
|
|
@@ -19552,10 +21146,13 @@ function PluginsPage() {
|
|
|
19552
21146
|
const refreshEnabled = /* @__PURE__ */ __name((name) => {
|
|
19553
21147
|
const d = pluginDescriptors.find((p) => p.name === name);
|
|
19554
21148
|
if (!d?.settingsGroup) return;
|
|
19555
|
-
fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) =>
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
|
|
21149
|
+
fetch(`/api/settings/${d.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
21150
|
+
const on = d.settingsGroup === "auth_providers" ? data.googleEnabled === "true" || data.googleEnabled === "1" || data.enabled === "true" && data.googleEnabled !== "false" : data.enabled !== "false";
|
|
21151
|
+
setEnabledMap((prev) => ({
|
|
21152
|
+
...prev,
|
|
21153
|
+
[name]: on
|
|
21154
|
+
}));
|
|
21155
|
+
}).catch(() => {
|
|
19559
21156
|
});
|
|
19560
21157
|
}, "refreshEnabled");
|
|
19561
21158
|
return /* @__PURE__ */ React.createElement("div", {
|
|
@@ -19637,6 +21234,7 @@ var init_PluginsPage = __esm({
|
|
|
19637
21234
|
init_checkbox();
|
|
19638
21235
|
init_select();
|
|
19639
21236
|
init_EventNotificationsPluginSettings();
|
|
21237
|
+
init_AuthProvidersPluginSettings();
|
|
19640
21238
|
init_ImageOrUrlField();
|
|
19641
21239
|
init_chat_email_intent();
|
|
19642
21240
|
init_llm_agent_scope();
|
|
@@ -19655,7 +21253,8 @@ var init_PluginsPage = __esm({
|
|
|
19655
21253
|
pg_boss: CalendarClock,
|
|
19656
21254
|
share: Share2,
|
|
19657
21255
|
whatsapp: MessageCircle,
|
|
19658
|
-
event_notifications: Bell
|
|
21256
|
+
event_notifications: Bell,
|
|
21257
|
+
auth_providers: KeyRound
|
|
19659
21258
|
};
|
|
19660
21259
|
__name(normalizeSmsProviderChoice, "normalizeSmsProviderChoice");
|
|
19661
21260
|
__name(normalizeMsg91ApiMode, "normalizeMsg91ApiMode");
|
|
@@ -19675,183 +21274,6 @@ var init_PluginsPage = __esm({
|
|
|
19675
21274
|
}
|
|
19676
21275
|
});
|
|
19677
21276
|
|
|
19678
|
-
// src/lib/vendor-role-defaults.ts
|
|
19679
|
-
function full() {
|
|
19680
|
-
return {
|
|
19681
|
-
canCreate: true,
|
|
19682
|
-
canRead: true,
|
|
19683
|
-
canUpdate: true,
|
|
19684
|
-
canDelete: true
|
|
19685
|
-
};
|
|
19686
|
-
}
|
|
19687
|
-
function read() {
|
|
19688
|
-
return {
|
|
19689
|
-
canCreate: false,
|
|
19690
|
-
canRead: true,
|
|
19691
|
-
canUpdate: false,
|
|
19692
|
-
canDelete: false
|
|
19693
|
-
};
|
|
19694
|
-
}
|
|
19695
|
-
function none() {
|
|
19696
|
-
return {
|
|
19697
|
-
canCreate: false,
|
|
19698
|
-
canRead: false,
|
|
19699
|
-
canUpdate: false,
|
|
19700
|
-
canDelete: false
|
|
19701
|
-
};
|
|
19702
|
-
}
|
|
19703
|
-
function readUpdate() {
|
|
19704
|
-
return {
|
|
19705
|
-
canCreate: false,
|
|
19706
|
-
canRead: true,
|
|
19707
|
-
canUpdate: true,
|
|
19708
|
-
canDelete: false
|
|
19709
|
-
};
|
|
19710
|
-
}
|
|
19711
|
-
function storePerms(spec) {
|
|
19712
|
-
const resolve = /* @__PURE__ */ __name((v) => {
|
|
19713
|
-
if (v === "full") return full();
|
|
19714
|
-
if (v === "read") return read();
|
|
19715
|
-
if (v === "none") return none();
|
|
19716
|
-
return v;
|
|
19717
|
-
}, "resolve");
|
|
19718
|
-
const out = {};
|
|
19719
|
-
for (const entity of STORE_ENTITIES) {
|
|
19720
|
-
out[entity] = resolve(spec[entity] ?? "none");
|
|
19721
|
-
}
|
|
19722
|
-
return out;
|
|
19723
|
-
}
|
|
19724
|
-
var STORE_ENTITIES;
|
|
19725
|
-
var init_vendor_role_defaults = __esm({
|
|
19726
|
-
"src/lib/vendor-role-defaults.ts"() {
|
|
19727
|
-
init_vendor_scope();
|
|
19728
|
-
[
|
|
19729
|
-
...VENDOR_STORE_RBAC_ENTITIES,
|
|
19730
|
-
"dashboard",
|
|
19731
|
-
"settings",
|
|
19732
|
-
"team",
|
|
19733
|
-
"roles"
|
|
19734
|
-
];
|
|
19735
|
-
STORE_ENTITIES = [
|
|
19736
|
-
...VENDOR_STORE_RBAC_ENTITIES
|
|
19737
|
-
];
|
|
19738
|
-
__name(full, "full");
|
|
19739
|
-
__name(read, "read");
|
|
19740
|
-
__name(none, "none");
|
|
19741
|
-
__name(readUpdate, "readUpdate");
|
|
19742
|
-
__name(storePerms, "storePerms");
|
|
19743
|
-
[
|
|
19744
|
-
{
|
|
19745
|
-
name: "Owner",
|
|
19746
|
-
description: "Full store access and team/role management",
|
|
19747
|
-
isSystem: true,
|
|
19748
|
-
isOwnerRole: true,
|
|
19749
|
-
permissions: {
|
|
19750
|
-
...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
|
|
19751
|
-
e,
|
|
19752
|
-
"full"
|
|
19753
|
-
]))),
|
|
19754
|
-
dashboard: read(),
|
|
19755
|
-
settings: readUpdate(),
|
|
19756
|
-
team: full(),
|
|
19757
|
-
roles: full()
|
|
19758
|
-
}
|
|
19759
|
-
},
|
|
19760
|
-
{
|
|
19761
|
-
name: "Manager",
|
|
19762
|
-
description: "Day-to-day store operations",
|
|
19763
|
-
isSystem: true,
|
|
19764
|
-
isOwnerRole: false,
|
|
19765
|
-
permissions: {
|
|
19766
|
-
...storePerms({
|
|
19767
|
-
products: "full",
|
|
19768
|
-
collections: "full",
|
|
19769
|
-
brands: "full",
|
|
19770
|
-
product_categories: "full",
|
|
19771
|
-
orders: "full",
|
|
19772
|
-
vendor_customers: "full",
|
|
19773
|
-
discounts: "full",
|
|
19774
|
-
order_discounts: "full",
|
|
19775
|
-
order_addresses: "full",
|
|
19776
|
-
payments: "read",
|
|
19777
|
-
taxes: "read",
|
|
19778
|
-
attributes: "read"
|
|
19779
|
-
}),
|
|
19780
|
-
dashboard: read(),
|
|
19781
|
-
settings: read(),
|
|
19782
|
-
team: none(),
|
|
19783
|
-
roles: none()
|
|
19784
|
-
}
|
|
19785
|
-
},
|
|
19786
|
-
{
|
|
19787
|
-
name: "Catalog Staff",
|
|
19788
|
-
description: "Manage products and catalog content",
|
|
19789
|
-
isSystem: true,
|
|
19790
|
-
isOwnerRole: false,
|
|
19791
|
-
permissions: {
|
|
19792
|
-
...storePerms({
|
|
19793
|
-
products: "full",
|
|
19794
|
-
collections: "full",
|
|
19795
|
-
brands: "full",
|
|
19796
|
-
product_categories: "full",
|
|
19797
|
-
attributes: "full",
|
|
19798
|
-
orders: "read",
|
|
19799
|
-
vendor_customers: "read"
|
|
19800
|
-
}),
|
|
19801
|
-
dashboard: read(),
|
|
19802
|
-
settings: none(),
|
|
19803
|
-
team: none(),
|
|
19804
|
-
roles: none()
|
|
19805
|
-
}
|
|
19806
|
-
},
|
|
19807
|
-
{
|
|
19808
|
-
name: "Fulfillment",
|
|
19809
|
-
description: "Orders and fulfillment",
|
|
19810
|
-
isSystem: true,
|
|
19811
|
-
isOwnerRole: false,
|
|
19812
|
-
permissions: {
|
|
19813
|
-
...storePerms({
|
|
19814
|
-
products: "read",
|
|
19815
|
-
orders: "full",
|
|
19816
|
-
order_addresses: "full",
|
|
19817
|
-
order_discounts: "read",
|
|
19818
|
-
vendor_customers: "read"
|
|
19819
|
-
}),
|
|
19820
|
-
dashboard: read(),
|
|
19821
|
-
settings: none(),
|
|
19822
|
-
team: none(),
|
|
19823
|
-
roles: none()
|
|
19824
|
-
}
|
|
19825
|
-
},
|
|
19826
|
-
{
|
|
19827
|
-
name: "Viewer",
|
|
19828
|
-
description: "Read-only access to store data",
|
|
19829
|
-
isSystem: true,
|
|
19830
|
-
isOwnerRole: false,
|
|
19831
|
-
permissions: {
|
|
19832
|
-
...storePerms(Object.fromEntries(STORE_ENTITIES.map((e) => [
|
|
19833
|
-
e,
|
|
19834
|
-
"read"
|
|
19835
|
-
]))),
|
|
19836
|
-
dashboard: read(),
|
|
19837
|
-
settings: none(),
|
|
19838
|
-
team: none(),
|
|
19839
|
-
roles: none()
|
|
19840
|
-
}
|
|
19841
|
-
}
|
|
19842
|
-
];
|
|
19843
|
-
}
|
|
19844
|
-
});
|
|
19845
|
-
|
|
19846
|
-
// src/auth/rbac-debug.ts
|
|
19847
|
-
var init_rbac_debug = __esm({
|
|
19848
|
-
"src/auth/rbac-debug.ts"() {
|
|
19849
|
-
init_permission_entities();
|
|
19850
|
-
init_vendor_role_defaults();
|
|
19851
|
-
init_vendor_scope();
|
|
19852
|
-
}
|
|
19853
|
-
});
|
|
19854
|
-
|
|
19855
21277
|
// src/auth/helpers.ts
|
|
19856
21278
|
function canManageRoles(user) {
|
|
19857
21279
|
return !!(user?.email && user.isRBACAdmin);
|
|
@@ -20009,23 +21431,6 @@ function VendorRolesPage() {
|
|
|
20009
21431
|
return next;
|
|
20010
21432
|
});
|
|
20011
21433
|
}, "setAllRows");
|
|
20012
|
-
const toggleEntityRow = /* @__PURE__ */ __name((entity) => {
|
|
20013
|
-
const isOwnerOnlyEntity = entity === "team" || entity === "roles";
|
|
20014
|
-
setMatrix((prev) => {
|
|
20015
|
-
const current = prev[entity];
|
|
20016
|
-
const isAllChecked = current?.canRead && (isOwnerOnlyEntity || current.canCreate && current.canUpdate && current.canDelete);
|
|
20017
|
-
return {
|
|
20018
|
-
...prev,
|
|
20019
|
-
[entity]: {
|
|
20020
|
-
entity,
|
|
20021
|
-
canRead: !isAllChecked,
|
|
20022
|
-
canCreate: isOwnerOnlyEntity ? false : !isAllChecked,
|
|
20023
|
-
canUpdate: isOwnerOnlyEntity ? false : !isAllChecked,
|
|
20024
|
-
canDelete: isOwnerOnlyEntity ? false : !isAllChecked
|
|
20025
|
-
}
|
|
20026
|
-
};
|
|
20027
|
-
});
|
|
20028
|
-
}, "toggleEntityRow");
|
|
20029
21434
|
const saveMatrix = /* @__PURE__ */ __name(async () => {
|
|
20030
21435
|
if (!selectedId) return;
|
|
20031
21436
|
setSaving(true);
|
|
@@ -20128,9 +21533,7 @@ function VendorRolesPage() {
|
|
|
20128
21533
|
className: "text-base font-semibold text-white"
|
|
20129
21534
|
}, "Vendor Store Roles & Permissions"), /* @__PURE__ */ React25__default.createElement("p", {
|
|
20130
21535
|
className: "mt-0.5 text-xs text-gray-300"
|
|
20131
|
-
}, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles).
|
|
20132
|
-
className: "font-mono bg-gray-700 px-1 py-0.5 rounded text-gray-200"
|
|
20133
|
-
}, "vendor_role_permissions"), " table and apply directly to team members assigned to that role.")), /* @__PURE__ */ React25__default.createElement("div", {
|
|
21536
|
+
}, "Set CRUD permissions for any role (Viewer, Manager, Fulfillment, Catalog Staff, or custom roles).")), /* @__PURE__ */ React25__default.createElement("div", {
|
|
20134
21537
|
className: "min-w-0 p-6"
|
|
20135
21538
|
}, error ? /* @__PURE__ */ React25__default.createElement("p", {
|
|
20136
21539
|
className: "mb-4 text-sm text-red-600 dark:text-red-400"
|
|
@@ -20221,31 +21624,35 @@ function VendorRolesPage() {
|
|
|
20221
21624
|
}), " Clear All"))), /* @__PURE__ */ React25__default.createElement("div", {
|
|
20222
21625
|
className: "overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700"
|
|
20223
21626
|
}, /* @__PURE__ */ React25__default.createElement("table", {
|
|
20224
|
-
className: "min-w-full text-
|
|
21627
|
+
className: "min-w-full text-sm"
|
|
20225
21628
|
}, /* @__PURE__ */ React25__default.createElement("thead", {
|
|
20226
|
-
className: "bg-
|
|
20227
|
-
}, /* @__PURE__ */ React25__default.createElement("tr",
|
|
20228
|
-
className: "
|
|
20229
|
-
},
|
|
20230
|
-
className: "px-3 py-2 font-
|
|
21629
|
+
className: "bg-white dark:bg-gray-800"
|
|
21630
|
+
}, /* @__PURE__ */ React25__default.createElement("tr", {
|
|
21631
|
+
className: "border-b border-gray-200 dark:border-gray-700"
|
|
21632
|
+
}, /* @__PURE__ */ React25__default.createElement("th", {
|
|
21633
|
+
className: "px-3 py-2.5 text-left font-semibold text-gray-700 dark:text-gray-300"
|
|
21634
|
+
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
21635
|
+
className: "flex items-center gap-2"
|
|
21636
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, "Entity"))), /* @__PURE__ */ React25__default.createElement("th", {
|
|
21637
|
+
className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
|
|
20231
21638
|
}, "Create"), /* @__PURE__ */ React25__default.createElement("th", {
|
|
20232
|
-
className: "px-3 py-2 font-
|
|
21639
|
+
className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
|
|
20233
21640
|
}, "Read"), /* @__PURE__ */ React25__default.createElement("th", {
|
|
20234
|
-
className: "px-3 py-2 font-
|
|
21641
|
+
className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
|
|
20235
21642
|
}, "Update"), /* @__PURE__ */ React25__default.createElement("th", {
|
|
20236
|
-
className: "px-3 py-2 font-
|
|
20237
|
-
}, "Delete"), /* @__PURE__ */ React25__default.createElement("
|
|
20238
|
-
className: "px-3 py-2 font-medium text-right"
|
|
20239
|
-
}, "Row Actions"))), /* @__PURE__ */ React25__default.createElement("tbody", null, entities.map((entity) => {
|
|
21643
|
+
className: "px-3 py-2.5 text-center font-semibold text-gray-700 dark:text-gray-300"
|
|
21644
|
+
}, "Delete"))), /* @__PURE__ */ React25__default.createElement("tbody", null, entities.map((entity) => {
|
|
20240
21645
|
const isOwnerOnlyEntity = entity === "team" || entity === "roles";
|
|
20241
21646
|
return /* @__PURE__ */ React25__default.createElement("tr", {
|
|
20242
21647
|
key: entity,
|
|
20243
|
-
className: "border-
|
|
21648
|
+
className: "border-b border-gray-100 last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800/50"
|
|
20244
21649
|
}, /* @__PURE__ */ React25__default.createElement("td", {
|
|
20245
|
-
className: "px-3 py-2 font-mono text-gray-800 dark:text-gray-200
|
|
20246
|
-
},
|
|
20247
|
-
className: "
|
|
20248
|
-
}, "
|
|
21650
|
+
className: "px-3 py-2.5 font-mono text-sm text-gray-800 dark:text-gray-200"
|
|
21651
|
+
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
21652
|
+
className: "flex items-center gap-2"
|
|
21653
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, entity), isOwnerOnlyEntity && /* @__PURE__ */ React25__default.createElement("span", {
|
|
21654
|
+
className: "text-[10px] text-amber-600 dark:text-amber-400"
|
|
21655
|
+
}, "(Owner managed)"))), [
|
|
20249
21656
|
"canCreate",
|
|
20250
21657
|
"canRead",
|
|
20251
21658
|
"canUpdate",
|
|
@@ -20254,21 +21661,15 @@ function VendorRolesPage() {
|
|
|
20254
21661
|
const disabled = isOwnerOnlyEntity && key !== "canRead";
|
|
20255
21662
|
return /* @__PURE__ */ React25__default.createElement("td", {
|
|
20256
21663
|
key,
|
|
20257
|
-
className: "px-3 py-2"
|
|
21664
|
+
className: "px-3 py-2.5 text-center"
|
|
20258
21665
|
}, /* @__PURE__ */ React25__default.createElement("input", {
|
|
20259
21666
|
type: "checkbox",
|
|
20260
21667
|
checked: disabled ? false : !!matrix[entity]?.[key],
|
|
20261
21668
|
disabled,
|
|
20262
21669
|
onChange: /* @__PURE__ */ __name(() => toggle(entity, key), "onChange"),
|
|
20263
|
-
className: disabled ? "cursor-not-allowed opacity-40" : "cursor-pointer"
|
|
21670
|
+
className: disabled ? "h-4 w-4 cursor-not-allowed opacity-40" : "h-4 w-4 cursor-pointer"
|
|
20264
21671
|
}));
|
|
20265
|
-
})
|
|
20266
|
-
className: "px-3 py-2 text-right"
|
|
20267
|
-
}, /* @__PURE__ */ React25__default.createElement("button", {
|
|
20268
|
-
type: "button",
|
|
20269
|
-
onClick: /* @__PURE__ */ __name(() => toggleEntityRow(entity), "onClick"),
|
|
20270
|
-
className: "text-[11px] text-blue-600 hover:underline dark:text-blue-400"
|
|
20271
|
-
}, "Toggle")));
|
|
21672
|
+
}));
|
|
20272
21673
|
})))))))), /* @__PURE__ */ React25__default.createElement(Dialog, {
|
|
20273
21674
|
open: deleteOpen,
|
|
20274
21675
|
onOpenChange: setDeleteOpen
|
|
@@ -20629,7 +22030,7 @@ function PlatformRolesPage() {
|
|
|
20629
22030
|
className: "p-2"
|
|
20630
22031
|
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
20631
22032
|
className: "flex items-center justify-center gap-1"
|
|
20632
|
-
}, /* @__PURE__ */ React25__default.createElement("span", null, "
|
|
22033
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, "Create"), /* @__PURE__ */ React25__default.createElement("input", {
|
|
20633
22034
|
type: "checkbox",
|
|
20634
22035
|
checked: isColumnAllChecked("canCreate"),
|
|
20635
22036
|
onChange: /* @__PURE__ */ __name(() => toggleColumn("canCreate"), "onChange")
|
|
@@ -20637,7 +22038,7 @@ function PlatformRolesPage() {
|
|
|
20637
22038
|
className: "p-2"
|
|
20638
22039
|
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
20639
22040
|
className: "flex items-center justify-center gap-1"
|
|
20640
|
-
}, /* @__PURE__ */ React25__default.createElement("span", null, "
|
|
22041
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, "Read"), /* @__PURE__ */ React25__default.createElement("input", {
|
|
20641
22042
|
type: "checkbox",
|
|
20642
22043
|
checked: isColumnAllChecked("canRead"),
|
|
20643
22044
|
onChange: /* @__PURE__ */ __name(() => toggleColumn("canRead"), "onChange")
|
|
@@ -20645,7 +22046,7 @@ function PlatformRolesPage() {
|
|
|
20645
22046
|
className: "p-2"
|
|
20646
22047
|
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
20647
22048
|
className: "flex items-center justify-center gap-1"
|
|
20648
|
-
}, /* @__PURE__ */ React25__default.createElement("span", null, "
|
|
22049
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, "Update"), /* @__PURE__ */ React25__default.createElement("input", {
|
|
20649
22050
|
type: "checkbox",
|
|
20650
22051
|
checked: isColumnAllChecked("canUpdate"),
|
|
20651
22052
|
onChange: /* @__PURE__ */ __name(() => toggleColumn("canUpdate"), "onChange")
|
|
@@ -20653,7 +22054,7 @@ function PlatformRolesPage() {
|
|
|
20653
22054
|
className: "p-2"
|
|
20654
22055
|
}, /* @__PURE__ */ React25__default.createElement("div", {
|
|
20655
22056
|
className: "flex items-center justify-center gap-1"
|
|
20656
|
-
}, /* @__PURE__ */ React25__default.createElement("span", null, "
|
|
22057
|
+
}, /* @__PURE__ */ React25__default.createElement("span", null, "Delete"), /* @__PURE__ */ React25__default.createElement("input", {
|
|
20657
22058
|
type: "checkbox",
|
|
20658
22059
|
checked: isColumnAllChecked("canDelete"),
|
|
20659
22060
|
onChange: /* @__PURE__ */ __name(() => toggleColumn("canDelete"), "onChange")
|
|
@@ -21146,39 +22547,90 @@ var init_VendorTeamPage = __esm({
|
|
|
21146
22547
|
__name(VendorTeamPage, "VendorTeamPage");
|
|
21147
22548
|
}
|
|
21148
22549
|
});
|
|
21149
|
-
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
21153
|
-
|
|
22550
|
+
function splitPhoneAndCountryCode(rawPhone, defaultCode = "+91") {
|
|
22551
|
+
if (!rawPhone || !rawPhone.trim()) {
|
|
22552
|
+
return {
|
|
22553
|
+
countryCode: defaultCode,
|
|
22554
|
+
phoneNumber: ""
|
|
22555
|
+
};
|
|
22556
|
+
}
|
|
22557
|
+
const trimmed = rawPhone.trim();
|
|
22558
|
+
const match = COUNTRY_PHONE_CODES.find((c) => trimmed.startsWith(c.code));
|
|
22559
|
+
if (match) {
|
|
22560
|
+
return {
|
|
22561
|
+
countryCode: match.code,
|
|
22562
|
+
phoneNumber: trimmed.slice(match.code.length).replace(/\s+/g, "")
|
|
22563
|
+
};
|
|
22564
|
+
}
|
|
22565
|
+
if (trimmed.startsWith("+")) {
|
|
22566
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
22567
|
+
if (spaceIdx > 0) {
|
|
22568
|
+
return {
|
|
22569
|
+
countryCode: trimmed.slice(0, spaceIdx),
|
|
22570
|
+
phoneNumber: trimmed.slice(spaceIdx + 1).replace(/\s+/g, "")
|
|
22571
|
+
};
|
|
22572
|
+
}
|
|
22573
|
+
}
|
|
22574
|
+
return {
|
|
22575
|
+
countryCode: defaultCode,
|
|
22576
|
+
phoneNumber: trimmed.replace(/\D/g, "")
|
|
22577
|
+
};
|
|
22578
|
+
}
|
|
22579
|
+
function formatPhoneWithCountryCode(countryCode, phoneNumber) {
|
|
22580
|
+
const cleanNum = phoneNumber.replace(/\D/g, "");
|
|
22581
|
+
if (!cleanNum) return null;
|
|
22582
|
+
const cleanCode = countryCode.trim() || "+91";
|
|
22583
|
+
return `${cleanCode}${cleanNum}`;
|
|
22584
|
+
}
|
|
22585
|
+
function validateGstin(gstin, options) {
|
|
22586
|
+
const required = options?.required === true;
|
|
22587
|
+
if (!gstin || !gstin.trim()) {
|
|
22588
|
+
if (required) return "GSTIN number is required";
|
|
22589
|
+
return null;
|
|
21154
22590
|
}
|
|
21155
|
-
if (
|
|
22591
|
+
if (!/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/i.test(gstin.trim())) {
|
|
22592
|
+
return "Invalid GSTIN number (15 characters, e.g. 22AAAAA0000A1Z5)";
|
|
22593
|
+
}
|
|
22594
|
+
return null;
|
|
22595
|
+
}
|
|
22596
|
+
function validateIndiaTaxIds(gstin, pan, options) {
|
|
22597
|
+
const gstinErr = validateGstin(gstin, {
|
|
22598
|
+
required: options?.requiredGstin
|
|
22599
|
+
});
|
|
22600
|
+
if (gstinErr) return gstinErr;
|
|
22601
|
+
if (options?.requiredPan && (!pan || !pan.trim())) {
|
|
22602
|
+
return "PAN is required";
|
|
22603
|
+
}
|
|
22604
|
+
if (pan && !/^[A-Z]{5}[0-9]{4}[A-Z]$/i.test(pan.trim())) {
|
|
21156
22605
|
return "Invalid PAN format (e.g. ABCDE1234F)";
|
|
21157
22606
|
}
|
|
21158
22607
|
return null;
|
|
21159
22608
|
}
|
|
21160
22609
|
function validateAadhaar(aadhaar) {
|
|
21161
22610
|
if (!aadhaar) return null;
|
|
21162
|
-
if (!/^[0-9]{12}$/.test(aadhaar)) {
|
|
22611
|
+
if (!/^[0-9]{12}$/.test(aadhaar.replace(/\s+/g, ""))) {
|
|
21163
22612
|
return "Invalid Aadhaar number (12 digits)";
|
|
21164
22613
|
}
|
|
21165
22614
|
return null;
|
|
21166
22615
|
}
|
|
21167
22616
|
function validatePersonKyc(person, options) {
|
|
21168
22617
|
const required = options?.required === true;
|
|
21169
|
-
if (required && !person.aadhaarNo) return "Aadhaar number is required";
|
|
21170
|
-
if (required && !person.panNo) return "PAN is required";
|
|
22618
|
+
if (required && (!person.aadhaarNo || !person.aadhaarNo.trim())) return "Aadhaar number is required";
|
|
22619
|
+
if (required && (!person.panNo || !person.panNo.trim())) return "PAN is required";
|
|
21171
22620
|
const aadhaarErr = validateAadhaar(person.aadhaarNo ?? null);
|
|
21172
22621
|
if (aadhaarErr) return aadhaarErr;
|
|
21173
|
-
const panErr = validateIndiaTaxIds(null, person.panNo ?? null
|
|
22622
|
+
const panErr = validateIndiaTaxIds(null, person.panNo ?? null, {
|
|
22623
|
+
requiredPan: required
|
|
22624
|
+
});
|
|
21174
22625
|
if (panErr) return panErr;
|
|
21175
22626
|
return null;
|
|
21176
22627
|
}
|
|
21177
22628
|
function readOwnerDesignation(metadata) {
|
|
21178
|
-
|
|
21179
|
-
|
|
22629
|
+
if (!metadata || typeof metadata !== "object") return "";
|
|
22630
|
+
const d = metadata.ownerDesignation;
|
|
22631
|
+
return typeof d === "string" ? d.trim() : "";
|
|
21180
22632
|
}
|
|
21181
|
-
var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES;
|
|
22633
|
+
var VENDOR_REGISTRATION_STATUSES, VENDOR_BUSINESS_TYPES, COUNTRY_PHONE_CODES;
|
|
21182
22634
|
var init_vendor_profile = __esm({
|
|
21183
22635
|
"src/lib/vendor-profile.ts"() {
|
|
21184
22636
|
VENDOR_REGISTRATION_STATUSES = [
|
|
@@ -21225,7 +22677,34 @@ var init_vendor_profile = __esm({
|
|
|
21225
22677
|
label: "Other"
|
|
21226
22678
|
}
|
|
21227
22679
|
];
|
|
22680
|
+
COUNTRY_PHONE_CODES = (() => {
|
|
22681
|
+
const list = [];
|
|
22682
|
+
const seenCodes = /* @__PURE__ */ new Set();
|
|
22683
|
+
for (const c of Country.getAllCountries()) {
|
|
22684
|
+
if (!c.phonecode) continue;
|
|
22685
|
+
const rawCode = c.phonecode.replace(/^\+/, "").trim();
|
|
22686
|
+
if (!rawCode) continue;
|
|
22687
|
+
const code = `+${rawCode}`;
|
|
22688
|
+
const key = `${code}-${c.name}`;
|
|
22689
|
+
if (seenCodes.has(key)) continue;
|
|
22690
|
+
seenCodes.add(key);
|
|
22691
|
+
list.push({
|
|
22692
|
+
code,
|
|
22693
|
+
country: c.name,
|
|
22694
|
+
isoCode: c.isoCode,
|
|
22695
|
+
label: `${code} (${c.name})`
|
|
22696
|
+
});
|
|
22697
|
+
}
|
|
22698
|
+
return list.sort((a, b) => {
|
|
22699
|
+
if (a.code === "+91" && b.code !== "+91") return -1;
|
|
22700
|
+
if (b.code === "+91" && a.code !== "+91") return 1;
|
|
22701
|
+
return a.country.localeCompare(b.country);
|
|
22702
|
+
});
|
|
22703
|
+
})();
|
|
22704
|
+
__name(splitPhoneAndCountryCode, "splitPhoneAndCountryCode");
|
|
22705
|
+
__name(formatPhoneWithCountryCode, "formatPhoneWithCountryCode");
|
|
21228
22706
|
new Set(VENDOR_REGISTRATION_STATUSES.map((s) => s.value));
|
|
22707
|
+
__name(validateGstin, "validateGstin");
|
|
21229
22708
|
__name(validateIndiaTaxIds, "validateIndiaTaxIds");
|
|
21230
22709
|
__name(validateAadhaar, "validateAadhaar");
|
|
21231
22710
|
__name(validatePersonKyc, "validatePersonKyc");
|
|
@@ -21828,10 +23307,26 @@ function SubmissionDetailPage({ submissionId }) {
|
|
|
21828
23307
|
}
|
|
21829
23308
|
const formName = submission.form?.name ?? `Form #${submission.formId}`;
|
|
21830
23309
|
const contact = submission.contact;
|
|
21831
|
-
const
|
|
21832
|
-
|
|
21833
|
-
|
|
21834
|
-
|
|
23310
|
+
const resolveFieldLabel = /* @__PURE__ */ __name((key) => {
|
|
23311
|
+
if (!key) return "\u2014";
|
|
23312
|
+
const rawKey = key.trim();
|
|
23313
|
+
const numericIdMatch = rawKey.match(/^(?:field_)?(\d+)$/i);
|
|
23314
|
+
const fieldIdStr = numericIdMatch ? numericIdMatch[1] : null;
|
|
23315
|
+
const fields = submission?.form?.fields ?? [];
|
|
23316
|
+
const field = fields.find((f) => fieldIdStr !== null && String(f.id) === fieldIdStr || String(f.id) === rawKey || f.name && f.name.trim().toLowerCase() === rawKey.toLowerCase() || f.label && f.label.trim().toLowerCase() === rawKey.toLowerCase());
|
|
23317
|
+
if (field) {
|
|
23318
|
+
if (field.label && field.label.trim() && !/^\d+$/.test(field.label.trim())) {
|
|
23319
|
+
return field.label.trim();
|
|
23320
|
+
}
|
|
23321
|
+
if (field.name && field.name.trim() && !/^\d+$/.test(field.name.trim())) {
|
|
23322
|
+
return field.name.trim().replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
23323
|
+
}
|
|
23324
|
+
}
|
|
23325
|
+
if (fieldIdStr !== null) {
|
|
23326
|
+
return `Field #${fieldIdStr}`;
|
|
23327
|
+
}
|
|
23328
|
+
return rawKey.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
23329
|
+
}, "resolveFieldLabel");
|
|
21835
23330
|
return /* @__PURE__ */ React.createElement("div", {
|
|
21836
23331
|
className: "rounded-lg bg-white shadow-md"
|
|
21837
23332
|
}, /* @__PURE__ */ React.createElement(DetailPageHeader, {
|
|
@@ -21890,7 +23385,7 @@ function SubmissionDetailPage({ submissionId }) {
|
|
|
21890
23385
|
key
|
|
21891
23386
|
}, /* @__PURE__ */ React.createElement("td", {
|
|
21892
23387
|
className: "py-2 px-3 text-gray-600 font-medium"
|
|
21893
|
-
},
|
|
23388
|
+
}, resolveFieldLabel(key)), /* @__PURE__ */ React.createElement("td", {
|
|
21894
23389
|
className: "py-2 px-3 text-gray-900 break-words min-w-0"
|
|
21895
23390
|
}, value === null || value === void 0 ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value)))))))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
|
|
21896
23391
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
@@ -23545,7 +25040,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23545
25040
|
productQuery
|
|
23546
25041
|
]);
|
|
23547
25042
|
const recalcPreview = useCallback(async () => {
|
|
23548
|
-
const orderLines = lines.filter((l) => !
|
|
25043
|
+
const orderLines = lines.filter((l) => !l.key.startsWith("reward-")).map((l) => ({
|
|
23549
25044
|
productId: l.productId,
|
|
23550
25045
|
quantity: l.quantity
|
|
23551
25046
|
}));
|
|
@@ -23569,19 +25064,22 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23569
25064
|
body: JSON.stringify({
|
|
23570
25065
|
orderLines,
|
|
23571
25066
|
currency,
|
|
23572
|
-
discountId: coupons[0]?.discountId ?? null
|
|
25067
|
+
discountId: coupons[0]?.discountId ?? null,
|
|
25068
|
+
discountIds: coupons.map((c) => c.discountId)
|
|
23573
25069
|
})
|
|
23574
25070
|
});
|
|
23575
25071
|
if (!res.ok) throw new Error("Calculate failed");
|
|
23576
25072
|
const data = await res.json();
|
|
23577
25073
|
const correctedLines = data.lines.map((cl) => {
|
|
23578
25074
|
if (cl.productId == null) return cl;
|
|
23579
|
-
const
|
|
25075
|
+
const pid = Number(cl.productId);
|
|
25076
|
+
const cached = productCache.current.get(pid);
|
|
23580
25077
|
if (!cl.found && cached) {
|
|
23581
|
-
const unitPrice2 = cached.price;
|
|
25078
|
+
const unitPrice2 = Number(cached.price ?? 0);
|
|
23582
25079
|
const subtotal3 = unitPrice2 * cl.quantity;
|
|
23583
25080
|
return {
|
|
23584
25081
|
...cl,
|
|
25082
|
+
productId: pid,
|
|
23585
25083
|
found: true,
|
|
23586
25084
|
unitPrice: unitPrice2,
|
|
23587
25085
|
subtotal: subtotal3,
|
|
@@ -23589,15 +25087,16 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23589
25087
|
total: subtotal3
|
|
23590
25088
|
};
|
|
23591
25089
|
}
|
|
23592
|
-
const unitPrice = cached?.price ?? cl.unitPrice;
|
|
25090
|
+
const unitPrice = Number(cached?.price ?? cl.unitPrice ?? 0);
|
|
23593
25091
|
const subtotal2 = unitPrice * cl.quantity;
|
|
23594
|
-
const taxRate = cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal : 0);
|
|
23595
|
-
const lineDiscount = cl.
|
|
25092
|
+
const taxRate = Number(cl.taxRate ?? (cl.subtotal > 0 && cl.tax > 0 ? cl.tax / cl.subtotal * 100 : 0));
|
|
25093
|
+
const lineDiscount = Number(cl.discount ?? 0);
|
|
23596
25094
|
const discountedBase = Math.max(0, subtotal2 - lineDiscount);
|
|
23597
25095
|
const tax2 = discountedBase * taxRate / 100;
|
|
23598
25096
|
const total2 = discountedBase + tax2;
|
|
23599
25097
|
return {
|
|
23600
25098
|
...cl,
|
|
25099
|
+
productId: pid,
|
|
23601
25100
|
unitPrice,
|
|
23602
25101
|
subtotal: subtotal2,
|
|
23603
25102
|
taxRate,
|
|
@@ -23605,13 +25104,14 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23605
25104
|
total: total2
|
|
23606
25105
|
};
|
|
23607
25106
|
});
|
|
23608
|
-
const rewardDisplayLines = lines.filter((l) =>
|
|
23609
|
-
const
|
|
25107
|
+
const rewardDisplayLines = lines.filter((l) => l.key.startsWith("reward-")).map((l) => {
|
|
25108
|
+
const pid = Number(l.productId);
|
|
25109
|
+
const cached = productCache.current.get(pid);
|
|
23610
25110
|
return {
|
|
23611
|
-
productId:
|
|
25111
|
+
productId: pid,
|
|
23612
25112
|
productName: l.label,
|
|
23613
25113
|
found: true,
|
|
23614
|
-
unitPrice: cached?.price ?? 0,
|
|
25114
|
+
unitPrice: Number(cached?.price ?? 0),
|
|
23615
25115
|
quantity: l.quantity,
|
|
23616
25116
|
subtotal: 0,
|
|
23617
25117
|
tax: 0,
|
|
@@ -23653,7 +25153,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23653
25153
|
useEffect(() => {
|
|
23654
25154
|
const automaticCoupons = coupons.filter((c) => c.isAutomatic);
|
|
23655
25155
|
if (automaticCoupons.length === 0) return;
|
|
23656
|
-
const nonRewardLines = lines.filter((l) => !
|
|
25156
|
+
const nonRewardLines = lines.filter((l) => !l.key.startsWith("reward-"));
|
|
23657
25157
|
if (nonRewardLines.length === 0) {
|
|
23658
25158
|
const autoIds = new Set(automaticCoupons.map((c) => c.discountId));
|
|
23659
25159
|
const rewardPids = new Set(automaticCoupons.flatMap((c) => c.rewardLines.map((r) => r.productId)));
|
|
@@ -23733,7 +25233,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23733
25233
|
cancelled = true;
|
|
23734
25234
|
};
|
|
23735
25235
|
}, [
|
|
23736
|
-
lines.filter((l) => !
|
|
25236
|
+
lines.filter((l) => !l.key.startsWith("reward-")).map((l) => `${l.productId}:${l.quantity}`).join(","),
|
|
23737
25237
|
coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
|
|
23738
25238
|
]);
|
|
23739
25239
|
function handleRemoveCoupon(couponCode) {
|
|
@@ -23855,7 +25355,10 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23855
25355
|
const calcByProductId = useMemo(() => {
|
|
23856
25356
|
const m = /* @__PURE__ */ new Map();
|
|
23857
25357
|
for (const l of preview?.lines ?? []) {
|
|
23858
|
-
if (l.productId != null && l.found)
|
|
25358
|
+
if (l.productId != null && l.found) {
|
|
25359
|
+
const pid = Number(l.productId);
|
|
25360
|
+
if (Number.isFinite(pid)) m.set(pid, l);
|
|
25361
|
+
}
|
|
23859
25362
|
}
|
|
23860
25363
|
return m;
|
|
23861
25364
|
}, [
|
|
@@ -23877,13 +25380,13 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23877
25380
|
}
|
|
23878
25381
|
const unknown = preview?.lines.filter((l) => !l.found) ?? [];
|
|
23879
25382
|
if (unknown.length > 0) {
|
|
23880
|
-
toast.error(
|
|
25383
|
+
toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
|
|
23881
25384
|
return;
|
|
23882
25385
|
}
|
|
23883
25386
|
setSubmitting(true);
|
|
25387
|
+
let resolvedContactId = effectiveContactId;
|
|
23884
25388
|
try {
|
|
23885
|
-
|
|
23886
|
-
if (!resolvedContactId && effectiveEmail) {
|
|
25389
|
+
if (resolvedContactId == null && effectiveEmail) {
|
|
23887
25390
|
try {
|
|
23888
25391
|
const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
|
|
23889
25392
|
if (res2.ok) {
|
|
@@ -23905,12 +25408,12 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
23905
25408
|
billingAddress,
|
|
23906
25409
|
shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
|
|
23907
25410
|
orderLines: lines.map((l) => {
|
|
23908
|
-
const isReward =
|
|
23909
|
-
const calc = preview?.lines.find((pl) => pl.productId === l.productId);
|
|
25411
|
+
const isReward = l.key.startsWith("reward-");
|
|
25412
|
+
const calc = preview?.lines.find((pl) => Number(pl.productId) === Number(l.productId));
|
|
23910
25413
|
return {
|
|
23911
25414
|
productId: l.productId,
|
|
23912
25415
|
quantity: l.quantity,
|
|
23913
|
-
unitPrice: isReward ? 0 : calc?.unitPrice ?? productCache.current.get(l.productId)?.price ?? 0
|
|
25416
|
+
unitPrice: isReward ? 0 : calc?.unitPrice ?? Number(productCache.current.get(l.productId)?.price ?? 0)
|
|
23914
25417
|
};
|
|
23915
25418
|
})
|
|
23916
25419
|
};
|
|
@@ -28886,8 +30389,8 @@ function ProductEditPage({ productId }) {
|
|
|
28886
30389
|
const [price, setPrice] = useState(0);
|
|
28887
30390
|
const [defaultPriceStr, setDefaultPriceStr] = useState("");
|
|
28888
30391
|
const [pricingConfig, setPricingConfig] = useState(DEFAULT_PRICING_CONFIG);
|
|
28889
|
-
const [compareAtPrice, setCompareAtPrice] = useState(
|
|
28890
|
-
const [quantity, setQuantity] = useState(1);
|
|
30392
|
+
const [compareAtPrice, setCompareAtPrice] = useState("");
|
|
30393
|
+
const [quantity, setQuantity] = useState("1");
|
|
28891
30394
|
const [status, setStatus] = useState("draft");
|
|
28892
30395
|
const [approvalStatus, setApprovalStatus] = useState("pending");
|
|
28893
30396
|
const [rejectionReason, setRejectionReason] = useState("");
|
|
@@ -29108,8 +30611,8 @@ function ProductEditPage({ productId }) {
|
|
|
29108
30611
|
setDefaultPriceStr(product.price != null && Number.isFinite(Number(product.price)) ? String(product.price) : "");
|
|
29109
30612
|
setPrice(product.price != null ? Number(product.price) : 0);
|
|
29110
30613
|
const rawCompare = product.compareAtPrice != null ? Number(product.compareAtPrice) : 0;
|
|
29111
|
-
setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare :
|
|
29112
|
-
setQuantity(product.quantity
|
|
30614
|
+
setCompareAtPrice(rawCompare != null && Number.isFinite(rawCompare) ? String(rawCompare) : "");
|
|
30615
|
+
setQuantity(product.quantity != null ? String(product.quantity) : "1");
|
|
29113
30616
|
setStatus(product.status ?? "draft");
|
|
29114
30617
|
setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
|
|
29115
30618
|
setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
|
|
@@ -29276,22 +30779,6 @@ function ProductEditPage({ productId }) {
|
|
|
29276
30779
|
return pricingConfig.defaultCurrency || "INR";
|
|
29277
30780
|
}
|
|
29278
30781
|
})();
|
|
29279
|
-
const handleNumberChange = /* @__PURE__ */ __name((setter, options) => (e) => {
|
|
29280
|
-
const value = e.target.value;
|
|
29281
|
-
if (value === "") {
|
|
29282
|
-
setter(0);
|
|
29283
|
-
return;
|
|
29284
|
-
}
|
|
29285
|
-
let num = options?.integer ? Number.parseInt(value, 10) : Number(value);
|
|
29286
|
-
if (Number.isNaN(num)) {
|
|
29287
|
-
setter(0);
|
|
29288
|
-
return;
|
|
29289
|
-
}
|
|
29290
|
-
if (options?.min !== void 0) {
|
|
29291
|
-
num = Math.max(options.min, num);
|
|
29292
|
-
}
|
|
29293
|
-
setter(num);
|
|
29294
|
-
}, "handleNumberChange");
|
|
29295
30782
|
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
29296
30783
|
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
29297
30784
|
setRejectDraft(rejectionReason);
|
|
@@ -29364,12 +30851,13 @@ function ProductEditPage({ productId }) {
|
|
|
29364
30851
|
return;
|
|
29365
30852
|
}
|
|
29366
30853
|
}
|
|
29367
|
-
const
|
|
30854
|
+
const quantityValue = quantity === "" ? 0 : Number(quantity);
|
|
30855
|
+
const quantityErrors = validateProductQuantity(quantityValue, hasVariants, parsedVariants);
|
|
29368
30856
|
if (quantityErrors.length) {
|
|
29369
30857
|
setErrors(quantityErrors);
|
|
29370
30858
|
return;
|
|
29371
30859
|
}
|
|
29372
|
-
const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) :
|
|
30860
|
+
const resolvedQuantity = hasVariants ? productQuantityFromVariants(parsedVariants) : quantityValue;
|
|
29373
30861
|
setSaving(true);
|
|
29374
30862
|
try {
|
|
29375
30863
|
const compareAtPriceValue = compareAtPrice ? Math.round(Number(compareAtPrice) * 100) / 100 : null;
|
|
@@ -29983,9 +31471,8 @@ function ProductEditPage({ productId }) {
|
|
|
29983
31471
|
type: "number",
|
|
29984
31472
|
value: compareAtPrice,
|
|
29985
31473
|
className: inputCls3,
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
})
|
|
31474
|
+
min: 0,
|
|
31475
|
+
onChange: /* @__PURE__ */ __name((e) => setCompareAtPrice(e.target.value), "onChange")
|
|
29989
31476
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
29990
31477
|
className: labelCls3
|
|
29991
31478
|
}, "Contact form"), /* @__PURE__ */ React.createElement("select", {
|
|
@@ -30008,10 +31495,7 @@ function ProductEditPage({ productId }) {
|
|
|
30008
31495
|
min: 0,
|
|
30009
31496
|
step: 1,
|
|
30010
31497
|
value: hasVariants ? productQuantityFromVariants(variantsFromForm(variantRows, pricingConfig.defaultCurrency)) : quantity,
|
|
30011
|
-
onChange:
|
|
30012
|
-
min: 0,
|
|
30013
|
-
integer: true
|
|
30014
|
-
}),
|
|
31498
|
+
onChange: /* @__PURE__ */ __name((e) => setQuantity(e.target.value), "onChange"),
|
|
30015
31499
|
className: `${inputCls3}${hasVariants ? " bg-gray-100" : ""}`,
|
|
30016
31500
|
required: !hasVariants,
|
|
30017
31501
|
readOnly: hasVariants,
|
|
@@ -30088,8 +31572,18 @@ function ProductEditPage({ productId }) {
|
|
|
30088
31572
|
value: row.taxId === "" ? "" : String(row.taxId),
|
|
30089
31573
|
onChange: /* @__PURE__ */ __name((e) => {
|
|
30090
31574
|
const v = e.target.value;
|
|
31575
|
+
if (v === "") {
|
|
31576
|
+
setTaxRow(i, {
|
|
31577
|
+
taxId: "",
|
|
31578
|
+
rate: ""
|
|
31579
|
+
});
|
|
31580
|
+
return;
|
|
31581
|
+
}
|
|
31582
|
+
const taxId = Number(v);
|
|
31583
|
+
const selectedTax = taxMasterList.find((t) => t.id === taxId);
|
|
30091
31584
|
setTaxRow(i, {
|
|
30092
|
-
taxId
|
|
31585
|
+
taxId,
|
|
31586
|
+
rate: selectedTax?.rate != null ? String(selectedTax.rate) : ""
|
|
30093
31587
|
});
|
|
30094
31588
|
}, "onChange"),
|
|
30095
31589
|
className: inputCls3
|
|
@@ -31914,6 +33408,35 @@ function EventEditPage({ eventId }) {
|
|
|
31914
33408
|
const [saving, setSaving] = useState(false);
|
|
31915
33409
|
const [errors, setErrors] = useState([]);
|
|
31916
33410
|
const [activeTab, setActiveTab] = useState("basic");
|
|
33411
|
+
const eventTabs = [
|
|
33412
|
+
{
|
|
33413
|
+
key: "basic",
|
|
33414
|
+
label: "Basic info"
|
|
33415
|
+
},
|
|
33416
|
+
{
|
|
33417
|
+
key: "venue",
|
|
33418
|
+
label: "Venue"
|
|
33419
|
+
},
|
|
33420
|
+
{
|
|
33421
|
+
key: "datetime",
|
|
33422
|
+
label: "Date & time"
|
|
33423
|
+
},
|
|
33424
|
+
{
|
|
33425
|
+
key: "settings",
|
|
33426
|
+
label: "Settings"
|
|
33427
|
+
}
|
|
33428
|
+
];
|
|
33429
|
+
const activeTabIndex = eventTabs.findIndex((tab) => tab.key === activeTab);
|
|
33430
|
+
const goToPreviousTab = /* @__PURE__ */ __name(() => {
|
|
33431
|
+
if (activeTabIndex > 0) {
|
|
33432
|
+
setActiveTab(eventTabs[activeTabIndex - 1].key);
|
|
33433
|
+
}
|
|
33434
|
+
}, "goToPreviousTab");
|
|
33435
|
+
const goToNextTab = /* @__PURE__ */ __name(() => {
|
|
33436
|
+
if (activeTabIndex < eventTabs.length - 1) {
|
|
33437
|
+
setActiveTab(eventTabs[activeTabIndex + 1].key);
|
|
33438
|
+
}
|
|
33439
|
+
}, "goToNextTab");
|
|
31917
33440
|
const [name, setName] = useState("");
|
|
31918
33441
|
const [slug, setSlug] = useState("");
|
|
31919
33442
|
const [description, setDescription] = useState("");
|
|
@@ -32733,7 +34256,19 @@ function EventEditPage({ eventId }) {
|
|
|
32733
34256
|
[key]: value
|
|
32734
34257
|
})), "onChange")
|
|
32735
34258
|
}))))
|
|
32736
|
-
}),
|
|
34259
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
34260
|
+
className: "flex items-center justify-between border-t border-gray-200 px-4 py-4 sm:px-6"
|
|
34261
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
34262
|
+
type: "button",
|
|
34263
|
+
onClick: goToPreviousTab,
|
|
34264
|
+
disabled: activeTabIndex === 0,
|
|
34265
|
+
className: "rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
|
|
34266
|
+
}, "Previous"), /* @__PURE__ */ React.createElement("button", {
|
|
34267
|
+
type: "button",
|
|
34268
|
+
onClick: goToNextTab,
|
|
34269
|
+
disabled: activeTabIndex === eventTabs.length - 1,
|
|
34270
|
+
className: "rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-40"
|
|
34271
|
+
}, "Next")), showEventProducts ? /* @__PURE__ */ React.createElement("div", {
|
|
32737
34272
|
className: "border-t border-gray-200 px-4 py-4 sm:px-6"
|
|
32738
34273
|
}, /* @__PURE__ */ React.createElement(EventProductsSection, {
|
|
32739
34274
|
eventId: eventRecordId,
|
|
@@ -33078,6 +34613,18 @@ function ComboEditPage({ comboId }) {
|
|
|
33078
34613
|
]);
|
|
33079
34614
|
return;
|
|
33080
34615
|
}
|
|
34616
|
+
if (!startsAt) {
|
|
34617
|
+
setErrors([
|
|
34618
|
+
"Starts at is required"
|
|
34619
|
+
]);
|
|
34620
|
+
return;
|
|
34621
|
+
}
|
|
34622
|
+
if (!endsAt) {
|
|
34623
|
+
setErrors([
|
|
34624
|
+
"Ends at is required"
|
|
34625
|
+
]);
|
|
34626
|
+
return;
|
|
34627
|
+
}
|
|
33081
34628
|
if (!priceStr.trim()) {
|
|
33082
34629
|
setErrors([
|
|
33083
34630
|
`Price (${defaultCurrency}) is required`
|
|
@@ -33367,18 +34914,20 @@ function ComboEditPage({ comboId }) {
|
|
|
33367
34914
|
className: "grid grid-cols-2 gap-4"
|
|
33368
34915
|
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
33369
34916
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
33370
|
-
}, "Starts at"), /* @__PURE__ */ React.createElement("input", {
|
|
34917
|
+
}, "Starts at *"), /* @__PURE__ */ React.createElement("input", {
|
|
33371
34918
|
type: "datetime-local",
|
|
33372
34919
|
value: startsAt,
|
|
33373
34920
|
onChange: /* @__PURE__ */ __name((e) => setStartsAt(e.target.value), "onChange"),
|
|
33374
|
-
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
34921
|
+
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
34922
|
+
required: true
|
|
33375
34923
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
33376
34924
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
33377
|
-
}, "Ends at"), /* @__PURE__ */ React.createElement("input", {
|
|
34925
|
+
}, "Ends at *"), /* @__PURE__ */ React.createElement("input", {
|
|
33378
34926
|
type: "datetime-local",
|
|
33379
34927
|
value: endsAt,
|
|
33380
34928
|
onChange: /* @__PURE__ */ __name((e) => setEndsAt(e.target.value), "onChange"),
|
|
33381
|
-
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
34929
|
+
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
34930
|
+
required: true
|
|
33382
34931
|
})))))),
|
|
33383
34932
|
sidebar: /* @__PURE__ */ React.createElement(React.Fragment, null)
|
|
33384
34933
|
}));
|
|
@@ -33464,7 +35013,8 @@ function VendorEditPage({ vendorId }) {
|
|
|
33464
35013
|
const [website, setWebsite] = useState("");
|
|
33465
35014
|
const [logo, setLogo] = useState("");
|
|
33466
35015
|
const [email, setEmail] = useState("");
|
|
33467
|
-
const [
|
|
35016
|
+
const [storePhoneCode, setStorePhoneCode] = useState("+91");
|
|
35017
|
+
const [storePhoneNum, setStorePhoneNum] = useState("");
|
|
33468
35018
|
const [addressLine1, setAddressLine1] = useState("");
|
|
33469
35019
|
const [addressLine2, setAddressLine2] = useState("");
|
|
33470
35020
|
const [city, setCity] = useState("");
|
|
@@ -33478,7 +35028,8 @@ function VendorEditPage({ vendorId }) {
|
|
|
33478
35028
|
const [metadata, setMetadata] = useState(null);
|
|
33479
35029
|
const [ownerName, setOwnerName] = useState("");
|
|
33480
35030
|
const [ownerEmail, setOwnerEmail] = useState("");
|
|
33481
|
-
const [
|
|
35031
|
+
const [ownerPhoneCode, setOwnerPhoneCode] = useState("+91");
|
|
35032
|
+
const [ownerPhoneNum, setOwnerPhoneNum] = useState("");
|
|
33482
35033
|
const [ownerDesignation, setOwnerDesignation] = useState("");
|
|
33483
35034
|
const [ownerAadhaarNo, setOwnerAadhaarNo] = useState("");
|
|
33484
35035
|
const [ownerPanNo, setOwnerPanNo] = useState("");
|
|
@@ -33502,7 +35053,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
33502
35053
|
website: website.trim() || null,
|
|
33503
35054
|
logo: logo.trim() || null,
|
|
33504
35055
|
email: email.trim() || null,
|
|
33505
|
-
phone:
|
|
35056
|
+
phone: formatPhoneWithCountryCode(storePhoneCode, storePhoneNum),
|
|
33506
35057
|
addressLine1: addressLine1.trim() || null,
|
|
33507
35058
|
addressLine2: addressLine2.trim() || null,
|
|
33508
35059
|
city: city.trim() || null,
|
|
@@ -33530,7 +35081,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
33530
35081
|
setWebsite(data.website ?? "");
|
|
33531
35082
|
setLogo(data.logo ?? "");
|
|
33532
35083
|
setEmail(data.email ?? "");
|
|
33533
|
-
|
|
35084
|
+
const sPhone = splitPhoneAndCountryCode(data.phone);
|
|
35085
|
+
setStorePhoneCode(sPhone.countryCode);
|
|
35086
|
+
setStorePhoneNum(sPhone.phoneNumber);
|
|
33534
35087
|
setAddressLine1(data.addressLine1 ?? "");
|
|
33535
35088
|
setAddressLine2(data.addressLine2 ?? "");
|
|
33536
35089
|
setCity(data.city ?? "");
|
|
@@ -33552,7 +35105,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
33552
35105
|
if (!cancelled) {
|
|
33553
35106
|
setOwnerName(user.name ?? "");
|
|
33554
35107
|
setOwnerEmail(user.email ?? "");
|
|
33555
|
-
|
|
35108
|
+
const oPhone = splitPhoneAndCountryCode(user.phone);
|
|
35109
|
+
setOwnerPhoneCode(oPhone.countryCode);
|
|
35110
|
+
setOwnerPhoneNum(oPhone.phoneNumber);
|
|
33556
35111
|
}
|
|
33557
35112
|
}
|
|
33558
35113
|
} else if (!cancelled) {
|
|
@@ -33699,7 +35254,10 @@ function VendorEditPage({ vendorId }) {
|
|
|
33699
35254
|
setSaving(false);
|
|
33700
35255
|
return;
|
|
33701
35256
|
}
|
|
33702
|
-
const
|
|
35257
|
+
const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
|
|
35258
|
+
const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
|
|
35259
|
+
required: true
|
|
35260
|
+
});
|
|
33703
35261
|
if (taxError) {
|
|
33704
35262
|
setErrors([
|
|
33705
35263
|
taxError
|
|
@@ -33741,7 +35299,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
33741
35299
|
user: {
|
|
33742
35300
|
name: ownerName.trim(),
|
|
33743
35301
|
email: ownerEmail.trim(),
|
|
33744
|
-
phone:
|
|
35302
|
+
phone: formattedOwnerPhone || void 0,
|
|
33745
35303
|
designation: ownerDesignation.trim() || void 0,
|
|
33746
35304
|
aadhaarNo: ownerAadhaarNo.trim(),
|
|
33747
35305
|
panNo: ownerPanNo.trim().toUpperCase(),
|
|
@@ -33805,7 +35363,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
33805
35363
|
setSaving(false);
|
|
33806
35364
|
return;
|
|
33807
35365
|
}
|
|
33808
|
-
const taxError =
|
|
35366
|
+
const taxError = validateGstin(gstin.trim().toUpperCase() || null, {
|
|
35367
|
+
required: false
|
|
35368
|
+
});
|
|
33809
35369
|
if (taxError) {
|
|
33810
35370
|
setErrors([
|
|
33811
35371
|
taxError
|
|
@@ -33841,6 +35401,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
33841
35401
|
return;
|
|
33842
35402
|
}
|
|
33843
35403
|
if (ownerUserId != null) {
|
|
35404
|
+
const formattedOwnerPhone = formatPhoneWithCountryCode(ownerPhoneCode, ownerPhoneNum);
|
|
33844
35405
|
const userRes = await fetch(`/api/users/${ownerUserId}`, {
|
|
33845
35406
|
method: "PUT",
|
|
33846
35407
|
headers: {
|
|
@@ -33849,7 +35410,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
33849
35410
|
body: JSON.stringify({
|
|
33850
35411
|
name: ownerName.trim(),
|
|
33851
35412
|
email: ownerEmail.trim(),
|
|
33852
|
-
phone:
|
|
35413
|
+
phone: formattedOwnerPhone || null
|
|
33853
35414
|
})
|
|
33854
35415
|
});
|
|
33855
35416
|
if (!userRes.ok) {
|
|
@@ -33983,12 +35544,23 @@ function VendorEditPage({ vendorId }) {
|
|
|
33983
35544
|
className: `mt-1 ${fieldClass}`
|
|
33984
35545
|
})), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
|
|
33985
35546
|
htmlFor: "vendorPhone"
|
|
33986
|
-
}, "Store phone"), /* @__PURE__ */ React25__default.createElement(
|
|
35547
|
+
}, "Store phone"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
35548
|
+
className: "flex gap-2 mt-1"
|
|
35549
|
+
}, /* @__PURE__ */ React25__default.createElement("select", {
|
|
35550
|
+
value: storePhoneCode,
|
|
35551
|
+
onChange: /* @__PURE__ */ __name((e) => setStorePhoneCode(e.target.value), "onChange"),
|
|
35552
|
+
className: "w-36 rounded-md border border-gray-300 px-3 py-2 text-sm bg-white focus:outline-none focus:ring-1 focus:ring-black"
|
|
35553
|
+
}, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__default.createElement("option", {
|
|
35554
|
+
key: c.code,
|
|
35555
|
+
value: c.code
|
|
35556
|
+
}, c.label))), /* @__PURE__ */ React25__default.createElement(Input, {
|
|
33987
35557
|
id: "vendorPhone",
|
|
33988
|
-
|
|
33989
|
-
|
|
33990
|
-
|
|
33991
|
-
|
|
35558
|
+
type: "tel",
|
|
35559
|
+
placeholder: "9876543210",
|
|
35560
|
+
value: storePhoneNum,
|
|
35561
|
+
onChange: /* @__PURE__ */ __name((e) => setStorePhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
|
|
35562
|
+
className: `flex-1 ${fieldClass}`
|
|
35563
|
+
}))))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
|
|
33992
35564
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
33993
35565
|
}, "Store address"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
33994
35566
|
className: sectionCls5
|
|
@@ -34041,12 +35613,15 @@ function VendorEditPage({ vendorId }) {
|
|
|
34041
35613
|
className: `mt-1 ${fieldClass}`
|
|
34042
35614
|
}))))), /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
|
|
34043
35615
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
34044
|
-
}, "Tax
|
|
35616
|
+
}, "Tax"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
34045
35617
|
className: sectionCls5
|
|
34046
35618
|
}, /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
|
|
34047
|
-
htmlFor: "gstin"
|
|
35619
|
+
htmlFor: "gstin",
|
|
35620
|
+
required: create
|
|
34048
35621
|
}, "GSTIN"), /* @__PURE__ */ React25__default.createElement(Input, {
|
|
34049
35622
|
id: "gstin",
|
|
35623
|
+
required: create,
|
|
35624
|
+
maxLength: 15,
|
|
34050
35625
|
value: gstin,
|
|
34051
35626
|
onChange: /* @__PURE__ */ __name((e) => setGstin(e.target.value.toUpperCase()), "onChange"),
|
|
34052
35627
|
placeholder: "22AAAAA0000A1Z5",
|
|
@@ -34076,12 +35651,23 @@ function VendorEditPage({ vendorId }) {
|
|
|
34076
35651
|
className: `mt-1 ${fieldClass}`
|
|
34077
35652
|
})), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
|
|
34078
35653
|
htmlFor: "ownerPhone"
|
|
34079
|
-
}, "Phone"), /* @__PURE__ */ React25__default.createElement(
|
|
35654
|
+
}, "Phone"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
35655
|
+
className: "flex gap-2 mt-1"
|
|
35656
|
+
}, /* @__PURE__ */ React25__default.createElement("select", {
|
|
35657
|
+
value: ownerPhoneCode,
|
|
35658
|
+
onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneCode(e.target.value), "onChange"),
|
|
35659
|
+
className: "w-36 rounded-md border border-gray-300 px-3 py-2 text-sm bg-white focus:outline-none focus:ring-1 focus:ring-black"
|
|
35660
|
+
}, COUNTRY_PHONE_CODES.map((c) => /* @__PURE__ */ React25__default.createElement("option", {
|
|
35661
|
+
key: c.code,
|
|
35662
|
+
value: c.code
|
|
35663
|
+
}, c.label))), /* @__PURE__ */ React25__default.createElement(Input, {
|
|
34080
35664
|
id: "ownerPhone",
|
|
34081
|
-
|
|
34082
|
-
|
|
34083
|
-
|
|
34084
|
-
|
|
35665
|
+
type: "tel",
|
|
35666
|
+
placeholder: "9876543210",
|
|
35667
|
+
value: ownerPhoneNum,
|
|
35668
|
+
onChange: /* @__PURE__ */ __name((e) => setOwnerPhoneNum(e.target.value.replace(/\D/g, "")), "onChange"),
|
|
35669
|
+
className: `flex-1 ${fieldClass}`
|
|
35670
|
+
}))), /* @__PURE__ */ React25__default.createElement("div", null, /* @__PURE__ */ React25__default.createElement(FieldLabel, {
|
|
34085
35671
|
htmlFor: "ownerDesignation"
|
|
34086
35672
|
}, "Designation"), /* @__PURE__ */ React25__default.createElement(Input, {
|
|
34087
35673
|
id: "ownerDesignation",
|
|
@@ -34165,8 +35751,8 @@ function VendorEditPage({ vendorId }) {
|
|
|
34165
35751
|
}), /* @__PURE__ */ React25__default.createElement(Label3, {
|
|
34166
35752
|
htmlFor: "termsAccepted",
|
|
34167
35753
|
className: "font-normal cursor-pointer text-sm leading-snug"
|
|
34168
|
-
}, "I confirm
|
|
34169
|
-
sidebar:
|
|
35754
|
+
}, "I confirm I have read and accepted the terms and conditions for selling on this platform."))))),
|
|
35755
|
+
sidebar: create ? void 0 : /* @__PURE__ */ React25__default.createElement("section", null, /* @__PURE__ */ React25__default.createElement("h2", {
|
|
34170
35756
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
34171
35757
|
}, "Status"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
34172
35758
|
className: sectionCls5
|
|
@@ -34201,18 +35787,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
34201
35787
|
readOnly: true,
|
|
34202
35788
|
value: active ? "Yes" : "No",
|
|
34203
35789
|
className: `mt-1 ${fieldClass} bg-gray-100`
|
|
34204
|
-
}))))
|
|
34205
|
-
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
34206
|
-
}, "Owner access"), /* @__PURE__ */ React25__default.createElement("div", {
|
|
34207
|
-
className: sectionCls5
|
|
34208
|
-
}, /* @__PURE__ */ React25__default.createElement("p", {
|
|
34209
|
-
className: "text-sm text-gray-600"
|
|
34210
|
-
}, "Creates the vendor and an invite for the owner. After create, you can send the invite email or copy the invite link."), /* @__PURE__ */ React25__default.createElement(Button, {
|
|
34211
|
-
type: "button",
|
|
34212
|
-
disabled: saving,
|
|
34213
|
-
onClick: handleCreate,
|
|
34214
|
-
className: "w-full"
|
|
34215
|
-
}, saving ? "Creating\u2026" : "Create vendor"))) : void 0)
|
|
35790
|
+
}))))
|
|
34216
35791
|
}), /* @__PURE__ */ React25__default.createElement(Dialog, {
|
|
34217
35792
|
open: inviteDialog != null,
|
|
34218
35793
|
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
@@ -35807,7 +37382,7 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
|
35807
37382
|
value: "minAmount"
|
|
35808
37383
|
}, "Minimum order amount"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35809
37384
|
value: "minQuantity"
|
|
35810
|
-
}, "Minimum
|
|
37385
|
+
}, "Minimum Cart Quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35811
37386
|
value: "productMinQuantity"
|
|
35812
37387
|
}, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35813
37388
|
value: "events"
|
|
@@ -37262,8 +38837,8 @@ function ContactDetailPage({ contactId }) {
|
|
|
37262
38837
|
setAddAddressError("Select country, state, and city from the lists.");
|
|
37263
38838
|
return;
|
|
37264
38839
|
}
|
|
37265
|
-
const { Country:
|
|
37266
|
-
const countryName =
|
|
38840
|
+
const { Country: Country3, State: State2 } = await import('country-state-city');
|
|
38841
|
+
const countryName = Country3.getCountryByCode(addressGeo.countryIso)?.name ?? "";
|
|
37267
38842
|
const stateName = State2.getStatesOfCountry(addressGeo.countryIso).find((s) => s.isoCode === addressGeo.stateIso)?.name ?? "";
|
|
37268
38843
|
const payload = {
|
|
37269
38844
|
contactId: Number(contactId),
|