@infuro/cms-core 1.0.72 → 1.0.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs +543 -120
- package/dist/admin.js +545 -122
- package/dist/api.cjs +37 -37
- package/dist/api.d.cts +13 -6
- package/dist/api.d.ts +13 -6
- package/dist/api.js +5 -5
- package/dist/auth.cjs +11 -11
- package/dist/auth.d.cts +15 -0
- package/dist/auth.d.ts +15 -0
- package/dist/auth.js +1 -1
- package/dist/{chunk-BQQMTQ4C.cjs → chunk-25T6HP5T.cjs} +2 -2
- package/dist/{chunk-TTOEY7AH.cjs → chunk-2JV4EDY3.cjs} +3 -3
- package/dist/{chunk-LJ5C4RBF.js → chunk-6TRH3VY7.js} +1 -1
- package/dist/{chunk-4MNVIGVJ.cjs → chunk-D3SR6CYU.cjs} +3 -3
- package/dist/{chunk-PQJIJ4ZX.js → chunk-DAUBLBTP.js} +1 -1
- package/dist/{chunk-SCE2GIKH.js → chunk-DEGN4BTH.js} +9 -0
- package/dist/{chunk-CKTRA3Q2.cjs → chunk-F3FKBNMQ.cjs} +9 -0
- package/dist/{chunk-SRGDHASW.cjs → chunk-H4UIIBFD.cjs} +2571 -2096
- package/dist/{chunk-YHVA5NHI.js → chunk-LUSJITTI.js} +43 -6
- package/dist/{chunk-43UVXBEH.js → chunk-OQOO2JMB.js} +2529 -2054
- package/dist/{chunk-HZZTDM5J.js → chunk-R5HLPH6P.js} +1 -1
- package/dist/{chunk-3LF4RYAJ.js → chunk-X3IJG25M.js} +1 -1
- package/dist/{chunk-2JHGIWH7.cjs → chunk-X5ILLSTD.cjs} +43 -6
- package/dist/{chunk-NIBEGDD4.cjs → chunk-ZBSH4GWB.cjs} +1 -1
- package/dist/{email-queue-MWP4R67H.js → email-queue-5TOJJT3P.js} +3 -3
- package/dist/{email-queue-BJDH2ID7.cjs → email-queue-X2A46VD3.cjs} +7 -7
- package/dist/{emit-order-notification-trigger-TBXAZAPS.js → emit-order-notification-trigger-3RAVWKLG.js} +1 -1
- package/dist/{emit-order-notification-trigger-I4HQSTEN.cjs → emit-order-notification-trigger-MY3KP7CD.cjs} +3 -3
- package/dist/{erp-order-invoice-5O42ZKC5.cjs → erp-order-invoice-23BDWC2H.cjs} +6 -6
- package/dist/{erp-order-invoice-JEQKFXG4.js → erp-order-invoice-SEZZUDMW.js} +2 -2
- package/dist/generate-local-invoice-pdf-V5FHS23Z.js +3 -0
- package/dist/{generate-local-invoice-pdf-2NCA2WBL.cjs → generate-local-invoice-pdf-XYBVKHNP.cjs} +2 -2
- package/dist/index.cjs +243 -243
- package/dist/index.d.cts +31 -9
- package/dist/index.d.ts +31 -9
- package/dist/index.js +10 -10
- package/dist/migrations/1783400000000-MakeProductConfigTaxIdNullable.ts +40 -0
- package/dist/{order-completion-otp-handlers-KHLZQQJX.js → order-completion-otp-handlers-2WOFSORP.js} +4 -4
- package/dist/{order-completion-otp-handlers-2ZKRK5SN.cjs → order-completion-otp-handlers-EWY47XUA.cjs} +6 -6
- package/dist/{order-notification-dispatcher-Z3MXNF4U.js → order-notification-dispatcher-COR2ZHM3.js} +4 -4
- package/dist/{order-notification-dispatcher-37KDVHBM.cjs → order-notification-dispatcher-KTOAMMSD.cjs} +9 -9
- package/package.json +1 -1
- package/dist/generate-local-invoice-pdf-XUH6GYEJ.js +0 -3
package/dist/admin.cjs
CHANGED
|
@@ -381,9 +381,8 @@ var init_vendor_scope = __esm({
|
|
|
381
381
|
}
|
|
382
382
|
});
|
|
383
383
|
function AdminHeader() {
|
|
384
|
-
const { data: session } = react.useSession();
|
|
384
|
+
const { data: session, update } = react.useSession();
|
|
385
385
|
const sessionUser = session?.user;
|
|
386
|
-
const roleLabel = sessionUser?.groupName?.trim() || (sessionUser?.isRBACAdmin ? "Administrator" : "Admin user");
|
|
387
386
|
const isVendor = isVendorPortalUser(sessionUser);
|
|
388
387
|
const isMobile = useIsMobile();
|
|
389
388
|
const configuredLogo = process.env.NEXT_PUBLIC_ADMIN_LOGO_URL || DEFAULT_ADMIN_LOGO;
|
|
@@ -392,9 +391,125 @@ function AdminHeader() {
|
|
|
392
391
|
const isDataLogo = React26.useMemo(() => logoSrc.startsWith("data:"), [
|
|
393
392
|
logoSrc
|
|
394
393
|
]);
|
|
394
|
+
const [isSwitching, setIsSwitching] = React26.useState(false);
|
|
395
|
+
const [cookieVendorId, setCookieVendorId] = React26.useState(() => {
|
|
396
|
+
if (typeof window === "undefined") return null;
|
|
397
|
+
try {
|
|
398
|
+
const match = document.cookie.match(/(?:^|;\s*)infuro_active_vendor_id=(\d+)/);
|
|
399
|
+
if (match && match[1]) return Number(match[1]);
|
|
400
|
+
const stored = localStorage.getItem("infuro_active_vendor_id");
|
|
401
|
+
if (stored) return Number(stored);
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
});
|
|
406
|
+
const [fetchedStores, setFetchedStores] = React26.useState(null);
|
|
407
|
+
const [fetchedRoleName, setFetchedRoleName] = React26.useState(null);
|
|
408
|
+
const [fetchedActiveVendorId, setFetchedActiveVendorId] = React26.useState(null);
|
|
409
|
+
React26.useEffect(() => {
|
|
410
|
+
if (sessionUser?.vendorStores && sessionUser.vendorStores.length > 1 && !cookieVendorId) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
let isMounted = true;
|
|
414
|
+
fetch("/api/admin/vendor/context").then((res) => res.ok ? res.json() : null).then((data) => {
|
|
415
|
+
if (!isMounted || !data) return;
|
|
416
|
+
if (Array.isArray(data.vendorStores)) {
|
|
417
|
+
setFetchedStores(data.vendorStores);
|
|
418
|
+
}
|
|
419
|
+
if (data.vendorRoleName) {
|
|
420
|
+
setFetchedRoleName(data.vendorRoleName);
|
|
421
|
+
}
|
|
422
|
+
if (typeof data.activeVendorId === "number") {
|
|
423
|
+
setFetchedActiveVendorId(data.activeVendorId);
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
427
|
+
window.dispatchEvent(new CustomEvent("infuro_vendor_context_updated", {
|
|
428
|
+
detail: data
|
|
429
|
+
}));
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
}).catch(() => {
|
|
433
|
+
});
|
|
434
|
+
return () => {
|
|
435
|
+
isMounted = false;
|
|
436
|
+
};
|
|
437
|
+
}, [
|
|
438
|
+
sessionUser?.email,
|
|
439
|
+
sessionUser?.vendorStores,
|
|
440
|
+
cookieVendorId
|
|
441
|
+
]);
|
|
442
|
+
const stores = React26.useMemo(() => {
|
|
443
|
+
if (sessionUser?.vendorStores && sessionUser.vendorStores.length > 0) {
|
|
444
|
+
return sessionUser.vendorStores;
|
|
445
|
+
}
|
|
446
|
+
return fetchedStores || [];
|
|
447
|
+
}, [
|
|
448
|
+
sessionUser?.vendorStores,
|
|
449
|
+
fetchedStores
|
|
450
|
+
]);
|
|
451
|
+
const effectiveActiveVendorId = cookieVendorId ?? sessionUser?.activeVendorId ?? fetchedActiveVendorId;
|
|
452
|
+
const activeStore = React26.useMemo(() => {
|
|
453
|
+
return stores.find((s) => s.id === effectiveActiveVendorId) || (stores.length > 0 ? stores[0] : null);
|
|
454
|
+
}, [
|
|
455
|
+
stores,
|
|
456
|
+
effectiveActiveVendorId
|
|
457
|
+
]);
|
|
458
|
+
const effectiveRoleName = activeStore?.roleName || sessionUser?.vendorRoleName || fetchedRoleName;
|
|
459
|
+
const roleLabel = ((isVendor || stores.length > 0) && effectiveRoleName ? effectiveRoleName : null) || sessionUser?.groupName?.trim() || (sessionUser?.isRBACAdmin ? "Administrator" : "Admin user");
|
|
395
460
|
const handleLogout = /* @__PURE__ */ __name(() => {
|
|
396
461
|
void adminSignOut();
|
|
397
462
|
}, "handleLogout");
|
|
463
|
+
const handleSwitchStore = /* @__PURE__ */ __name(async (vendorId) => {
|
|
464
|
+
if (vendorId === activeStore?.id || isSwitching) return;
|
|
465
|
+
try {
|
|
466
|
+
setIsSwitching(true);
|
|
467
|
+
document.cookie = `infuro_active_vendor_id=${vendorId}; path=/; max-age=31536000; SameSite=Lax`;
|
|
468
|
+
try {
|
|
469
|
+
localStorage.setItem("infuro_active_vendor_id", String(vendorId));
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
setCookieVendorId(vendorId);
|
|
473
|
+
const res = await fetch("/api/admin/vendor/switch", {
|
|
474
|
+
method: "POST",
|
|
475
|
+
headers: {
|
|
476
|
+
"Content-Type": "application/json"
|
|
477
|
+
},
|
|
478
|
+
body: JSON.stringify({
|
|
479
|
+
vendorId
|
|
480
|
+
})
|
|
481
|
+
});
|
|
482
|
+
if (res.ok) {
|
|
483
|
+
const data = await res.json();
|
|
484
|
+
try {
|
|
485
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
486
|
+
window.dispatchEvent(new CustomEvent("infuro_vendor_context_updated", {
|
|
487
|
+
detail: data
|
|
488
|
+
}));
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
if (typeof update === "function") {
|
|
492
|
+
try {
|
|
493
|
+
await update({
|
|
494
|
+
activeVendorId: vendorId,
|
|
495
|
+
vendorRole: data.vendorRole,
|
|
496
|
+
vendorRoleId: data.vendorRoleId,
|
|
497
|
+
vendorRoleName: data.vendorRoleName,
|
|
498
|
+
isVendorRoleOwner: data.isVendorRoleOwner,
|
|
499
|
+
vendorEntityPerms: data.vendorEntityPerms,
|
|
500
|
+
vendorStores: data.vendorStores
|
|
501
|
+
});
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
window.location.reload();
|
|
506
|
+
}
|
|
507
|
+
} catch (err) {
|
|
508
|
+
console.error("Failed to switch store:", err);
|
|
509
|
+
} finally {
|
|
510
|
+
setIsSwitching(false);
|
|
511
|
+
}
|
|
512
|
+
}, "handleSwitchStore");
|
|
398
513
|
return /* @__PURE__ */ React.createElement("header", {
|
|
399
514
|
className: "bg-white border-b border-gray-200 px-4 py-2"
|
|
400
515
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -427,7 +542,46 @@ function AdminHeader() {
|
|
|
427
542
|
className: "text-sm font-semibold text-gray-800"
|
|
428
543
|
}, "Infuro"))), /* @__PURE__ */ React.createElement("div", {
|
|
429
544
|
className: "flex items-center space-x-3"
|
|
430
|
-
}, /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
545
|
+
}, stores.length > 1 && /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
546
|
+
asChild: true
|
|
547
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
548
|
+
variant: "outline",
|
|
549
|
+
size: "sm",
|
|
550
|
+
disabled: isSwitching,
|
|
551
|
+
className: "h-8 px-2.5 flex items-center gap-1.5 border-gray-200 bg-gray-50/80 hover:bg-gray-100 text-gray-800 text-xs font-medium rounded-lg shadow-none transition-colors"
|
|
552
|
+
}, isSwitching ? /* @__PURE__ */ React.createElement(LucideIcons.Loader2, {
|
|
553
|
+
className: "h-3.5 w-3.5 animate-spin text-gray-500 shrink-0"
|
|
554
|
+
}) : /* @__PURE__ */ React.createElement(LucideIcons.Store, {
|
|
555
|
+
className: "h-3.5 w-3.5 text-gray-500 shrink-0"
|
|
556
|
+
}), /* @__PURE__ */ React.createElement("span", {
|
|
557
|
+
className: "max-w-[130px] sm:max-w-[180px] truncate font-semibold"
|
|
558
|
+
}, activeStore?.name || "Select Store"), activeStore?.roleName && /* @__PURE__ */ React.createElement("span", {
|
|
559
|
+
className: "hidden sm:inline-block px-1.5 py-0.5 text-[10px] font-medium bg-gray-200/80 text-gray-700 rounded"
|
|
560
|
+
}, activeStore.roleName), /* @__PURE__ */ React.createElement(LucideIcons.ChevronDown, {
|
|
561
|
+
className: "h-3 w-3 text-gray-400 shrink-0"
|
|
562
|
+
}))), /* @__PURE__ */ React.createElement(DropdownMenuContent, {
|
|
563
|
+
align: "end",
|
|
564
|
+
className: "w-56 p-1"
|
|
565
|
+
}, /* @__PURE__ */ React.createElement(DropdownMenuLabel, {
|
|
566
|
+
className: "text-[11px] font-semibold text-gray-500 uppercase px-2 py-1"
|
|
567
|
+
}, "Switch Store"), /* @__PURE__ */ React.createElement(DropdownMenuSeparator, {
|
|
568
|
+
className: "my-1"
|
|
569
|
+
}), stores.map((st) => {
|
|
570
|
+
const isSelected = st.id === activeStore?.id;
|
|
571
|
+
return /* @__PURE__ */ React.createElement(DropdownMenuItem, {
|
|
572
|
+
key: st.id,
|
|
573
|
+
onClick: /* @__PURE__ */ __name(() => handleSwitchStore(st.id), "onClick"),
|
|
574
|
+
className: "flex items-center justify-between px-2.5 py-2 cursor-pointer text-xs rounded-md"
|
|
575
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
576
|
+
className: "flex flex-col gap-0.5 truncate pr-2"
|
|
577
|
+
}, /* @__PURE__ */ React.createElement("span", {
|
|
578
|
+
className: `font-medium ${isSelected ? "text-primary font-semibold" : "text-gray-800"}`
|
|
579
|
+
}, st.name), st.roleName && /* @__PURE__ */ React.createElement("span", {
|
|
580
|
+
className: "text-[10px] text-gray-400"
|
|
581
|
+
}, st.roleName)), isSelected && /* @__PURE__ */ React.createElement(LucideIcons.Check, {
|
|
582
|
+
className: "h-3.5 w-3.5 text-primary shrink-0"
|
|
583
|
+
}));
|
|
584
|
+
}))), /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
431
585
|
asChild: true
|
|
432
586
|
}, /* @__PURE__ */ React.createElement(Button, {
|
|
433
587
|
variant: "ghost",
|
|
@@ -895,7 +1049,7 @@ var init_admin_config_context = __esm({
|
|
|
895
1049
|
var CMS_VERSION;
|
|
896
1050
|
var init_cms_version = __esm({
|
|
897
1051
|
"src/lib/cms-version.ts"() {
|
|
898
|
-
CMS_VERSION = "1.0.
|
|
1052
|
+
CMS_VERSION = "1.0.74" ;
|
|
899
1053
|
}
|
|
900
1054
|
});
|
|
901
1055
|
function useCatalogCategories(enabled = true) {
|
|
@@ -1003,8 +1157,54 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
1003
1157
|
const searchParams = navigation.useSearchParams();
|
|
1004
1158
|
const { data: session } = react.useSession();
|
|
1005
1159
|
const sessionUser = session?.user;
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1160
|
+
const [vendorContext, setVendorContext] = React26.useState(() => {
|
|
1161
|
+
if (typeof window === "undefined") return null;
|
|
1162
|
+
try {
|
|
1163
|
+
const stored = localStorage.getItem("infuro_active_vendor_context");
|
|
1164
|
+
if (stored) return JSON.parse(stored);
|
|
1165
|
+
} catch {
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
});
|
|
1169
|
+
React26.useEffect(() => {
|
|
1170
|
+
const onContextUpdated = /* @__PURE__ */ __name((e) => {
|
|
1171
|
+
const detail = e.detail;
|
|
1172
|
+
if (detail) setVendorContext(detail);
|
|
1173
|
+
}, "onContextUpdated");
|
|
1174
|
+
window.addEventListener("infuro_vendor_context_updated", onContextUpdated);
|
|
1175
|
+
fetch("/api/admin/vendor/context").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
1176
|
+
if (data) {
|
|
1177
|
+
setVendorContext(data);
|
|
1178
|
+
try {
|
|
1179
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
1180
|
+
} catch {
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}).catch(() => {
|
|
1184
|
+
});
|
|
1185
|
+
return () => {
|
|
1186
|
+
window.removeEventListener("infuro_vendor_context_updated", onContextUpdated);
|
|
1187
|
+
};
|
|
1188
|
+
}, []);
|
|
1189
|
+
const effectiveSessionUser = React26.useMemo(() => {
|
|
1190
|
+
if (!sessionUser) return null;
|
|
1191
|
+
if (!vendorContext) return sessionUser;
|
|
1192
|
+
return {
|
|
1193
|
+
...sessionUser,
|
|
1194
|
+
activeVendorId: vendorContext.activeVendorId ?? sessionUser.activeVendorId,
|
|
1195
|
+
vendorRole: vendorContext.vendorRole ?? sessionUser.vendorRole,
|
|
1196
|
+
vendorRoleId: vendorContext.vendorRoleId ?? sessionUser.vendorRoleId,
|
|
1197
|
+
vendorRoleName: vendorContext.vendorRoleName ?? sessionUser.vendorRoleName,
|
|
1198
|
+
isVendorRoleOwner: vendorContext.isVendorRoleOwner ?? sessionUser.isVendorRoleOwner,
|
|
1199
|
+
vendorEntityPerms: vendorContext.vendorEntityPerms ?? sessionUser.vendorEntityPerms,
|
|
1200
|
+
vendorStores: vendorContext.vendorStores ?? sessionUser.vendorStores
|
|
1201
|
+
};
|
|
1202
|
+
}, [
|
|
1203
|
+
sessionUser,
|
|
1204
|
+
vendorContext
|
|
1205
|
+
]);
|
|
1206
|
+
const showVendorOnboard = canOnboardVendors(effectiveSessionUser);
|
|
1207
|
+
const vendorPortal = isVendorPortalUser(effectiveSessionUser);
|
|
1008
1208
|
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands } = React26.useContext(exports.AdminConfigContext);
|
|
1009
1209
|
const showStoreNav = storeEnabled || vendorPortal;
|
|
1010
1210
|
const showPlatformNav = !vendorPortal;
|
|
@@ -1031,8 +1231,8 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
1031
1231
|
const headingCls = "text-[11px] font-semibold text-gray-400 uppercase tracking-wider px-2.5 mb-1.5";
|
|
1032
1232
|
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";
|
|
1033
1233
|
const canReadEntity = /* @__PURE__ */ __name((entity) => {
|
|
1034
|
-
if (!
|
|
1035
|
-
return sessionHasEntityAccessFromExplanation(
|
|
1234
|
+
if (!effectiveSessionUser) return true;
|
|
1235
|
+
return sessionHasEntityAccessFromExplanation(effectiveSessionUser, entity, "read");
|
|
1036
1236
|
}, "canReadEntity");
|
|
1037
1237
|
return /* @__PURE__ */ React.createElement("aside", {
|
|
1038
1238
|
className: asideCls
|
|
@@ -1242,12 +1442,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
1242
1442
|
className: `${linkCls} ${isActive("/admin/vendor-profile") ? linkActive : linkInactive}`
|
|
1243
1443
|
}, /* @__PURE__ */ React.createElement(LucideIcons.User, {
|
|
1244
1444
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendor-profile") ? iconActive : iconInactive}`
|
|
1245
|
-
}), "Profile")), canManageVendorTeam(
|
|
1445
|
+
}), "Profile")), canManageVendorTeam(effectiveSessionUser) && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
1246
1446
|
href: "/admin/vendor-team",
|
|
1247
1447
|
className: `${linkCls} ${isActive("/admin/vendor-team") ? linkActive : linkInactive}`
|
|
1248
1448
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Users, {
|
|
1249
1449
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendor-team") ? iconActive : iconInactive}`
|
|
1250
|
-
}), "Team")), canManageVendorRoles(
|
|
1450
|
+
}), "Team")), canManageVendorRoles(effectiveSessionUser) && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
1251
1451
|
href: "/admin/vendor-roles",
|
|
1252
1452
|
className: `${linkCls} ${isActive("/admin/vendor-roles") ? linkActive : linkInactive}`
|
|
1253
1453
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Shield, {
|
|
@@ -6438,6 +6638,20 @@ var init_FieldConfiguration = __esm({
|
|
|
6438
6638
|
}
|
|
6439
6639
|
});
|
|
6440
6640
|
|
|
6641
|
+
// src/lib/slug-sanitizer.ts
|
|
6642
|
+
function sanitizeSlugInput(value) {
|
|
6643
|
+
return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-");
|
|
6644
|
+
}
|
|
6645
|
+
function normalizeSlug(value) {
|
|
6646
|
+
return sanitizeSlugInput(value).replace(/^-+|-+$/g, "");
|
|
6647
|
+
}
|
|
6648
|
+
var init_slug_sanitizer = __esm({
|
|
6649
|
+
"src/lib/slug-sanitizer.ts"() {
|
|
6650
|
+
__name(sanitizeSlugInput, "sanitizeSlugInput");
|
|
6651
|
+
__name(normalizeSlug, "normalizeSlug");
|
|
6652
|
+
}
|
|
6653
|
+
});
|
|
6654
|
+
|
|
6441
6655
|
// src/components/Admin/FormBuilder.tsx
|
|
6442
6656
|
var FormBuilder_exports = {};
|
|
6443
6657
|
__export(FormBuilder_exports, {
|
|
@@ -6516,18 +6730,6 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6516
6730
|
loadId,
|
|
6517
6731
|
loadFormData
|
|
6518
6732
|
]);
|
|
6519
|
-
React26.useEffect(() => {
|
|
6520
|
-
if (formData.name && !formData.slug) {
|
|
6521
|
-
const generatedSlug = formData.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
6522
|
-
setFormData((prev) => ({
|
|
6523
|
-
...prev,
|
|
6524
|
-
slug: generatedSlug
|
|
6525
|
-
}));
|
|
6526
|
-
}
|
|
6527
|
-
}, [
|
|
6528
|
-
formData.name,
|
|
6529
|
-
formData.slug
|
|
6530
|
-
]);
|
|
6531
6733
|
const addField = /* @__PURE__ */ __name(() => {
|
|
6532
6734
|
const newField = {
|
|
6533
6735
|
id: `field_${Date.now()}`,
|
|
@@ -6632,6 +6834,7 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6632
6834
|
setErrors([]);
|
|
6633
6835
|
const payload = {
|
|
6634
6836
|
...formData,
|
|
6837
|
+
slug: normalizeSlug(formData.slug),
|
|
6635
6838
|
published: isPublishing ? true : formData.published,
|
|
6636
6839
|
fields: formData.fields.map((field) => {
|
|
6637
6840
|
const numericId = typeof field.id === "number" ? field.id : /^\d+$/.test(String(field.id)) ? parseInt(String(field.id), 10) : void 0;
|
|
@@ -6762,8 +6965,12 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6762
6965
|
value: formData.slug,
|
|
6763
6966
|
onChange: /* @__PURE__ */ __name((e) => setFormData((prev) => ({
|
|
6764
6967
|
...prev,
|
|
6765
|
-
slug: e.target.value
|
|
6968
|
+
slug: sanitizeSlugInput(e.target.value)
|
|
6766
6969
|
})), "onChange"),
|
|
6970
|
+
onBlur: /* @__PURE__ */ __name(() => setFormData((prev) => ({
|
|
6971
|
+
...prev,
|
|
6972
|
+
slug: normalizeSlug(prev.slug)
|
|
6973
|
+
})), "onBlur"),
|
|
6767
6974
|
placeholder: "form-slug"
|
|
6768
6975
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
6769
6976
|
className: "block text-sm font-medium text-gray-700 mb-1"
|
|
@@ -6952,6 +7159,7 @@ var init_FormBuilder = __esm({
|
|
|
6952
7159
|
init_switch();
|
|
6953
7160
|
init_badge();
|
|
6954
7161
|
init_FieldConfiguration();
|
|
7162
|
+
init_slug_sanitizer();
|
|
6955
7163
|
__name(FormBuilder, "FormBuilder");
|
|
6956
7164
|
}
|
|
6957
7165
|
});
|
|
@@ -8001,9 +8209,10 @@ function BlogEditor({ existingBlog, duplicateSource }) {
|
|
|
8001
8209
|
setIsSaving(false);
|
|
8002
8210
|
return;
|
|
8003
8211
|
}
|
|
8212
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
8004
8213
|
const body = {
|
|
8005
8214
|
title,
|
|
8006
|
-
slug,
|
|
8215
|
+
slug: normalizedSlug,
|
|
8007
8216
|
content,
|
|
8008
8217
|
published: isPublishing,
|
|
8009
8218
|
tags,
|
|
@@ -8273,7 +8482,8 @@ function BlogEditor({ existingBlog, duplicateSource }) {
|
|
|
8273
8482
|
type: "text",
|
|
8274
8483
|
placeholder: "blog-post-url",
|
|
8275
8484
|
value: slug,
|
|
8276
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
8485
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
8486
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
8277
8487
|
className: "h-8 text-sm"
|
|
8278
8488
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
8279
8489
|
className: "text-xs text-gray-500 mt-1"
|
|
@@ -8366,6 +8576,7 @@ var init_BlogEditorPage = __esm({
|
|
|
8366
8576
|
init_JoditRichText();
|
|
8367
8577
|
init_CategoryAutocomplete();
|
|
8368
8578
|
init_UserAutocomplete();
|
|
8579
|
+
init_slug_sanitizer();
|
|
8369
8580
|
__name(BlogEditor, "BlogEditor");
|
|
8370
8581
|
}
|
|
8371
8582
|
});
|
|
@@ -25967,7 +26178,9 @@ function OrderDiscountPanel({ orderLines, currency, contactId, appliedCoupons, o
|
|
|
25967
26178
|
if (!res.ok) return;
|
|
25968
26179
|
const data = await res.json();
|
|
25969
26180
|
const rows = Array.isArray(data?.data) ? data.data : [];
|
|
25970
|
-
|
|
26181
|
+
const now = Date.now();
|
|
26182
|
+
const activeRows = rows.filter((d) => !d.validUntil || new Date(d.validUntil).getTime() > now);
|
|
26183
|
+
if (!cancelled) setDiscounts(activeRows);
|
|
25971
26184
|
} catch {
|
|
25972
26185
|
} finally {
|
|
25973
26186
|
if (!cancelled) setLoading(false);
|
|
@@ -26433,6 +26646,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26433
26646
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/orders";
|
|
26434
26647
|
const [customers, setCustomers] = React26.useState([]);
|
|
26435
26648
|
const [customersLoading, setCustomersLoading] = React26.useState(true);
|
|
26649
|
+
const [selectedRowId, setSelectedRowId] = React26.useState(null);
|
|
26436
26650
|
const [contactId, setContactId] = React26.useState(null);
|
|
26437
26651
|
const [selectedCustomerId, setSelectedCustomerId] = React26.useState(null);
|
|
26438
26652
|
const [contactName, setContactName] = React26.useState("");
|
|
@@ -26736,9 +26950,11 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26736
26950
|
const rows = (Array.isArray(data?.data) ? data.data : []).map((row) => {
|
|
26737
26951
|
const id = Number(row.id);
|
|
26738
26952
|
if (!Number.isFinite(id)) return null;
|
|
26739
|
-
const
|
|
26953
|
+
const rawContactId = Number(row.contactId ?? row.contact?.id);
|
|
26954
|
+
const rawCustomerId = Number(row.customerId ?? row.customer?.id);
|
|
26740
26955
|
return {
|
|
26741
26956
|
id,
|
|
26957
|
+
contactId: Number.isFinite(rawContactId) && rawContactId > 0 ? rawContactId : null,
|
|
26742
26958
|
customerId: Number.isFinite(rawCustomerId) && rawCustomerId > 0 ? rawCustomerId : null,
|
|
26743
26959
|
name: row.name ?? null,
|
|
26744
26960
|
email: row.email ?? null,
|
|
@@ -26756,6 +26972,15 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26756
26972
|
cancelled = true;
|
|
26757
26973
|
};
|
|
26758
26974
|
}, []);
|
|
26975
|
+
React26.useEffect(() => {
|
|
26976
|
+
if (contactId == null && !contactEmail) return;
|
|
26977
|
+
const match = customers.find((c) => contactId != null && c.contactId === contactId || contactEmail && c.email?.toLowerCase() === contactEmail.toLowerCase());
|
|
26978
|
+
if (match) setSelectedRowId(match.id);
|
|
26979
|
+
}, [
|
|
26980
|
+
contactId,
|
|
26981
|
+
contactEmail,
|
|
26982
|
+
customers
|
|
26983
|
+
]);
|
|
26759
26984
|
async function loadCustomerContacts(customerId) {
|
|
26760
26985
|
setCustomerContactsLoading(true);
|
|
26761
26986
|
setCustomerContacts([]);
|
|
@@ -26836,6 +27061,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26836
27061
|
__name(loadContactAddresses, "loadContactAddresses");
|
|
26837
27062
|
function handleCustomerSelect(value) {
|
|
26838
27063
|
if (value === MANUAL_CUSTOMER) {
|
|
27064
|
+
setSelectedRowId(null);
|
|
26839
27065
|
setContactId(null);
|
|
26840
27066
|
setSelectedCustomerId(null);
|
|
26841
27067
|
setCustomerContacts([]);
|
|
@@ -26843,6 +27069,9 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26843
27069
|
setSubContactName("");
|
|
26844
27070
|
setSubContactEmail("");
|
|
26845
27071
|
setSubContactPhone("");
|
|
27072
|
+
setContactName("");
|
|
27073
|
+
setContactEmail("");
|
|
27074
|
+
setContactPhone("");
|
|
26846
27075
|
setBillingAddress({
|
|
26847
27076
|
...emptyAddress
|
|
26848
27077
|
});
|
|
@@ -26851,11 +27080,12 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26851
27080
|
});
|
|
26852
27081
|
return;
|
|
26853
27082
|
}
|
|
26854
|
-
const
|
|
26855
|
-
if (!Number.isFinite(
|
|
26856
|
-
const customer = customers.find((c) => c.id ===
|
|
27083
|
+
const rowId = Number(value);
|
|
27084
|
+
if (!Number.isFinite(rowId)) return;
|
|
27085
|
+
const customer = customers.find((c) => c.id === rowId);
|
|
26857
27086
|
if (!customer) return;
|
|
26858
|
-
|
|
27087
|
+
setSelectedRowId(rowId);
|
|
27088
|
+
setContactId(customer.contactId ?? null);
|
|
26859
27089
|
setSelectedCustomerId(customer.customerId ?? null);
|
|
26860
27090
|
setContactName(customer.name ?? "");
|
|
26861
27091
|
setContactEmail(customer.email ?? "");
|
|
@@ -26866,7 +27096,9 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26866
27096
|
setSubContactPhone("");
|
|
26867
27097
|
setPlaceForSomeoneElse(false);
|
|
26868
27098
|
if (customer.customerId != null) loadCustomerContacts(customer.customerId);
|
|
26869
|
-
if (customer.
|
|
27099
|
+
if (customer.contactId != null) {
|
|
27100
|
+
loadContactAddresses(customer.contactId);
|
|
27101
|
+
} else if (customer.email) {
|
|
26870
27102
|
loadContactAddressesByEmail(customer.email);
|
|
26871
27103
|
} else {
|
|
26872
27104
|
setBillingAddress({
|
|
@@ -26952,14 +27184,55 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26952
27184
|
status: "available"
|
|
26953
27185
|
});
|
|
26954
27186
|
if (q.length >= 2) params.set("search", q);
|
|
26955
|
-
const res = await
|
|
27187
|
+
const [res, epRes, evRes] = await Promise.all([
|
|
27188
|
+
fetch(`/api/products?${params}`),
|
|
27189
|
+
fetch("/api/event_products?limit=500").catch(() => null),
|
|
27190
|
+
fetch("/api/events?limit=500").catch(() => null)
|
|
27191
|
+
]);
|
|
26956
27192
|
if (!res.ok) {
|
|
26957
27193
|
setProductHits([]);
|
|
26958
27194
|
return;
|
|
26959
27195
|
}
|
|
26960
27196
|
const body = await res.json();
|
|
26961
27197
|
const hits = Array.isArray(body.data) ? body.data : [];
|
|
26962
|
-
|
|
27198
|
+
const eventMap = /* @__PURE__ */ new Map();
|
|
27199
|
+
if (evRes && evRes.ok) {
|
|
27200
|
+
const evBody = await evRes.json().catch(() => ({}));
|
|
27201
|
+
const evList = Array.isArray(evBody.data) ? evBody.data : [];
|
|
27202
|
+
for (const ev of evList) {
|
|
27203
|
+
if (ev && ev.id) eventMap.set(Number(ev.id), ev);
|
|
27204
|
+
}
|
|
27205
|
+
}
|
|
27206
|
+
const productEventLimits = /* @__PURE__ */ new Map();
|
|
27207
|
+
if (epRes && epRes.ok) {
|
|
27208
|
+
const epBody = await epRes.json().catch(() => ({}));
|
|
27209
|
+
const epList = Array.isArray(epBody.data) ? epBody.data : [];
|
|
27210
|
+
for (const ep of epList) {
|
|
27211
|
+
const ev = eventMap.get(Number(ep.eventId));
|
|
27212
|
+
if (ev) {
|
|
27213
|
+
const maxGroup = typeof ev.maxGroupPurchaseQuantity === "number" && Number.isFinite(ev.maxGroupPurchaseQuantity) && ev.maxGroupPurchaseQuantity > 0 ? Math.floor(ev.maxGroupPurchaseQuantity) : null;
|
|
27214
|
+
const allowsGroup = ev.allowGroupOrders !== false;
|
|
27215
|
+
let cap;
|
|
27216
|
+
if (maxGroup !== null && maxGroup > 1) {
|
|
27217
|
+
cap = maxGroup;
|
|
27218
|
+
} else if (!allowsGroup) {
|
|
27219
|
+
cap = 1;
|
|
27220
|
+
} else if (maxGroup !== null) {
|
|
27221
|
+
cap = maxGroup;
|
|
27222
|
+
} else {
|
|
27223
|
+
cap = 999;
|
|
27224
|
+
}
|
|
27225
|
+
productEventLimits.set(Number(ep.productId), cap);
|
|
27226
|
+
}
|
|
27227
|
+
}
|
|
27228
|
+
}
|
|
27229
|
+
hits.forEach((p) => {
|
|
27230
|
+
const cap = productEventLimits.get(p.id);
|
|
27231
|
+
if (cap !== void 0) {
|
|
27232
|
+
p.maxPurchaseLimit = cap;
|
|
27233
|
+
}
|
|
27234
|
+
productCache.current.set(p.id, p);
|
|
27235
|
+
});
|
|
26963
27236
|
setProductHits(hits);
|
|
26964
27237
|
} catch {
|
|
26965
27238
|
setProductHits([]);
|
|
@@ -27236,6 +27509,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27236
27509
|
sonner.toast.message("Select one or more products in the list first");
|
|
27237
27510
|
return;
|
|
27238
27511
|
}
|
|
27512
|
+
let reachedLimit = false;
|
|
27239
27513
|
setLines((prev) => {
|
|
27240
27514
|
const next = [
|
|
27241
27515
|
...prev
|
|
@@ -27243,29 +27517,47 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27243
27517
|
for (const id of stagedIds) {
|
|
27244
27518
|
const hit = productCache.current.get(id);
|
|
27245
27519
|
if (!hit) continue;
|
|
27520
|
+
const maxAllowed = typeof hit.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27246
27521
|
const idx = next.findIndex((l) => l.productId === id);
|
|
27247
27522
|
if (idx >= 0) {
|
|
27248
27523
|
const row = next[idx];
|
|
27249
|
-
|
|
27250
|
-
|
|
27251
|
-
|
|
27252
|
-
|
|
27253
|
-
|
|
27254
|
-
|
|
27255
|
-
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27524
|
+
if (row.quantity >= maxAllowed) {
|
|
27525
|
+
reachedLimit = true;
|
|
27526
|
+
} else {
|
|
27527
|
+
next[idx] = {
|
|
27528
|
+
...row,
|
|
27529
|
+
quantity: Math.min(maxAllowed, row.quantity + 1)
|
|
27530
|
+
};
|
|
27531
|
+
}
|
|
27532
|
+
} else {
|
|
27533
|
+
next.push({
|
|
27534
|
+
key: String(id),
|
|
27535
|
+
productId: id,
|
|
27536
|
+
label: hit.name ?? `Product #${id}`,
|
|
27537
|
+
sku: hit.sku,
|
|
27538
|
+
quantity: 1
|
|
27539
|
+
});
|
|
27540
|
+
}
|
|
27260
27541
|
}
|
|
27261
27542
|
return next;
|
|
27262
27543
|
});
|
|
27263
27544
|
setStagedIds(/* @__PURE__ */ new Set());
|
|
27264
|
-
|
|
27545
|
+
if (reachedLimit) {
|
|
27546
|
+
sonner.toast.error("One or more items reached their maximum purchase limit");
|
|
27547
|
+
} else {
|
|
27548
|
+
sonner.toast.success("Added to cart");
|
|
27549
|
+
}
|
|
27265
27550
|
}
|
|
27266
27551
|
__name(addStagedToCart, "addStagedToCart");
|
|
27267
27552
|
function updateQty(key, quantity) {
|
|
27268
|
-
const
|
|
27553
|
+
const line = lines.find((l) => l.key === key);
|
|
27554
|
+
const hit = line ? productCache.current.get(line.productId) : null;
|
|
27555
|
+
const maxAllowed = typeof hit?.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27556
|
+
const parsed = Math.max(1, Math.floor(quantity) || 1);
|
|
27557
|
+
const q = Math.min(maxAllowed, parsed);
|
|
27558
|
+
if (parsed > maxAllowed) {
|
|
27559
|
+
sonner.toast.error(`Maximum purchase limit for "${line?.label ?? "this product"}" is ${maxAllowed}`);
|
|
27560
|
+
}
|
|
27269
27561
|
setLines((prev) => prev.map((l) => l.key === key ? {
|
|
27270
27562
|
...l,
|
|
27271
27563
|
quantity: q
|
|
@@ -27315,6 +27607,13 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27315
27607
|
sonner.toast.error("Add at least one product to the cart");
|
|
27316
27608
|
return;
|
|
27317
27609
|
}
|
|
27610
|
+
for (const line of lines) {
|
|
27611
|
+
const hit = productCache.current.get(line.productId);
|
|
27612
|
+
if (hit?.maxPurchaseLimit && line.quantity > hit.maxPurchaseLimit) {
|
|
27613
|
+
sonner.toast.error(`Quantity for "${line.label}" cannot exceed maximum allowed limit of ${hit.maxPurchaseLimit}`);
|
|
27614
|
+
return;
|
|
27615
|
+
}
|
|
27616
|
+
}
|
|
27318
27617
|
const unknown = preview?.lines.filter((l) => !l.found) ?? [];
|
|
27319
27618
|
if (unknown.length > 0) {
|
|
27320
27619
|
sonner.toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
|
|
@@ -27344,6 +27643,9 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27344
27643
|
const orderContactId = resolvedContactId;
|
|
27345
27644
|
const updatedMetadata = {
|
|
27346
27645
|
...orderMetadata,
|
|
27646
|
+
customerName: effectiveName || void 0,
|
|
27647
|
+
customerEmail: effectiveEmail || void 0,
|
|
27648
|
+
customerPhone: effectivePhone || void 0,
|
|
27347
27649
|
orderDate: orderDate || void 0,
|
|
27348
27650
|
bookingDate: bookingDate || void 0,
|
|
27349
27651
|
serviceDate: bookingDate || void 0,
|
|
@@ -27372,13 +27674,14 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27372
27674
|
})
|
|
27373
27675
|
};
|
|
27374
27676
|
if (orderContactId != null) payload.contactId = orderContactId;
|
|
27375
|
-
if (!placeForSomeoneElse &&
|
|
27677
|
+
if (!placeForSomeoneElse && selectedCustomerId != null) payload.customerId = selectedCustomerId;
|
|
27376
27678
|
if (placeForSomeoneElse && selectedCustomerId != null) {
|
|
27377
27679
|
payload.accountCustomerId = selectedCustomerId;
|
|
27378
27680
|
}
|
|
27379
27681
|
if (coupons.length > 0) payload.discountId = coupons[0].discountId;
|
|
27380
|
-
|
|
27381
|
-
|
|
27682
|
+
const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27683
|
+
if (!editOrderId && orderDate && orderDate !== todayIso) {
|
|
27684
|
+
payload.createdAt = `${orderDate}T${(/* @__PURE__ */ new Date()).toISOString().slice(11)}`;
|
|
27382
27685
|
}
|
|
27383
27686
|
const url = editOrderId ? `/api/orders/${editOrderId}` : "/api/orders";
|
|
27384
27687
|
const method = editOrderId ? "PUT" : "POST";
|
|
@@ -27465,7 +27768,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27465
27768
|
}
|
|
27466
27769
|
__name(handleSubmit, "handleSubmit");
|
|
27467
27770
|
const allVisibleStaged = productHits.length > 0 && productHits.every((p) => stagedIds.has(p.id));
|
|
27468
|
-
const customerSelectValue =
|
|
27771
|
+
const customerSelectValue = selectedRowId != null ? String(selectedRowId) : MANUAL_CUSTOMER;
|
|
27469
27772
|
const subContactSelectValue = subContactId != null ? String(subContactId) : MANUAL_CONTACT;
|
|
27470
27773
|
if (editOrderId && loadingOrder) {
|
|
27471
27774
|
return /* @__PURE__ */ React.createElement("div", {
|
|
@@ -27977,6 +28280,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27977
28280
|
}, /* @__PURE__ */ React.createElement(Input, {
|
|
27978
28281
|
type: "number",
|
|
27979
28282
|
min: 1,
|
|
28283
|
+
max: productCache.current.get(line.productId)?.maxPurchaseLimit ?? void 0,
|
|
27980
28284
|
className: "h-8 w-14 ml-auto text-right",
|
|
27981
28285
|
value: line.quantity,
|
|
27982
28286
|
onChange: /* @__PURE__ */ __name((e) => updateQty(line.key, Number(e.target.value)), "onChange"),
|
|
@@ -30577,9 +30881,10 @@ function SaveButton({ pageId, pageData, existingSeoId, onSeoIdChange, onSaved, c
|
|
|
30577
30881
|
}
|
|
30578
30882
|
}
|
|
30579
30883
|
}
|
|
30884
|
+
const normalizedSlug = normalizeSlug(pageData.slug);
|
|
30580
30885
|
const payload = {
|
|
30581
30886
|
title: pageData.title,
|
|
30582
|
-
slug:
|
|
30887
|
+
slug: normalizedSlug,
|
|
30583
30888
|
content,
|
|
30584
30889
|
published: pageData.published
|
|
30585
30890
|
};
|
|
@@ -30819,7 +31124,8 @@ function PageBuilderPage({ pageId }) {
|
|
|
30819
31124
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
30820
31125
|
}, "Slug *"), /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
30821
31126
|
value: slug,
|
|
30822
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
31127
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31128
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
30823
31129
|
placeholder: "page-url-slug",
|
|
30824
31130
|
className: "h-8 text-sm"
|
|
30825
31131
|
})))), /* @__PURE__ */ React26__namespace.default.createElement(RightSidebar, {
|
|
@@ -30856,6 +31162,7 @@ var init_PageBuilderPage = __esm({
|
|
|
30856
31162
|
init_admin_config_context();
|
|
30857
31163
|
init_registry();
|
|
30858
31164
|
init_ImageOrUrlField();
|
|
31165
|
+
init_slug_sanitizer();
|
|
30859
31166
|
__name(createSelectable, "createSelectable");
|
|
30860
31167
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
30861
31168
|
__name(getIcon, "getIcon");
|
|
@@ -31089,7 +31396,8 @@ function BrandEditPage({ brandId }) {
|
|
|
31089
31396
|
]);
|
|
31090
31397
|
return;
|
|
31091
31398
|
}
|
|
31092
|
-
|
|
31399
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
31400
|
+
if (!normalizedSlug) {
|
|
31093
31401
|
setErrors([
|
|
31094
31402
|
"Slug is required"
|
|
31095
31403
|
]);
|
|
@@ -31097,10 +31405,10 @@ function BrandEditPage({ brandId }) {
|
|
|
31097
31405
|
}
|
|
31098
31406
|
setSaving(true);
|
|
31099
31407
|
try {
|
|
31100
|
-
const savedSeoId = await saveSeo(seo,
|
|
31408
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
31101
31409
|
const payload = {
|
|
31102
31410
|
name: name.trim(),
|
|
31103
|
-
slug:
|
|
31411
|
+
slug: normalizedSlug,
|
|
31104
31412
|
description: description || null,
|
|
31105
31413
|
logo: logo || null,
|
|
31106
31414
|
active,
|
|
@@ -31190,7 +31498,9 @@ function BrandEditPage({ brandId }) {
|
|
|
31190
31498
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
31191
31499
|
type: "text",
|
|
31192
31500
|
value: slug,
|
|
31193
|
-
|
|
31501
|
+
placeholder: "brand-slug",
|
|
31502
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31503
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
31194
31504
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31195
31505
|
required: true
|
|
31196
31506
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -31250,6 +31560,7 @@ var init_BrandEditPage = __esm({
|
|
|
31250
31560
|
init_DetailPageHeader();
|
|
31251
31561
|
init_vendor_scope();
|
|
31252
31562
|
init_ImageOrUrlField();
|
|
31563
|
+
init_slug_sanitizer();
|
|
31253
31564
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31254
31565
|
__name(BrandEditPage, "BrandEditPage");
|
|
31255
31566
|
}
|
|
@@ -32714,11 +33025,21 @@ function ProductEditPage({ productId }) {
|
|
|
32714
33025
|
const pcData = await pcRes.json();
|
|
32715
33026
|
const pcs = Array.isArray(pcData.data) ? pcData.data : [];
|
|
32716
33027
|
if (pcs.length > 0) {
|
|
32717
|
-
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
|
|
33028
|
+
const validTaxPcs = pcs.filter((p) => p.taxId != null && Number(p.taxId) > 0);
|
|
33029
|
+
if (validTaxPcs.length > 0) {
|
|
33030
|
+
setTaxRows(validTaxPcs.map((p) => ({
|
|
33031
|
+
taxId: p.taxId,
|
|
33032
|
+
rate: p.rate != null && p.rate !== "" ? String(p.rate) : ""
|
|
33033
|
+
})));
|
|
33034
|
+
} else if (!cancelled) {
|
|
33035
|
+
setTaxRows([
|
|
33036
|
+
{
|
|
33037
|
+
taxId: "",
|
|
33038
|
+
rate: ""
|
|
33039
|
+
}
|
|
33040
|
+
]);
|
|
33041
|
+
}
|
|
33042
|
+
const firstRefund = pcs.find((p) => p.refundPolicyId != null)?.refundPolicyId;
|
|
32722
33043
|
setRefundPolicyId(firstRefund != null ? Number(firstRefund) : null);
|
|
32723
33044
|
} else if (!cancelled) {
|
|
32724
33045
|
setTaxRows([
|
|
@@ -32727,6 +33048,7 @@ function ProductEditPage({ productId }) {
|
|
|
32727
33048
|
rate: ""
|
|
32728
33049
|
}
|
|
32729
33050
|
]);
|
|
33051
|
+
setRefundPolicyId(null);
|
|
32730
33052
|
}
|
|
32731
33053
|
}
|
|
32732
33054
|
const paRes = await fetch(`/api/product_attributes?productId=${sourceProductId}&limit=100`);
|
|
@@ -33103,14 +33425,7 @@ function ProductEditPage({ productId }) {
|
|
|
33103
33425
|
if (a == null || b == null) return true;
|
|
33104
33426
|
return Math.abs(a - b) > 1e-6;
|
|
33105
33427
|
}, "ratesDiffer");
|
|
33106
|
-
const
|
|
33107
|
-
for (const row of taxRows) {
|
|
33108
|
-
if (row.taxId === "") continue;
|
|
33109
|
-
wantedConfig.set(row.taxId, {
|
|
33110
|
-
rate: parseTaxRate(row.rate),
|
|
33111
|
-
refundPolicyId
|
|
33112
|
-
});
|
|
33113
|
-
}
|
|
33428
|
+
const validTaxRows = taxRows.filter((row) => row.taxId !== "");
|
|
33114
33429
|
console.log("[product_config rows to save]:", taxRows, "refundPolicyId:", refundPolicyId);
|
|
33115
33430
|
const pcListRes = await fetch(`/api/product_config?productId=${savedId}&limit=200`);
|
|
33116
33431
|
const pcListData = pcListRes.ok ? await pcListRes.json() : {
|
|
@@ -33118,17 +33433,60 @@ function ProductEditPage({ productId }) {
|
|
|
33118
33433
|
};
|
|
33119
33434
|
console.log("[product existing pc rows]:", pcListData);
|
|
33120
33435
|
const existingPc = Array.isArray(pcListData.data) ? pcListData.data : [];
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33436
|
+
if (validTaxRows.length > 0) {
|
|
33437
|
+
const wantedTaxIds = new Set(validTaxRows.map((r) => Number(r.taxId)));
|
|
33438
|
+
for (const ep of existingPc) {
|
|
33439
|
+
if (ep.taxId == null || !wantedTaxIds.has(Number(ep.taxId))) {
|
|
33440
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33441
|
+
method: "DELETE"
|
|
33442
|
+
});
|
|
33443
|
+
}
|
|
33126
33444
|
}
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33445
|
+
for (const row of validTaxRows) {
|
|
33446
|
+
const taxId = Number(row.taxId);
|
|
33447
|
+
const rate = parseTaxRate(row.rate);
|
|
33448
|
+
const ep = existingPc.find((e) => e.taxId != null && Number(e.taxId) === taxId);
|
|
33449
|
+
if (!ep) {
|
|
33450
|
+
await fetch("/api/product_config", {
|
|
33451
|
+
method: "POST",
|
|
33452
|
+
headers: {
|
|
33453
|
+
"Content-Type": "application/json"
|
|
33454
|
+
},
|
|
33455
|
+
body: JSON.stringify({
|
|
33456
|
+
productId: Number(savedId),
|
|
33457
|
+
taxId,
|
|
33458
|
+
rate,
|
|
33459
|
+
refundPolicyId
|
|
33460
|
+
})
|
|
33461
|
+
});
|
|
33462
|
+
} else {
|
|
33463
|
+
const existingRate = ep.rate == null || String(ep.rate).trim() === "" ? null : Number(ep.rate);
|
|
33464
|
+
const er = Number.isFinite(existingRate) ? existingRate : null;
|
|
33465
|
+
const policyChanged = ep.refundPolicyId !== refundPolicyId;
|
|
33466
|
+
if (ratesDiffer(er, rate) || policyChanged) {
|
|
33467
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33468
|
+
method: "PUT",
|
|
33469
|
+
headers: {
|
|
33470
|
+
"Content-Type": "application/json"
|
|
33471
|
+
},
|
|
33472
|
+
body: JSON.stringify({
|
|
33473
|
+
rate,
|
|
33474
|
+
refundPolicyId
|
|
33475
|
+
})
|
|
33476
|
+
});
|
|
33477
|
+
}
|
|
33478
|
+
}
|
|
33479
|
+
}
|
|
33480
|
+
} else if (refundPolicyId != null) {
|
|
33481
|
+
const nullTaxRow = existingPc.find((e) => e.taxId == null);
|
|
33482
|
+
for (const ep of existingPc) {
|
|
33483
|
+
if (ep.id !== nullTaxRow?.id) {
|
|
33484
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33485
|
+
method: "DELETE"
|
|
33486
|
+
});
|
|
33487
|
+
}
|
|
33488
|
+
}
|
|
33489
|
+
if (!nullTaxRow) {
|
|
33132
33490
|
await fetch("/api/product_config", {
|
|
33133
33491
|
method: "POST",
|
|
33134
33492
|
headers: {
|
|
@@ -33136,27 +33494,27 @@ function ProductEditPage({ productId }) {
|
|
|
33136
33494
|
},
|
|
33137
33495
|
body: JSON.stringify({
|
|
33138
33496
|
productId: Number(savedId),
|
|
33139
|
-
taxId,
|
|
33140
|
-
rate:
|
|
33141
|
-
refundPolicyId
|
|
33497
|
+
taxId: null,
|
|
33498
|
+
rate: null,
|
|
33499
|
+
refundPolicyId
|
|
33500
|
+
})
|
|
33501
|
+
});
|
|
33502
|
+
} else if (nullTaxRow.refundPolicyId !== refundPolicyId) {
|
|
33503
|
+
await fetch(`/api/product_config/${nullTaxRow.id}`, {
|
|
33504
|
+
method: "PUT",
|
|
33505
|
+
headers: {
|
|
33506
|
+
"Content-Type": "application/json"
|
|
33507
|
+
},
|
|
33508
|
+
body: JSON.stringify({
|
|
33509
|
+
refundPolicyId
|
|
33142
33510
|
})
|
|
33143
33511
|
});
|
|
33144
|
-
}
|
|
33145
|
-
|
|
33146
|
-
|
|
33147
|
-
|
|
33148
|
-
|
|
33149
|
-
|
|
33150
|
-
method: "PUT",
|
|
33151
|
-
headers: {
|
|
33152
|
-
"Content-Type": "application/json"
|
|
33153
|
-
},
|
|
33154
|
-
body: JSON.stringify({
|
|
33155
|
-
rate: cfg.rate,
|
|
33156
|
-
refundPolicyId: cfg.refundPolicyId
|
|
33157
|
-
})
|
|
33158
|
-
});
|
|
33159
|
-
}
|
|
33512
|
+
}
|
|
33513
|
+
} else {
|
|
33514
|
+
for (const ep of existingPc) {
|
|
33515
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33516
|
+
method: "DELETE"
|
|
33517
|
+
});
|
|
33160
33518
|
}
|
|
33161
33519
|
}
|
|
33162
33520
|
if (hasVariants) {
|
|
@@ -34072,7 +34430,8 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34072
34430
|
]);
|
|
34073
34431
|
return;
|
|
34074
34432
|
}
|
|
34075
|
-
|
|
34433
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
34434
|
+
if (!normalizedSlug) {
|
|
34076
34435
|
setErrors([
|
|
34077
34436
|
"Slug is required"
|
|
34078
34437
|
]);
|
|
@@ -34080,10 +34439,10 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34080
34439
|
}
|
|
34081
34440
|
setSaving(true);
|
|
34082
34441
|
try {
|
|
34083
|
-
const savedSeoId = await saveSeo(seo,
|
|
34442
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
34084
34443
|
const payload = {
|
|
34085
34444
|
name: name.trim(),
|
|
34086
|
-
slug:
|
|
34445
|
+
slug: normalizedSlug,
|
|
34087
34446
|
hsn: hsn.trim() || null,
|
|
34088
34447
|
categoryId: categoryId || null,
|
|
34089
34448
|
brandId: brandId || null,
|
|
@@ -34252,7 +34611,9 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34252
34611
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
34253
34612
|
type: "text",
|
|
34254
34613
|
value: slug,
|
|
34255
|
-
|
|
34614
|
+
placeholder: "collection-slug",
|
|
34615
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
34616
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
34256
34617
|
className: inputCls4,
|
|
34257
34618
|
required: true
|
|
34258
34619
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -34488,6 +34849,7 @@ var init_CollectionEditPage = __esm({
|
|
|
34488
34849
|
init_admin_config_context();
|
|
34489
34850
|
init_category_related_product_labels();
|
|
34490
34851
|
init_ImageOrUrlField();
|
|
34852
|
+
init_slug_sanitizer();
|
|
34491
34853
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
34492
34854
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
34493
34855
|
url: "",
|
|
@@ -35667,7 +36029,9 @@ var init_event_entity_types = __esm({
|
|
|
35667
36029
|
// src/admin/pages/EventEditPage.tsx
|
|
35668
36030
|
var EventEditPage_exports = {};
|
|
35669
36031
|
__export(EventEditPage_exports, {
|
|
35670
|
-
default: () => EventEditPage
|
|
36032
|
+
default: () => EventEditPage,
|
|
36033
|
+
normalizeSlug: () => normalizeSlug2,
|
|
36034
|
+
sanitizeSlugInput: () => sanitizeSlugInput2
|
|
35671
36035
|
});
|
|
35672
36036
|
function RequiredLabel({ children }) {
|
|
35673
36037
|
return /* @__PURE__ */ React.createElement("label", {
|
|
@@ -35676,6 +36040,12 @@ function RequiredLabel({ children }) {
|
|
|
35676
36040
|
className: "text-red-600"
|
|
35677
36041
|
}, "*"));
|
|
35678
36042
|
}
|
|
36043
|
+
function sanitizeSlugInput2(value) {
|
|
36044
|
+
return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-");
|
|
36045
|
+
}
|
|
36046
|
+
function normalizeSlug2(value) {
|
|
36047
|
+
return sanitizeSlugInput2(value).replace(/^-+|-+$/g, "");
|
|
36048
|
+
}
|
|
35679
36049
|
function toIsoOrNull(value, timezone) {
|
|
35680
36050
|
const trimmed = value.trim();
|
|
35681
36051
|
if (!trimmed) return null;
|
|
@@ -35766,7 +36136,7 @@ function EventEditPage({ eventId }) {
|
|
|
35766
36136
|
const [supportContact, setSupportContact] = React26.useState("");
|
|
35767
36137
|
const [allowGroupOrders, setAllowGroupOrders] = React26.useState(false);
|
|
35768
36138
|
const [sendIndividualTicketPDFToAttendees, setSendIndividualTicketPDFToAttendees] = React26.useState(false);
|
|
35769
|
-
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = React26.useState("");
|
|
36139
|
+
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = React26.useState("1");
|
|
35770
36140
|
const [sponsors, setSponsors] = React26.useState([]);
|
|
35771
36141
|
const [agenda, setAgenda] = React26.useState([]);
|
|
35772
36142
|
const [workshops, setWorkshops] = React26.useState([]);
|
|
@@ -35892,9 +36262,10 @@ function EventEditPage({ eventId }) {
|
|
|
35892
36262
|
}
|
|
35893
36263
|
setOfficialWebsiteUrl(data.officialWebsiteUrl ?? "");
|
|
35894
36264
|
setSupportContact(data.supportContact ?? "");
|
|
35895
|
-
|
|
36265
|
+
const loadedAllowGroup = data.allowGroupOrders ?? false;
|
|
36266
|
+
setAllowGroupOrders(loadedAllowGroup);
|
|
35896
36267
|
setSendIndividualTicketPDFToAttendees(data.sendIndividualTicketPDFToAttendees ?? false);
|
|
35897
|
-
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : "");
|
|
36268
|
+
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : loadedAllowGroup ? "10" : "1");
|
|
35898
36269
|
setSponsors(parseNamedListFromApi(data.sponsors, "sponsor"));
|
|
35899
36270
|
setAgenda(parseNamedListFromApi(data.agenda, "agenda"));
|
|
35900
36271
|
setWorkshops(parseNamedListFromApi(data.workshops, "workshop"));
|
|
@@ -35940,8 +36311,13 @@ function EventEditPage({ eventId }) {
|
|
|
35940
36311
|
]);
|
|
35941
36312
|
const buildPayload = /* @__PURE__ */ __name(() => {
|
|
35942
36313
|
const nextErrors = [];
|
|
36314
|
+
const normalizedSlug = normalizeSlug2(slug);
|
|
35943
36315
|
if (!name.trim()) nextErrors.push("Name is required");
|
|
35944
|
-
if (!
|
|
36316
|
+
if (!normalizedSlug) {
|
|
36317
|
+
nextErrors.push("Slug is required");
|
|
36318
|
+
} else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(normalizedSlug)) {
|
|
36319
|
+
nextErrors.push("Slug must contain only lowercase letters, numbers, and hyphens without leading or trailing hyphens or slashes");
|
|
36320
|
+
}
|
|
35945
36321
|
if (!startDate.trim()) nextErrors.push("Event Start Date & Time is required");
|
|
35946
36322
|
if (!endDate.trim()) nextErrors.push("Event End Date & Time is required");
|
|
35947
36323
|
if (startDate.trim() && !timezone.trim()) {
|
|
@@ -35967,6 +36343,20 @@ function EventEditPage({ eventId }) {
|
|
|
35967
36343
|
nextErrors.push("Event End Date & Time must be after Event Start Date & Time");
|
|
35968
36344
|
}
|
|
35969
36345
|
}
|
|
36346
|
+
if (registrationOpensAt.trim() && registrationClosesAt.trim()) {
|
|
36347
|
+
const regOpen = new Date(toIsoOrNull(registrationOpensAt, timezone) ?? "");
|
|
36348
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36349
|
+
if (!Number.isNaN(regOpen.getTime()) && !Number.isNaN(regClose.getTime()) && regClose < regOpen) {
|
|
36350
|
+
nextErrors.push("Registration Closes Date & Time must be after Registration Opens Date & Time");
|
|
36351
|
+
}
|
|
36352
|
+
}
|
|
36353
|
+
if (registrationClosesAt.trim() && endDate.trim()) {
|
|
36354
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36355
|
+
const end = new Date(toIsoOrNull(endDate, timezone) ?? "");
|
|
36356
|
+
if (!Number.isNaN(regClose.getTime()) && !Number.isNaN(end.getTime()) && regClose > end) {
|
|
36357
|
+
nextErrors.push("Registration cannot close after Event End Date & Time");
|
|
36358
|
+
}
|
|
36359
|
+
}
|
|
35970
36360
|
if (additionalVenueDetails.length > ADDITIONAL_VENUE_MAX_CHARS) {
|
|
35971
36361
|
nextErrors.push(`Additional Venue Details must be ${ADDITIONAL_VENUE_MAX_CHARS} characters or fewer`);
|
|
35972
36362
|
}
|
|
@@ -35976,7 +36366,7 @@ function EventEditPage({ eventId }) {
|
|
|
35976
36366
|
}
|
|
35977
36367
|
const payload = {
|
|
35978
36368
|
name: name.trim(),
|
|
35979
|
-
slug:
|
|
36369
|
+
slug: normalizedSlug,
|
|
35980
36370
|
description: description.trim() || null,
|
|
35981
36371
|
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
35982
36372
|
comingSoon,
|
|
@@ -36004,7 +36394,7 @@ function EventEditPage({ eventId }) {
|
|
|
36004
36394
|
supportContact: supportContact.trim() || null,
|
|
36005
36395
|
allowGroupOrders,
|
|
36006
36396
|
sendIndividualTicketPDFToAttendees,
|
|
36007
|
-
maxGroupPurchaseQuantity: maxGroupPurchaseQuantity
|
|
36397
|
+
maxGroupPurchaseQuantity: allowGroupOrders ? Number(maxGroupPurchaseQuantity) > 0 ? Number(maxGroupPurchaseQuantity) : 10 : 1,
|
|
36008
36398
|
sponsors: namedListToPayload(sponsors, {
|
|
36009
36399
|
legacySponsorKeys: true
|
|
36010
36400
|
}),
|
|
@@ -36272,9 +36662,16 @@ function EventEditPage({ eventId }) {
|
|
|
36272
36662
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36273
36663
|
className: labelCls6
|
|
36274
36664
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
36665
|
+
id: "event-slug",
|
|
36275
36666
|
type: "text",
|
|
36276
36667
|
value: slug,
|
|
36277
|
-
|
|
36668
|
+
placeholder: "event-slug",
|
|
36669
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36670
|
+
setSlug(sanitizeSlugInput2(e.target.value));
|
|
36671
|
+
}, "onChange"),
|
|
36672
|
+
onBlur: /* @__PURE__ */ __name(() => {
|
|
36673
|
+
setSlug((prev) => normalizeSlug2(prev));
|
|
36674
|
+
}, "onBlur"),
|
|
36278
36675
|
className: inputCls6
|
|
36279
36676
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36280
36677
|
className: labelCls6
|
|
@@ -36534,7 +36931,16 @@ function EventEditPage({ eventId }) {
|
|
|
36534
36931
|
}, "Max group purchase quantity"), /* @__PURE__ */ React.createElement("input", {
|
|
36535
36932
|
type: "number",
|
|
36536
36933
|
value: maxGroupPurchaseQuantity,
|
|
36537
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36934
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36935
|
+
const val = e.target.value;
|
|
36936
|
+
setMaxGroupPurchaseQuantity(val);
|
|
36937
|
+
const num = Number(val);
|
|
36938
|
+
if (Number.isFinite(num) && num > 1) {
|
|
36939
|
+
setAllowGroupOrders(true);
|
|
36940
|
+
} else if (Number.isFinite(num) && num <= 1) {
|
|
36941
|
+
setAllowGroupOrders(false);
|
|
36942
|
+
}
|
|
36943
|
+
}, "onChange"),
|
|
36538
36944
|
className: inputCls6,
|
|
36539
36945
|
min: 1
|
|
36540
36946
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -36544,7 +36950,18 @@ function EventEditPage({ eventId }) {
|
|
|
36544
36950
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
36545
36951
|
type: "checkbox",
|
|
36546
36952
|
checked: allowGroupOrders,
|
|
36547
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36953
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36954
|
+
const checked = e.target.checked;
|
|
36955
|
+
setAllowGroupOrders(checked);
|
|
36956
|
+
if (!checked) {
|
|
36957
|
+
setMaxGroupPurchaseQuantity("1");
|
|
36958
|
+
} else {
|
|
36959
|
+
const current = Number(maxGroupPurchaseQuantity);
|
|
36960
|
+
if (!Number.isFinite(current) || current <= 1) {
|
|
36961
|
+
setMaxGroupPurchaseQuantity("10");
|
|
36962
|
+
}
|
|
36963
|
+
}
|
|
36964
|
+
}, "onChange"),
|
|
36548
36965
|
className: "h-4 w-4 rounded border-gray-300"
|
|
36549
36966
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
36550
36967
|
className: "text-sm text-gray-900"
|
|
@@ -36618,6 +37035,8 @@ var init_EventEditPage = __esm({
|
|
|
36618
37035
|
labelCls6 = "block text-xs font-medium text-gray-600 mb-1";
|
|
36619
37036
|
inputCls6 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
36620
37037
|
__name(RequiredLabel, "RequiredLabel");
|
|
37038
|
+
__name(sanitizeSlugInput2, "sanitizeSlugInput");
|
|
37039
|
+
__name(normalizeSlug2, "normalizeSlug");
|
|
36621
37040
|
ENTITY_TYPE_OPTIONS = [
|
|
36622
37041
|
...EVENT_ENTITY_TYPE_OPTIONS
|
|
36623
37042
|
];
|
|
@@ -36996,7 +37415,7 @@ function ComboEditPage({ comboId }) {
|
|
|
36996
37415
|
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
36997
37416
|
const payload = {
|
|
36998
37417
|
name: trimmedName,
|
|
36999
|
-
slug: slug.trim()
|
|
37418
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37000
37419
|
desc: desc || null,
|
|
37001
37420
|
eventId: resolvedEventId,
|
|
37002
37421
|
price,
|
|
@@ -37091,8 +37510,9 @@ function ComboEditPage({ comboId }) {
|
|
|
37091
37510
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
37092
37511
|
type: "text",
|
|
37093
37512
|
value: slug,
|
|
37094
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37095
|
-
|
|
37513
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
37514
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
37515
|
+
placeholder: "combo-slug",
|
|
37096
37516
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
37097
37517
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
37098
37518
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -37258,6 +37678,7 @@ var init_ComboEditPage = __esm({
|
|
|
37258
37678
|
init_DetailPageHeader();
|
|
37259
37679
|
init_inventory_validation();
|
|
37260
37680
|
init_admin_config_context();
|
|
37681
|
+
init_slug_sanitizer();
|
|
37261
37682
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
37262
37683
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
37263
37684
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -37489,7 +37910,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37489
37910
|
const vendorPayload = /* @__PURE__ */ __name(() => ({
|
|
37490
37911
|
name: name.trim(),
|
|
37491
37912
|
legalName: legalName.trim() || null,
|
|
37492
|
-
slug: slug.trim()
|
|
37913
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37493
37914
|
businessType: businessType || null,
|
|
37494
37915
|
description: description.trim() || null,
|
|
37495
37916
|
website: website.trim() || null,
|
|
@@ -37831,7 +38252,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37831
38252
|
signal: abortController.signal,
|
|
37832
38253
|
body: JSON.stringify({
|
|
37833
38254
|
...vendorPayload(),
|
|
37834
|
-
slug: slug.trim(),
|
|
38255
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37835
38256
|
metadata: (() => {
|
|
37836
38257
|
const next = {
|
|
37837
38258
|
...metadata ?? {}
|
|
@@ -37954,8 +38375,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
37954
38375
|
}, "Slug ", create ? "(optional)" : "*"), /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
37955
38376
|
id: "vendorSlug",
|
|
37956
38377
|
value: slug,
|
|
37957
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37958
|
-
|
|
38378
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
38379
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
38380
|
+
placeholder: "vendor-slug",
|
|
37959
38381
|
className: `mt-1 ${fieldClass}`
|
|
37960
38382
|
})), /* @__PURE__ */ React26__namespace.default.createElement("div", null, /* @__PURE__ */ React26__namespace.default.createElement(FieldLabel, {
|
|
37961
38383
|
htmlFor: "businessType"
|
|
@@ -38296,6 +38718,7 @@ var init_VendorEditPage = __esm({
|
|
|
38296
38718
|
init_checkbox();
|
|
38297
38719
|
init_dialog();
|
|
38298
38720
|
init_vendor_profile();
|
|
38721
|
+
init_slug_sanitizer();
|
|
38299
38722
|
init_vendor_list_config();
|
|
38300
38723
|
init_vendor_access_denied();
|
|
38301
38724
|
init_vendor_access_denied();
|