@infuro/cms-core 1.0.72 → 1.0.73
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 +507 -109
- package/dist/admin.js +509 -111
- package/dist/api.cjs +33 -33
- package/dist/api.d.cts +13 -6
- package/dist/api.d.ts +13 -6
- package/dist/api.js +1 -1
- 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-43UVXBEH.js → chunk-4LF5OGBF.js} +2459 -2039
- 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-HQ3A43HO.cjs} +2458 -2038
- package/dist/index.cjs +217 -217
- package/dist/index.d.cts +31 -9
- package/dist/index.d.ts +31 -9
- package/dist/index.js +3 -3
- package/dist/migrations/1783400000000-MakeProductConfigTaxIdNullable.ts +40 -0
- package/package.json +1 -1
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.73" ;
|
|
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
|
});
|
|
@@ -26952,14 +27163,55 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26952
27163
|
status: "available"
|
|
26953
27164
|
});
|
|
26954
27165
|
if (q.length >= 2) params.set("search", q);
|
|
26955
|
-
const res = await
|
|
27166
|
+
const [res, epRes, evRes] = await Promise.all([
|
|
27167
|
+
fetch(`/api/products?${params}`),
|
|
27168
|
+
fetch("/api/event_products?limit=500").catch(() => null),
|
|
27169
|
+
fetch("/api/events?limit=500").catch(() => null)
|
|
27170
|
+
]);
|
|
26956
27171
|
if (!res.ok) {
|
|
26957
27172
|
setProductHits([]);
|
|
26958
27173
|
return;
|
|
26959
27174
|
}
|
|
26960
27175
|
const body = await res.json();
|
|
26961
27176
|
const hits = Array.isArray(body.data) ? body.data : [];
|
|
26962
|
-
|
|
27177
|
+
const eventMap = /* @__PURE__ */ new Map();
|
|
27178
|
+
if (evRes && evRes.ok) {
|
|
27179
|
+
const evBody = await evRes.json().catch(() => ({}));
|
|
27180
|
+
const evList = Array.isArray(evBody.data) ? evBody.data : [];
|
|
27181
|
+
for (const ev of evList) {
|
|
27182
|
+
if (ev && ev.id) eventMap.set(Number(ev.id), ev);
|
|
27183
|
+
}
|
|
27184
|
+
}
|
|
27185
|
+
const productEventLimits = /* @__PURE__ */ new Map();
|
|
27186
|
+
if (epRes && epRes.ok) {
|
|
27187
|
+
const epBody = await epRes.json().catch(() => ({}));
|
|
27188
|
+
const epList = Array.isArray(epBody.data) ? epBody.data : [];
|
|
27189
|
+
for (const ep of epList) {
|
|
27190
|
+
const ev = eventMap.get(Number(ep.eventId));
|
|
27191
|
+
if (ev) {
|
|
27192
|
+
const maxGroup = typeof ev.maxGroupPurchaseQuantity === "number" && Number.isFinite(ev.maxGroupPurchaseQuantity) && ev.maxGroupPurchaseQuantity > 0 ? Math.floor(ev.maxGroupPurchaseQuantity) : null;
|
|
27193
|
+
const allowsGroup = ev.allowGroupOrders !== false;
|
|
27194
|
+
let cap;
|
|
27195
|
+
if (maxGroup !== null && maxGroup > 1) {
|
|
27196
|
+
cap = maxGroup;
|
|
27197
|
+
} else if (!allowsGroup) {
|
|
27198
|
+
cap = 1;
|
|
27199
|
+
} else if (maxGroup !== null) {
|
|
27200
|
+
cap = maxGroup;
|
|
27201
|
+
} else {
|
|
27202
|
+
cap = 999;
|
|
27203
|
+
}
|
|
27204
|
+
productEventLimits.set(Number(ep.productId), cap);
|
|
27205
|
+
}
|
|
27206
|
+
}
|
|
27207
|
+
}
|
|
27208
|
+
hits.forEach((p) => {
|
|
27209
|
+
const cap = productEventLimits.get(p.id);
|
|
27210
|
+
if (cap !== void 0) {
|
|
27211
|
+
p.maxPurchaseLimit = cap;
|
|
27212
|
+
}
|
|
27213
|
+
productCache.current.set(p.id, p);
|
|
27214
|
+
});
|
|
26963
27215
|
setProductHits(hits);
|
|
26964
27216
|
} catch {
|
|
26965
27217
|
setProductHits([]);
|
|
@@ -27236,6 +27488,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27236
27488
|
sonner.toast.message("Select one or more products in the list first");
|
|
27237
27489
|
return;
|
|
27238
27490
|
}
|
|
27491
|
+
let reachedLimit = false;
|
|
27239
27492
|
setLines((prev) => {
|
|
27240
27493
|
const next = [
|
|
27241
27494
|
...prev
|
|
@@ -27243,29 +27496,47 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27243
27496
|
for (const id of stagedIds) {
|
|
27244
27497
|
const hit = productCache.current.get(id);
|
|
27245
27498
|
if (!hit) continue;
|
|
27499
|
+
const maxAllowed = typeof hit.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27246
27500
|
const idx = next.findIndex((l) => l.productId === id);
|
|
27247
27501
|
if (idx >= 0) {
|
|
27248
27502
|
const row = next[idx];
|
|
27249
|
-
|
|
27250
|
-
|
|
27251
|
-
|
|
27252
|
-
|
|
27253
|
-
|
|
27254
|
-
|
|
27255
|
-
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27503
|
+
if (row.quantity >= maxAllowed) {
|
|
27504
|
+
reachedLimit = true;
|
|
27505
|
+
} else {
|
|
27506
|
+
next[idx] = {
|
|
27507
|
+
...row,
|
|
27508
|
+
quantity: Math.min(maxAllowed, row.quantity + 1)
|
|
27509
|
+
};
|
|
27510
|
+
}
|
|
27511
|
+
} else {
|
|
27512
|
+
next.push({
|
|
27513
|
+
key: String(id),
|
|
27514
|
+
productId: id,
|
|
27515
|
+
label: hit.name ?? `Product #${id}`,
|
|
27516
|
+
sku: hit.sku,
|
|
27517
|
+
quantity: 1
|
|
27518
|
+
});
|
|
27519
|
+
}
|
|
27260
27520
|
}
|
|
27261
27521
|
return next;
|
|
27262
27522
|
});
|
|
27263
27523
|
setStagedIds(/* @__PURE__ */ new Set());
|
|
27264
|
-
|
|
27524
|
+
if (reachedLimit) {
|
|
27525
|
+
sonner.toast.error("One or more items reached their maximum purchase limit");
|
|
27526
|
+
} else {
|
|
27527
|
+
sonner.toast.success("Added to cart");
|
|
27528
|
+
}
|
|
27265
27529
|
}
|
|
27266
27530
|
__name(addStagedToCart, "addStagedToCart");
|
|
27267
27531
|
function updateQty(key, quantity) {
|
|
27268
|
-
const
|
|
27532
|
+
const line = lines.find((l) => l.key === key);
|
|
27533
|
+
const hit = line ? productCache.current.get(line.productId) : null;
|
|
27534
|
+
const maxAllowed = typeof hit?.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27535
|
+
const parsed = Math.max(1, Math.floor(quantity) || 1);
|
|
27536
|
+
const q = Math.min(maxAllowed, parsed);
|
|
27537
|
+
if (parsed > maxAllowed) {
|
|
27538
|
+
sonner.toast.error(`Maximum purchase limit for "${line?.label ?? "this product"}" is ${maxAllowed}`);
|
|
27539
|
+
}
|
|
27269
27540
|
setLines((prev) => prev.map((l) => l.key === key ? {
|
|
27270
27541
|
...l,
|
|
27271
27542
|
quantity: q
|
|
@@ -27315,6 +27586,13 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27315
27586
|
sonner.toast.error("Add at least one product to the cart");
|
|
27316
27587
|
return;
|
|
27317
27588
|
}
|
|
27589
|
+
for (const line of lines) {
|
|
27590
|
+
const hit = productCache.current.get(line.productId);
|
|
27591
|
+
if (hit?.maxPurchaseLimit && line.quantity > hit.maxPurchaseLimit) {
|
|
27592
|
+
sonner.toast.error(`Quantity for "${line.label}" cannot exceed maximum allowed limit of ${hit.maxPurchaseLimit}`);
|
|
27593
|
+
return;
|
|
27594
|
+
}
|
|
27595
|
+
}
|
|
27318
27596
|
const unknown = preview?.lines.filter((l) => !l.found) ?? [];
|
|
27319
27597
|
if (unknown.length > 0) {
|
|
27320
27598
|
sonner.toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
|
|
@@ -27977,6 +28255,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27977
28255
|
}, /* @__PURE__ */ React.createElement(Input, {
|
|
27978
28256
|
type: "number",
|
|
27979
28257
|
min: 1,
|
|
28258
|
+
max: productCache.current.get(line.productId)?.maxPurchaseLimit ?? void 0,
|
|
27980
28259
|
className: "h-8 w-14 ml-auto text-right",
|
|
27981
28260
|
value: line.quantity,
|
|
27982
28261
|
onChange: /* @__PURE__ */ __name((e) => updateQty(line.key, Number(e.target.value)), "onChange"),
|
|
@@ -30577,9 +30856,10 @@ function SaveButton({ pageId, pageData, existingSeoId, onSeoIdChange, onSaved, c
|
|
|
30577
30856
|
}
|
|
30578
30857
|
}
|
|
30579
30858
|
}
|
|
30859
|
+
const normalizedSlug = normalizeSlug(pageData.slug);
|
|
30580
30860
|
const payload = {
|
|
30581
30861
|
title: pageData.title,
|
|
30582
|
-
slug:
|
|
30862
|
+
slug: normalizedSlug,
|
|
30583
30863
|
content,
|
|
30584
30864
|
published: pageData.published
|
|
30585
30865
|
};
|
|
@@ -30819,7 +31099,8 @@ function PageBuilderPage({ pageId }) {
|
|
|
30819
31099
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
30820
31100
|
}, "Slug *"), /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
30821
31101
|
value: slug,
|
|
30822
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
31102
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31103
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
30823
31104
|
placeholder: "page-url-slug",
|
|
30824
31105
|
className: "h-8 text-sm"
|
|
30825
31106
|
})))), /* @__PURE__ */ React26__namespace.default.createElement(RightSidebar, {
|
|
@@ -30856,6 +31137,7 @@ var init_PageBuilderPage = __esm({
|
|
|
30856
31137
|
init_admin_config_context();
|
|
30857
31138
|
init_registry();
|
|
30858
31139
|
init_ImageOrUrlField();
|
|
31140
|
+
init_slug_sanitizer();
|
|
30859
31141
|
__name(createSelectable, "createSelectable");
|
|
30860
31142
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
30861
31143
|
__name(getIcon, "getIcon");
|
|
@@ -31089,7 +31371,8 @@ function BrandEditPage({ brandId }) {
|
|
|
31089
31371
|
]);
|
|
31090
31372
|
return;
|
|
31091
31373
|
}
|
|
31092
|
-
|
|
31374
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
31375
|
+
if (!normalizedSlug) {
|
|
31093
31376
|
setErrors([
|
|
31094
31377
|
"Slug is required"
|
|
31095
31378
|
]);
|
|
@@ -31097,10 +31380,10 @@ function BrandEditPage({ brandId }) {
|
|
|
31097
31380
|
}
|
|
31098
31381
|
setSaving(true);
|
|
31099
31382
|
try {
|
|
31100
|
-
const savedSeoId = await saveSeo(seo,
|
|
31383
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
31101
31384
|
const payload = {
|
|
31102
31385
|
name: name.trim(),
|
|
31103
|
-
slug:
|
|
31386
|
+
slug: normalizedSlug,
|
|
31104
31387
|
description: description || null,
|
|
31105
31388
|
logo: logo || null,
|
|
31106
31389
|
active,
|
|
@@ -31190,7 +31473,9 @@ function BrandEditPage({ brandId }) {
|
|
|
31190
31473
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
31191
31474
|
type: "text",
|
|
31192
31475
|
value: slug,
|
|
31193
|
-
|
|
31476
|
+
placeholder: "brand-slug",
|
|
31477
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31478
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
31194
31479
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31195
31480
|
required: true
|
|
31196
31481
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -31250,6 +31535,7 @@ var init_BrandEditPage = __esm({
|
|
|
31250
31535
|
init_DetailPageHeader();
|
|
31251
31536
|
init_vendor_scope();
|
|
31252
31537
|
init_ImageOrUrlField();
|
|
31538
|
+
init_slug_sanitizer();
|
|
31253
31539
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31254
31540
|
__name(BrandEditPage, "BrandEditPage");
|
|
31255
31541
|
}
|
|
@@ -32714,11 +33000,21 @@ function ProductEditPage({ productId }) {
|
|
|
32714
33000
|
const pcData = await pcRes.json();
|
|
32715
33001
|
const pcs = Array.isArray(pcData.data) ? pcData.data : [];
|
|
32716
33002
|
if (pcs.length > 0) {
|
|
32717
|
-
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
|
|
33003
|
+
const validTaxPcs = pcs.filter((p) => p.taxId != null && Number(p.taxId) > 0);
|
|
33004
|
+
if (validTaxPcs.length > 0) {
|
|
33005
|
+
setTaxRows(validTaxPcs.map((p) => ({
|
|
33006
|
+
taxId: p.taxId,
|
|
33007
|
+
rate: p.rate != null && p.rate !== "" ? String(p.rate) : ""
|
|
33008
|
+
})));
|
|
33009
|
+
} else if (!cancelled) {
|
|
33010
|
+
setTaxRows([
|
|
33011
|
+
{
|
|
33012
|
+
taxId: "",
|
|
33013
|
+
rate: ""
|
|
33014
|
+
}
|
|
33015
|
+
]);
|
|
33016
|
+
}
|
|
33017
|
+
const firstRefund = pcs.find((p) => p.refundPolicyId != null)?.refundPolicyId;
|
|
32722
33018
|
setRefundPolicyId(firstRefund != null ? Number(firstRefund) : null);
|
|
32723
33019
|
} else if (!cancelled) {
|
|
32724
33020
|
setTaxRows([
|
|
@@ -32727,6 +33023,7 @@ function ProductEditPage({ productId }) {
|
|
|
32727
33023
|
rate: ""
|
|
32728
33024
|
}
|
|
32729
33025
|
]);
|
|
33026
|
+
setRefundPolicyId(null);
|
|
32730
33027
|
}
|
|
32731
33028
|
}
|
|
32732
33029
|
const paRes = await fetch(`/api/product_attributes?productId=${sourceProductId}&limit=100`);
|
|
@@ -33103,14 +33400,7 @@ function ProductEditPage({ productId }) {
|
|
|
33103
33400
|
if (a == null || b == null) return true;
|
|
33104
33401
|
return Math.abs(a - b) > 1e-6;
|
|
33105
33402
|
}, "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
|
-
}
|
|
33403
|
+
const validTaxRows = taxRows.filter((row) => row.taxId !== "");
|
|
33114
33404
|
console.log("[product_config rows to save]:", taxRows, "refundPolicyId:", refundPolicyId);
|
|
33115
33405
|
const pcListRes = await fetch(`/api/product_config?productId=${savedId}&limit=200`);
|
|
33116
33406
|
const pcListData = pcListRes.ok ? await pcListRes.json() : {
|
|
@@ -33118,17 +33408,60 @@ function ProductEditPage({ productId }) {
|
|
|
33118
33408
|
};
|
|
33119
33409
|
console.log("[product existing pc rows]:", pcListData);
|
|
33120
33410
|
const existingPc = Array.isArray(pcListData.data) ? pcListData.data : [];
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33411
|
+
if (validTaxRows.length > 0) {
|
|
33412
|
+
const wantedTaxIds = new Set(validTaxRows.map((r) => Number(r.taxId)));
|
|
33413
|
+
for (const ep of existingPc) {
|
|
33414
|
+
if (ep.taxId == null || !wantedTaxIds.has(Number(ep.taxId))) {
|
|
33415
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33416
|
+
method: "DELETE"
|
|
33417
|
+
});
|
|
33418
|
+
}
|
|
33126
33419
|
}
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33420
|
+
for (const row of validTaxRows) {
|
|
33421
|
+
const taxId = Number(row.taxId);
|
|
33422
|
+
const rate = parseTaxRate(row.rate);
|
|
33423
|
+
const ep = existingPc.find((e) => e.taxId != null && Number(e.taxId) === taxId);
|
|
33424
|
+
if (!ep) {
|
|
33425
|
+
await fetch("/api/product_config", {
|
|
33426
|
+
method: "POST",
|
|
33427
|
+
headers: {
|
|
33428
|
+
"Content-Type": "application/json"
|
|
33429
|
+
},
|
|
33430
|
+
body: JSON.stringify({
|
|
33431
|
+
productId: Number(savedId),
|
|
33432
|
+
taxId,
|
|
33433
|
+
rate,
|
|
33434
|
+
refundPolicyId
|
|
33435
|
+
})
|
|
33436
|
+
});
|
|
33437
|
+
} else {
|
|
33438
|
+
const existingRate = ep.rate == null || String(ep.rate).trim() === "" ? null : Number(ep.rate);
|
|
33439
|
+
const er = Number.isFinite(existingRate) ? existingRate : null;
|
|
33440
|
+
const policyChanged = ep.refundPolicyId !== refundPolicyId;
|
|
33441
|
+
if (ratesDiffer(er, rate) || policyChanged) {
|
|
33442
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33443
|
+
method: "PUT",
|
|
33444
|
+
headers: {
|
|
33445
|
+
"Content-Type": "application/json"
|
|
33446
|
+
},
|
|
33447
|
+
body: JSON.stringify({
|
|
33448
|
+
rate,
|
|
33449
|
+
refundPolicyId
|
|
33450
|
+
})
|
|
33451
|
+
});
|
|
33452
|
+
}
|
|
33453
|
+
}
|
|
33454
|
+
}
|
|
33455
|
+
} else if (refundPolicyId != null) {
|
|
33456
|
+
const nullTaxRow = existingPc.find((e) => e.taxId == null);
|
|
33457
|
+
for (const ep of existingPc) {
|
|
33458
|
+
if (ep.id !== nullTaxRow?.id) {
|
|
33459
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33460
|
+
method: "DELETE"
|
|
33461
|
+
});
|
|
33462
|
+
}
|
|
33463
|
+
}
|
|
33464
|
+
if (!nullTaxRow) {
|
|
33132
33465
|
await fetch("/api/product_config", {
|
|
33133
33466
|
method: "POST",
|
|
33134
33467
|
headers: {
|
|
@@ -33136,27 +33469,27 @@ function ProductEditPage({ productId }) {
|
|
|
33136
33469
|
},
|
|
33137
33470
|
body: JSON.stringify({
|
|
33138
33471
|
productId: Number(savedId),
|
|
33139
|
-
taxId,
|
|
33140
|
-
rate:
|
|
33141
|
-
refundPolicyId
|
|
33472
|
+
taxId: null,
|
|
33473
|
+
rate: null,
|
|
33474
|
+
refundPolicyId
|
|
33475
|
+
})
|
|
33476
|
+
});
|
|
33477
|
+
} else if (nullTaxRow.refundPolicyId !== refundPolicyId) {
|
|
33478
|
+
await fetch(`/api/product_config/${nullTaxRow.id}`, {
|
|
33479
|
+
method: "PUT",
|
|
33480
|
+
headers: {
|
|
33481
|
+
"Content-Type": "application/json"
|
|
33482
|
+
},
|
|
33483
|
+
body: JSON.stringify({
|
|
33484
|
+
refundPolicyId
|
|
33142
33485
|
})
|
|
33143
33486
|
});
|
|
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
|
-
}
|
|
33487
|
+
}
|
|
33488
|
+
} else {
|
|
33489
|
+
for (const ep of existingPc) {
|
|
33490
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33491
|
+
method: "DELETE"
|
|
33492
|
+
});
|
|
33160
33493
|
}
|
|
33161
33494
|
}
|
|
33162
33495
|
if (hasVariants) {
|
|
@@ -34072,7 +34405,8 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34072
34405
|
]);
|
|
34073
34406
|
return;
|
|
34074
34407
|
}
|
|
34075
|
-
|
|
34408
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
34409
|
+
if (!normalizedSlug) {
|
|
34076
34410
|
setErrors([
|
|
34077
34411
|
"Slug is required"
|
|
34078
34412
|
]);
|
|
@@ -34080,10 +34414,10 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34080
34414
|
}
|
|
34081
34415
|
setSaving(true);
|
|
34082
34416
|
try {
|
|
34083
|
-
const savedSeoId = await saveSeo(seo,
|
|
34417
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
34084
34418
|
const payload = {
|
|
34085
34419
|
name: name.trim(),
|
|
34086
|
-
slug:
|
|
34420
|
+
slug: normalizedSlug,
|
|
34087
34421
|
hsn: hsn.trim() || null,
|
|
34088
34422
|
categoryId: categoryId || null,
|
|
34089
34423
|
brandId: brandId || null,
|
|
@@ -34252,7 +34586,9 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34252
34586
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
34253
34587
|
type: "text",
|
|
34254
34588
|
value: slug,
|
|
34255
|
-
|
|
34589
|
+
placeholder: "collection-slug",
|
|
34590
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
34591
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
34256
34592
|
className: inputCls4,
|
|
34257
34593
|
required: true
|
|
34258
34594
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -34488,6 +34824,7 @@ var init_CollectionEditPage = __esm({
|
|
|
34488
34824
|
init_admin_config_context();
|
|
34489
34825
|
init_category_related_product_labels();
|
|
34490
34826
|
init_ImageOrUrlField();
|
|
34827
|
+
init_slug_sanitizer();
|
|
34491
34828
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
34492
34829
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
34493
34830
|
url: "",
|
|
@@ -35667,7 +36004,9 @@ var init_event_entity_types = __esm({
|
|
|
35667
36004
|
// src/admin/pages/EventEditPage.tsx
|
|
35668
36005
|
var EventEditPage_exports = {};
|
|
35669
36006
|
__export(EventEditPage_exports, {
|
|
35670
|
-
default: () => EventEditPage
|
|
36007
|
+
default: () => EventEditPage,
|
|
36008
|
+
normalizeSlug: () => normalizeSlug2,
|
|
36009
|
+
sanitizeSlugInput: () => sanitizeSlugInput2
|
|
35671
36010
|
});
|
|
35672
36011
|
function RequiredLabel({ children }) {
|
|
35673
36012
|
return /* @__PURE__ */ React.createElement("label", {
|
|
@@ -35676,6 +36015,12 @@ function RequiredLabel({ children }) {
|
|
|
35676
36015
|
className: "text-red-600"
|
|
35677
36016
|
}, "*"));
|
|
35678
36017
|
}
|
|
36018
|
+
function sanitizeSlugInput2(value) {
|
|
36019
|
+
return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-");
|
|
36020
|
+
}
|
|
36021
|
+
function normalizeSlug2(value) {
|
|
36022
|
+
return sanitizeSlugInput2(value).replace(/^-+|-+$/g, "");
|
|
36023
|
+
}
|
|
35679
36024
|
function toIsoOrNull(value, timezone) {
|
|
35680
36025
|
const trimmed = value.trim();
|
|
35681
36026
|
if (!trimmed) return null;
|
|
@@ -35766,7 +36111,7 @@ function EventEditPage({ eventId }) {
|
|
|
35766
36111
|
const [supportContact, setSupportContact] = React26.useState("");
|
|
35767
36112
|
const [allowGroupOrders, setAllowGroupOrders] = React26.useState(false);
|
|
35768
36113
|
const [sendIndividualTicketPDFToAttendees, setSendIndividualTicketPDFToAttendees] = React26.useState(false);
|
|
35769
|
-
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = React26.useState("");
|
|
36114
|
+
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = React26.useState("1");
|
|
35770
36115
|
const [sponsors, setSponsors] = React26.useState([]);
|
|
35771
36116
|
const [agenda, setAgenda] = React26.useState([]);
|
|
35772
36117
|
const [workshops, setWorkshops] = React26.useState([]);
|
|
@@ -35892,9 +36237,10 @@ function EventEditPage({ eventId }) {
|
|
|
35892
36237
|
}
|
|
35893
36238
|
setOfficialWebsiteUrl(data.officialWebsiteUrl ?? "");
|
|
35894
36239
|
setSupportContact(data.supportContact ?? "");
|
|
35895
|
-
|
|
36240
|
+
const loadedAllowGroup = data.allowGroupOrders ?? false;
|
|
36241
|
+
setAllowGroupOrders(loadedAllowGroup);
|
|
35896
36242
|
setSendIndividualTicketPDFToAttendees(data.sendIndividualTicketPDFToAttendees ?? false);
|
|
35897
|
-
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : "");
|
|
36243
|
+
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : loadedAllowGroup ? "10" : "1");
|
|
35898
36244
|
setSponsors(parseNamedListFromApi(data.sponsors, "sponsor"));
|
|
35899
36245
|
setAgenda(parseNamedListFromApi(data.agenda, "agenda"));
|
|
35900
36246
|
setWorkshops(parseNamedListFromApi(data.workshops, "workshop"));
|
|
@@ -35940,8 +36286,13 @@ function EventEditPage({ eventId }) {
|
|
|
35940
36286
|
]);
|
|
35941
36287
|
const buildPayload = /* @__PURE__ */ __name(() => {
|
|
35942
36288
|
const nextErrors = [];
|
|
36289
|
+
const normalizedSlug = normalizeSlug2(slug);
|
|
35943
36290
|
if (!name.trim()) nextErrors.push("Name is required");
|
|
35944
|
-
if (!
|
|
36291
|
+
if (!normalizedSlug) {
|
|
36292
|
+
nextErrors.push("Slug is required");
|
|
36293
|
+
} else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(normalizedSlug)) {
|
|
36294
|
+
nextErrors.push("Slug must contain only lowercase letters, numbers, and hyphens without leading or trailing hyphens or slashes");
|
|
36295
|
+
}
|
|
35945
36296
|
if (!startDate.trim()) nextErrors.push("Event Start Date & Time is required");
|
|
35946
36297
|
if (!endDate.trim()) nextErrors.push("Event End Date & Time is required");
|
|
35947
36298
|
if (startDate.trim() && !timezone.trim()) {
|
|
@@ -35967,6 +36318,20 @@ function EventEditPage({ eventId }) {
|
|
|
35967
36318
|
nextErrors.push("Event End Date & Time must be after Event Start Date & Time");
|
|
35968
36319
|
}
|
|
35969
36320
|
}
|
|
36321
|
+
if (registrationOpensAt.trim() && registrationClosesAt.trim()) {
|
|
36322
|
+
const regOpen = new Date(toIsoOrNull(registrationOpensAt, timezone) ?? "");
|
|
36323
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36324
|
+
if (!Number.isNaN(regOpen.getTime()) && !Number.isNaN(regClose.getTime()) && regClose < regOpen) {
|
|
36325
|
+
nextErrors.push("Registration Closes Date & Time must be after Registration Opens Date & Time");
|
|
36326
|
+
}
|
|
36327
|
+
}
|
|
36328
|
+
if (registrationClosesAt.trim() && endDate.trim()) {
|
|
36329
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36330
|
+
const end = new Date(toIsoOrNull(endDate, timezone) ?? "");
|
|
36331
|
+
if (!Number.isNaN(regClose.getTime()) && !Number.isNaN(end.getTime()) && regClose > end) {
|
|
36332
|
+
nextErrors.push("Registration cannot close after Event End Date & Time");
|
|
36333
|
+
}
|
|
36334
|
+
}
|
|
35970
36335
|
if (additionalVenueDetails.length > ADDITIONAL_VENUE_MAX_CHARS) {
|
|
35971
36336
|
nextErrors.push(`Additional Venue Details must be ${ADDITIONAL_VENUE_MAX_CHARS} characters or fewer`);
|
|
35972
36337
|
}
|
|
@@ -35976,7 +36341,7 @@ function EventEditPage({ eventId }) {
|
|
|
35976
36341
|
}
|
|
35977
36342
|
const payload = {
|
|
35978
36343
|
name: name.trim(),
|
|
35979
|
-
slug:
|
|
36344
|
+
slug: normalizedSlug,
|
|
35980
36345
|
description: description.trim() || null,
|
|
35981
36346
|
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
35982
36347
|
comingSoon,
|
|
@@ -36004,7 +36369,7 @@ function EventEditPage({ eventId }) {
|
|
|
36004
36369
|
supportContact: supportContact.trim() || null,
|
|
36005
36370
|
allowGroupOrders,
|
|
36006
36371
|
sendIndividualTicketPDFToAttendees,
|
|
36007
|
-
maxGroupPurchaseQuantity: maxGroupPurchaseQuantity
|
|
36372
|
+
maxGroupPurchaseQuantity: allowGroupOrders ? Number(maxGroupPurchaseQuantity) > 0 ? Number(maxGroupPurchaseQuantity) : 10 : 1,
|
|
36008
36373
|
sponsors: namedListToPayload(sponsors, {
|
|
36009
36374
|
legacySponsorKeys: true
|
|
36010
36375
|
}),
|
|
@@ -36272,9 +36637,16 @@ function EventEditPage({ eventId }) {
|
|
|
36272
36637
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36273
36638
|
className: labelCls6
|
|
36274
36639
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
36640
|
+
id: "event-slug",
|
|
36275
36641
|
type: "text",
|
|
36276
36642
|
value: slug,
|
|
36277
|
-
|
|
36643
|
+
placeholder: "event-slug",
|
|
36644
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36645
|
+
setSlug(sanitizeSlugInput2(e.target.value));
|
|
36646
|
+
}, "onChange"),
|
|
36647
|
+
onBlur: /* @__PURE__ */ __name(() => {
|
|
36648
|
+
setSlug((prev) => normalizeSlug2(prev));
|
|
36649
|
+
}, "onBlur"),
|
|
36278
36650
|
className: inputCls6
|
|
36279
36651
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36280
36652
|
className: labelCls6
|
|
@@ -36534,7 +36906,16 @@ function EventEditPage({ eventId }) {
|
|
|
36534
36906
|
}, "Max group purchase quantity"), /* @__PURE__ */ React.createElement("input", {
|
|
36535
36907
|
type: "number",
|
|
36536
36908
|
value: maxGroupPurchaseQuantity,
|
|
36537
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36909
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36910
|
+
const val = e.target.value;
|
|
36911
|
+
setMaxGroupPurchaseQuantity(val);
|
|
36912
|
+
const num = Number(val);
|
|
36913
|
+
if (Number.isFinite(num) && num > 1) {
|
|
36914
|
+
setAllowGroupOrders(true);
|
|
36915
|
+
} else if (Number.isFinite(num) && num <= 1) {
|
|
36916
|
+
setAllowGroupOrders(false);
|
|
36917
|
+
}
|
|
36918
|
+
}, "onChange"),
|
|
36538
36919
|
className: inputCls6,
|
|
36539
36920
|
min: 1
|
|
36540
36921
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -36544,7 +36925,18 @@ function EventEditPage({ eventId }) {
|
|
|
36544
36925
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
36545
36926
|
type: "checkbox",
|
|
36546
36927
|
checked: allowGroupOrders,
|
|
36547
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36928
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36929
|
+
const checked = e.target.checked;
|
|
36930
|
+
setAllowGroupOrders(checked);
|
|
36931
|
+
if (!checked) {
|
|
36932
|
+
setMaxGroupPurchaseQuantity("1");
|
|
36933
|
+
} else {
|
|
36934
|
+
const current = Number(maxGroupPurchaseQuantity);
|
|
36935
|
+
if (!Number.isFinite(current) || current <= 1) {
|
|
36936
|
+
setMaxGroupPurchaseQuantity("10");
|
|
36937
|
+
}
|
|
36938
|
+
}
|
|
36939
|
+
}, "onChange"),
|
|
36548
36940
|
className: "h-4 w-4 rounded border-gray-300"
|
|
36549
36941
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
36550
36942
|
className: "text-sm text-gray-900"
|
|
@@ -36618,6 +37010,8 @@ var init_EventEditPage = __esm({
|
|
|
36618
37010
|
labelCls6 = "block text-xs font-medium text-gray-600 mb-1";
|
|
36619
37011
|
inputCls6 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
36620
37012
|
__name(RequiredLabel, "RequiredLabel");
|
|
37013
|
+
__name(sanitizeSlugInput2, "sanitizeSlugInput");
|
|
37014
|
+
__name(normalizeSlug2, "normalizeSlug");
|
|
36621
37015
|
ENTITY_TYPE_OPTIONS = [
|
|
36622
37016
|
...EVENT_ENTITY_TYPE_OPTIONS
|
|
36623
37017
|
];
|
|
@@ -36996,7 +37390,7 @@ function ComboEditPage({ comboId }) {
|
|
|
36996
37390
|
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
36997
37391
|
const payload = {
|
|
36998
37392
|
name: trimmedName,
|
|
36999
|
-
slug: slug.trim()
|
|
37393
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37000
37394
|
desc: desc || null,
|
|
37001
37395
|
eventId: resolvedEventId,
|
|
37002
37396
|
price,
|
|
@@ -37091,8 +37485,9 @@ function ComboEditPage({ comboId }) {
|
|
|
37091
37485
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
37092
37486
|
type: "text",
|
|
37093
37487
|
value: slug,
|
|
37094
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37095
|
-
|
|
37488
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
37489
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
37490
|
+
placeholder: "combo-slug",
|
|
37096
37491
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
37097
37492
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
37098
37493
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -37258,6 +37653,7 @@ var init_ComboEditPage = __esm({
|
|
|
37258
37653
|
init_DetailPageHeader();
|
|
37259
37654
|
init_inventory_validation();
|
|
37260
37655
|
init_admin_config_context();
|
|
37656
|
+
init_slug_sanitizer();
|
|
37261
37657
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
37262
37658
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
37263
37659
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -37489,7 +37885,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37489
37885
|
const vendorPayload = /* @__PURE__ */ __name(() => ({
|
|
37490
37886
|
name: name.trim(),
|
|
37491
37887
|
legalName: legalName.trim() || null,
|
|
37492
|
-
slug: slug.trim()
|
|
37888
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37493
37889
|
businessType: businessType || null,
|
|
37494
37890
|
description: description.trim() || null,
|
|
37495
37891
|
website: website.trim() || null,
|
|
@@ -37831,7 +38227,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37831
38227
|
signal: abortController.signal,
|
|
37832
38228
|
body: JSON.stringify({
|
|
37833
38229
|
...vendorPayload(),
|
|
37834
|
-
slug: slug.trim(),
|
|
38230
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37835
38231
|
metadata: (() => {
|
|
37836
38232
|
const next = {
|
|
37837
38233
|
...metadata ?? {}
|
|
@@ -37954,8 +38350,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
37954
38350
|
}, "Slug ", create ? "(optional)" : "*"), /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
37955
38351
|
id: "vendorSlug",
|
|
37956
38352
|
value: slug,
|
|
37957
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37958
|
-
|
|
38353
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
38354
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
38355
|
+
placeholder: "vendor-slug",
|
|
37959
38356
|
className: `mt-1 ${fieldClass}`
|
|
37960
38357
|
})), /* @__PURE__ */ React26__namespace.default.createElement("div", null, /* @__PURE__ */ React26__namespace.default.createElement(FieldLabel, {
|
|
37961
38358
|
htmlFor: "businessType"
|
|
@@ -38296,6 +38693,7 @@ var init_VendorEditPage = __esm({
|
|
|
38296
38693
|
init_checkbox();
|
|
38297
38694
|
init_dialog();
|
|
38298
38695
|
init_vendor_profile();
|
|
38696
|
+
init_slug_sanitizer();
|
|
38299
38697
|
init_vendor_list_config();
|
|
38300
38698
|
init_vendor_access_denied();
|
|
38301
38699
|
init_vendor_access_denied();
|