@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.js
CHANGED
|
@@ -3,14 +3,14 @@ import { useSession, signOut, signIn } from 'next-auth/react';
|
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
5
|
import * as React26 from 'react';
|
|
6
|
-
import React26__default, { useState, useMemo,
|
|
6
|
+
import React26__default, { useState, useMemo, useEffect, useContext, useRef, useCallback, Suspense, createElement, createContext } from 'react';
|
|
7
7
|
var React = React26__default;
|
|
8
8
|
|
|
9
9
|
import { Slot } from '@radix-ui/react-slot';
|
|
10
10
|
import { cva } from 'class-variance-authority';
|
|
11
11
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
12
12
|
import * as LucideIcons from 'lucide-react';
|
|
13
|
-
import { ChevronDown, User, Settings, LogOut, LayoutDashboard,
|
|
13
|
+
import { Loader2, Store, ChevronDown, Check, User, Settings, LogOut, LayoutDashboard, FolderTree, Layers, Building2, CalendarDays, ShoppingBag, Package, Receipt, BadgePercent, ShoppingCart, CreditCard, Users, Inbox, File, LinkIcon, ClipboardList, MessageSquare, Image as Image$1, Shield, FileText, Puzzle, X, ShieldAlert, Plus, MoreVertical, Upload, Download, Edit, Copy, RefreshCw, Trash2, Mail, Save, Search, AlertCircle, MapPin, BarChart3, TrendingUp, UserCheck, Activity, Globe, Eye, MousePointer, ArrowLeftRight, RotateCcw, KeyRound, CalendarClock, CheckCircle2, XCircle, CheckSquare, Square, XSquare, Rss, ArrowLeft, PlayCircle, UserPlus, Pencil, ShieldCheck, Smartphone, Send, FolderPlus, LayoutGrid, List, Move, Archive, Edit2, ExternalLink, Power, Star, ChevronUp, Zap, Filter, ImageIcon, GripVertical, ChevronRight, ArrowRight, ArrowUpRight, IndianRupee, Clock, Play, MessageCircle, Bot, FileUp, Tag, Box, SlidersHorizontal, Calculator, Link2 as Link2$1, Unlink, Gift, MoreHorizontal, EyeOff, Phone, QrCode, Car, Settings2, DollarSign, Hash, Circle, ChevronLeft, Bell, Share2, HardDrive, Folder, Film } from 'lucide-react';
|
|
14
14
|
import Image from 'next/image';
|
|
15
15
|
import Link2 from 'next/link';
|
|
16
16
|
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
|
@@ -347,9 +347,8 @@ var init_vendor_scope = __esm({
|
|
|
347
347
|
}
|
|
348
348
|
});
|
|
349
349
|
function AdminHeader() {
|
|
350
|
-
const { data: session } = useSession();
|
|
350
|
+
const { data: session, update } = useSession();
|
|
351
351
|
const sessionUser = session?.user;
|
|
352
|
-
const roleLabel = sessionUser?.groupName?.trim() || (sessionUser?.isRBACAdmin ? "Administrator" : "Admin user");
|
|
353
352
|
const isVendor = isVendorPortalUser(sessionUser);
|
|
354
353
|
const isMobile = useIsMobile();
|
|
355
354
|
const configuredLogo = process.env.NEXT_PUBLIC_ADMIN_LOGO_URL || DEFAULT_ADMIN_LOGO;
|
|
@@ -358,9 +357,125 @@ function AdminHeader() {
|
|
|
358
357
|
const isDataLogo = useMemo(() => logoSrc.startsWith("data:"), [
|
|
359
358
|
logoSrc
|
|
360
359
|
]);
|
|
360
|
+
const [isSwitching, setIsSwitching] = useState(false);
|
|
361
|
+
const [cookieVendorId, setCookieVendorId] = useState(() => {
|
|
362
|
+
if (typeof window === "undefined") return null;
|
|
363
|
+
try {
|
|
364
|
+
const match = document.cookie.match(/(?:^|;\s*)infuro_active_vendor_id=(\d+)/);
|
|
365
|
+
if (match && match[1]) return Number(match[1]);
|
|
366
|
+
const stored = localStorage.getItem("infuro_active_vendor_id");
|
|
367
|
+
if (stored) return Number(stored);
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
});
|
|
372
|
+
const [fetchedStores, setFetchedStores] = useState(null);
|
|
373
|
+
const [fetchedRoleName, setFetchedRoleName] = useState(null);
|
|
374
|
+
const [fetchedActiveVendorId, setFetchedActiveVendorId] = useState(null);
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
if (sessionUser?.vendorStores && sessionUser.vendorStores.length > 1 && !cookieVendorId) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
let isMounted = true;
|
|
380
|
+
fetch("/api/admin/vendor/context").then((res) => res.ok ? res.json() : null).then((data) => {
|
|
381
|
+
if (!isMounted || !data) return;
|
|
382
|
+
if (Array.isArray(data.vendorStores)) {
|
|
383
|
+
setFetchedStores(data.vendorStores);
|
|
384
|
+
}
|
|
385
|
+
if (data.vendorRoleName) {
|
|
386
|
+
setFetchedRoleName(data.vendorRoleName);
|
|
387
|
+
}
|
|
388
|
+
if (typeof data.activeVendorId === "number") {
|
|
389
|
+
setFetchedActiveVendorId(data.activeVendorId);
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
393
|
+
window.dispatchEvent(new CustomEvent("infuro_vendor_context_updated", {
|
|
394
|
+
detail: data
|
|
395
|
+
}));
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
}).catch(() => {
|
|
399
|
+
});
|
|
400
|
+
return () => {
|
|
401
|
+
isMounted = false;
|
|
402
|
+
};
|
|
403
|
+
}, [
|
|
404
|
+
sessionUser?.email,
|
|
405
|
+
sessionUser?.vendorStores,
|
|
406
|
+
cookieVendorId
|
|
407
|
+
]);
|
|
408
|
+
const stores = useMemo(() => {
|
|
409
|
+
if (sessionUser?.vendorStores && sessionUser.vendorStores.length > 0) {
|
|
410
|
+
return sessionUser.vendorStores;
|
|
411
|
+
}
|
|
412
|
+
return fetchedStores || [];
|
|
413
|
+
}, [
|
|
414
|
+
sessionUser?.vendorStores,
|
|
415
|
+
fetchedStores
|
|
416
|
+
]);
|
|
417
|
+
const effectiveActiveVendorId = cookieVendorId ?? sessionUser?.activeVendorId ?? fetchedActiveVendorId;
|
|
418
|
+
const activeStore = useMemo(() => {
|
|
419
|
+
return stores.find((s) => s.id === effectiveActiveVendorId) || (stores.length > 0 ? stores[0] : null);
|
|
420
|
+
}, [
|
|
421
|
+
stores,
|
|
422
|
+
effectiveActiveVendorId
|
|
423
|
+
]);
|
|
424
|
+
const effectiveRoleName = activeStore?.roleName || sessionUser?.vendorRoleName || fetchedRoleName;
|
|
425
|
+
const roleLabel = ((isVendor || stores.length > 0) && effectiveRoleName ? effectiveRoleName : null) || sessionUser?.groupName?.trim() || (sessionUser?.isRBACAdmin ? "Administrator" : "Admin user");
|
|
361
426
|
const handleLogout = /* @__PURE__ */ __name(() => {
|
|
362
427
|
void adminSignOut();
|
|
363
428
|
}, "handleLogout");
|
|
429
|
+
const handleSwitchStore = /* @__PURE__ */ __name(async (vendorId) => {
|
|
430
|
+
if (vendorId === activeStore?.id || isSwitching) return;
|
|
431
|
+
try {
|
|
432
|
+
setIsSwitching(true);
|
|
433
|
+
document.cookie = `infuro_active_vendor_id=${vendorId}; path=/; max-age=31536000; SameSite=Lax`;
|
|
434
|
+
try {
|
|
435
|
+
localStorage.setItem("infuro_active_vendor_id", String(vendorId));
|
|
436
|
+
} catch {
|
|
437
|
+
}
|
|
438
|
+
setCookieVendorId(vendorId);
|
|
439
|
+
const res = await fetch("/api/admin/vendor/switch", {
|
|
440
|
+
method: "POST",
|
|
441
|
+
headers: {
|
|
442
|
+
"Content-Type": "application/json"
|
|
443
|
+
},
|
|
444
|
+
body: JSON.stringify({
|
|
445
|
+
vendorId
|
|
446
|
+
})
|
|
447
|
+
});
|
|
448
|
+
if (res.ok) {
|
|
449
|
+
const data = await res.json();
|
|
450
|
+
try {
|
|
451
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
452
|
+
window.dispatchEvent(new CustomEvent("infuro_vendor_context_updated", {
|
|
453
|
+
detail: data
|
|
454
|
+
}));
|
|
455
|
+
} catch {
|
|
456
|
+
}
|
|
457
|
+
if (typeof update === "function") {
|
|
458
|
+
try {
|
|
459
|
+
await update({
|
|
460
|
+
activeVendorId: vendorId,
|
|
461
|
+
vendorRole: data.vendorRole,
|
|
462
|
+
vendorRoleId: data.vendorRoleId,
|
|
463
|
+
vendorRoleName: data.vendorRoleName,
|
|
464
|
+
isVendorRoleOwner: data.isVendorRoleOwner,
|
|
465
|
+
vendorEntityPerms: data.vendorEntityPerms,
|
|
466
|
+
vendorStores: data.vendorStores
|
|
467
|
+
});
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
window.location.reload();
|
|
472
|
+
}
|
|
473
|
+
} catch (err) {
|
|
474
|
+
console.error("Failed to switch store:", err);
|
|
475
|
+
} finally {
|
|
476
|
+
setIsSwitching(false);
|
|
477
|
+
}
|
|
478
|
+
}, "handleSwitchStore");
|
|
364
479
|
return /* @__PURE__ */ React.createElement("header", {
|
|
365
480
|
className: "bg-white border-b border-gray-200 px-4 py-2"
|
|
366
481
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -393,7 +508,46 @@ function AdminHeader() {
|
|
|
393
508
|
className: "text-sm font-semibold text-gray-800"
|
|
394
509
|
}, "Infuro"))), /* @__PURE__ */ React.createElement("div", {
|
|
395
510
|
className: "flex items-center space-x-3"
|
|
396
|
-
}, /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
511
|
+
}, stores.length > 1 && /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
512
|
+
asChild: true
|
|
513
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
514
|
+
variant: "outline",
|
|
515
|
+
size: "sm",
|
|
516
|
+
disabled: isSwitching,
|
|
517
|
+
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"
|
|
518
|
+
}, isSwitching ? /* @__PURE__ */ React.createElement(Loader2, {
|
|
519
|
+
className: "h-3.5 w-3.5 animate-spin text-gray-500 shrink-0"
|
|
520
|
+
}) : /* @__PURE__ */ React.createElement(Store, {
|
|
521
|
+
className: "h-3.5 w-3.5 text-gray-500 shrink-0"
|
|
522
|
+
}), /* @__PURE__ */ React.createElement("span", {
|
|
523
|
+
className: "max-w-[130px] sm:max-w-[180px] truncate font-semibold"
|
|
524
|
+
}, activeStore?.name || "Select Store"), activeStore?.roleName && /* @__PURE__ */ React.createElement("span", {
|
|
525
|
+
className: "hidden sm:inline-block px-1.5 py-0.5 text-[10px] font-medium bg-gray-200/80 text-gray-700 rounded"
|
|
526
|
+
}, activeStore.roleName), /* @__PURE__ */ React.createElement(ChevronDown, {
|
|
527
|
+
className: "h-3 w-3 text-gray-400 shrink-0"
|
|
528
|
+
}))), /* @__PURE__ */ React.createElement(DropdownMenuContent, {
|
|
529
|
+
align: "end",
|
|
530
|
+
className: "w-56 p-1"
|
|
531
|
+
}, /* @__PURE__ */ React.createElement(DropdownMenuLabel, {
|
|
532
|
+
className: "text-[11px] font-semibold text-gray-500 uppercase px-2 py-1"
|
|
533
|
+
}, "Switch Store"), /* @__PURE__ */ React.createElement(DropdownMenuSeparator, {
|
|
534
|
+
className: "my-1"
|
|
535
|
+
}), stores.map((st) => {
|
|
536
|
+
const isSelected = st.id === activeStore?.id;
|
|
537
|
+
return /* @__PURE__ */ React.createElement(DropdownMenuItem, {
|
|
538
|
+
key: st.id,
|
|
539
|
+
onClick: /* @__PURE__ */ __name(() => handleSwitchStore(st.id), "onClick"),
|
|
540
|
+
className: "flex items-center justify-between px-2.5 py-2 cursor-pointer text-xs rounded-md"
|
|
541
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
542
|
+
className: "flex flex-col gap-0.5 truncate pr-2"
|
|
543
|
+
}, /* @__PURE__ */ React.createElement("span", {
|
|
544
|
+
className: `font-medium ${isSelected ? "text-primary font-semibold" : "text-gray-800"}`
|
|
545
|
+
}, st.name), st.roleName && /* @__PURE__ */ React.createElement("span", {
|
|
546
|
+
className: "text-[10px] text-gray-400"
|
|
547
|
+
}, st.roleName)), isSelected && /* @__PURE__ */ React.createElement(Check, {
|
|
548
|
+
className: "h-3.5 w-3.5 text-primary shrink-0"
|
|
549
|
+
}));
|
|
550
|
+
}))), /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, {
|
|
397
551
|
asChild: true
|
|
398
552
|
}, /* @__PURE__ */ React.createElement(Button, {
|
|
399
553
|
variant: "ghost",
|
|
@@ -861,7 +1015,7 @@ var init_admin_config_context = __esm({
|
|
|
861
1015
|
var CMS_VERSION;
|
|
862
1016
|
var init_cms_version = __esm({
|
|
863
1017
|
"src/lib/cms-version.ts"() {
|
|
864
|
-
CMS_VERSION = "1.0.
|
|
1018
|
+
CMS_VERSION = "1.0.73" ;
|
|
865
1019
|
}
|
|
866
1020
|
});
|
|
867
1021
|
function useCatalogCategories(enabled = true) {
|
|
@@ -969,8 +1123,54 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
969
1123
|
const searchParams = useSearchParams();
|
|
970
1124
|
const { data: session } = useSession();
|
|
971
1125
|
const sessionUser = session?.user;
|
|
972
|
-
const
|
|
973
|
-
|
|
1126
|
+
const [vendorContext, setVendorContext] = useState(() => {
|
|
1127
|
+
if (typeof window === "undefined") return null;
|
|
1128
|
+
try {
|
|
1129
|
+
const stored = localStorage.getItem("infuro_active_vendor_context");
|
|
1130
|
+
if (stored) return JSON.parse(stored);
|
|
1131
|
+
} catch {
|
|
1132
|
+
}
|
|
1133
|
+
return null;
|
|
1134
|
+
});
|
|
1135
|
+
useEffect(() => {
|
|
1136
|
+
const onContextUpdated = /* @__PURE__ */ __name((e) => {
|
|
1137
|
+
const detail = e.detail;
|
|
1138
|
+
if (detail) setVendorContext(detail);
|
|
1139
|
+
}, "onContextUpdated");
|
|
1140
|
+
window.addEventListener("infuro_vendor_context_updated", onContextUpdated);
|
|
1141
|
+
fetch("/api/admin/vendor/context").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
1142
|
+
if (data) {
|
|
1143
|
+
setVendorContext(data);
|
|
1144
|
+
try {
|
|
1145
|
+
localStorage.setItem("infuro_active_vendor_context", JSON.stringify(data));
|
|
1146
|
+
} catch {
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}).catch(() => {
|
|
1150
|
+
});
|
|
1151
|
+
return () => {
|
|
1152
|
+
window.removeEventListener("infuro_vendor_context_updated", onContextUpdated);
|
|
1153
|
+
};
|
|
1154
|
+
}, []);
|
|
1155
|
+
const effectiveSessionUser = useMemo(() => {
|
|
1156
|
+
if (!sessionUser) return null;
|
|
1157
|
+
if (!vendorContext) return sessionUser;
|
|
1158
|
+
return {
|
|
1159
|
+
...sessionUser,
|
|
1160
|
+
activeVendorId: vendorContext.activeVendorId ?? sessionUser.activeVendorId,
|
|
1161
|
+
vendorRole: vendorContext.vendorRole ?? sessionUser.vendorRole,
|
|
1162
|
+
vendorRoleId: vendorContext.vendorRoleId ?? sessionUser.vendorRoleId,
|
|
1163
|
+
vendorRoleName: vendorContext.vendorRoleName ?? sessionUser.vendorRoleName,
|
|
1164
|
+
isVendorRoleOwner: vendorContext.isVendorRoleOwner ?? sessionUser.isVendorRoleOwner,
|
|
1165
|
+
vendorEntityPerms: vendorContext.vendorEntityPerms ?? sessionUser.vendorEntityPerms,
|
|
1166
|
+
vendorStores: vendorContext.vendorStores ?? sessionUser.vendorStores
|
|
1167
|
+
};
|
|
1168
|
+
}, [
|
|
1169
|
+
sessionUser,
|
|
1170
|
+
vendorContext
|
|
1171
|
+
]);
|
|
1172
|
+
const showVendorOnboard = canOnboardVendors(effectiveSessionUser);
|
|
1173
|
+
const vendorPortal = isVendorPortalUser(effectiveSessionUser);
|
|
974
1174
|
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands } = useContext(AdminConfigContext);
|
|
975
1175
|
const showStoreNav = storeEnabled || vendorPortal;
|
|
976
1176
|
const showPlatformNav = !vendorPortal;
|
|
@@ -997,8 +1197,8 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
997
1197
|
const headingCls = "text-[11px] font-semibold text-gray-400 uppercase tracking-wider px-2.5 mb-1.5";
|
|
998
1198
|
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";
|
|
999
1199
|
const canReadEntity = /* @__PURE__ */ __name((entity) => {
|
|
1000
|
-
if (!
|
|
1001
|
-
return sessionHasEntityAccessFromExplanation(
|
|
1200
|
+
if (!effectiveSessionUser) return true;
|
|
1201
|
+
return sessionHasEntityAccessFromExplanation(effectiveSessionUser, entity, "read");
|
|
1002
1202
|
}, "canReadEntity");
|
|
1003
1203
|
return /* @__PURE__ */ React.createElement("aside", {
|
|
1004
1204
|
className: asideCls
|
|
@@ -1208,12 +1408,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
1208
1408
|
className: `${linkCls} ${isActive("/admin/vendor-profile") ? linkActive : linkInactive}`
|
|
1209
1409
|
}, /* @__PURE__ */ React.createElement(User, {
|
|
1210
1410
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendor-profile") ? iconActive : iconInactive}`
|
|
1211
|
-
}), "Profile")), canManageVendorTeam(
|
|
1411
|
+
}), "Profile")), canManageVendorTeam(effectiveSessionUser) && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1212
1412
|
href: "/admin/vendor-team",
|
|
1213
1413
|
className: `${linkCls} ${isActive("/admin/vendor-team") ? linkActive : linkInactive}`
|
|
1214
1414
|
}, /* @__PURE__ */ React.createElement(Users, {
|
|
1215
1415
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendor-team") ? iconActive : iconInactive}`
|
|
1216
|
-
}), "Team")), canManageVendorRoles(
|
|
1416
|
+
}), "Team")), canManageVendorRoles(effectiveSessionUser) && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
1217
1417
|
href: "/admin/vendor-roles",
|
|
1218
1418
|
className: `${linkCls} ${isActive("/admin/vendor-roles") ? linkActive : linkInactive}`
|
|
1219
1419
|
}, /* @__PURE__ */ React.createElement(Shield, {
|
|
@@ -6404,6 +6604,20 @@ var init_FieldConfiguration = __esm({
|
|
|
6404
6604
|
}
|
|
6405
6605
|
});
|
|
6406
6606
|
|
|
6607
|
+
// src/lib/slug-sanitizer.ts
|
|
6608
|
+
function sanitizeSlugInput(value) {
|
|
6609
|
+
return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-");
|
|
6610
|
+
}
|
|
6611
|
+
function normalizeSlug(value) {
|
|
6612
|
+
return sanitizeSlugInput(value).replace(/^-+|-+$/g, "");
|
|
6613
|
+
}
|
|
6614
|
+
var init_slug_sanitizer = __esm({
|
|
6615
|
+
"src/lib/slug-sanitizer.ts"() {
|
|
6616
|
+
__name(sanitizeSlugInput, "sanitizeSlugInput");
|
|
6617
|
+
__name(normalizeSlug, "normalizeSlug");
|
|
6618
|
+
}
|
|
6619
|
+
});
|
|
6620
|
+
|
|
6407
6621
|
// src/components/Admin/FormBuilder.tsx
|
|
6408
6622
|
var FormBuilder_exports = {};
|
|
6409
6623
|
__export(FormBuilder_exports, {
|
|
@@ -6482,18 +6696,6 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6482
6696
|
loadId,
|
|
6483
6697
|
loadFormData
|
|
6484
6698
|
]);
|
|
6485
|
-
useEffect(() => {
|
|
6486
|
-
if (formData.name && !formData.slug) {
|
|
6487
|
-
const generatedSlug = formData.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
6488
|
-
setFormData((prev) => ({
|
|
6489
|
-
...prev,
|
|
6490
|
-
slug: generatedSlug
|
|
6491
|
-
}));
|
|
6492
|
-
}
|
|
6493
|
-
}, [
|
|
6494
|
-
formData.name,
|
|
6495
|
-
formData.slug
|
|
6496
|
-
]);
|
|
6497
6699
|
const addField = /* @__PURE__ */ __name(() => {
|
|
6498
6700
|
const newField = {
|
|
6499
6701
|
id: `field_${Date.now()}`,
|
|
@@ -6598,6 +6800,7 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6598
6800
|
setErrors([]);
|
|
6599
6801
|
const payload = {
|
|
6600
6802
|
...formData,
|
|
6803
|
+
slug: normalizeSlug(formData.slug),
|
|
6601
6804
|
published: isPublishing ? true : formData.published,
|
|
6602
6805
|
fields: formData.fields.map((field) => {
|
|
6603
6806
|
const numericId = typeof field.id === "number" ? field.id : /^\d+$/.test(String(field.id)) ? parseInt(String(field.id), 10) : void 0;
|
|
@@ -6728,8 +6931,12 @@ function FormBuilder({ formId, duplicateFromId }) {
|
|
|
6728
6931
|
value: formData.slug,
|
|
6729
6932
|
onChange: /* @__PURE__ */ __name((e) => setFormData((prev) => ({
|
|
6730
6933
|
...prev,
|
|
6731
|
-
slug: e.target.value
|
|
6934
|
+
slug: sanitizeSlugInput(e.target.value)
|
|
6732
6935
|
})), "onChange"),
|
|
6936
|
+
onBlur: /* @__PURE__ */ __name(() => setFormData((prev) => ({
|
|
6937
|
+
...prev,
|
|
6938
|
+
slug: normalizeSlug(prev.slug)
|
|
6939
|
+
})), "onBlur"),
|
|
6733
6940
|
placeholder: "form-slug"
|
|
6734
6941
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
6735
6942
|
className: "block text-sm font-medium text-gray-700 mb-1"
|
|
@@ -6918,6 +7125,7 @@ var init_FormBuilder = __esm({
|
|
|
6918
7125
|
init_switch();
|
|
6919
7126
|
init_badge();
|
|
6920
7127
|
init_FieldConfiguration();
|
|
7128
|
+
init_slug_sanitizer();
|
|
6921
7129
|
__name(FormBuilder, "FormBuilder");
|
|
6922
7130
|
}
|
|
6923
7131
|
});
|
|
@@ -7967,9 +8175,10 @@ function BlogEditor({ existingBlog, duplicateSource }) {
|
|
|
7967
8175
|
setIsSaving(false);
|
|
7968
8176
|
return;
|
|
7969
8177
|
}
|
|
8178
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
7970
8179
|
const body = {
|
|
7971
8180
|
title,
|
|
7972
|
-
slug,
|
|
8181
|
+
slug: normalizedSlug,
|
|
7973
8182
|
content,
|
|
7974
8183
|
published: isPublishing,
|
|
7975
8184
|
tags,
|
|
@@ -8239,7 +8448,8 @@ function BlogEditor({ existingBlog, duplicateSource }) {
|
|
|
8239
8448
|
type: "text",
|
|
8240
8449
|
placeholder: "blog-post-url",
|
|
8241
8450
|
value: slug,
|
|
8242
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
8451
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
8452
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
8243
8453
|
className: "h-8 text-sm"
|
|
8244
8454
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
8245
8455
|
className: "text-xs text-gray-500 mt-1"
|
|
@@ -8332,6 +8542,7 @@ var init_BlogEditorPage = __esm({
|
|
|
8332
8542
|
init_JoditRichText();
|
|
8333
8543
|
init_CategoryAutocomplete();
|
|
8334
8544
|
init_UserAutocomplete();
|
|
8545
|
+
init_slug_sanitizer();
|
|
8335
8546
|
__name(BlogEditor, "BlogEditor");
|
|
8336
8547
|
}
|
|
8337
8548
|
});
|
|
@@ -26918,14 +27129,55 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
26918
27129
|
status: "available"
|
|
26919
27130
|
});
|
|
26920
27131
|
if (q.length >= 2) params.set("search", q);
|
|
26921
|
-
const res = await
|
|
27132
|
+
const [res, epRes, evRes] = await Promise.all([
|
|
27133
|
+
fetch(`/api/products?${params}`),
|
|
27134
|
+
fetch("/api/event_products?limit=500").catch(() => null),
|
|
27135
|
+
fetch("/api/events?limit=500").catch(() => null)
|
|
27136
|
+
]);
|
|
26922
27137
|
if (!res.ok) {
|
|
26923
27138
|
setProductHits([]);
|
|
26924
27139
|
return;
|
|
26925
27140
|
}
|
|
26926
27141
|
const body = await res.json();
|
|
26927
27142
|
const hits = Array.isArray(body.data) ? body.data : [];
|
|
26928
|
-
|
|
27143
|
+
const eventMap = /* @__PURE__ */ new Map();
|
|
27144
|
+
if (evRes && evRes.ok) {
|
|
27145
|
+
const evBody = await evRes.json().catch(() => ({}));
|
|
27146
|
+
const evList = Array.isArray(evBody.data) ? evBody.data : [];
|
|
27147
|
+
for (const ev of evList) {
|
|
27148
|
+
if (ev && ev.id) eventMap.set(Number(ev.id), ev);
|
|
27149
|
+
}
|
|
27150
|
+
}
|
|
27151
|
+
const productEventLimits = /* @__PURE__ */ new Map();
|
|
27152
|
+
if (epRes && epRes.ok) {
|
|
27153
|
+
const epBody = await epRes.json().catch(() => ({}));
|
|
27154
|
+
const epList = Array.isArray(epBody.data) ? epBody.data : [];
|
|
27155
|
+
for (const ep of epList) {
|
|
27156
|
+
const ev = eventMap.get(Number(ep.eventId));
|
|
27157
|
+
if (ev) {
|
|
27158
|
+
const maxGroup = typeof ev.maxGroupPurchaseQuantity === "number" && Number.isFinite(ev.maxGroupPurchaseQuantity) && ev.maxGroupPurchaseQuantity > 0 ? Math.floor(ev.maxGroupPurchaseQuantity) : null;
|
|
27159
|
+
const allowsGroup = ev.allowGroupOrders !== false;
|
|
27160
|
+
let cap;
|
|
27161
|
+
if (maxGroup !== null && maxGroup > 1) {
|
|
27162
|
+
cap = maxGroup;
|
|
27163
|
+
} else if (!allowsGroup) {
|
|
27164
|
+
cap = 1;
|
|
27165
|
+
} else if (maxGroup !== null) {
|
|
27166
|
+
cap = maxGroup;
|
|
27167
|
+
} else {
|
|
27168
|
+
cap = 999;
|
|
27169
|
+
}
|
|
27170
|
+
productEventLimits.set(Number(ep.productId), cap);
|
|
27171
|
+
}
|
|
27172
|
+
}
|
|
27173
|
+
}
|
|
27174
|
+
hits.forEach((p) => {
|
|
27175
|
+
const cap = productEventLimits.get(p.id);
|
|
27176
|
+
if (cap !== void 0) {
|
|
27177
|
+
p.maxPurchaseLimit = cap;
|
|
27178
|
+
}
|
|
27179
|
+
productCache.current.set(p.id, p);
|
|
27180
|
+
});
|
|
26929
27181
|
setProductHits(hits);
|
|
26930
27182
|
} catch {
|
|
26931
27183
|
setProductHits([]);
|
|
@@ -27202,6 +27454,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27202
27454
|
toast.message("Select one or more products in the list first");
|
|
27203
27455
|
return;
|
|
27204
27456
|
}
|
|
27457
|
+
let reachedLimit = false;
|
|
27205
27458
|
setLines((prev) => {
|
|
27206
27459
|
const next = [
|
|
27207
27460
|
...prev
|
|
@@ -27209,29 +27462,47 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27209
27462
|
for (const id of stagedIds) {
|
|
27210
27463
|
const hit = productCache.current.get(id);
|
|
27211
27464
|
if (!hit) continue;
|
|
27465
|
+
const maxAllowed = typeof hit.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27212
27466
|
const idx = next.findIndex((l) => l.productId === id);
|
|
27213
27467
|
if (idx >= 0) {
|
|
27214
27468
|
const row = next[idx];
|
|
27215
|
-
|
|
27216
|
-
|
|
27217
|
-
|
|
27218
|
-
|
|
27219
|
-
|
|
27220
|
-
|
|
27221
|
-
|
|
27222
|
-
|
|
27223
|
-
|
|
27224
|
-
|
|
27225
|
-
|
|
27469
|
+
if (row.quantity >= maxAllowed) {
|
|
27470
|
+
reachedLimit = true;
|
|
27471
|
+
} else {
|
|
27472
|
+
next[idx] = {
|
|
27473
|
+
...row,
|
|
27474
|
+
quantity: Math.min(maxAllowed, row.quantity + 1)
|
|
27475
|
+
};
|
|
27476
|
+
}
|
|
27477
|
+
} else {
|
|
27478
|
+
next.push({
|
|
27479
|
+
key: String(id),
|
|
27480
|
+
productId: id,
|
|
27481
|
+
label: hit.name ?? `Product #${id}`,
|
|
27482
|
+
sku: hit.sku,
|
|
27483
|
+
quantity: 1
|
|
27484
|
+
});
|
|
27485
|
+
}
|
|
27226
27486
|
}
|
|
27227
27487
|
return next;
|
|
27228
27488
|
});
|
|
27229
27489
|
setStagedIds(/* @__PURE__ */ new Set());
|
|
27230
|
-
|
|
27490
|
+
if (reachedLimit) {
|
|
27491
|
+
toast.error("One or more items reached their maximum purchase limit");
|
|
27492
|
+
} else {
|
|
27493
|
+
toast.success("Added to cart");
|
|
27494
|
+
}
|
|
27231
27495
|
}
|
|
27232
27496
|
__name(addStagedToCart, "addStagedToCart");
|
|
27233
27497
|
function updateQty(key, quantity) {
|
|
27234
|
-
const
|
|
27498
|
+
const line = lines.find((l) => l.key === key);
|
|
27499
|
+
const hit = line ? productCache.current.get(line.productId) : null;
|
|
27500
|
+
const maxAllowed = typeof hit?.maxPurchaseLimit === "number" && hit.maxPurchaseLimit > 0 ? hit.maxPurchaseLimit : 99999;
|
|
27501
|
+
const parsed = Math.max(1, Math.floor(quantity) || 1);
|
|
27502
|
+
const q = Math.min(maxAllowed, parsed);
|
|
27503
|
+
if (parsed > maxAllowed) {
|
|
27504
|
+
toast.error(`Maximum purchase limit for "${line?.label ?? "this product"}" is ${maxAllowed}`);
|
|
27505
|
+
}
|
|
27235
27506
|
setLines((prev) => prev.map((l) => l.key === key ? {
|
|
27236
27507
|
...l,
|
|
27237
27508
|
quantity: q
|
|
@@ -27281,6 +27552,13 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27281
27552
|
toast.error("Add at least one product to the cart");
|
|
27282
27553
|
return;
|
|
27283
27554
|
}
|
|
27555
|
+
for (const line of lines) {
|
|
27556
|
+
const hit = productCache.current.get(line.productId);
|
|
27557
|
+
if (hit?.maxPurchaseLimit && line.quantity > hit.maxPurchaseLimit) {
|
|
27558
|
+
toast.error(`Quantity for "${line.label}" cannot exceed maximum allowed limit of ${hit.maxPurchaseLimit}`);
|
|
27559
|
+
return;
|
|
27560
|
+
}
|
|
27561
|
+
}
|
|
27284
27562
|
const unknown = preview?.lines.filter((l) => !l.found) ?? [];
|
|
27285
27563
|
if (unknown.length > 0) {
|
|
27286
27564
|
toast.error(`One or more items could not be validated: ${unknown.map((u) => u.productName).join(", ")}`);
|
|
@@ -27943,6 +28221,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
27943
28221
|
}, /* @__PURE__ */ React.createElement(Input, {
|
|
27944
28222
|
type: "number",
|
|
27945
28223
|
min: 1,
|
|
28224
|
+
max: productCache.current.get(line.productId)?.maxPurchaseLimit ?? void 0,
|
|
27946
28225
|
className: "h-8 w-14 ml-auto text-right",
|
|
27947
28226
|
value: line.quantity,
|
|
27948
28227
|
onChange: /* @__PURE__ */ __name((e) => updateQty(line.key, Number(e.target.value)), "onChange"),
|
|
@@ -30543,9 +30822,10 @@ function SaveButton({ pageId, pageData, existingSeoId, onSeoIdChange, onSaved, c
|
|
|
30543
30822
|
}
|
|
30544
30823
|
}
|
|
30545
30824
|
}
|
|
30825
|
+
const normalizedSlug = normalizeSlug(pageData.slug);
|
|
30546
30826
|
const payload = {
|
|
30547
30827
|
title: pageData.title,
|
|
30548
|
-
slug:
|
|
30828
|
+
slug: normalizedSlug,
|
|
30549
30829
|
content,
|
|
30550
30830
|
published: pageData.published
|
|
30551
30831
|
};
|
|
@@ -30785,7 +31065,8 @@ function PageBuilderPage({ pageId }) {
|
|
|
30785
31065
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
30786
31066
|
}, "Slug *"), /* @__PURE__ */ React26__default.createElement(Input, {
|
|
30787
31067
|
value: slug,
|
|
30788
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
31068
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31069
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
30789
31070
|
placeholder: "page-url-slug",
|
|
30790
31071
|
className: "h-8 text-sm"
|
|
30791
31072
|
})))), /* @__PURE__ */ React26__default.createElement(RightSidebar, {
|
|
@@ -30822,6 +31103,7 @@ var init_PageBuilderPage = __esm({
|
|
|
30822
31103
|
init_admin_config_context();
|
|
30823
31104
|
init_registry();
|
|
30824
31105
|
init_ImageOrUrlField();
|
|
31106
|
+
init_slug_sanitizer();
|
|
30825
31107
|
__name(createSelectable, "createSelectable");
|
|
30826
31108
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
30827
31109
|
__name(getIcon, "getIcon");
|
|
@@ -31055,7 +31337,8 @@ function BrandEditPage({ brandId }) {
|
|
|
31055
31337
|
]);
|
|
31056
31338
|
return;
|
|
31057
31339
|
}
|
|
31058
|
-
|
|
31340
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
31341
|
+
if (!normalizedSlug) {
|
|
31059
31342
|
setErrors([
|
|
31060
31343
|
"Slug is required"
|
|
31061
31344
|
]);
|
|
@@ -31063,10 +31346,10 @@ function BrandEditPage({ brandId }) {
|
|
|
31063
31346
|
}
|
|
31064
31347
|
setSaving(true);
|
|
31065
31348
|
try {
|
|
31066
|
-
const savedSeoId = await saveSeo(seo,
|
|
31349
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
31067
31350
|
const payload = {
|
|
31068
31351
|
name: name.trim(),
|
|
31069
|
-
slug:
|
|
31352
|
+
slug: normalizedSlug,
|
|
31070
31353
|
description: description || null,
|
|
31071
31354
|
logo: logo || null,
|
|
31072
31355
|
active,
|
|
@@ -31156,7 +31439,9 @@ function BrandEditPage({ brandId }) {
|
|
|
31156
31439
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
31157
31440
|
type: "text",
|
|
31158
31441
|
value: slug,
|
|
31159
|
-
|
|
31442
|
+
placeholder: "brand-slug",
|
|
31443
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
31444
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
31160
31445
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31161
31446
|
required: true
|
|
31162
31447
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -31216,6 +31501,7 @@ var init_BrandEditPage = __esm({
|
|
|
31216
31501
|
init_DetailPageHeader();
|
|
31217
31502
|
init_vendor_scope();
|
|
31218
31503
|
init_ImageOrUrlField();
|
|
31504
|
+
init_slug_sanitizer();
|
|
31219
31505
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31220
31506
|
__name(BrandEditPage, "BrandEditPage");
|
|
31221
31507
|
}
|
|
@@ -32680,11 +32966,21 @@ function ProductEditPage({ productId }) {
|
|
|
32680
32966
|
const pcData = await pcRes.json();
|
|
32681
32967
|
const pcs = Array.isArray(pcData.data) ? pcData.data : [];
|
|
32682
32968
|
if (pcs.length > 0) {
|
|
32683
|
-
|
|
32684
|
-
|
|
32685
|
-
|
|
32686
|
-
|
|
32687
|
-
|
|
32969
|
+
const validTaxPcs = pcs.filter((p) => p.taxId != null && Number(p.taxId) > 0);
|
|
32970
|
+
if (validTaxPcs.length > 0) {
|
|
32971
|
+
setTaxRows(validTaxPcs.map((p) => ({
|
|
32972
|
+
taxId: p.taxId,
|
|
32973
|
+
rate: p.rate != null && p.rate !== "" ? String(p.rate) : ""
|
|
32974
|
+
})));
|
|
32975
|
+
} else if (!cancelled) {
|
|
32976
|
+
setTaxRows([
|
|
32977
|
+
{
|
|
32978
|
+
taxId: "",
|
|
32979
|
+
rate: ""
|
|
32980
|
+
}
|
|
32981
|
+
]);
|
|
32982
|
+
}
|
|
32983
|
+
const firstRefund = pcs.find((p) => p.refundPolicyId != null)?.refundPolicyId;
|
|
32688
32984
|
setRefundPolicyId(firstRefund != null ? Number(firstRefund) : null);
|
|
32689
32985
|
} else if (!cancelled) {
|
|
32690
32986
|
setTaxRows([
|
|
@@ -32693,6 +32989,7 @@ function ProductEditPage({ productId }) {
|
|
|
32693
32989
|
rate: ""
|
|
32694
32990
|
}
|
|
32695
32991
|
]);
|
|
32992
|
+
setRefundPolicyId(null);
|
|
32696
32993
|
}
|
|
32697
32994
|
}
|
|
32698
32995
|
const paRes = await fetch(`/api/product_attributes?productId=${sourceProductId}&limit=100`);
|
|
@@ -33069,14 +33366,7 @@ function ProductEditPage({ productId }) {
|
|
|
33069
33366
|
if (a == null || b == null) return true;
|
|
33070
33367
|
return Math.abs(a - b) > 1e-6;
|
|
33071
33368
|
}, "ratesDiffer");
|
|
33072
|
-
const
|
|
33073
|
-
for (const row of taxRows) {
|
|
33074
|
-
if (row.taxId === "") continue;
|
|
33075
|
-
wantedConfig.set(row.taxId, {
|
|
33076
|
-
rate: parseTaxRate(row.rate),
|
|
33077
|
-
refundPolicyId
|
|
33078
|
-
});
|
|
33079
|
-
}
|
|
33369
|
+
const validTaxRows = taxRows.filter((row) => row.taxId !== "");
|
|
33080
33370
|
console.log("[product_config rows to save]:", taxRows, "refundPolicyId:", refundPolicyId);
|
|
33081
33371
|
const pcListRes = await fetch(`/api/product_config?productId=${savedId}&limit=200`);
|
|
33082
33372
|
const pcListData = pcListRes.ok ? await pcListRes.json() : {
|
|
@@ -33084,17 +33374,60 @@ function ProductEditPage({ productId }) {
|
|
|
33084
33374
|
};
|
|
33085
33375
|
console.log("[product existing pc rows]:", pcListData);
|
|
33086
33376
|
const existingPc = Array.isArray(pcListData.data) ? pcListData.data : [];
|
|
33087
|
-
|
|
33088
|
-
|
|
33089
|
-
|
|
33090
|
-
|
|
33091
|
-
|
|
33377
|
+
if (validTaxRows.length > 0) {
|
|
33378
|
+
const wantedTaxIds = new Set(validTaxRows.map((r) => Number(r.taxId)));
|
|
33379
|
+
for (const ep of existingPc) {
|
|
33380
|
+
if (ep.taxId == null || !wantedTaxIds.has(Number(ep.taxId))) {
|
|
33381
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33382
|
+
method: "DELETE"
|
|
33383
|
+
});
|
|
33384
|
+
}
|
|
33092
33385
|
}
|
|
33093
|
-
|
|
33094
|
-
|
|
33095
|
-
|
|
33096
|
-
|
|
33097
|
-
|
|
33386
|
+
for (const row of validTaxRows) {
|
|
33387
|
+
const taxId = Number(row.taxId);
|
|
33388
|
+
const rate = parseTaxRate(row.rate);
|
|
33389
|
+
const ep = existingPc.find((e) => e.taxId != null && Number(e.taxId) === taxId);
|
|
33390
|
+
if (!ep) {
|
|
33391
|
+
await fetch("/api/product_config", {
|
|
33392
|
+
method: "POST",
|
|
33393
|
+
headers: {
|
|
33394
|
+
"Content-Type": "application/json"
|
|
33395
|
+
},
|
|
33396
|
+
body: JSON.stringify({
|
|
33397
|
+
productId: Number(savedId),
|
|
33398
|
+
taxId,
|
|
33399
|
+
rate,
|
|
33400
|
+
refundPolicyId
|
|
33401
|
+
})
|
|
33402
|
+
});
|
|
33403
|
+
} else {
|
|
33404
|
+
const existingRate = ep.rate == null || String(ep.rate).trim() === "" ? null : Number(ep.rate);
|
|
33405
|
+
const er = Number.isFinite(existingRate) ? existingRate : null;
|
|
33406
|
+
const policyChanged = ep.refundPolicyId !== refundPolicyId;
|
|
33407
|
+
if (ratesDiffer(er, rate) || policyChanged) {
|
|
33408
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33409
|
+
method: "PUT",
|
|
33410
|
+
headers: {
|
|
33411
|
+
"Content-Type": "application/json"
|
|
33412
|
+
},
|
|
33413
|
+
body: JSON.stringify({
|
|
33414
|
+
rate,
|
|
33415
|
+
refundPolicyId
|
|
33416
|
+
})
|
|
33417
|
+
});
|
|
33418
|
+
}
|
|
33419
|
+
}
|
|
33420
|
+
}
|
|
33421
|
+
} else if (refundPolicyId != null) {
|
|
33422
|
+
const nullTaxRow = existingPc.find((e) => e.taxId == null);
|
|
33423
|
+
for (const ep of existingPc) {
|
|
33424
|
+
if (ep.id !== nullTaxRow?.id) {
|
|
33425
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33426
|
+
method: "DELETE"
|
|
33427
|
+
});
|
|
33428
|
+
}
|
|
33429
|
+
}
|
|
33430
|
+
if (!nullTaxRow) {
|
|
33098
33431
|
await fetch("/api/product_config", {
|
|
33099
33432
|
method: "POST",
|
|
33100
33433
|
headers: {
|
|
@@ -33102,27 +33435,27 @@ function ProductEditPage({ productId }) {
|
|
|
33102
33435
|
},
|
|
33103
33436
|
body: JSON.stringify({
|
|
33104
33437
|
productId: Number(savedId),
|
|
33105
|
-
taxId,
|
|
33106
|
-
rate:
|
|
33107
|
-
refundPolicyId
|
|
33438
|
+
taxId: null,
|
|
33439
|
+
rate: null,
|
|
33440
|
+
refundPolicyId
|
|
33441
|
+
})
|
|
33442
|
+
});
|
|
33443
|
+
} else if (nullTaxRow.refundPolicyId !== refundPolicyId) {
|
|
33444
|
+
await fetch(`/api/product_config/${nullTaxRow.id}`, {
|
|
33445
|
+
method: "PUT",
|
|
33446
|
+
headers: {
|
|
33447
|
+
"Content-Type": "application/json"
|
|
33448
|
+
},
|
|
33449
|
+
body: JSON.stringify({
|
|
33450
|
+
refundPolicyId
|
|
33108
33451
|
})
|
|
33109
33452
|
});
|
|
33110
|
-
}
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
method: "PUT",
|
|
33117
|
-
headers: {
|
|
33118
|
-
"Content-Type": "application/json"
|
|
33119
|
-
},
|
|
33120
|
-
body: JSON.stringify({
|
|
33121
|
-
rate: cfg.rate,
|
|
33122
|
-
refundPolicyId: cfg.refundPolicyId
|
|
33123
|
-
})
|
|
33124
|
-
});
|
|
33125
|
-
}
|
|
33453
|
+
}
|
|
33454
|
+
} else {
|
|
33455
|
+
for (const ep of existingPc) {
|
|
33456
|
+
await fetch(`/api/product_config/${ep.id}`, {
|
|
33457
|
+
method: "DELETE"
|
|
33458
|
+
});
|
|
33126
33459
|
}
|
|
33127
33460
|
}
|
|
33128
33461
|
if (hasVariants) {
|
|
@@ -34038,7 +34371,8 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34038
34371
|
]);
|
|
34039
34372
|
return;
|
|
34040
34373
|
}
|
|
34041
|
-
|
|
34374
|
+
const normalizedSlug = normalizeSlug(slug);
|
|
34375
|
+
if (!normalizedSlug) {
|
|
34042
34376
|
setErrors([
|
|
34043
34377
|
"Slug is required"
|
|
34044
34378
|
]);
|
|
@@ -34046,10 +34380,10 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34046
34380
|
}
|
|
34047
34381
|
setSaving(true);
|
|
34048
34382
|
try {
|
|
34049
|
-
const savedSeoId = await saveSeo(seo,
|
|
34383
|
+
const savedSeoId = await saveSeo(seo, normalizedSlug, seoId);
|
|
34050
34384
|
const payload = {
|
|
34051
34385
|
name: name.trim(),
|
|
34052
|
-
slug:
|
|
34386
|
+
slug: normalizedSlug,
|
|
34053
34387
|
hsn: hsn.trim() || null,
|
|
34054
34388
|
categoryId: categoryId || null,
|
|
34055
34389
|
brandId: brandId || null,
|
|
@@ -34218,7 +34552,9 @@ function CollectionEditPage({ collectionId }) {
|
|
|
34218
34552
|
}, "Slug *"), /* @__PURE__ */ React.createElement("input", {
|
|
34219
34553
|
type: "text",
|
|
34220
34554
|
value: slug,
|
|
34221
|
-
|
|
34555
|
+
placeholder: "collection-slug",
|
|
34556
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
34557
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
34222
34558
|
className: inputCls4,
|
|
34223
34559
|
required: true
|
|
34224
34560
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -34454,6 +34790,7 @@ var init_CollectionEditPage = __esm({
|
|
|
34454
34790
|
init_admin_config_context();
|
|
34455
34791
|
init_category_related_product_labels();
|
|
34456
34792
|
init_ImageOrUrlField();
|
|
34793
|
+
init_slug_sanitizer();
|
|
34457
34794
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
34458
34795
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
34459
34796
|
url: "",
|
|
@@ -35633,7 +35970,9 @@ var init_event_entity_types = __esm({
|
|
|
35633
35970
|
// src/admin/pages/EventEditPage.tsx
|
|
35634
35971
|
var EventEditPage_exports = {};
|
|
35635
35972
|
__export(EventEditPage_exports, {
|
|
35636
|
-
default: () => EventEditPage
|
|
35973
|
+
default: () => EventEditPage,
|
|
35974
|
+
normalizeSlug: () => normalizeSlug2,
|
|
35975
|
+
sanitizeSlugInput: () => sanitizeSlugInput2
|
|
35637
35976
|
});
|
|
35638
35977
|
function RequiredLabel({ children }) {
|
|
35639
35978
|
return /* @__PURE__ */ React.createElement("label", {
|
|
@@ -35642,6 +35981,12 @@ function RequiredLabel({ children }) {
|
|
|
35642
35981
|
className: "text-red-600"
|
|
35643
35982
|
}, "*"));
|
|
35644
35983
|
}
|
|
35984
|
+
function sanitizeSlugInput2(value) {
|
|
35985
|
+
return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-");
|
|
35986
|
+
}
|
|
35987
|
+
function normalizeSlug2(value) {
|
|
35988
|
+
return sanitizeSlugInput2(value).replace(/^-+|-+$/g, "");
|
|
35989
|
+
}
|
|
35645
35990
|
function toIsoOrNull(value, timezone) {
|
|
35646
35991
|
const trimmed = value.trim();
|
|
35647
35992
|
if (!trimmed) return null;
|
|
@@ -35732,7 +36077,7 @@ function EventEditPage({ eventId }) {
|
|
|
35732
36077
|
const [supportContact, setSupportContact] = useState("");
|
|
35733
36078
|
const [allowGroupOrders, setAllowGroupOrders] = useState(false);
|
|
35734
36079
|
const [sendIndividualTicketPDFToAttendees, setSendIndividualTicketPDFToAttendees] = useState(false);
|
|
35735
|
-
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = useState("");
|
|
36080
|
+
const [maxGroupPurchaseQuantity, setMaxGroupPurchaseQuantity] = useState("1");
|
|
35736
36081
|
const [sponsors, setSponsors] = useState([]);
|
|
35737
36082
|
const [agenda, setAgenda] = useState([]);
|
|
35738
36083
|
const [workshops, setWorkshops] = useState([]);
|
|
@@ -35858,9 +36203,10 @@ function EventEditPage({ eventId }) {
|
|
|
35858
36203
|
}
|
|
35859
36204
|
setOfficialWebsiteUrl(data.officialWebsiteUrl ?? "");
|
|
35860
36205
|
setSupportContact(data.supportContact ?? "");
|
|
35861
|
-
|
|
36206
|
+
const loadedAllowGroup = data.allowGroupOrders ?? false;
|
|
36207
|
+
setAllowGroupOrders(loadedAllowGroup);
|
|
35862
36208
|
setSendIndividualTicketPDFToAttendees(data.sendIndividualTicketPDFToAttendees ?? false);
|
|
35863
|
-
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : "");
|
|
36209
|
+
setMaxGroupPurchaseQuantity(data.maxGroupPurchaseQuantity != null ? String(data.maxGroupPurchaseQuantity) : loadedAllowGroup ? "10" : "1");
|
|
35864
36210
|
setSponsors(parseNamedListFromApi(data.sponsors, "sponsor"));
|
|
35865
36211
|
setAgenda(parseNamedListFromApi(data.agenda, "agenda"));
|
|
35866
36212
|
setWorkshops(parseNamedListFromApi(data.workshops, "workshop"));
|
|
@@ -35906,8 +36252,13 @@ function EventEditPage({ eventId }) {
|
|
|
35906
36252
|
]);
|
|
35907
36253
|
const buildPayload = /* @__PURE__ */ __name(() => {
|
|
35908
36254
|
const nextErrors = [];
|
|
36255
|
+
const normalizedSlug = normalizeSlug2(slug);
|
|
35909
36256
|
if (!name.trim()) nextErrors.push("Name is required");
|
|
35910
|
-
if (!
|
|
36257
|
+
if (!normalizedSlug) {
|
|
36258
|
+
nextErrors.push("Slug is required");
|
|
36259
|
+
} else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(normalizedSlug)) {
|
|
36260
|
+
nextErrors.push("Slug must contain only lowercase letters, numbers, and hyphens without leading or trailing hyphens or slashes");
|
|
36261
|
+
}
|
|
35911
36262
|
if (!startDate.trim()) nextErrors.push("Event Start Date & Time is required");
|
|
35912
36263
|
if (!endDate.trim()) nextErrors.push("Event End Date & Time is required");
|
|
35913
36264
|
if (startDate.trim() && !timezone.trim()) {
|
|
@@ -35933,6 +36284,20 @@ function EventEditPage({ eventId }) {
|
|
|
35933
36284
|
nextErrors.push("Event End Date & Time must be after Event Start Date & Time");
|
|
35934
36285
|
}
|
|
35935
36286
|
}
|
|
36287
|
+
if (registrationOpensAt.trim() && registrationClosesAt.trim()) {
|
|
36288
|
+
const regOpen = new Date(toIsoOrNull(registrationOpensAt, timezone) ?? "");
|
|
36289
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36290
|
+
if (!Number.isNaN(regOpen.getTime()) && !Number.isNaN(regClose.getTime()) && regClose < regOpen) {
|
|
36291
|
+
nextErrors.push("Registration Closes Date & Time must be after Registration Opens Date & Time");
|
|
36292
|
+
}
|
|
36293
|
+
}
|
|
36294
|
+
if (registrationClosesAt.trim() && endDate.trim()) {
|
|
36295
|
+
const regClose = new Date(toIsoOrNull(registrationClosesAt, timezone) ?? "");
|
|
36296
|
+
const end = new Date(toIsoOrNull(endDate, timezone) ?? "");
|
|
36297
|
+
if (!Number.isNaN(regClose.getTime()) && !Number.isNaN(end.getTime()) && regClose > end) {
|
|
36298
|
+
nextErrors.push("Registration cannot close after Event End Date & Time");
|
|
36299
|
+
}
|
|
36300
|
+
}
|
|
35936
36301
|
if (additionalVenueDetails.length > ADDITIONAL_VENUE_MAX_CHARS) {
|
|
35937
36302
|
nextErrors.push(`Additional Venue Details must be ${ADDITIONAL_VENUE_MAX_CHARS} characters or fewer`);
|
|
35938
36303
|
}
|
|
@@ -35942,7 +36307,7 @@ function EventEditPage({ eventId }) {
|
|
|
35942
36307
|
}
|
|
35943
36308
|
const payload = {
|
|
35944
36309
|
name: name.trim(),
|
|
35945
|
-
slug:
|
|
36310
|
+
slug: normalizedSlug,
|
|
35946
36311
|
description: description.trim() || null,
|
|
35947
36312
|
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
35948
36313
|
comingSoon,
|
|
@@ -35970,7 +36335,7 @@ function EventEditPage({ eventId }) {
|
|
|
35970
36335
|
supportContact: supportContact.trim() || null,
|
|
35971
36336
|
allowGroupOrders,
|
|
35972
36337
|
sendIndividualTicketPDFToAttendees,
|
|
35973
|
-
maxGroupPurchaseQuantity: maxGroupPurchaseQuantity
|
|
36338
|
+
maxGroupPurchaseQuantity: allowGroupOrders ? Number(maxGroupPurchaseQuantity) > 0 ? Number(maxGroupPurchaseQuantity) : 10 : 1,
|
|
35974
36339
|
sponsors: namedListToPayload(sponsors, {
|
|
35975
36340
|
legacySponsorKeys: true
|
|
35976
36341
|
}),
|
|
@@ -36238,9 +36603,16 @@ function EventEditPage({ eventId }) {
|
|
|
36238
36603
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36239
36604
|
className: labelCls6
|
|
36240
36605
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
36606
|
+
id: "event-slug",
|
|
36241
36607
|
type: "text",
|
|
36242
36608
|
value: slug,
|
|
36243
|
-
|
|
36609
|
+
placeholder: "event-slug",
|
|
36610
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36611
|
+
setSlug(sanitizeSlugInput2(e.target.value));
|
|
36612
|
+
}, "onChange"),
|
|
36613
|
+
onBlur: /* @__PURE__ */ __name(() => {
|
|
36614
|
+
setSlug((prev) => normalizeSlug2(prev));
|
|
36615
|
+
}, "onBlur"),
|
|
36244
36616
|
className: inputCls6
|
|
36245
36617
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
36246
36618
|
className: labelCls6
|
|
@@ -36500,7 +36872,16 @@ function EventEditPage({ eventId }) {
|
|
|
36500
36872
|
}, "Max group purchase quantity"), /* @__PURE__ */ React.createElement("input", {
|
|
36501
36873
|
type: "number",
|
|
36502
36874
|
value: maxGroupPurchaseQuantity,
|
|
36503
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36875
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36876
|
+
const val = e.target.value;
|
|
36877
|
+
setMaxGroupPurchaseQuantity(val);
|
|
36878
|
+
const num = Number(val);
|
|
36879
|
+
if (Number.isFinite(num) && num > 1) {
|
|
36880
|
+
setAllowGroupOrders(true);
|
|
36881
|
+
} else if (Number.isFinite(num) && num <= 1) {
|
|
36882
|
+
setAllowGroupOrders(false);
|
|
36883
|
+
}
|
|
36884
|
+
}, "onChange"),
|
|
36504
36885
|
className: inputCls6,
|
|
36505
36886
|
min: 1
|
|
36506
36887
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -36510,7 +36891,18 @@ function EventEditPage({ eventId }) {
|
|
|
36510
36891
|
}, /* @__PURE__ */ React.createElement("input", {
|
|
36511
36892
|
type: "checkbox",
|
|
36512
36893
|
checked: allowGroupOrders,
|
|
36513
|
-
onChange: /* @__PURE__ */ __name((e) =>
|
|
36894
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
36895
|
+
const checked = e.target.checked;
|
|
36896
|
+
setAllowGroupOrders(checked);
|
|
36897
|
+
if (!checked) {
|
|
36898
|
+
setMaxGroupPurchaseQuantity("1");
|
|
36899
|
+
} else {
|
|
36900
|
+
const current = Number(maxGroupPurchaseQuantity);
|
|
36901
|
+
if (!Number.isFinite(current) || current <= 1) {
|
|
36902
|
+
setMaxGroupPurchaseQuantity("10");
|
|
36903
|
+
}
|
|
36904
|
+
}
|
|
36905
|
+
}, "onChange"),
|
|
36514
36906
|
className: "h-4 w-4 rounded border-gray-300"
|
|
36515
36907
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
36516
36908
|
className: "text-sm text-gray-900"
|
|
@@ -36584,6 +36976,8 @@ var init_EventEditPage = __esm({
|
|
|
36584
36976
|
labelCls6 = "block text-xs font-medium text-gray-600 mb-1";
|
|
36585
36977
|
inputCls6 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
36586
36978
|
__name(RequiredLabel, "RequiredLabel");
|
|
36979
|
+
__name(sanitizeSlugInput2, "sanitizeSlugInput");
|
|
36980
|
+
__name(normalizeSlug2, "normalizeSlug");
|
|
36587
36981
|
ENTITY_TYPE_OPTIONS = [
|
|
36588
36982
|
...EVENT_ENTITY_TYPE_OPTIONS
|
|
36589
36983
|
];
|
|
@@ -36962,7 +37356,7 @@ function ComboEditPage({ comboId }) {
|
|
|
36962
37356
|
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
36963
37357
|
const payload = {
|
|
36964
37358
|
name: trimmedName,
|
|
36965
|
-
slug: slug.trim()
|
|
37359
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
36966
37360
|
desc: desc || null,
|
|
36967
37361
|
eventId: resolvedEventId,
|
|
36968
37362
|
price,
|
|
@@ -37057,8 +37451,9 @@ function ComboEditPage({ comboId }) {
|
|
|
37057
37451
|
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
37058
37452
|
type: "text",
|
|
37059
37453
|
value: slug,
|
|
37060
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37061
|
-
|
|
37454
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
37455
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
37456
|
+
placeholder: "combo-slug",
|
|
37062
37457
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
37063
37458
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
37064
37459
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -37224,6 +37619,7 @@ var init_ComboEditPage = __esm({
|
|
|
37224
37619
|
init_DetailPageHeader();
|
|
37225
37620
|
init_inventory_validation();
|
|
37226
37621
|
init_admin_config_context();
|
|
37622
|
+
init_slug_sanitizer();
|
|
37227
37623
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
37228
37624
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
37229
37625
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -37455,7 +37851,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37455
37851
|
const vendorPayload = /* @__PURE__ */ __name(() => ({
|
|
37456
37852
|
name: name.trim(),
|
|
37457
37853
|
legalName: legalName.trim() || null,
|
|
37458
|
-
slug: slug.trim()
|
|
37854
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37459
37855
|
businessType: businessType || null,
|
|
37460
37856
|
description: description.trim() || null,
|
|
37461
37857
|
website: website.trim() || null,
|
|
@@ -37797,7 +38193,7 @@ function VendorEditPage({ vendorId }) {
|
|
|
37797
38193
|
signal: abortController.signal,
|
|
37798
38194
|
body: JSON.stringify({
|
|
37799
38195
|
...vendorPayload(),
|
|
37800
|
-
slug: slug.trim(),
|
|
38196
|
+
slug: slug.trim() ? normalizeSlug(slug) : void 0,
|
|
37801
38197
|
metadata: (() => {
|
|
37802
38198
|
const next = {
|
|
37803
38199
|
...metadata ?? {}
|
|
@@ -37920,8 +38316,9 @@ function VendorEditPage({ vendorId }) {
|
|
|
37920
38316
|
}, "Slug ", create ? "(optional)" : "*"), /* @__PURE__ */ React26__default.createElement(Input, {
|
|
37921
38317
|
id: "vendorSlug",
|
|
37922
38318
|
value: slug,
|
|
37923
|
-
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
37924
|
-
|
|
38319
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(sanitizeSlugInput(e.target.value)), "onChange"),
|
|
38320
|
+
onBlur: /* @__PURE__ */ __name(() => setSlug((prev) => normalizeSlug(prev)), "onBlur"),
|
|
38321
|
+
placeholder: "vendor-slug",
|
|
37925
38322
|
className: `mt-1 ${fieldClass}`
|
|
37926
38323
|
})), /* @__PURE__ */ React26__default.createElement("div", null, /* @__PURE__ */ React26__default.createElement(FieldLabel, {
|
|
37927
38324
|
htmlFor: "businessType"
|
|
@@ -38262,6 +38659,7 @@ var init_VendorEditPage = __esm({
|
|
|
38262
38659
|
init_checkbox();
|
|
38263
38660
|
init_dialog();
|
|
38264
38661
|
init_vendor_profile();
|
|
38662
|
+
init_slug_sanitizer();
|
|
38265
38663
|
init_vendor_list_config();
|
|
38266
38664
|
init_vendor_access_denied();
|
|
38267
38665
|
init_vendor_access_denied();
|