@infuro/cms-core 1.0.41 → 1.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -18
- package/dist/admin.cjs +959 -303
- package/dist/admin.d.cts +14 -2
- package/dist/admin.d.ts +14 -2
- package/dist/admin.js +959 -303
- package/dist/api.cjs +35 -35
- package/dist/api.js +3 -3
- package/dist/auth.cjs +51 -51
- package/dist/auth.js +3 -3
- package/dist/{chunk-UXMDSNIG.js → chunk-BLR6H5GL.js} +1 -1
- package/dist/{chunk-P7QCLE5W.js → chunk-DN65KDIA.js} +11 -1
- package/dist/{chunk-4BG7XYSH.js → chunk-I6NH72MO.js} +1 -1
- package/dist/{chunk-EG4ICILK.js → chunk-IX7X4ILO.js} +811 -143
- package/dist/{chunk-QU554KAZ.cjs → chunk-SCPKMZG5.cjs} +862 -182
- package/dist/{chunk-ETZN4OQI.cjs → chunk-TAHX46EI.cjs} +6 -6
- package/dist/{chunk-WMXPKKXU.cjs → chunk-X6UQFV4X.cjs} +11 -1
- package/dist/{chunk-OIGCAGH7.cjs → chunk-ZMRQ72F6.cjs} +7 -7
- package/dist/index.cjs +290 -242
- package/dist/index.d.cts +110 -5
- package/dist/index.d.ts +110 -5
- package/dist/index.js +6 -6
- package/dist/migrations/1781300000000-BrandIsCatalog.ts +61 -0
- package/dist/migrations/1781400000000-ComboEventIdNullable.ts +30 -0
- package/dist/migrations/1781500000000-ProductApprovalWorkflow.ts +30 -0
- package/dist/migrations/1781600000000-ProductApprovalStatusColumn.ts +53 -0
- package/dist/migrations/1781700000000-EventApprovalWorkflow.ts +32 -0
- package/dist/{rbac-debug-db-QWHOENBR.js → rbac-debug-db-2SGNLANF.js} +1 -1
- package/dist/{rbac-debug-db-KHG4FJC3.cjs → rbac-debug-db-4Z6A425H.cjs} +2 -2
- package/package.json +1 -1
package/dist/admin.cjs
CHANGED
|
@@ -492,7 +492,7 @@ var init_admin_config_context = __esm({
|
|
|
492
492
|
var CMS_VERSION;
|
|
493
493
|
var init_cms_version = __esm({
|
|
494
494
|
"src/lib/cms-version.ts"() {
|
|
495
|
-
CMS_VERSION = "1.0.
|
|
495
|
+
CMS_VERSION = "1.0.43" ;
|
|
496
496
|
}
|
|
497
497
|
});
|
|
498
498
|
function useCatalogCategories(enabled = true) {
|
|
@@ -602,9 +602,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
602
602
|
const sessionUser = session?.user;
|
|
603
603
|
const showVendorOnboard = canOnboardVendors(sessionUser);
|
|
604
604
|
const vendorPortal = isVendorPortalUser(sessionUser);
|
|
605
|
-
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
605
|
+
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands } = React26.useContext(exports.AdminConfigContext);
|
|
606
606
|
const showStoreNav = storeEnabled || vendorPortal;
|
|
607
607
|
const showPlatformNav = !vendorPortal;
|
|
608
|
+
const showVendorCategories = !vendorPortal || vendorCanCreateCategories === true;
|
|
609
|
+
const showVendorCollections = !vendorPortal || vendorCanCreateCollections === true;
|
|
610
|
+
const showVendorBrands = !vendorPortal || vendorCanCreateBrands === true;
|
|
608
611
|
const isDrawer = variant === "drawer";
|
|
609
612
|
const { categories: catalogCategories } = useCatalogCategories(showStoreNav);
|
|
610
613
|
searchParams.get("categoryId")?.trim() ?? "";
|
|
@@ -713,17 +716,17 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
713
716
|
className: `${linkCls} ${isActive("/admin/vendors") ? linkActive : linkInactive}`
|
|
714
717
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Store, {
|
|
715
718
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendors") ? iconActive : iconInactive}`
|
|
716
|
-
}), "Vendors")),
|
|
719
|
+
}), "Vendors")), showVendorCategories && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
717
720
|
href: "/admin/product_categories",
|
|
718
721
|
className: `${linkCls} ${isActive("/admin/product_categories") ? linkActive : linkInactive}`
|
|
719
722
|
}, /* @__PURE__ */ React.createElement(LucideIcons.FolderTree, {
|
|
720
723
|
className: `h-4 w-4 mr-2 ${isActive("/admin/product_categories") ? iconActive : iconInactive}`
|
|
721
|
-
}), "Categories")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
724
|
+
}), "Categories")), showVendorCollections && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
722
725
|
href: "/admin/collections",
|
|
723
726
|
className: `${linkCls} ${isActive("/admin/collections") ? linkActive : linkInactive}`
|
|
724
727
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Layers, {
|
|
725
728
|
className: `h-4 w-4 mr-2 ${isActive("/admin/collections") ? iconActive : iconInactive}`
|
|
726
|
-
}), "Collections")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
729
|
+
}), "Collections")), showVendorBrands && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
727
730
|
href: "/admin/brands",
|
|
728
731
|
className: `${linkCls} ${isActive("/admin/brands") ? linkActive : linkInactive}`
|
|
729
732
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Building2, {
|
|
@@ -1406,33 +1409,52 @@ function parseBooleanSetting(value) {
|
|
|
1406
1409
|
}
|
|
1407
1410
|
return null;
|
|
1408
1411
|
}
|
|
1409
|
-
function
|
|
1412
|
+
function useMultiVendorSettings() {
|
|
1410
1413
|
const [multiVendorEnabled, setMultiVendorEnabled] = React26.useState(true);
|
|
1414
|
+
const [vendorCanCreateCategories, setVendorCanCreateCategories] = React26.useState(false);
|
|
1415
|
+
const [vendorCanCreateCollections, setVendorCanCreateCollections] = React26.useState(false);
|
|
1416
|
+
const [vendorCanCreateBrands, setVendorCanCreateBrands] = React26.useState(false);
|
|
1417
|
+
const [requireProductApproval, setRequireProductApproval] = React26.useState(false);
|
|
1411
1418
|
React26.useEffect(() => {
|
|
1412
1419
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
1413
1420
|
const parsed = parseBooleanSetting(data?.enabled ?? data?.value ?? data?.isEnabled ?? data?.active ?? data?.state);
|
|
1414
1421
|
setMultiVendorEnabled(parsed ?? true);
|
|
1422
|
+
setVendorCanCreateCategories(parseBooleanSetting(data?.vendorCanCreateCategories) === true);
|
|
1423
|
+
setVendorCanCreateCollections(parseBooleanSetting(data?.vendorCanCreateCollections) === true);
|
|
1424
|
+
setVendorCanCreateBrands(parseBooleanSetting(data?.vendorCanCreateBrands) === true);
|
|
1425
|
+
setRequireProductApproval(parseBooleanSetting(data?.requireProductApproval) === true);
|
|
1415
1426
|
}).catch(() => {
|
|
1416
1427
|
});
|
|
1417
1428
|
}, []);
|
|
1418
|
-
return
|
|
1429
|
+
return {
|
|
1430
|
+
multiVendorEnabled,
|
|
1431
|
+
vendorCanCreateCategories,
|
|
1432
|
+
vendorCanCreateCollections,
|
|
1433
|
+
vendorCanCreateBrands,
|
|
1434
|
+
requireProductApproval
|
|
1435
|
+
};
|
|
1419
1436
|
}
|
|
1420
|
-
function
|
|
1437
|
+
function useEventsSettings() {
|
|
1421
1438
|
const [eventsEnabled, setEventsEnabled] = React26.useState(true);
|
|
1439
|
+
const [requireEventApproval, setRequireEventApproval] = React26.useState(false);
|
|
1422
1440
|
React26.useEffect(() => {
|
|
1423
1441
|
fetch("/api/settings/events").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
1424
1442
|
const parsed = parseBooleanSetting(data?.enabled ?? data?.value ?? data?.isEnabled ?? data?.active ?? data?.state);
|
|
1425
1443
|
setEventsEnabled(parsed ?? true);
|
|
1444
|
+
setRequireEventApproval(parseBooleanSetting(data?.requireEventApproval) === true);
|
|
1426
1445
|
}).catch(() => {
|
|
1427
1446
|
});
|
|
1428
1447
|
}, []);
|
|
1429
|
-
return
|
|
1448
|
+
return {
|
|
1449
|
+
eventsEnabled,
|
|
1450
|
+
requireEventApproval
|
|
1451
|
+
};
|
|
1430
1452
|
}
|
|
1431
1453
|
function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, theme, themeRegistry, pluginDescriptors = [] }) {
|
|
1432
1454
|
const resolvedTheme = useResolvedTheme(theme, themeRegistry);
|
|
1433
1455
|
const { storeEnabled, currency } = useStoreEnabled();
|
|
1434
|
-
const multiVendorEnabled =
|
|
1435
|
-
const eventsEnabled =
|
|
1456
|
+
const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
|
|
1457
|
+
const { eventsEnabled, requireEventApproval } = useEventsSettings();
|
|
1436
1458
|
const mergedPluginDescriptors = React26.useMemo(() => {
|
|
1437
1459
|
const seen = new Set(pluginDescriptors.map((p) => p.name));
|
|
1438
1460
|
const extra = BUILTIN_PLUGIN_DESCRIPTORS.filter((p) => !seen.has(p.name));
|
|
@@ -1455,6 +1477,11 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1455
1477
|
storeEnabled,
|
|
1456
1478
|
currency,
|
|
1457
1479
|
multiVendorEnabled,
|
|
1480
|
+
vendorCanCreateCategories,
|
|
1481
|
+
vendorCanCreateCollections,
|
|
1482
|
+
vendorCanCreateBrands,
|
|
1483
|
+
requireProductApproval,
|
|
1484
|
+
requireEventApproval,
|
|
1458
1485
|
eventsEnabled
|
|
1459
1486
|
}), [
|
|
1460
1487
|
customNavItems,
|
|
@@ -1468,6 +1495,11 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1468
1495
|
storeEnabled,
|
|
1469
1496
|
currency,
|
|
1470
1497
|
multiVendorEnabled,
|
|
1498
|
+
vendorCanCreateCategories,
|
|
1499
|
+
vendorCanCreateCollections,
|
|
1500
|
+
vendorCanCreateBrands,
|
|
1501
|
+
requireProductApproval,
|
|
1502
|
+
requireEventApproval,
|
|
1471
1503
|
eventsEnabled
|
|
1472
1504
|
]);
|
|
1473
1505
|
return /* @__PURE__ */ React.createElement(exports.AdminConfigContext.Provider, {
|
|
@@ -1504,8 +1536,8 @@ var init_AdminLayout = __esm({
|
|
|
1504
1536
|
__name(useResolvedTheme, "useResolvedTheme");
|
|
1505
1537
|
__name(useStoreEnabled, "useStoreEnabled");
|
|
1506
1538
|
__name(parseBooleanSetting, "parseBooleanSetting");
|
|
1507
|
-
__name(
|
|
1508
|
-
__name(
|
|
1539
|
+
__name(useMultiVendorSettings, "useMultiVendorSettings");
|
|
1540
|
+
__name(useEventsSettings, "useEventsSettings");
|
|
1509
1541
|
__name(AdminLayout, "AdminLayout");
|
|
1510
1542
|
}
|
|
1511
1543
|
});
|
|
@@ -6676,9 +6708,10 @@ var init_CategoryAutocomplete = __esm({
|
|
|
6676
6708
|
__name(CategoryAutocomplete, "CategoryAutocomplete");
|
|
6677
6709
|
}
|
|
6678
6710
|
});
|
|
6679
|
-
function UserAutocomplete({ selectedUserId, onUserChange, placeholder = "Select author...", className = "" }) {
|
|
6711
|
+
function UserAutocomplete({ selectedUserId, onUserChange, placeholder = "Select author...", className = "", groupName = ADMIN_GROUP_NAME }) {
|
|
6680
6712
|
const [inputValue, setInputValue] = React26.useState("");
|
|
6681
6713
|
const [suggestions, setSuggestions] = React26.useState([]);
|
|
6714
|
+
const [selectedUser, setSelectedUser] = React26.useState(null);
|
|
6682
6715
|
const [isLoading, setIsLoading] = React26.useState(false);
|
|
6683
6716
|
const [showSuggestions, setShowSuggestions] = React26.useState(false);
|
|
6684
6717
|
const inputRef = React26.useRef(null);
|
|
@@ -6687,14 +6720,18 @@ function UserAutocomplete({ selectedUserId, onUserChange, placeholder = "Select
|
|
|
6687
6720
|
setIsLoading(true);
|
|
6688
6721
|
try {
|
|
6689
6722
|
const params = new URLSearchParams();
|
|
6690
|
-
if (query.trim())
|
|
6691
|
-
params.append("search", query);
|
|
6692
|
-
}
|
|
6723
|
+
if (query.trim()) params.append("search", query);
|
|
6693
6724
|
params.append("limit", "50");
|
|
6725
|
+
const group = groupName.trim() || ADMIN_GROUP_NAME;
|
|
6726
|
+
params.append("groupName", group);
|
|
6694
6727
|
const response = await fetch(`/api/users?${params}`);
|
|
6695
6728
|
if (response.ok) {
|
|
6696
6729
|
const data = await response.json();
|
|
6697
|
-
|
|
6730
|
+
const rows = Array.isArray(data.data) ? data.data : [];
|
|
6731
|
+
setSuggestions(rows.filter((u) => {
|
|
6732
|
+
const g = u.group?.name;
|
|
6733
|
+
return g == null || g === group;
|
|
6734
|
+
}));
|
|
6698
6735
|
}
|
|
6699
6736
|
} catch (error) {
|
|
6700
6737
|
console.error("Error fetching users:", error);
|
|
@@ -6708,28 +6745,57 @@ function UserAutocomplete({ selectedUserId, onUserChange, placeholder = "Select
|
|
|
6708
6745
|
}, 300);
|
|
6709
6746
|
return () => clearTimeout(timeoutId);
|
|
6710
6747
|
}, [
|
|
6711
|
-
inputValue
|
|
6748
|
+
inputValue,
|
|
6749
|
+
groupName
|
|
6750
|
+
]);
|
|
6751
|
+
React26.useEffect(() => {
|
|
6752
|
+
if (selectedUserId == null) {
|
|
6753
|
+
setSelectedUser(null);
|
|
6754
|
+
return;
|
|
6755
|
+
}
|
|
6756
|
+
if (selectedUser?.id === selectedUserId) return;
|
|
6757
|
+
let cancelled = false;
|
|
6758
|
+
(async () => {
|
|
6759
|
+
try {
|
|
6760
|
+
const res = await fetch(`/api/users/${selectedUserId}`);
|
|
6761
|
+
if (!res.ok || cancelled) return;
|
|
6762
|
+
const data = await res.json();
|
|
6763
|
+
if (!cancelled && data?.id != null) {
|
|
6764
|
+
setSelectedUser({
|
|
6765
|
+
id: Number(data.id),
|
|
6766
|
+
name: String(data.name ?? ""),
|
|
6767
|
+
email: String(data.email ?? "")
|
|
6768
|
+
});
|
|
6769
|
+
}
|
|
6770
|
+
} catch {
|
|
6771
|
+
}
|
|
6772
|
+
})();
|
|
6773
|
+
return () => {
|
|
6774
|
+
cancelled = true;
|
|
6775
|
+
};
|
|
6776
|
+
}, [
|
|
6777
|
+
selectedUserId,
|
|
6778
|
+
selectedUser?.id
|
|
6712
6779
|
]);
|
|
6713
6780
|
const handleInputChange = /* @__PURE__ */ __name((e) => {
|
|
6714
|
-
|
|
6715
|
-
setInputValue(value);
|
|
6781
|
+
setInputValue(e.target.value);
|
|
6716
6782
|
setShowSuggestions(true);
|
|
6717
6783
|
}, "handleInputChange");
|
|
6718
6784
|
const handleSuggestionSelect = /* @__PURE__ */ __name((user) => {
|
|
6719
6785
|
onUserChange(user.id);
|
|
6786
|
+
setSelectedUser(user);
|
|
6720
6787
|
setInputValue("");
|
|
6721
6788
|
setShowSuggestions(false);
|
|
6722
6789
|
setSuggestions([]);
|
|
6723
6790
|
}, "handleSuggestionSelect");
|
|
6724
6791
|
const handleRemoveUser = /* @__PURE__ */ __name(() => {
|
|
6725
6792
|
onUserChange(null);
|
|
6793
|
+
setSelectedUser(null);
|
|
6726
6794
|
}, "handleRemoveUser");
|
|
6727
6795
|
const handleKeyPress = /* @__PURE__ */ __name((e) => {
|
|
6728
6796
|
if (e.key === "Enter") {
|
|
6729
6797
|
e.preventDefault();
|
|
6730
|
-
if (suggestions.length > 0)
|
|
6731
|
-
handleSuggestionSelect(suggestions[0]);
|
|
6732
|
-
}
|
|
6798
|
+
if (suggestions.length > 0) handleSuggestionSelect(suggestions[0]);
|
|
6733
6799
|
} else if (e.key === "Escape") {
|
|
6734
6800
|
setShowSuggestions(false);
|
|
6735
6801
|
inputRef.current?.blur();
|
|
@@ -6744,14 +6810,13 @@ function UserAutocomplete({ selectedUserId, onUserChange, placeholder = "Select
|
|
|
6744
6810
|
document.addEventListener("mousedown", handleClickOutside);
|
|
6745
6811
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
6746
6812
|
}, []);
|
|
6747
|
-
const selectedUser = suggestions.find((user) => user.id === selectedUserId);
|
|
6748
6813
|
return /* @__PURE__ */ React.createElement("div", {
|
|
6749
6814
|
className: `relative ${className}`
|
|
6750
6815
|
}, selectedUser && /* @__PURE__ */ React.createElement("div", {
|
|
6751
6816
|
className: "mb-2"
|
|
6752
6817
|
}, /* @__PURE__ */ React.createElement(Badge, {
|
|
6753
6818
|
className: "flex items-center gap-1"
|
|
6754
|
-
}, selectedUser.name
|
|
6819
|
+
}, selectedUser.name || selectedUser.email || `User #${selectedUser.id}`, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
6755
6820
|
size: 12,
|
|
6756
6821
|
className: "cursor-pointer hover:text-red-500",
|
|
6757
6822
|
onClick: handleRemoveUser
|
|
@@ -6795,6 +6860,7 @@ var init_UserAutocomplete = __esm({
|
|
|
6795
6860
|
"use client";
|
|
6796
6861
|
init_input();
|
|
6797
6862
|
init_badge();
|
|
6863
|
+
init_permission_entities();
|
|
6798
6864
|
__name(UserAutocomplete, "UserAutocomplete");
|
|
6799
6865
|
}
|
|
6800
6866
|
});
|
|
@@ -7225,7 +7291,8 @@ function BlogEditor({ existingBlog, duplicateSource }) {
|
|
|
7225
7291
|
selectedUserId: authorId,
|
|
7226
7292
|
onUserChange: setAuthorId,
|
|
7227
7293
|
placeholder: "Select author...",
|
|
7228
|
-
className: "w-full"
|
|
7294
|
+
className: "w-full",
|
|
7295
|
+
groupName: "Administrator"
|
|
7229
7296
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageUpload, {
|
|
7230
7297
|
value: coverImage,
|
|
7231
7298
|
onChange: setCoverImage,
|
|
@@ -7736,6 +7803,155 @@ var init_ComponentSettings = __esm({
|
|
|
7736
7803
|
__name(ComponentSettings, "ComponentSettings");
|
|
7737
7804
|
}
|
|
7738
7805
|
});
|
|
7806
|
+
function ImageOrUrlField({ label, value, onChange, placeholder = "https://\u2026", inputClassName = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm", labelClassName = "block text-xs font-medium text-gray-600 mb-1", previewVariant = "banner", maxSizeMb = 10 }) {
|
|
7807
|
+
const fileInputRef = React26.useRef(null);
|
|
7808
|
+
const [isUploading, setIsUploading] = React26.useState(false);
|
|
7809
|
+
const [error, setError] = React26.useState(null);
|
|
7810
|
+
const [lightboxOpen, setLightboxOpen] = React26.useState(false);
|
|
7811
|
+
const previewCls = previewVariant === "logo" ? "h-20 w-20 rounded-md border border-gray-200 bg-white object-contain cursor-pointer" : "max-h-32 w-full rounded-md border border-gray-200 bg-gray-50 object-cover cursor-pointer";
|
|
7812
|
+
const closeLightbox = React26.useCallback(() => setLightboxOpen(false), []);
|
|
7813
|
+
React26.useEffect(() => {
|
|
7814
|
+
if (!lightboxOpen) return;
|
|
7815
|
+
const onKeyDown = /* @__PURE__ */ __name((e) => {
|
|
7816
|
+
if (e.key === "Escape") closeLightbox();
|
|
7817
|
+
}, "onKeyDown");
|
|
7818
|
+
window.addEventListener("keydown", onKeyDown);
|
|
7819
|
+
const prevOverflow = document.body.style.overflow;
|
|
7820
|
+
document.body.style.overflow = "hidden";
|
|
7821
|
+
return () => {
|
|
7822
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
7823
|
+
document.body.style.overflow = prevOverflow;
|
|
7824
|
+
};
|
|
7825
|
+
}, [
|
|
7826
|
+
lightboxOpen,
|
|
7827
|
+
closeLightbox
|
|
7828
|
+
]);
|
|
7829
|
+
const handleUpload = React26.useCallback(async (file) => {
|
|
7830
|
+
setError(null);
|
|
7831
|
+
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
7832
|
+
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
7833
|
+
return;
|
|
7834
|
+
}
|
|
7835
|
+
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
7836
|
+
setError(`File must be under ${maxSizeMb}MB`);
|
|
7837
|
+
return;
|
|
7838
|
+
}
|
|
7839
|
+
setIsUploading(true);
|
|
7840
|
+
try {
|
|
7841
|
+
const formData = new FormData();
|
|
7842
|
+
formData.append("file", file);
|
|
7843
|
+
const response = await fetch("/api/upload", {
|
|
7844
|
+
method: "POST",
|
|
7845
|
+
body: formData
|
|
7846
|
+
});
|
|
7847
|
+
const data = await response.json();
|
|
7848
|
+
if (!response.ok) {
|
|
7849
|
+
throw new Error(data.error || data.details || "Upload failed");
|
|
7850
|
+
}
|
|
7851
|
+
onChange(data.filePath ?? "");
|
|
7852
|
+
} catch (err) {
|
|
7853
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
7854
|
+
} finally {
|
|
7855
|
+
setIsUploading(false);
|
|
7856
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
7857
|
+
}
|
|
7858
|
+
}, [
|
|
7859
|
+
maxSizeMb,
|
|
7860
|
+
onChange
|
|
7861
|
+
]);
|
|
7862
|
+
const onFileChange = React26.useCallback((e) => {
|
|
7863
|
+
const file = e.target.files?.[0];
|
|
7864
|
+
if (file) void handleUpload(file);
|
|
7865
|
+
}, [
|
|
7866
|
+
handleUpload
|
|
7867
|
+
]);
|
|
7868
|
+
const trimmed = value.trim();
|
|
7869
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
7870
|
+
className: "space-y-2"
|
|
7871
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
7872
|
+
className: labelClassName
|
|
7873
|
+
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7874
|
+
className: "flex items-start gap-3"
|
|
7875
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7876
|
+
src: trimmed,
|
|
7877
|
+
alt: label,
|
|
7878
|
+
className: previewCls,
|
|
7879
|
+
role: "button",
|
|
7880
|
+
tabIndex: 0,
|
|
7881
|
+
title: "Click to enlarge",
|
|
7882
|
+
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
7883
|
+
onKeyDown: /* @__PURE__ */ __name((e) => {
|
|
7884
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7885
|
+
e.preventDefault();
|
|
7886
|
+
setLightboxOpen(true);
|
|
7887
|
+
}
|
|
7888
|
+
}, "onKeyDown"),
|
|
7889
|
+
onError: /* @__PURE__ */ __name((e) => {
|
|
7890
|
+
e.currentTarget.style.display = "none";
|
|
7891
|
+
}, "onError")
|
|
7892
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7893
|
+
type: "button",
|
|
7894
|
+
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
7895
|
+
className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-600 hover:bg-gray-50"
|
|
7896
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
7897
|
+
className: "h-3 w-3"
|
|
7898
|
+
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
7899
|
+
className: "flex flex-wrap gap-2"
|
|
7900
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
7901
|
+
type: "url",
|
|
7902
|
+
value,
|
|
7903
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
7904
|
+
setError(null);
|
|
7905
|
+
onChange(e.target.value);
|
|
7906
|
+
}, "onChange"),
|
|
7907
|
+
placeholder,
|
|
7908
|
+
className: `${inputClassName} min-w-0 flex-1`
|
|
7909
|
+
}), /* @__PURE__ */ React.createElement("input", {
|
|
7910
|
+
ref: fileInputRef,
|
|
7911
|
+
type: "file",
|
|
7912
|
+
accept: ACCEPTED_TYPES.join(","),
|
|
7913
|
+
onChange: onFileChange,
|
|
7914
|
+
className: "hidden",
|
|
7915
|
+
disabled: isUploading
|
|
7916
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7917
|
+
type: "button",
|
|
7918
|
+
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
7919
|
+
disabled: isUploading,
|
|
7920
|
+
className: "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
7921
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
|
|
7922
|
+
className: "h-3.5 w-3.5"
|
|
7923
|
+
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
7924
|
+
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
7925
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.AlertCircle, {
|
|
7926
|
+
className: "h-3.5 w-3.5 shrink-0"
|
|
7927
|
+
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
7928
|
+
className: "text-xs text-gray-500"
|
|
7929
|
+
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"), lightboxOpen && trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7930
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4",
|
|
7931
|
+
role: "dialog",
|
|
7932
|
+
"aria-modal": "true",
|
|
7933
|
+
"aria-label": `${label} preview`,
|
|
7934
|
+
onClick: closeLightbox
|
|
7935
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7936
|
+
src: trimmed,
|
|
7937
|
+
alt: label,
|
|
7938
|
+
className: "max-h-[90vh] max-w-[90vw] rounded-md object-contain shadow-lg",
|
|
7939
|
+
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
7940
|
+
})) : null);
|
|
7941
|
+
}
|
|
7942
|
+
var ACCEPTED_TYPES;
|
|
7943
|
+
var init_ImageOrUrlField = __esm({
|
|
7944
|
+
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
7945
|
+
"use client";
|
|
7946
|
+
ACCEPTED_TYPES = [
|
|
7947
|
+
"image/jpeg",
|
|
7948
|
+
"image/png",
|
|
7949
|
+
"image/gif",
|
|
7950
|
+
"image/webp"
|
|
7951
|
+
];
|
|
7952
|
+
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
7953
|
+
}
|
|
7954
|
+
});
|
|
7739
7955
|
function generateId() {
|
|
7740
7956
|
return `nav_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
7741
7957
|
}
|
|
@@ -8038,22 +8254,16 @@ function NavbarEditor({ config, onChange }) {
|
|
|
8038
8254
|
className: "space-y-6"
|
|
8039
8255
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8040
8256
|
className: "space-y-3"
|
|
8041
|
-
}, /* @__PURE__ */ React.createElement(
|
|
8042
|
-
|
|
8043
|
-
}, "Logo"), /* @__PURE__ */ React.createElement("div", {
|
|
8044
|
-
className: "flex items-center gap-3"
|
|
8045
|
-
}, config.logo && /* @__PURE__ */ React.createElement("img", {
|
|
8046
|
-
src: config.logo,
|
|
8047
|
-
alt: "Logo",
|
|
8048
|
-
className: "h-10 rounded border object-contain"
|
|
8049
|
-
}), /* @__PURE__ */ React.createElement(Input, {
|
|
8257
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
8258
|
+
label: "Logo",
|
|
8050
8259
|
value: config.logo || "",
|
|
8051
|
-
onChange: /* @__PURE__ */ __name((
|
|
8260
|
+
onChange: /* @__PURE__ */ __name((logo) => onChange({
|
|
8052
8261
|
...config,
|
|
8053
|
-
logo
|
|
8262
|
+
logo
|
|
8054
8263
|
}), "onChange"),
|
|
8264
|
+
previewVariant: "logo",
|
|
8055
8265
|
placeholder: "Logo image URL"
|
|
8056
|
-
}))
|
|
8266
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
8057
8267
|
className: "space-y-3"
|
|
8058
8268
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8059
8269
|
className: "flex items-center justify-between"
|
|
@@ -8092,6 +8302,7 @@ var init_NavbarEditor = __esm({
|
|
|
8092
8302
|
init_button();
|
|
8093
8303
|
init_switch();
|
|
8094
8304
|
init_label();
|
|
8305
|
+
init_ImageOrUrlField();
|
|
8095
8306
|
__name(generateId, "generateId");
|
|
8096
8307
|
__name(NavItemEditor, "NavItemEditor");
|
|
8097
8308
|
__name(updateItemInTree, "updateItemInTree");
|
|
@@ -11040,10 +11251,32 @@ function renderListPrice(value, item) {
|
|
|
11040
11251
|
return `${formatted} ${currency}`;
|
|
11041
11252
|
}
|
|
11042
11253
|
}
|
|
11254
|
+
function renderListThumbnail(url) {
|
|
11255
|
+
const trimmed = url.trim();
|
|
11256
|
+
if (!trimmed) return "\u2014";
|
|
11257
|
+
return React26.createElement("img", {
|
|
11258
|
+
src: trimmed,
|
|
11259
|
+
alt: "",
|
|
11260
|
+
className: "h-10 w-10 rounded border border-gray-200 bg-white object-contain"
|
|
11261
|
+
});
|
|
11262
|
+
}
|
|
11263
|
+
function productListImageUrl(item) {
|
|
11264
|
+
const meta = item.metadata;
|
|
11265
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return "";
|
|
11266
|
+
const images = meta.images;
|
|
11267
|
+
if (!Array.isArray(images)) return "";
|
|
11268
|
+
const rows = images;
|
|
11269
|
+
const def = rows.find((r) => r?.isDefault && typeof r.url === "string" && r.url.trim());
|
|
11270
|
+
if (def && typeof def.url === "string") return def.url.trim();
|
|
11271
|
+
const first = rows.find((r) => typeof r.url === "string" && r.url.trim());
|
|
11272
|
+
return first && typeof first.url === "string" ? first.url.trim() : "";
|
|
11273
|
+
}
|
|
11043
11274
|
exports.STORE_CRUD_CONFIGS = void 0;
|
|
11044
11275
|
var init_store_crud_configs = __esm({
|
|
11045
11276
|
"src/admin/store-crud-configs.ts"() {
|
|
11046
11277
|
__name(renderListPrice, "renderListPrice");
|
|
11278
|
+
__name(renderListThumbnail, "renderListThumbnail");
|
|
11279
|
+
__name(productListImageUrl, "productListImageUrl");
|
|
11047
11280
|
exports.STORE_CRUD_CONFIGS = {
|
|
11048
11281
|
products: {
|
|
11049
11282
|
title: "Products",
|
|
@@ -11069,9 +11302,40 @@ var init_store_crud_configs = __esm({
|
|
|
11069
11302
|
label: "Out of stock"
|
|
11070
11303
|
}
|
|
11071
11304
|
]
|
|
11305
|
+
},
|
|
11306
|
+
{
|
|
11307
|
+
param: "approvalStatus",
|
|
11308
|
+
label: "Approval",
|
|
11309
|
+
type: "select",
|
|
11310
|
+
options: [
|
|
11311
|
+
{
|
|
11312
|
+
value: "",
|
|
11313
|
+
label: "All"
|
|
11314
|
+
},
|
|
11315
|
+
{
|
|
11316
|
+
value: "pending",
|
|
11317
|
+
label: "Pending"
|
|
11318
|
+
},
|
|
11319
|
+
{
|
|
11320
|
+
value: "approved",
|
|
11321
|
+
label: "Approved"
|
|
11322
|
+
},
|
|
11323
|
+
{
|
|
11324
|
+
value: "rejected",
|
|
11325
|
+
label: "Rejected"
|
|
11326
|
+
}
|
|
11327
|
+
]
|
|
11072
11328
|
}
|
|
11073
11329
|
],
|
|
11074
11330
|
columns: [
|
|
11331
|
+
{
|
|
11332
|
+
field: "metadata",
|
|
11333
|
+
displayName: "Image",
|
|
11334
|
+
hideInCreate: true,
|
|
11335
|
+
hideInEdit: true,
|
|
11336
|
+
listFilter: false,
|
|
11337
|
+
render: /* @__PURE__ */ __name((_value, item) => renderListThumbnail(productListImageUrl(item)), "render")
|
|
11338
|
+
},
|
|
11075
11339
|
{
|
|
11076
11340
|
field: "name",
|
|
11077
11341
|
displayName: "Name"
|
|
@@ -11133,6 +11397,33 @@ var init_store_crud_configs = __esm({
|
|
|
11133
11397
|
}
|
|
11134
11398
|
]
|
|
11135
11399
|
},
|
|
11400
|
+
{
|
|
11401
|
+
field: "approvalStatus",
|
|
11402
|
+
displayName: "Approval",
|
|
11403
|
+
type: "select",
|
|
11404
|
+
listFilter: false,
|
|
11405
|
+
options: [
|
|
11406
|
+
{
|
|
11407
|
+
value: "pending",
|
|
11408
|
+
label: "Pending"
|
|
11409
|
+
},
|
|
11410
|
+
{
|
|
11411
|
+
value: "approved",
|
|
11412
|
+
label: "Approved"
|
|
11413
|
+
},
|
|
11414
|
+
{
|
|
11415
|
+
value: "rejected",
|
|
11416
|
+
label: "Rejected"
|
|
11417
|
+
}
|
|
11418
|
+
],
|
|
11419
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
11420
|
+
const v = value == null || value === "" ? null : String(value);
|
|
11421
|
+
if (v === "pending") return "Pending";
|
|
11422
|
+
if (v === "approved") return "Approved";
|
|
11423
|
+
if (v === "rejected") return "Rejected";
|
|
11424
|
+
return "\u2014";
|
|
11425
|
+
}, "render")
|
|
11426
|
+
},
|
|
11136
11427
|
{
|
|
11137
11428
|
field: "featured",
|
|
11138
11429
|
displayName: "Featured",
|
|
@@ -11256,6 +11547,14 @@ var init_store_crud_configs = __esm({
|
|
|
11256
11547
|
title: "Collections",
|
|
11257
11548
|
apiEndpoint: "/api/collections",
|
|
11258
11549
|
columns: [
|
|
11550
|
+
{
|
|
11551
|
+
field: "image",
|
|
11552
|
+
displayName: "Image",
|
|
11553
|
+
hideInCreate: true,
|
|
11554
|
+
hideInEdit: true,
|
|
11555
|
+
listFilter: false,
|
|
11556
|
+
render: /* @__PURE__ */ __name((value) => renderListThumbnail(typeof value === "string" ? value : ""), "render")
|
|
11557
|
+
},
|
|
11259
11558
|
{
|
|
11260
11559
|
field: "name",
|
|
11261
11560
|
displayName: "Name"
|
|
@@ -11652,6 +11951,13 @@ var init_store_crud_configs = __esm({
|
|
|
11652
11951
|
field: "slug",
|
|
11653
11952
|
displayName: "Slug"
|
|
11654
11953
|
},
|
|
11954
|
+
{
|
|
11955
|
+
field: "isCatalog",
|
|
11956
|
+
displayName: "Catalog",
|
|
11957
|
+
type: "boolean",
|
|
11958
|
+
hideInCreate: true,
|
|
11959
|
+
hideInEdit: true
|
|
11960
|
+
},
|
|
11655
11961
|
{
|
|
11656
11962
|
field: "active",
|
|
11657
11963
|
displayName: "Active",
|
|
@@ -11954,6 +12260,29 @@ var init_store_crud_configs = __esm({
|
|
|
11954
12260
|
label: "Paid"
|
|
11955
12261
|
}
|
|
11956
12262
|
]
|
|
12263
|
+
},
|
|
12264
|
+
{
|
|
12265
|
+
param: "approvalStatus",
|
|
12266
|
+
label: "Approval",
|
|
12267
|
+
type: "select",
|
|
12268
|
+
options: [
|
|
12269
|
+
{
|
|
12270
|
+
value: "",
|
|
12271
|
+
label: "All"
|
|
12272
|
+
},
|
|
12273
|
+
{
|
|
12274
|
+
value: "pending",
|
|
12275
|
+
label: "Pending"
|
|
12276
|
+
},
|
|
12277
|
+
{
|
|
12278
|
+
value: "approved",
|
|
12279
|
+
label: "Approved"
|
|
12280
|
+
},
|
|
12281
|
+
{
|
|
12282
|
+
value: "rejected",
|
|
12283
|
+
label: "Rejected"
|
|
12284
|
+
}
|
|
12285
|
+
]
|
|
11957
12286
|
}
|
|
11958
12287
|
],
|
|
11959
12288
|
columns: [
|
|
@@ -11985,6 +12314,33 @@ var init_store_crud_configs = __esm({
|
|
|
11985
12314
|
displayName: "Active",
|
|
11986
12315
|
type: "boolean"
|
|
11987
12316
|
},
|
|
12317
|
+
{
|
|
12318
|
+
field: "approvalStatus",
|
|
12319
|
+
displayName: "Approval",
|
|
12320
|
+
type: "select",
|
|
12321
|
+
listFilter: false,
|
|
12322
|
+
options: [
|
|
12323
|
+
{
|
|
12324
|
+
value: "pending",
|
|
12325
|
+
label: "Pending"
|
|
12326
|
+
},
|
|
12327
|
+
{
|
|
12328
|
+
value: "approved",
|
|
12329
|
+
label: "Approved"
|
|
12330
|
+
},
|
|
12331
|
+
{
|
|
12332
|
+
value: "rejected",
|
|
12333
|
+
label: "Rejected"
|
|
12334
|
+
}
|
|
12335
|
+
],
|
|
12336
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
12337
|
+
const v = value == null || value === "" ? null : String(value);
|
|
12338
|
+
if (v === "pending") return "Pending";
|
|
12339
|
+
if (v === "approved") return "Approved";
|
|
12340
|
+
if (v === "rejected") return "Rejected";
|
|
12341
|
+
return "\u2014";
|
|
12342
|
+
}, "render")
|
|
12343
|
+
},
|
|
11988
12344
|
{
|
|
11989
12345
|
field: "comingSoon",
|
|
11990
12346
|
displayName: "Coming soon",
|
|
@@ -12271,7 +12627,12 @@ function SettingsPage() {
|
|
|
12271
12627
|
const [themeSettingsLoading, setThemeSettingsLoading] = React26.useState(true);
|
|
12272
12628
|
const [storeEnabled, setStoreEnabled] = React26.useState(false);
|
|
12273
12629
|
const [multiVendorEnabled, setMultiVendorEnabled] = React26.useState(true);
|
|
12630
|
+
const [vendorCanCreateCategories, setVendorCanCreateCategories] = React26.useState(false);
|
|
12631
|
+
const [vendorCanCreateCollections, setVendorCanCreateCollections] = React26.useState(false);
|
|
12632
|
+
const [vendorCanCreateBrands, setVendorCanCreateBrands] = React26.useState(false);
|
|
12633
|
+
const [requireProductApproval, setRequireProductApproval] = React26.useState(false);
|
|
12274
12634
|
const [eventsEnabled, setEventsEnabled] = React26.useState(true);
|
|
12635
|
+
const [requireEventApproval, setRequireEventApproval] = React26.useState(false);
|
|
12275
12636
|
const [storeSettingsLoading, setStoreSettingsLoading] = React26.useState(true);
|
|
12276
12637
|
const [currency, setCurrency] = React26.useState(DEFAULT_CURRENCY);
|
|
12277
12638
|
const [currencies, setCurrencies] = React26.useState([]);
|
|
@@ -12331,10 +12692,15 @@ function SettingsPage() {
|
|
|
12331
12692
|
}).finally(() => setStoreSettingsLoading(false));
|
|
12332
12693
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12333
12694
|
setMultiVendorEnabled(data.enabled !== "false");
|
|
12695
|
+
setVendorCanCreateCategories(data.vendorCanCreateCategories === "true");
|
|
12696
|
+
setVendorCanCreateCollections(data.vendorCanCreateCollections === "true");
|
|
12697
|
+
setVendorCanCreateBrands(data.vendorCanCreateBrands === "true");
|
|
12698
|
+
setRequireProductApproval(data.requireProductApproval === "true");
|
|
12334
12699
|
}).catch(() => {
|
|
12335
12700
|
});
|
|
12336
12701
|
fetch("/api/settings/events").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12337
12702
|
setEventsEnabled(data.enabled !== "false");
|
|
12703
|
+
setRequireEventApproval(data.requireEventApproval === "true");
|
|
12338
12704
|
}).catch(() => {
|
|
12339
12705
|
});
|
|
12340
12706
|
fetch("/api/currencies/exchange-rates").then((r) => r.ok ? r.json() : []).then((data) => {
|
|
@@ -12462,6 +12828,22 @@ function SettingsPage() {
|
|
|
12462
12828
|
enabled: {
|
|
12463
12829
|
value: multiVendorEnabled ? "true" : "false",
|
|
12464
12830
|
type: "public"
|
|
12831
|
+
},
|
|
12832
|
+
vendorCanCreateCategories: {
|
|
12833
|
+
value: vendorCanCreateCategories ? "true" : "false",
|
|
12834
|
+
type: "public"
|
|
12835
|
+
},
|
|
12836
|
+
vendorCanCreateCollections: {
|
|
12837
|
+
value: vendorCanCreateCollections ? "true" : "false",
|
|
12838
|
+
type: "public"
|
|
12839
|
+
},
|
|
12840
|
+
vendorCanCreateBrands: {
|
|
12841
|
+
value: vendorCanCreateBrands ? "true" : "false",
|
|
12842
|
+
type: "public"
|
|
12843
|
+
},
|
|
12844
|
+
requireProductApproval: {
|
|
12845
|
+
value: requireProductApproval ? "true" : "false",
|
|
12846
|
+
type: "public"
|
|
12465
12847
|
}
|
|
12466
12848
|
})
|
|
12467
12849
|
});
|
|
@@ -12474,6 +12856,10 @@ function SettingsPage() {
|
|
|
12474
12856
|
enabled: {
|
|
12475
12857
|
value: eventsEnabled ? "true" : "false",
|
|
12476
12858
|
type: "public"
|
|
12859
|
+
},
|
|
12860
|
+
requireEventApproval: {
|
|
12861
|
+
value: requireEventApproval ? "true" : "false",
|
|
12862
|
+
type: "public"
|
|
12477
12863
|
}
|
|
12478
12864
|
})
|
|
12479
12865
|
});
|
|
@@ -12745,8 +13131,46 @@ function SettingsPage() {
|
|
|
12745
13131
|
className: "text-sm font-medium text-gray-700"
|
|
12746
13132
|
}, "Multi Vendor"), /* @__PURE__ */ React.createElement("p", {
|
|
12747
13133
|
className: "text-xs text-gray-500"
|
|
12748
|
-
}, "Enable vendor portals, vendor logins, and vendor-scoped store data. Disabling this blocks vendor-only accounts from signing in."))), storeEnabled && /* @__PURE__ */ React.createElement("div", {
|
|
12749
|
-
className: "mt-4
|
|
13134
|
+
}, "Enable vendor portals, vendor logins, and vendor-scoped store data. Disabling this blocks vendor-only accounts from signing in."))), storeEnabled && multiVendorEnabled && /* @__PURE__ */ React.createElement("div", {
|
|
13135
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13136
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
13137
|
+
className: "text-sm font-medium text-gray-700"
|
|
13138
|
+
}, "Vendor catalog permissions"), /* @__PURE__ */ React.createElement("p", {
|
|
13139
|
+
className: "text-xs text-gray-500 mb-3"
|
|
13140
|
+
}, "When off (default), vendors only pick admin-created categories, collections, and brands on products. When on, vendors also get that Store tab and can create their own.")), /* @__PURE__ */ React.createElement("div", {
|
|
13141
|
+
className: "flex items-center gap-3"
|
|
13142
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13143
|
+
checked: vendorCanCreateCategories,
|
|
13144
|
+
onCheckedChange: setVendorCanCreateCategories
|
|
13145
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13146
|
+
className: "text-sm font-medium text-gray-700"
|
|
13147
|
+
}, "Can vendors create categories")), /* @__PURE__ */ React.createElement("div", {
|
|
13148
|
+
className: "flex items-center gap-3"
|
|
13149
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13150
|
+
checked: vendorCanCreateCollections,
|
|
13151
|
+
onCheckedChange: setVendorCanCreateCollections
|
|
13152
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13153
|
+
className: "text-sm font-medium text-gray-700"
|
|
13154
|
+
}, "Can vendors create collections")), /* @__PURE__ */ React.createElement("div", {
|
|
13155
|
+
className: "flex items-center gap-3"
|
|
13156
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13157
|
+
checked: vendorCanCreateBrands,
|
|
13158
|
+
onCheckedChange: setVendorCanCreateBrands
|
|
13159
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13160
|
+
className: "text-sm font-medium text-gray-700"
|
|
13161
|
+
}, "Can vendors create brands")), /* @__PURE__ */ React.createElement("div", {
|
|
13162
|
+
className: "flex items-start gap-3"
|
|
13163
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13164
|
+
checked: requireProductApproval,
|
|
13165
|
+
onCheckedChange: setRequireProductApproval
|
|
13166
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13167
|
+
className: "text-sm font-medium text-gray-700"
|
|
13168
|
+
}, "Require product approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13169
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13170
|
+
}, "When on, new vendor products start as waiting for approval. Admins approve or reject; only after approval can the vendor set the product to available (live).")))), storeEnabled && /* @__PURE__ */ React.createElement("div", {
|
|
13171
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13172
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13173
|
+
className: "flex items-center gap-3"
|
|
12750
13174
|
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
12751
13175
|
checked: eventsEnabled,
|
|
12752
13176
|
onCheckedChange: setEventsEnabled
|
|
@@ -12754,7 +13178,16 @@ function SettingsPage() {
|
|
|
12754
13178
|
className: "text-sm font-medium text-gray-700"
|
|
12755
13179
|
}, "Events"), /* @__PURE__ */ React.createElement("p", {
|
|
12756
13180
|
className: "text-xs text-gray-500"
|
|
12757
|
-
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))),
|
|
13181
|
+
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))), eventsEnabled && multiVendorEnabled ? /* @__PURE__ */ React.createElement("div", {
|
|
13182
|
+
className: "flex items-start gap-3 pl-1 border-t border-gray-200 pt-3"
|
|
13183
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13184
|
+
checked: requireEventApproval,
|
|
13185
|
+
onCheckedChange: setRequireEventApproval
|
|
13186
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13187
|
+
className: "text-sm font-medium text-gray-700"
|
|
13188
|
+
}, "Require event approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13189
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13190
|
+
}, "When on, new vendor events start as pending. Admins approve or reject; approval activates the event (live)."))) : null), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
12758
13191
|
className: "flex items-center justify-between mb-1"
|
|
12759
13192
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
12760
13193
|
className: "block text-sm font-semibold text-gray-700"
|
|
@@ -13593,7 +14026,11 @@ function ScheduleAuthorSelect({ value, onChange, disabled }) {
|
|
|
13593
14026
|
(async () => {
|
|
13594
14027
|
setLoading(true);
|
|
13595
14028
|
try {
|
|
13596
|
-
const
|
|
14029
|
+
const params = new URLSearchParams({
|
|
14030
|
+
limit: "100",
|
|
14031
|
+
groupName: ADMIN_GROUP_NAME
|
|
14032
|
+
});
|
|
14033
|
+
const res = await fetch(`/api/users?${params}`);
|
|
13597
14034
|
if (!res.ok || cancelled) return;
|
|
13598
14035
|
const data = await res.json();
|
|
13599
14036
|
const rows = Array.isArray(data.data) ? data.data : [];
|
|
@@ -13626,7 +14063,7 @@ function ScheduleAuthorSelect({ value, onChange, disabled }) {
|
|
|
13626
14063
|
}, /* @__PURE__ */ React.createElement(SelectTrigger, {
|
|
13627
14064
|
className: "w-full"
|
|
13628
14065
|
}, /* @__PURE__ */ React.createElement(SelectValue, {
|
|
13629
|
-
placeholder: users.length === 0 ? "No
|
|
14066
|
+
placeholder: users.length === 0 ? "No administrators found" : "Select author"
|
|
13630
14067
|
})), /* @__PURE__ */ React.createElement(SelectContent, {
|
|
13631
14068
|
className: "max-h-72"
|
|
13632
14069
|
}, users.map((u) => /* @__PURE__ */ React.createElement(SelectItem, {
|
|
@@ -13638,6 +14075,7 @@ var init_ScheduleAuthorSelect = __esm({
|
|
|
13638
14075
|
"src/admin/ScheduleAuthorSelect.tsx"() {
|
|
13639
14076
|
"use client";
|
|
13640
14077
|
init_select();
|
|
14078
|
+
init_permission_entities();
|
|
13641
14079
|
__name(userLabel, "userLabel");
|
|
13642
14080
|
__name(ScheduleAuthorSelect, "ScheduleAuthorSelect");
|
|
13643
14081
|
}
|
|
@@ -17024,15 +17462,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17024
17462
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17025
17463
|
}, "Layout below merges with Branding settings; values here override branding when set. Use absolute URLs for logos in email."), /* @__PURE__ */ React.createElement("div", {
|
|
17026
17464
|
className: "space-y-1"
|
|
17027
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17028
|
-
|
|
17029
|
-
className: "text-sm"
|
|
17030
|
-
}, "Logo URL (optional override)"), /* @__PURE__ */ React.createElement(Input, {
|
|
17031
|
-
id: `${settingsGroup}-logoUrl`,
|
|
17465
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17466
|
+
label: "Logo (optional override)",
|
|
17032
17467
|
value: logoUrl,
|
|
17033
|
-
onChange:
|
|
17468
|
+
onChange: setLogoUrl,
|
|
17469
|
+
previewVariant: "logo",
|
|
17034
17470
|
placeholder: "https://\u2026",
|
|
17035
|
-
|
|
17471
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17036
17472
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17037
17473
|
className: "space-y-1"
|
|
17038
17474
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17085,22 +17521,23 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17085
17521
|
className: "flex flex-wrap items-end gap-2 border-b border-border/60 pb-3 dark:border-gray-600"
|
|
17086
17522
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
17087
17523
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17088
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17089
|
-
|
|
17090
|
-
}, "Icon image URL"), /* @__PURE__ */ React.createElement(Input, {
|
|
17524
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17525
|
+
label: "Icon image",
|
|
17091
17526
|
value: row.iconUrl,
|
|
17092
|
-
onChange: /* @__PURE__ */ __name((
|
|
17527
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
17093
17528
|
const next = [
|
|
17094
17529
|
...socialLinkRows
|
|
17095
17530
|
];
|
|
17096
17531
|
next[i] = {
|
|
17097
17532
|
...next[i],
|
|
17098
|
-
iconUrl:
|
|
17533
|
+
iconUrl: v
|
|
17099
17534
|
};
|
|
17100
17535
|
setSocialLinkRows(next);
|
|
17101
17536
|
}, "onChange"),
|
|
17537
|
+
previewVariant: "logo",
|
|
17102
17538
|
placeholder: "https://\u2026",
|
|
17103
|
-
|
|
17539
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3",
|
|
17540
|
+
labelClassName: "text-xs text-muted-foreground"
|
|
17104
17541
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17105
17542
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17106
17543
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17747,15 +18184,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17747
18184
|
className: "h-8 text-sm"
|
|
17748
18185
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17749
18186
|
className: "space-y-1"
|
|
17750
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17751
|
-
|
|
17752
|
-
className: "text-sm"
|
|
17753
|
-
}, "Icon image URL"), /* @__PURE__ */ React.createElement(Input, {
|
|
17754
|
-
id: `${settingsGroup}-iconImageUrl`,
|
|
18187
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
18188
|
+
label: "Icon image",
|
|
17755
18189
|
value: iconImageUrl,
|
|
17756
|
-
onChange:
|
|
18190
|
+
onChange: setIconImageUrl,
|
|
18191
|
+
previewVariant: "logo",
|
|
17757
18192
|
placeholder: "https://\u2026 or /images/chat-icon.png",
|
|
17758
|
-
|
|
18193
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17759
18194
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
17760
18195
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17761
18196
|
}, "PNG or image URL. Leave empty to use emoji below.")), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -18315,6 +18750,7 @@ var init_PluginsPage = __esm({
|
|
|
18315
18750
|
init_checkbox();
|
|
18316
18751
|
init_select();
|
|
18317
18752
|
init_EventNotificationsPluginSettings();
|
|
18753
|
+
init_ImageOrUrlField();
|
|
18318
18754
|
init_chat_email_intent();
|
|
18319
18755
|
init_llm_agent_scope();
|
|
18320
18756
|
__name(normalizeLinkedInOrganizations, "normalizeLinkedInOrganizations");
|
|
@@ -19998,7 +20434,7 @@ var init_VendorPortalProfilePage = __esm({
|
|
|
19998
20434
|
__name(VendorPortalProfilePage, "VendorPortalProfilePage");
|
|
19999
20435
|
}
|
|
20000
20436
|
});
|
|
20001
|
-
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, menuItems = [] }) {
|
|
20437
|
+
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, headerExtra, menuItems = [] }) {
|
|
20002
20438
|
const router = navigation.useRouter();
|
|
20003
20439
|
const handleClose = /* @__PURE__ */ __name(() => {
|
|
20004
20440
|
if (onClose) onClose();
|
|
@@ -20022,7 +20458,7 @@ function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", close
|
|
|
20022
20458
|
className: "text-sm text-gray-400 truncate"
|
|
20023
20459
|
}, subtitle))), /* @__PURE__ */ React.createElement("div", {
|
|
20024
20460
|
className: "flex items-center gap-2 shrink-0"
|
|
20025
|
-
}, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
20461
|
+
}, headerExtra, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
20026
20462
|
className: "flex items-center gap-2 md:hidden"
|
|
20027
20463
|
}, menuItems.map((item, i) => {
|
|
20028
20464
|
const Icon2 = item.icon;
|
|
@@ -25202,15 +25638,11 @@ function SeoTabContent(props) {
|
|
|
25202
25638
|
onChange: /* @__PURE__ */ __name((e) => props.setSeoOgDescription(e.target.value), "onChange"),
|
|
25203
25639
|
placeholder: "Open Graph description",
|
|
25204
25640
|
rows: 2
|
|
25205
|
-
})), /* @__PURE__ */ React26__namespace.default.createElement(
|
|
25206
|
-
|
|
25207
|
-
}, /* @__PURE__ */ React26__namespace.default.createElement(Label3, {
|
|
25208
|
-
className: "text-xs"
|
|
25209
|
-
}, "OG Image"), /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
25641
|
+
})), /* @__PURE__ */ React26__namespace.default.createElement(ImageOrUrlField, {
|
|
25642
|
+
label: "OG Image",
|
|
25210
25643
|
value: props.seoOgImage,
|
|
25211
|
-
onChange:
|
|
25212
|
-
|
|
25213
|
-
})));
|
|
25644
|
+
onChange: props.setSeoOgImage
|
|
25645
|
+
}));
|
|
25214
25646
|
}
|
|
25215
25647
|
function CollapsibleSection({ title, icon: Icon2, open, onToggle, children }) {
|
|
25216
25648
|
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
@@ -25596,6 +26028,7 @@ var init_PageBuilderPage = __esm({
|
|
|
25596
26028
|
init_ComponentSettings();
|
|
25597
26029
|
init_admin_config_context();
|
|
25598
26030
|
init_registry();
|
|
26031
|
+
init_ImageOrUrlField();
|
|
25599
26032
|
__name(createSelectable, "createSelectable");
|
|
25600
26033
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
25601
26034
|
__name(getIcon, "getIcon");
|
|
@@ -25658,15 +26091,12 @@ function SeoSection({ values, onChange }) {
|
|
|
25658
26091
|
onChange: /* @__PURE__ */ __name((e) => onChange("seoOgDescription", e.target.value), "onChange"),
|
|
25659
26092
|
className: textareaCls,
|
|
25660
26093
|
rows: 2
|
|
25661
|
-
})), /* @__PURE__ */ React.createElement(
|
|
25662
|
-
|
|
25663
|
-
}, "OG Image"), /* @__PURE__ */ React.createElement("input", {
|
|
25664
|
-
type: "url",
|
|
26094
|
+
})), /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26095
|
+
label: "OG Image",
|
|
25665
26096
|
value: values.seoOgImage,
|
|
25666
|
-
onChange: /* @__PURE__ */ __name((
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
}))));
|
|
26097
|
+
onChange: /* @__PURE__ */ __name((v) => onChange("seoOgImage", v), "onChange"),
|
|
26098
|
+
inputClassName: inputCls9
|
|
26099
|
+
})));
|
|
25670
26100
|
}
|
|
25671
26101
|
async function saveSeo(seo, slug, existingSeoId) {
|
|
25672
26102
|
const hasSeo = seo.seoTitle || seo.seoDescription || seo.seoKeywords || seo.seoOgTitle || seo.seoOgDescription || seo.seoOgImage;
|
|
@@ -25731,6 +26161,7 @@ async function fetchSeo(seoId) {
|
|
|
25731
26161
|
var init_SeoSection = __esm({
|
|
25732
26162
|
"src/components/Admin/SeoSection.tsx"() {
|
|
25733
26163
|
"use client";
|
|
26164
|
+
init_ImageOrUrlField();
|
|
25734
26165
|
__name(SeoSection, "SeoSection");
|
|
25735
26166
|
__name(saveSeo, "saveSeo");
|
|
25736
26167
|
__name(fetchSeo, "fetchSeo");
|
|
@@ -25745,6 +26176,8 @@ __export(BrandEditPage_exports, {
|
|
|
25745
26176
|
function BrandEditPage({ brandId }) {
|
|
25746
26177
|
const router = navigation.useRouter();
|
|
25747
26178
|
const searchParams = navigation.useSearchParams();
|
|
26179
|
+
const { data: session } = react.useSession();
|
|
26180
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
25748
26181
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/brands";
|
|
25749
26182
|
const create = isCreate(brandId);
|
|
25750
26183
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -25846,6 +26279,9 @@ function BrandEditPage({ brandId }) {
|
|
|
25846
26279
|
active,
|
|
25847
26280
|
sortOrder
|
|
25848
26281
|
};
|
|
26282
|
+
if (!vendorPortal) {
|
|
26283
|
+
payload.isCatalog = true;
|
|
26284
|
+
}
|
|
25849
26285
|
if (savedSeoId) payload.seoId = savedSeoId;
|
|
25850
26286
|
const res = await fetch(create ? "/api/brands" : `/api/brands/${brandId}`, {
|
|
25851
26287
|
method: create ? "POST" : "PUT",
|
|
@@ -25937,13 +26373,12 @@ function BrandEditPage({ brandId }) {
|
|
|
25937
26373
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
25938
26374
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm min-h-[80px]",
|
|
25939
26375
|
rows: 3
|
|
25940
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
25941
|
-
|
|
25942
|
-
}, "Logo URL"), /* @__PURE__ */ React.createElement("input", {
|
|
25943
|
-
type: "url",
|
|
26376
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26377
|
+
label: "Logo",
|
|
25944
26378
|
value: logo,
|
|
25945
|
-
onChange:
|
|
25946
|
-
|
|
26379
|
+
onChange: setLogo,
|
|
26380
|
+
previewVariant: "logo",
|
|
26381
|
+
inputClassName: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
25947
26382
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
25948
26383
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
25949
26384
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -25986,6 +26421,8 @@ var init_BrandEditPage = __esm({
|
|
|
25986
26421
|
init_SeoSection();
|
|
25987
26422
|
init_DetailPageLayout();
|
|
25988
26423
|
init_DetailPageHeader();
|
|
26424
|
+
init_vendor_scope();
|
|
26425
|
+
init_ImageOrUrlField();
|
|
25989
26426
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
25990
26427
|
__name(BrandEditPage, "BrandEditPage");
|
|
25991
26428
|
}
|
|
@@ -26893,16 +27330,38 @@ function ProductVariantsSection({ hasVariants, onHasVariantsChange, variantOptio
|
|
|
26893
27330
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
26894
27331
|
value: "sold"
|
|
26895
27332
|
}, "Sold"))), /* @__PURE__ */ React.createElement("td", {
|
|
26896
|
-
className: "px-3 py-2 align-top"
|
|
26897
|
-
},
|
|
26898
|
-
|
|
26899
|
-
|
|
26900
|
-
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
27333
|
+
className: "px-3 py-2 align-top min-w-[240px]"
|
|
27334
|
+
}, (() => {
|
|
27335
|
+
const lines = row.imageUrlsText === "" ? [
|
|
27336
|
+
""
|
|
27337
|
+
] : row.imageUrlsText.split("\n");
|
|
27338
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
27339
|
+
className: "space-y-2"
|
|
27340
|
+
}, lines.map((url, ui) => /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
27341
|
+
key: ui,
|
|
27342
|
+
label: ui === 0 ? "Images" : `Image ${ui + 1}`,
|
|
27343
|
+
value: url,
|
|
27344
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
27345
|
+
const next = [
|
|
27346
|
+
...lines
|
|
27347
|
+
];
|
|
27348
|
+
next[ui] = v;
|
|
27349
|
+
setVariantField(i, "imageUrlsText", next.join("\n"));
|
|
27350
|
+
}, "onChange"),
|
|
27351
|
+
inputClassName: inputCls2
|
|
27352
|
+
})), /* @__PURE__ */ React.createElement("button", {
|
|
27353
|
+
type: "button",
|
|
27354
|
+
onClick: /* @__PURE__ */ __name(() => setVariantField(i, "imageUrlsText", [
|
|
27355
|
+
...lines,
|
|
27356
|
+
""
|
|
27357
|
+
].join("\n")), "onClick"),
|
|
27358
|
+
className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1 text-[11px] text-gray-700 hover:bg-gray-50"
|
|
27359
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
|
|
27360
|
+
className: "h-3 w-3"
|
|
27361
|
+
}), " Add image"), /* @__PURE__ */ React.createElement("p", {
|
|
27362
|
+
className: "text-[11px] text-gray-500"
|
|
27363
|
+
}, "First image is used on PDP."));
|
|
27364
|
+
})()), /* @__PURE__ */ React.createElement("td", {
|
|
26906
27365
|
className: "px-3 py-2 align-top text-right"
|
|
26907
27366
|
}, /* @__PURE__ */ React.createElement("button", {
|
|
26908
27367
|
type: "button",
|
|
@@ -26917,6 +27376,7 @@ var init_ProductVariantsSection = __esm({
|
|
|
26917
27376
|
"src/admin/pages/ProductVariantsSection.tsx"() {
|
|
26918
27377
|
"use client";
|
|
26919
27378
|
init_product_variants();
|
|
27379
|
+
init_ImageOrUrlField();
|
|
26920
27380
|
labelCls2 = "block text-xs font-medium text-gray-600 mb-1";
|
|
26921
27381
|
inputCls2 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
26922
27382
|
sectionCls2 = "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50";
|
|
@@ -27112,13 +27572,16 @@ function ProductEditPage({ productId }) {
|
|
|
27112
27572
|
const router = navigation.useRouter();
|
|
27113
27573
|
const searchParams = navigation.useSearchParams();
|
|
27114
27574
|
const { data: session } = react.useSession();
|
|
27575
|
+
const { eventsEnabled, requireProductApproval } = React26.useContext(exports.AdminConfigContext);
|
|
27576
|
+
const eventsOn = eventsEnabled !== false;
|
|
27577
|
+
const approvalOn = requireProductApproval === true;
|
|
27115
27578
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
27116
27579
|
const categoryIdParam = searchParams.get("categoryId")?.trim() ?? "";
|
|
27117
27580
|
const collectionIdParam = searchParams.get("collectionId")?.trim() ?? "";
|
|
27118
27581
|
const eventIdParam = searchParams.get("eventId")?.trim() ?? "";
|
|
27119
27582
|
const lockCategoryFromStore = isCreate2(productId) && /^\d+$/.test(categoryIdParam);
|
|
27120
27583
|
const lockCollectionFromQuery = isCreate2(productId) && /^\d+$/.test(collectionIdParam);
|
|
27121
|
-
const lockEventFromQuery = isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27584
|
+
const lockEventFromQuery = eventsOn && isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27122
27585
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? (lockCategoryFromStore ? `/admin/products?categoryId=${categoryIdParam}` : "/admin/products");
|
|
27123
27586
|
const create = isCreate2(productId);
|
|
27124
27587
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -27149,6 +27612,11 @@ function ProductEditPage({ productId }) {
|
|
|
27149
27612
|
const [compareAtPrice, setCompareAtPrice] = React26.useState(0);
|
|
27150
27613
|
const [quantity, setQuantity] = React26.useState(1);
|
|
27151
27614
|
const [status, setStatus] = React26.useState("draft");
|
|
27615
|
+
const [approvalStatus, setApprovalStatus] = React26.useState("pending");
|
|
27616
|
+
const [rejectionReason, setRejectionReason] = React26.useState("");
|
|
27617
|
+
const [rejectModalOpen, setRejectModalOpen] = React26.useState(false);
|
|
27618
|
+
const [rejectDraft, setRejectDraft] = React26.useState("");
|
|
27619
|
+
const approvalBeforeRejectRef = React26.useRef("pending");
|
|
27152
27620
|
const [featured, setFeatured] = React26.useState(false);
|
|
27153
27621
|
const [description, setDescription] = React26.useState("");
|
|
27154
27622
|
const [images, setImages] = React26.useState([
|
|
@@ -27253,7 +27721,7 @@ function ProductEditPage({ productId }) {
|
|
|
27253
27721
|
try {
|
|
27254
27722
|
const [brandRes, catRes, attrRes, taxesRes, eventsRes, formsRes, refundRes] = await Promise.all([
|
|
27255
27723
|
fetch("/api/brands?limit=500"),
|
|
27256
|
-
fetch("/api/product_categories?limit=500&isCatalog=true"),
|
|
27724
|
+
fetch(vendorPortal ? "/api/product_categories?limit=500" : "/api/product_categories?limit=500&isCatalog=true"),
|
|
27257
27725
|
fetch("/api/attributes?limit=500"),
|
|
27258
27726
|
fetch("/api/taxes?limit=200&sortField=name&sortOrder=asc"),
|
|
27259
27727
|
fetch("/api/events?limit=200&sortField=startDate&sortOrder=desc"),
|
|
@@ -27366,6 +27834,8 @@ function ProductEditPage({ productId }) {
|
|
|
27366
27834
|
setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
|
|
27367
27835
|
setQuantity(product.quantity ?? 1);
|
|
27368
27836
|
setStatus(product.status ?? "draft");
|
|
27837
|
+
setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
|
|
27838
|
+
setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
|
|
27369
27839
|
setFeatured(product.featured ?? false);
|
|
27370
27840
|
setDescription((m && typeof m.description === "string" ? m.description : "") ?? "");
|
|
27371
27841
|
const rawImages = m?.images;
|
|
@@ -27501,6 +27971,16 @@ function ProductEditPage({ productId }) {
|
|
|
27501
27971
|
create,
|
|
27502
27972
|
eventId
|
|
27503
27973
|
]);
|
|
27974
|
+
React26.useEffect(() => {
|
|
27975
|
+
if (create && vendorPortal && approvalOn) {
|
|
27976
|
+
setApprovalStatus("pending");
|
|
27977
|
+
setStatus((s) => s === "available" ? "draft" : s);
|
|
27978
|
+
}
|
|
27979
|
+
}, [
|
|
27980
|
+
create,
|
|
27981
|
+
vendorPortal,
|
|
27982
|
+
approvalOn
|
|
27983
|
+
]);
|
|
27504
27984
|
React26.useEffect(() => {
|
|
27505
27985
|
if (!create || !name.trim()) return;
|
|
27506
27986
|
setProductSlug(slugifyProductName(name));
|
|
@@ -27535,6 +28015,33 @@ function ProductEditPage({ productId }) {
|
|
|
27535
28015
|
}
|
|
27536
28016
|
setter(num);
|
|
27537
28017
|
}, "handleNumberChange");
|
|
28018
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
28019
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
28020
|
+
setRejectDraft(rejectionReason);
|
|
28021
|
+
setApprovalStatus("rejected");
|
|
28022
|
+
setRejectModalOpen(true);
|
|
28023
|
+
}, "openRejectModal");
|
|
28024
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
28025
|
+
const reason = rejectDraft.trim();
|
|
28026
|
+
if (!reason) return;
|
|
28027
|
+
setRejectionReason(reason);
|
|
28028
|
+
setRejectModalOpen(false);
|
|
28029
|
+
}, "confirmRejectReason");
|
|
28030
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
28031
|
+
if (!rejectionReason.trim()) {
|
|
28032
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
28033
|
+
}
|
|
28034
|
+
setRejectDraft(rejectionReason);
|
|
28035
|
+
setRejectModalOpen(false);
|
|
28036
|
+
}, "cancelRejectModal");
|
|
28037
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
28038
|
+
if (value === "rejected") {
|
|
28039
|
+
openRejectModal(approvalStatus);
|
|
28040
|
+
return;
|
|
28041
|
+
}
|
|
28042
|
+
setApprovalStatus(value);
|
|
28043
|
+
if (value !== "rejected") setRejectionReason("");
|
|
28044
|
+
}, "handleApprovalSelect");
|
|
27538
28045
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
27539
28046
|
setErrors([]);
|
|
27540
28047
|
if (!name.trim()) {
|
|
@@ -27543,6 +28050,14 @@ function ProductEditPage({ productId }) {
|
|
|
27543
28050
|
]);
|
|
27544
28051
|
return;
|
|
27545
28052
|
}
|
|
28053
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
28054
|
+
setRejectDraft("");
|
|
28055
|
+
setRejectModalOpen(true);
|
|
28056
|
+
setErrors([
|
|
28057
|
+
"Rejection reason is required"
|
|
28058
|
+
]);
|
|
28059
|
+
return;
|
|
28060
|
+
}
|
|
27546
28061
|
if (!defaultPriceStr.trim()) {
|
|
27547
28062
|
setErrors([
|
|
27548
28063
|
`${pricingConfig.defaultCurrency} price is required`
|
|
@@ -27605,11 +28120,21 @@ function ProductEditPage({ productId }) {
|
|
|
27605
28120
|
currencyPrices: null,
|
|
27606
28121
|
compareAtPrice: compareAtPriceValue,
|
|
27607
28122
|
quantity: resolvedQuantity,
|
|
27608
|
-
status,
|
|
28123
|
+
status: create && vendorPortal && approvalOn && status === "available" ? "draft" : status,
|
|
27609
28124
|
featured,
|
|
27610
28125
|
contactFormId,
|
|
27611
28126
|
metadata
|
|
27612
28127
|
};
|
|
28128
|
+
if (approvalOn) {
|
|
28129
|
+
if (vendorPortal && create) {
|
|
28130
|
+
productPayload.approvalStatus = "pending";
|
|
28131
|
+
} else if (!vendorPortal) {
|
|
28132
|
+
productPayload.approvalStatus = approvalStatus;
|
|
28133
|
+
if (approvalStatus === "rejected") {
|
|
28134
|
+
productPayload.rejectionReason = rejectionReason.trim();
|
|
28135
|
+
}
|
|
28136
|
+
}
|
|
28137
|
+
}
|
|
27613
28138
|
const res = await fetch(create ? "/api/products" : `/api/products/${productId}`, {
|
|
27614
28139
|
method: create ? "POST" : "PUT",
|
|
27615
28140
|
headers: {
|
|
@@ -27631,6 +28156,15 @@ function ProductEditPage({ productId }) {
|
|
|
27631
28156
|
if (typeof savedProduct.slug === "string") {
|
|
27632
28157
|
setProductSlug(savedProduct.slug);
|
|
27633
28158
|
}
|
|
28159
|
+
if (typeof savedProduct.status === "string") {
|
|
28160
|
+
setStatus(savedProduct.status);
|
|
28161
|
+
}
|
|
28162
|
+
if (typeof savedProduct.approvalStatus === "string") {
|
|
28163
|
+
setApprovalStatus(savedProduct.approvalStatus);
|
|
28164
|
+
}
|
|
28165
|
+
if (approvalStatus === "approved") {
|
|
28166
|
+
setRejectionReason("");
|
|
28167
|
+
}
|
|
27634
28168
|
const savedId = create ? savedProduct.id : productId;
|
|
27635
28169
|
const savedSeoId = await saveSeo(seo, productSlugValue, seoId);
|
|
27636
28170
|
const linkedSeoId = savedSeoId ?? savedProduct.seoId ?? null;
|
|
@@ -27831,7 +28365,7 @@ function ProductEditPage({ productId }) {
|
|
|
27831
28365
|
}
|
|
27832
28366
|
setVariantRows([]);
|
|
27833
28367
|
}
|
|
27834
|
-
if (create && eventId != null) {
|
|
28368
|
+
if (create && eventsOn && eventId != null) {
|
|
27835
28369
|
const attachRes = await fetch("/api/event_products", {
|
|
27836
28370
|
method: "POST",
|
|
27837
28371
|
headers: {
|
|
@@ -27936,6 +28470,27 @@ function ProductEditPage({ productId }) {
|
|
|
27936
28470
|
title: pageTitle,
|
|
27937
28471
|
subtitle: pageSubtitle,
|
|
27938
28472
|
closeHref: listReturnUrl,
|
|
28473
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
28474
|
+
className: "flex items-center gap-2"
|
|
28475
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
28476
|
+
value: approvalStatus,
|
|
28477
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
28478
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
28479
|
+
"aria-label": "Approval status"
|
|
28480
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
28481
|
+
value: "pending"
|
|
28482
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
28483
|
+
value: "approved"
|
|
28484
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
28485
|
+
value: "rejected"
|
|
28486
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
28487
|
+
type: "button",
|
|
28488
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
28489
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
28490
|
+
title: rejectionReason || "Add rejection reason"
|
|
28491
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
28492
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
28493
|
+
}, approvalStatus.replace(/_/g, " ")) : null,
|
|
27939
28494
|
menuItems: [
|
|
27940
28495
|
{
|
|
27941
28496
|
label: saving ? "Saving..." : "Save",
|
|
@@ -27948,7 +28503,31 @@ function ProductEditPage({ productId }) {
|
|
|
27948
28503
|
onClick: /* @__PURE__ */ __name(() => setFeatured(!featured), "onClick")
|
|
27949
28504
|
}
|
|
27950
28505
|
]
|
|
27951
|
-
}),
|
|
28506
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
28507
|
+
open: rejectModalOpen,
|
|
28508
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
28509
|
+
if (!open) cancelRejectModal();
|
|
28510
|
+
}, "onOpenChange")
|
|
28511
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
28512
|
+
className: "max-w-md"
|
|
28513
|
+
}, /* @__PURE__ */ React.createElement(DialogHeader, null, /* @__PURE__ */ React.createElement(DialogTitle, null, "Rejection reason"), /* @__PURE__ */ React.createElement(DialogDescription, null, "Explain what the vendor needs to fix. This is required before you can save a rejected product.")), /* @__PURE__ */ React.createElement("textarea", {
|
|
28514
|
+
value: rejectDraft,
|
|
28515
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
28516
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
28517
|
+
placeholder: "Explain what needs to change\u2026",
|
|
28518
|
+
autoFocus: true
|
|
28519
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
28520
|
+
className: "gap-2 sm:gap-0"
|
|
28521
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
28522
|
+
type: "button",
|
|
28523
|
+
variant: "outline",
|
|
28524
|
+
onClick: cancelRejectModal
|
|
28525
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
28526
|
+
type: "button",
|
|
28527
|
+
variant: "destructive",
|
|
28528
|
+
disabled: !rejectDraft.trim(),
|
|
28529
|
+
onClick: confirmRejectReason
|
|
28530
|
+
}, "Confirm reject")))), errors.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
27952
28531
|
className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4"
|
|
27953
28532
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
27954
28533
|
className: "flex"
|
|
@@ -28071,7 +28650,7 @@ function ProductEditPage({ productId }) {
|
|
|
28071
28650
|
className: "mt-1 text-xs text-gray-500"
|
|
28072
28651
|
}, "Only collections in the selected category are listed.") : /* @__PURE__ */ React.createElement("p", {
|
|
28073
28652
|
className: "mt-1 text-xs text-gray-500"
|
|
28074
|
-
}, "Select a category to load collections.")), create ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28653
|
+
}, "Select a category to load collections.")), create && eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28075
28654
|
className: labelCls3
|
|
28076
28655
|
}, "Event"), /* @__PURE__ */ React.createElement("select", {
|
|
28077
28656
|
value: eventId ?? "",
|
|
@@ -28167,7 +28746,8 @@ function ProductEditPage({ productId }) {
|
|
|
28167
28746
|
}, "Status"), /* @__PURE__ */ React.createElement("select", {
|
|
28168
28747
|
value: status,
|
|
28169
28748
|
onChange: /* @__PURE__ */ __name((e) => setStatus(e.target.value), "onChange"),
|
|
28170
|
-
className: inputCls3
|
|
28749
|
+
className: inputCls3,
|
|
28750
|
+
disabled: approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected")
|
|
28171
28751
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28172
28752
|
value: "draft"
|
|
28173
28753
|
}, "Draft"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28176,7 +28756,11 @@ function ProductEditPage({ productId }) {
|
|
|
28176
28756
|
value: "reserved"
|
|
28177
28757
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
28178
28758
|
value: "sold"
|
|
28179
|
-
}, "Sold"))
|
|
28759
|
+
}, "Sold")), approvalOn && vendorPortal && approvalStatus === "pending" ? /* @__PURE__ */ React.createElement("p", {
|
|
28760
|
+
className: "mt-1 text-xs text-gray-500"
|
|
28761
|
+
}, "Waiting for admin approval. The product goes live when approved.") : null, approvalOn && vendorPortal && approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("p", {
|
|
28762
|
+
className: "mt-1 text-xs text-red-600"
|
|
28763
|
+
}, "Rejected", rejectionReason ? `: ${rejectionReason}` : "", ".") : null)))), /* @__PURE__ */ React.createElement(ProductVariantsSection, {
|
|
28180
28764
|
hasVariants,
|
|
28181
28765
|
onHasVariantsChange: setHasVariants,
|
|
28182
28766
|
variantOptionRows,
|
|
@@ -28303,14 +28887,11 @@ function ProductEditPage({ productId }) {
|
|
|
28303
28887
|
className: "flex flex-wrap items-start gap-2 p-2 bg-white rounded border border-gray-200"
|
|
28304
28888
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
28305
28889
|
className: "flex-1 min-w-[200px]"
|
|
28306
|
-
}, /* @__PURE__ */ React.createElement(
|
|
28307
|
-
|
|
28308
|
-
}, "Image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28309
|
-
type: "url",
|
|
28890
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
28891
|
+
label: "Image",
|
|
28310
28892
|
value: row.url,
|
|
28311
|
-
onChange: /* @__PURE__ */ __name((
|
|
28312
|
-
|
|
28313
|
-
placeholder: "https://..."
|
|
28893
|
+
onChange: /* @__PURE__ */ __name((v) => setImage(i, "url", v), "onChange"),
|
|
28894
|
+
inputClassName: inputCls3
|
|
28314
28895
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
28315
28896
|
className: "flex-1 min-w-[120px]"
|
|
28316
28897
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -28389,6 +28970,8 @@ var init_ProductEditPage = __esm({
|
|
|
28389
28970
|
"use client";
|
|
28390
28971
|
init_vendor_scope();
|
|
28391
28972
|
init_admin_list_return_url();
|
|
28973
|
+
init_dialog();
|
|
28974
|
+
init_button();
|
|
28392
28975
|
init_SeoSection();
|
|
28393
28976
|
init_DetailPageLayout();
|
|
28394
28977
|
init_DetailPageHeader();
|
|
@@ -28399,6 +28982,8 @@ var init_ProductEditPage = __esm({
|
|
|
28399
28982
|
init_inventory_validation();
|
|
28400
28983
|
init_category_item_label();
|
|
28401
28984
|
init_use_category_collections();
|
|
28985
|
+
init_ImageOrUrlField();
|
|
28986
|
+
init_admin_config_context();
|
|
28402
28987
|
init_ProductVariantsSection();
|
|
28403
28988
|
init_product_variants();
|
|
28404
28989
|
__name(parseCategoryIdFromReturnUrl, "parseCategoryIdFromReturnUrl");
|
|
@@ -28668,12 +29253,6 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28668
29253
|
]);
|
|
28669
29254
|
return;
|
|
28670
29255
|
}
|
|
28671
|
-
if (create && !categoryId) {
|
|
28672
|
-
setErrors([
|
|
28673
|
-
"Category is required"
|
|
28674
|
-
]);
|
|
28675
|
-
return;
|
|
28676
|
-
}
|
|
28677
29256
|
setSaving(true);
|
|
28678
29257
|
try {
|
|
28679
29258
|
const savedSeoId = await saveSeo(seo, slug.trim(), seoId);
|
|
@@ -28894,14 +29473,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28894
29473
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
28895
29474
|
className: `${inputCls4} min-h-[80px]`,
|
|
28896
29475
|
rows: 3
|
|
28897
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
28898
|
-
|
|
28899
|
-
}, "Cover image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28900
|
-
type: "url",
|
|
29476
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29477
|
+
label: "Cover image",
|
|
28901
29478
|
value: image,
|
|
28902
|
-
onChange:
|
|
28903
|
-
|
|
28904
|
-
placeholder: "https://..."
|
|
29479
|
+
onChange: setImage,
|
|
29480
|
+
inputClassName: inputCls4
|
|
28905
29481
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28906
29482
|
className: labelCls4
|
|
28907
29483
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -28928,17 +29504,24 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28928
29504
|
className: "text-xs font-medium text-gray-700 mb-2"
|
|
28929
29505
|
}, "Hero carousel"), heroSlides.map((slide, i) => /* @__PURE__ */ React.createElement("div", {
|
|
28930
29506
|
key: i,
|
|
28931
|
-
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border"
|
|
28932
|
-
}, /* @__PURE__ */ React.createElement("
|
|
29507
|
+
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border items-start"
|
|
29508
|
+
}, slide.type === "image" ? /* @__PURE__ */ React.createElement("div", {
|
|
29509
|
+
className: "flex-1 min-w-[200px]"
|
|
29510
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29511
|
+
label: "Slide image",
|
|
29512
|
+
value: slide.url,
|
|
29513
|
+
onChange: /* @__PURE__ */ __name((v) => updateHeroSlide(i, "url", v), "onChange"),
|
|
29514
|
+
inputClassName: inputCls4
|
|
29515
|
+
})) : /* @__PURE__ */ React.createElement("input", {
|
|
28933
29516
|
type: "url",
|
|
28934
29517
|
value: slide.url,
|
|
28935
29518
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "url", e.target.value), "onChange"),
|
|
28936
|
-
placeholder: "
|
|
29519
|
+
placeholder: "Video URL",
|
|
28937
29520
|
className: `${inputCls4} flex-1 min-w-[200px]`
|
|
28938
29521
|
}), /* @__PURE__ */ React.createElement("select", {
|
|
28939
29522
|
value: slide.type,
|
|
28940
29523
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "type", e.target.value), "onChange"),
|
|
28941
|
-
className: `${inputCls4} w-24`
|
|
29524
|
+
className: `${inputCls4} w-24 mt-6`
|
|
28942
29525
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28943
29526
|
value: "image"
|
|
28944
29527
|
}, "Image"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28948,11 +29531,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28948
29531
|
value: slide.caption,
|
|
28949
29532
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "caption", e.target.value), "onChange"),
|
|
28950
29533
|
placeholder: "Caption",
|
|
28951
|
-
className: `${inputCls4} flex-1 min-w-[120px]`
|
|
29534
|
+
className: `${inputCls4} flex-1 min-w-[120px] mt-6`
|
|
28952
29535
|
}), /* @__PURE__ */ React.createElement("button", {
|
|
28953
29536
|
type: "button",
|
|
28954
29537
|
onClick: /* @__PURE__ */ __name(() => removeHeroSlide(i), "onClick"),
|
|
28955
|
-
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0"
|
|
29538
|
+
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mt-6"
|
|
28956
29539
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Trash2, {
|
|
28957
29540
|
className: "h-4 w-4"
|
|
28958
29541
|
})))), /* @__PURE__ */ React.createElement("button", {
|
|
@@ -29079,6 +29662,7 @@ var init_CollectionEditPage = __esm({
|
|
|
29079
29662
|
init_use_catalog_categories();
|
|
29080
29663
|
init_admin_config_context();
|
|
29081
29664
|
init_category_related_product_labels();
|
|
29665
|
+
init_ImageOrUrlField();
|
|
29082
29666
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
29083
29667
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
29084
29668
|
url: "",
|
|
@@ -29096,116 +29680,6 @@ var init_CollectionEditPage = __esm({
|
|
|
29096
29680
|
__name(CollectionEditPage, "CollectionEditPage");
|
|
29097
29681
|
}
|
|
29098
29682
|
});
|
|
29099
|
-
function ImageOrUrlField({ label, value, onChange, placeholder = "https://\u2026", inputClassName = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm", labelClassName = "block text-xs font-medium text-gray-600 mb-1", previewVariant = "banner", maxSizeMb = 10 }) {
|
|
29100
|
-
const fileInputRef = React26.useRef(null);
|
|
29101
|
-
const [isUploading, setIsUploading] = React26.useState(false);
|
|
29102
|
-
const [error, setError] = React26.useState(null);
|
|
29103
|
-
const previewCls = previewVariant === "logo" ? "h-20 w-20 rounded-md border border-gray-200 bg-white object-contain" : "max-h-32 w-full rounded-md border border-gray-200 bg-gray-50 object-cover";
|
|
29104
|
-
const handleUpload = React26.useCallback(async (file) => {
|
|
29105
|
-
setError(null);
|
|
29106
|
-
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
29107
|
-
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
29108
|
-
return;
|
|
29109
|
-
}
|
|
29110
|
-
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
29111
|
-
setError(`File must be under ${maxSizeMb}MB`);
|
|
29112
|
-
return;
|
|
29113
|
-
}
|
|
29114
|
-
setIsUploading(true);
|
|
29115
|
-
try {
|
|
29116
|
-
const formData = new FormData();
|
|
29117
|
-
formData.append("file", file);
|
|
29118
|
-
const response = await fetch("/api/upload", {
|
|
29119
|
-
method: "POST",
|
|
29120
|
-
body: formData
|
|
29121
|
-
});
|
|
29122
|
-
const data = await response.json();
|
|
29123
|
-
if (!response.ok) {
|
|
29124
|
-
throw new Error(data.error || data.details || "Upload failed");
|
|
29125
|
-
}
|
|
29126
|
-
onChange(data.filePath ?? "");
|
|
29127
|
-
} catch (err) {
|
|
29128
|
-
setError(err instanceof Error ? err.message : "Upload failed");
|
|
29129
|
-
} finally {
|
|
29130
|
-
setIsUploading(false);
|
|
29131
|
-
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
29132
|
-
}
|
|
29133
|
-
}, [
|
|
29134
|
-
maxSizeMb,
|
|
29135
|
-
onChange
|
|
29136
|
-
]);
|
|
29137
|
-
const onFileChange = React26.useCallback((e) => {
|
|
29138
|
-
const file = e.target.files?.[0];
|
|
29139
|
-
if (file) void handleUpload(file);
|
|
29140
|
-
}, [
|
|
29141
|
-
handleUpload
|
|
29142
|
-
]);
|
|
29143
|
-
const trimmed = value.trim();
|
|
29144
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
29145
|
-
className: "space-y-2"
|
|
29146
|
-
}, /* @__PURE__ */ React.createElement("label", {
|
|
29147
|
-
className: labelClassName
|
|
29148
|
-
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
29149
|
-
className: "flex items-start gap-3"
|
|
29150
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
29151
|
-
src: trimmed,
|
|
29152
|
-
alt: label,
|
|
29153
|
-
className: previewCls,
|
|
29154
|
-
onError: /* @__PURE__ */ __name((e) => {
|
|
29155
|
-
e.currentTarget.style.display = "none";
|
|
29156
|
-
}, "onError")
|
|
29157
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
29158
|
-
type: "button",
|
|
29159
|
-
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
29160
|
-
className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-600 hover:bg-gray-50"
|
|
29161
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
29162
|
-
className: "h-3 w-3"
|
|
29163
|
-
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
29164
|
-
className: "flex flex-wrap gap-2"
|
|
29165
|
-
}, /* @__PURE__ */ React.createElement("input", {
|
|
29166
|
-
type: "url",
|
|
29167
|
-
value,
|
|
29168
|
-
onChange: /* @__PURE__ */ __name((e) => {
|
|
29169
|
-
setError(null);
|
|
29170
|
-
onChange(e.target.value);
|
|
29171
|
-
}, "onChange"),
|
|
29172
|
-
placeholder,
|
|
29173
|
-
className: `${inputClassName} min-w-0 flex-1`
|
|
29174
|
-
}), /* @__PURE__ */ React.createElement("input", {
|
|
29175
|
-
ref: fileInputRef,
|
|
29176
|
-
type: "file",
|
|
29177
|
-
accept: ACCEPTED_TYPES.join(","),
|
|
29178
|
-
onChange: onFileChange,
|
|
29179
|
-
className: "hidden",
|
|
29180
|
-
disabled: isUploading
|
|
29181
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
29182
|
-
type: "button",
|
|
29183
|
-
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
29184
|
-
disabled: isUploading,
|
|
29185
|
-
className: "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
29186
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
|
|
29187
|
-
className: "h-3.5 w-3.5"
|
|
29188
|
-
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
29189
|
-
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
29190
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.AlertCircle, {
|
|
29191
|
-
className: "h-3.5 w-3.5 shrink-0"
|
|
29192
|
-
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
29193
|
-
className: "text-xs text-gray-500"
|
|
29194
|
-
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"));
|
|
29195
|
-
}
|
|
29196
|
-
var ACCEPTED_TYPES;
|
|
29197
|
-
var init_ImageOrUrlField = __esm({
|
|
29198
|
-
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
29199
|
-
"use client";
|
|
29200
|
-
ACCEPTED_TYPES = [
|
|
29201
|
-
"image/jpeg",
|
|
29202
|
-
"image/png",
|
|
29203
|
-
"image/gif",
|
|
29204
|
-
"image/webp"
|
|
29205
|
-
];
|
|
29206
|
-
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
29207
|
-
}
|
|
29208
|
-
});
|
|
29209
29683
|
function AttachProductModal({ open, onOpenChange, categoryId, categoryName, excludeProductIds = [], onAttach }) {
|
|
29210
29684
|
const [attachingId, setAttachingId] = React26.useState(null);
|
|
29211
29685
|
const [error, setError] = React26.useState(null);
|
|
@@ -30150,6 +30624,10 @@ function combineCityCountry(city, country) {
|
|
|
30150
30624
|
function EventEditPage({ eventId }) {
|
|
30151
30625
|
const router = navigation.useRouter();
|
|
30152
30626
|
const searchParams = navigation.useSearchParams();
|
|
30627
|
+
const { data: session } = react.useSession();
|
|
30628
|
+
const { requireEventApproval } = React26.useContext(exports.AdminConfigContext);
|
|
30629
|
+
const approvalOn = requireEventApproval === true;
|
|
30630
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
30153
30631
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/events";
|
|
30154
30632
|
const create = isCreate4(eventId);
|
|
30155
30633
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -30162,6 +30640,11 @@ function EventEditPage({ eventId }) {
|
|
|
30162
30640
|
const [slug, setSlug] = React26.useState("");
|
|
30163
30641
|
const [description, setDescription] = React26.useState("");
|
|
30164
30642
|
const [isActive, setIsActive] = React26.useState(true);
|
|
30643
|
+
const [approvalStatus, setApprovalStatus] = React26.useState("pending");
|
|
30644
|
+
const [rejectionReason, setRejectionReason] = React26.useState("");
|
|
30645
|
+
const [rejectModalOpen, setRejectModalOpen] = React26.useState(false);
|
|
30646
|
+
const [rejectDraft, setRejectDraft] = React26.useState("");
|
|
30647
|
+
const approvalBeforeRejectRef = React26.useRef("pending");
|
|
30165
30648
|
const [comingSoon, setComingSoon] = React26.useState(false);
|
|
30166
30649
|
const [bannerImageUrl, setBannerImageUrl] = React26.useState("");
|
|
30167
30650
|
const [logoUrl, setLogoUrl] = React26.useState("");
|
|
@@ -30247,6 +30730,16 @@ function EventEditPage({ eventId }) {
|
|
|
30247
30730
|
cancelled = true;
|
|
30248
30731
|
};
|
|
30249
30732
|
}, []);
|
|
30733
|
+
React26.useEffect(() => {
|
|
30734
|
+
if (create && vendorPortal && approvalOn) {
|
|
30735
|
+
setApprovalStatus("pending");
|
|
30736
|
+
setIsActive(false);
|
|
30737
|
+
}
|
|
30738
|
+
}, [
|
|
30739
|
+
create,
|
|
30740
|
+
vendorPortal,
|
|
30741
|
+
approvalOn
|
|
30742
|
+
]);
|
|
30250
30743
|
React26.useEffect(() => {
|
|
30251
30744
|
let cancelled = false;
|
|
30252
30745
|
(async () => {
|
|
@@ -30268,6 +30761,8 @@ function EventEditPage({ eventId }) {
|
|
|
30268
30761
|
setSlug(data.slug ?? "");
|
|
30269
30762
|
setDescription(data.description ?? "");
|
|
30270
30763
|
setIsActive(data.isActive ?? true);
|
|
30764
|
+
setApprovalStatus(typeof data.approvalStatus === "string" && data.approvalStatus ? data.approvalStatus : "pending");
|
|
30765
|
+
setRejectionReason(typeof data.rejectionReason === "string" ? data.rejectionReason : "");
|
|
30271
30766
|
setComingSoon(data.comingSoon ?? false);
|
|
30272
30767
|
setBannerImageUrl(data.bannerImageUrl ?? "");
|
|
30273
30768
|
setLogoUrl(data.logoUrl ?? "");
|
|
@@ -30379,11 +30874,11 @@ function EventEditPage({ eventId }) {
|
|
|
30379
30874
|
setErrors(nextErrors);
|
|
30380
30875
|
return null;
|
|
30381
30876
|
}
|
|
30382
|
-
|
|
30877
|
+
const payload = {
|
|
30383
30878
|
name: name.trim(),
|
|
30384
30879
|
slug: slug.trim(),
|
|
30385
30880
|
description: description.trim() || null,
|
|
30386
|
-
isActive,
|
|
30881
|
+
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
30387
30882
|
comingSoon,
|
|
30388
30883
|
bannerImageUrl: bannerImageUrl.trim() || null,
|
|
30389
30884
|
logoUrl: logoUrl.trim() || null,
|
|
@@ -30421,9 +30916,55 @@ function EventEditPage({ eventId }) {
|
|
|
30421
30916
|
sortOrder,
|
|
30422
30917
|
contactFormId
|
|
30423
30918
|
};
|
|
30919
|
+
if (approvalOn) {
|
|
30920
|
+
if (vendorPortal && create) {
|
|
30921
|
+
payload.approvalStatus = "pending";
|
|
30922
|
+
} else if (!vendorPortal) {
|
|
30923
|
+
payload.approvalStatus = approvalStatus;
|
|
30924
|
+
if (approvalStatus === "rejected") {
|
|
30925
|
+
payload.rejectionReason = rejectionReason.trim();
|
|
30926
|
+
}
|
|
30927
|
+
}
|
|
30928
|
+
}
|
|
30929
|
+
return payload;
|
|
30424
30930
|
}, "buildPayload");
|
|
30931
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
30932
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
30933
|
+
setRejectDraft(rejectionReason);
|
|
30934
|
+
setApprovalStatus("rejected");
|
|
30935
|
+
setRejectModalOpen(true);
|
|
30936
|
+
}, "openRejectModal");
|
|
30937
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
30938
|
+
const reason = rejectDraft.trim();
|
|
30939
|
+
if (!reason) return;
|
|
30940
|
+
setRejectionReason(reason);
|
|
30941
|
+
setRejectModalOpen(false);
|
|
30942
|
+
}, "confirmRejectReason");
|
|
30943
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
30944
|
+
if (!rejectionReason.trim()) {
|
|
30945
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
30946
|
+
}
|
|
30947
|
+
setRejectDraft(rejectionReason);
|
|
30948
|
+
setRejectModalOpen(false);
|
|
30949
|
+
}, "cancelRejectModal");
|
|
30950
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
30951
|
+
if (value === "rejected") {
|
|
30952
|
+
openRejectModal(approvalStatus);
|
|
30953
|
+
return;
|
|
30954
|
+
}
|
|
30955
|
+
setApprovalStatus(value);
|
|
30956
|
+
if (value !== "rejected") setRejectionReason("");
|
|
30957
|
+
}, "handleApprovalSelect");
|
|
30425
30958
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
30426
30959
|
setErrors([]);
|
|
30960
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
30961
|
+
setRejectDraft("");
|
|
30962
|
+
setRejectModalOpen(true);
|
|
30963
|
+
setErrors([
|
|
30964
|
+
"Rejection reason is required"
|
|
30965
|
+
]);
|
|
30966
|
+
return;
|
|
30967
|
+
}
|
|
30427
30968
|
const payload = buildPayload();
|
|
30428
30969
|
if (!payload) return;
|
|
30429
30970
|
setSaving(true);
|
|
@@ -30443,6 +30984,9 @@ function EventEditPage({ eventId }) {
|
|
|
30443
30984
|
return;
|
|
30444
30985
|
}
|
|
30445
30986
|
const saved = await res.json();
|
|
30987
|
+
if (typeof saved.isActive === "boolean") setIsActive(saved.isActive);
|
|
30988
|
+
if (typeof saved.approvalStatus === "string") setApprovalStatus(saved.approvalStatus);
|
|
30989
|
+
if (approvalStatus === "approved") setRejectionReason("");
|
|
30446
30990
|
const savedId = create ? saved.id : Number(eventId);
|
|
30447
30991
|
if (savedId != null && !Number.isNaN(savedId)) {
|
|
30448
30992
|
router.push(`/admin/events/${savedId}/edit?from=${encodeURIComponent(listReturnUrl)}`);
|
|
@@ -30505,19 +31049,66 @@ function EventEditPage({ eventId }) {
|
|
|
30505
31049
|
title: create ? "Add event" : "Edit event",
|
|
30506
31050
|
subtitle: create ? "Create a new event" : "Update event details and tickets",
|
|
30507
31051
|
closeHref: listReturnUrl,
|
|
31052
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
31053
|
+
className: "flex items-center gap-2"
|
|
31054
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
31055
|
+
value: approvalStatus,
|
|
31056
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
31057
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
31058
|
+
"aria-label": "Approval status"
|
|
31059
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
31060
|
+
value: "pending"
|
|
31061
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
31062
|
+
value: "approved"
|
|
31063
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
31064
|
+
value: "rejected"
|
|
31065
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
31066
|
+
type: "button",
|
|
31067
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
31068
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
31069
|
+
title: rejectionReason || "Add rejection reason"
|
|
31070
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
31071
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
31072
|
+
}, approvalStatus.replace(/_/g, " "), approvalStatus === "rejected" && rejectionReason ? ` \u2014 ${rejectionReason}` : "") : null,
|
|
30508
31073
|
menuItems: [
|
|
30509
31074
|
{
|
|
30510
31075
|
label: saving ? "Saving..." : "Save",
|
|
30511
31076
|
icon: LucideIcons.Save,
|
|
30512
31077
|
onClick: handleSave
|
|
30513
31078
|
},
|
|
30514
|
-
|
|
30515
|
-
|
|
30516
|
-
|
|
30517
|
-
|
|
30518
|
-
|
|
31079
|
+
...approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected") ? [] : [
|
|
31080
|
+
{
|
|
31081
|
+
label: isActive ? "Deactivate" : "Activate",
|
|
31082
|
+
icon: LucideIcons.Power,
|
|
31083
|
+
onClick: /* @__PURE__ */ __name(() => setIsActive(!isActive), "onClick")
|
|
31084
|
+
}
|
|
31085
|
+
]
|
|
30519
31086
|
]
|
|
30520
|
-
}),
|
|
31087
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
31088
|
+
open: rejectModalOpen,
|
|
31089
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
31090
|
+
if (!open) cancelRejectModal();
|
|
31091
|
+
}, "onOpenChange")
|
|
31092
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
31093
|
+
className: "max-w-md"
|
|
31094
|
+
}, /* @__PURE__ */ React.createElement(DialogHeader, null, /* @__PURE__ */ React.createElement(DialogTitle, null, "Rejection reason"), /* @__PURE__ */ React.createElement(DialogDescription, null, "Explain what the vendor needs to fix. This is required before you can save a rejected event.")), /* @__PURE__ */ React.createElement("textarea", {
|
|
31095
|
+
value: rejectDraft,
|
|
31096
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
31097
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31098
|
+
placeholder: "Explain what needs to change\u2026",
|
|
31099
|
+
autoFocus: true
|
|
31100
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
31101
|
+
className: "gap-2 sm:gap-0"
|
|
31102
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
31103
|
+
type: "button",
|
|
31104
|
+
variant: "outline",
|
|
31105
|
+
onClick: cancelRejectModal
|
|
31106
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
31107
|
+
type: "button",
|
|
31108
|
+
variant: "destructive",
|
|
31109
|
+
disabled: !rejectDraft.trim(),
|
|
31110
|
+
onClick: confirmRejectReason
|
|
31111
|
+
}, "Confirm reject")))), !create && logoUrl.trim() ? /* @__PURE__ */ React.createElement("div", {
|
|
30521
31112
|
className: "flex items-center gap-3 px-4 sm:px-6 py-3 border-b border-gray-100 bg-gray-50/80"
|
|
30522
31113
|
}, /* @__PURE__ */ React.createElement("img", {
|
|
30523
31114
|
src: logoUrl.trim(),
|
|
@@ -30876,11 +31467,15 @@ var init_EventEditPage = __esm({
|
|
|
30876
31467
|
"src/admin/pages/EventEditPage.tsx"() {
|
|
30877
31468
|
"use client";
|
|
30878
31469
|
init_admin_list_return_url();
|
|
31470
|
+
init_vendor_scope();
|
|
30879
31471
|
init_DetailPageLayout();
|
|
30880
31472
|
init_DetailPageHeader();
|
|
30881
31473
|
init_ImageOrUrlField();
|
|
30882
31474
|
init_JoditRichText();
|
|
30883
31475
|
init_EventProductsSection();
|
|
31476
|
+
init_admin_config_context();
|
|
31477
|
+
init_dialog();
|
|
31478
|
+
init_button();
|
|
30884
31479
|
init_EventManagementFields();
|
|
30885
31480
|
init_event_named_lists();
|
|
30886
31481
|
init_social_media_links();
|
|
@@ -30956,6 +31551,8 @@ async function validateComboProductInventory(productIds) {
|
|
|
30956
31551
|
function ComboEditPage({ comboId }) {
|
|
30957
31552
|
const router = navigation.useRouter();
|
|
30958
31553
|
const searchParams = navigation.useSearchParams();
|
|
31554
|
+
const { eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
31555
|
+
const eventsOn = eventsEnabled !== false;
|
|
30959
31556
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/combos";
|
|
30960
31557
|
const create = isCreate5(comboId);
|
|
30961
31558
|
const duplicateFrom = searchParams.get("duplicateFrom")?.trim();
|
|
@@ -30977,8 +31574,9 @@ function ComboEditPage({ comboId }) {
|
|
|
30977
31574
|
const [productOptions, setProductOptions] = React26.useState([]);
|
|
30978
31575
|
const [fixedItems, setFixedItems] = React26.useState([]);
|
|
30979
31576
|
const [addonItems, setAddonItems] = React26.useState([]);
|
|
31577
|
+
const canPickProducts = !eventsOn || Boolean(eventId);
|
|
30980
31578
|
React26.useEffect(() => {
|
|
30981
|
-
if (!eventId) {
|
|
31579
|
+
if (!eventsOn || !eventId) {
|
|
30982
31580
|
setDefaultCurrency("INR");
|
|
30983
31581
|
return;
|
|
30984
31582
|
}
|
|
@@ -30990,9 +31588,14 @@ function ComboEditPage({ comboId }) {
|
|
|
30990
31588
|
cancelled = true;
|
|
30991
31589
|
};
|
|
30992
31590
|
}, [
|
|
30993
|
-
eventId
|
|
31591
|
+
eventId,
|
|
31592
|
+
eventsOn
|
|
30994
31593
|
]);
|
|
30995
31594
|
React26.useEffect(() => {
|
|
31595
|
+
if (!eventsOn) {
|
|
31596
|
+
setEventOptions([]);
|
|
31597
|
+
return;
|
|
31598
|
+
}
|
|
30996
31599
|
let cancelled = false;
|
|
30997
31600
|
(async () => {
|
|
30998
31601
|
try {
|
|
@@ -31012,45 +31615,63 @@ function ComboEditPage({ comboId }) {
|
|
|
31012
31615
|
return () => {
|
|
31013
31616
|
cancelled = true;
|
|
31014
31617
|
};
|
|
31015
|
-
}, [
|
|
31618
|
+
}, [
|
|
31619
|
+
eventsOn
|
|
31620
|
+
]);
|
|
31016
31621
|
React26.useEffect(() => {
|
|
31017
|
-
if (!eventId) {
|
|
31018
|
-
setProductOptions([]);
|
|
31019
|
-
return;
|
|
31020
|
-
}
|
|
31021
31622
|
let cancelled = false;
|
|
31022
31623
|
(async () => {
|
|
31023
31624
|
try {
|
|
31024
|
-
|
|
31025
|
-
|
|
31625
|
+
if (eventsOn) {
|
|
31626
|
+
if (!eventId) {
|
|
31627
|
+
setProductOptions([]);
|
|
31628
|
+
return;
|
|
31629
|
+
}
|
|
31630
|
+
const res2 = await fetch(`/api/event_products?eventId=${eventId}&limit=500`);
|
|
31631
|
+
if (!res2.ok) return;
|
|
31026
31632
|
const data = await res2.json();
|
|
31027
|
-
if (
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
|
|
31031
|
-
|
|
31032
|
-
const prodData = await prodRes.json();
|
|
31033
|
-
if (!cancelled && Array.isArray(prodData.data)) {
|
|
31034
|
-
const fetched = prodData.data.map((p) => ({
|
|
31035
|
-
value: String(p.id),
|
|
31036
|
-
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31037
|
-
}));
|
|
31038
|
-
setProductOptions((prev) => {
|
|
31039
|
-
const merged = [
|
|
31040
|
-
...fetched
|
|
31041
|
-
];
|
|
31042
|
-
for (const p of prev) {
|
|
31043
|
-
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31044
|
-
}
|
|
31045
|
-
return merged;
|
|
31046
|
-
});
|
|
31047
|
-
}
|
|
31048
|
-
}
|
|
31049
|
-
} else {
|
|
31050
|
-
setProductOptions([]);
|
|
31051
|
-
}
|
|
31633
|
+
if (cancelled || !Array.isArray(data.data)) return;
|
|
31634
|
+
const productIds = data.data.map((ep) => ep.productId).filter(Boolean);
|
|
31635
|
+
if (productIds.length === 0) {
|
|
31636
|
+
setProductOptions([]);
|
|
31637
|
+
return;
|
|
31052
31638
|
}
|
|
31639
|
+
const prodRes2 = await fetch(`/api/products?ids=${productIds.join(",")}&limit=500`);
|
|
31640
|
+
if (!prodRes2.ok) return;
|
|
31641
|
+
const prodData2 = await prodRes2.json();
|
|
31642
|
+
if (cancelled || !Array.isArray(prodData2.data)) return;
|
|
31643
|
+
const fetched2 = prodData2.data.map((p) => ({
|
|
31644
|
+
value: String(p.id),
|
|
31645
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31646
|
+
}));
|
|
31647
|
+
setProductOptions((prev) => {
|
|
31648
|
+
const merged = [
|
|
31649
|
+
...fetched2
|
|
31650
|
+
];
|
|
31651
|
+
for (const p of prev) {
|
|
31652
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31653
|
+
}
|
|
31654
|
+
return merged;
|
|
31655
|
+
});
|
|
31656
|
+
return;
|
|
31053
31657
|
}
|
|
31658
|
+
const prodRes = await fetch("/api/products?limit=500&sortField=name&sortOrder=asc");
|
|
31659
|
+
if (!prodRes.ok) return;
|
|
31660
|
+
const prodData = await prodRes.json();
|
|
31661
|
+
if (cancelled || !Array.isArray(prodData.data)) return;
|
|
31662
|
+
const fetched = prodData.data.map((p) => ({
|
|
31663
|
+
value: String(p.id),
|
|
31664
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31665
|
+
}));
|
|
31666
|
+
setProductOptions((prev) => {
|
|
31667
|
+
const merged = [
|
|
31668
|
+
...fetched
|
|
31669
|
+
];
|
|
31670
|
+
for (const p of prev) {
|
|
31671
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31672
|
+
}
|
|
31673
|
+
return merged;
|
|
31674
|
+
});
|
|
31054
31675
|
} catch {
|
|
31055
31676
|
}
|
|
31056
31677
|
})();
|
|
@@ -31058,7 +31679,8 @@ function ComboEditPage({ comboId }) {
|
|
|
31058
31679
|
cancelled = true;
|
|
31059
31680
|
};
|
|
31060
31681
|
}, [
|
|
31061
|
-
eventId
|
|
31682
|
+
eventId,
|
|
31683
|
+
eventsOn
|
|
31062
31684
|
]);
|
|
31063
31685
|
React26.useEffect(() => {
|
|
31064
31686
|
let cancelled = false;
|
|
@@ -31169,7 +31791,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31169
31791
|
]);
|
|
31170
31792
|
return;
|
|
31171
31793
|
}
|
|
31172
|
-
if (!trimmedEventId || !/^\d+$/.test(trimmedEventId)) {
|
|
31794
|
+
if (eventsOn && (!trimmedEventId || !/^\d+$/.test(trimmedEventId))) {
|
|
31173
31795
|
setErrors([
|
|
31174
31796
|
"Event is required"
|
|
31175
31797
|
]);
|
|
@@ -31228,10 +31850,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31228
31850
|
}
|
|
31229
31851
|
setSaving(true);
|
|
31230
31852
|
try {
|
|
31853
|
+
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
31231
31854
|
const payload = {
|
|
31232
31855
|
name: trimmedName,
|
|
31233
31856
|
desc: desc || null,
|
|
31234
|
-
eventId:
|
|
31857
|
+
eventId: resolvedEventId,
|
|
31235
31858
|
price,
|
|
31236
31859
|
currencyPrices: null,
|
|
31237
31860
|
minSelectableItems,
|
|
@@ -31325,7 +31948,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31325
31948
|
value: desc,
|
|
31326
31949
|
onChange: /* @__PURE__ */ __name((e) => setDesc(e.target.value), "onChange"),
|
|
31327
31950
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm min-h-[100px]"
|
|
31328
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31951
|
+
})), eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31329
31952
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
31330
31953
|
}, "Event *"), /* @__PURE__ */ React.createElement("select", {
|
|
31331
31954
|
value: eventId,
|
|
@@ -31340,7 +31963,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31340
31963
|
}, "Select event"), eventOptions.map((o) => /* @__PURE__ */ React.createElement("option", {
|
|
31341
31964
|
key: o.value,
|
|
31342
31965
|
value: o.value
|
|
31343
|
-
}, o.label)))))),
|
|
31966
|
+
}, o.label)))) : null)), canPickProducts && /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
|
|
31344
31967
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
31345
31968
|
}, "Combo items"), /* @__PURE__ */ React.createElement("div", {
|
|
31346
31969
|
className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-5"
|
|
@@ -31408,11 +32031,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31408
32031
|
step: "0.01",
|
|
31409
32032
|
value: priceStr,
|
|
31410
32033
|
onChange: /* @__PURE__ */ __name((e) => setPriceStr(e.target.value), "onChange"),
|
|
31411
|
-
disabled: !
|
|
32034
|
+
disabled: !canPickProducts,
|
|
31412
32035
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm disabled:bg-gray-100"
|
|
31413
32036
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
31414
32037
|
className: "text-xs text-gray-400 mt-1"
|
|
31415
|
-
}, eventId ? "Other currencies use the event\u2019s supported currencies and exchange rates." : "Select an event to set the combo price.")), /* @__PURE__ */ React.createElement("div", {
|
|
32038
|
+
}, eventsOn ? eventId ? "Other currencies use the event\u2019s supported currencies and exchange rates." : "Select an event to set the combo price." : "Price uses the store default currency (INR unless configured otherwise).")), /* @__PURE__ */ React.createElement("div", {
|
|
31416
32039
|
className: "grid grid-cols-2 gap-4"
|
|
31417
32040
|
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31418
32041
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -31478,6 +32101,7 @@ var init_ComboEditPage = __esm({
|
|
|
31478
32101
|
init_DetailPageLayout();
|
|
31479
32102
|
init_DetailPageHeader();
|
|
31480
32103
|
init_inventory_validation();
|
|
32104
|
+
init_admin_config_context();
|
|
31481
32105
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31482
32106
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
31483
32107
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -32312,9 +32936,9 @@ var VendorCategoryWorkspacePage_exports = {};
|
|
|
32312
32936
|
__export(VendorCategoryWorkspacePage_exports, {
|
|
32313
32937
|
default: () => VendorCategoryWorkspacePage
|
|
32314
32938
|
});
|
|
32315
|
-
function buildAdminProductColumns(base,
|
|
32939
|
+
function buildAdminProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32316
32940
|
const extraColumns = [
|
|
32317
|
-
...
|
|
32941
|
+
...showVendorColumn ? [
|
|
32318
32942
|
VENDOR_EXTRA_COLUMN
|
|
32319
32943
|
] : [],
|
|
32320
32944
|
...eventsEnabled ? [
|
|
@@ -32333,9 +32957,9 @@ function buildAdminProductColumns(base, multiVendorEnabled, eventsEnabled) {
|
|
|
32333
32957
|
...cols.slice(nameIdx + 1)
|
|
32334
32958
|
];
|
|
32335
32959
|
}
|
|
32336
|
-
function buildAllProductColumns(base,
|
|
32960
|
+
function buildAllProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32337
32961
|
const extraColumns = [
|
|
32338
|
-
...
|
|
32962
|
+
...showVendorColumn ? [
|
|
32339
32963
|
VENDOR_EXTRA_COLUMN
|
|
32340
32964
|
] : [],
|
|
32341
32965
|
...eventsEnabled ? [
|
|
@@ -32385,7 +33009,8 @@ function VendorCategoryWorkspacePage() {
|
|
|
32385
33009
|
const workspaceFrom = activeCategoryId ? `/admin/products?categoryId=${activeCategoryId}` : "/admin/products";
|
|
32386
33010
|
const itemLabel = activeCategory ? categorySingularName(activeCategory.name) : "Product";
|
|
32387
33011
|
const productColumns2 = React26.useMemo(() => {
|
|
32388
|
-
const
|
|
33012
|
+
const showVendorColumn = multiVendorEnabled !== false && !vendorPortal;
|
|
33013
|
+
const base = showAllProducts ? buildAllProductColumns(exports.STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false) : buildAdminProductColumns(exports.STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false);
|
|
32389
33014
|
return withCollectionRelationApi(base, activeCategoryId, vendorPortal);
|
|
32390
33015
|
}, [
|
|
32391
33016
|
vendorPortal,
|
|
@@ -33711,7 +34336,7 @@ function CustomerPicker({ value, label, onChange }) {
|
|
|
33711
34336
|
className: "text-xs text-gray-400 shrink-0 truncate"
|
|
33712
34337
|
}, c.email ?? ""))))));
|
|
33713
34338
|
}
|
|
33714
|
-
function ConditionCard({ condition, onChange, onRemove }) {
|
|
34339
|
+
function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
33715
34340
|
const iconMap = {
|
|
33716
34341
|
minAmount: /* @__PURE__ */ React.createElement(LucideIcons.DollarSign, {
|
|
33717
34342
|
className: "h-3.5 w-3.5 text-blue-500"
|
|
@@ -33767,7 +34392,7 @@ function ConditionCard({ condition, onChange, onRemove }) {
|
|
|
33767
34392
|
value: "minQuantity"
|
|
33768
34393
|
}, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33769
34394
|
value: "productMinQuantity"
|
|
33770
|
-
}, "Product"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34395
|
+
}, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33771
34396
|
value: "events"
|
|
33772
34397
|
}, "Events"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33773
34398
|
value: "nthOrder"
|
|
@@ -34001,11 +34626,13 @@ function RewardCard({ reward, onChange, onRemove, discountType, discountValue, e
|
|
|
34001
34626
|
})));
|
|
34002
34627
|
}
|
|
34003
34628
|
function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE", discountValue = "" }) {
|
|
34629
|
+
const { eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
34630
|
+
const eventsOn = eventsEnabled !== false;
|
|
34004
34631
|
const parsed = ruleTreeToFriendly(rules);
|
|
34005
34632
|
const [groups, setGroups] = React26.useState(parsed.groups);
|
|
34006
34633
|
const [groupOperator, setGroupOperator] = React26.useState(parsed.groupOperator);
|
|
34007
34634
|
const [rewards, setRewards] = React26.useState(parsed.rewards);
|
|
34008
|
-
const rewardEventId = groups.flatMap((g) => g.conditions).find((c) => c.kind === "events" && c.eventId != null)?.eventId ?? null;
|
|
34635
|
+
const rewardEventId = eventsOn ? groups.flatMap((g) => g.conditions).find((c) => c.kind === "events" && c.eventId != null)?.eventId ?? null : null;
|
|
34009
34636
|
React26.useEffect(() => {
|
|
34010
34637
|
const missingNameIds = [];
|
|
34011
34638
|
for (const g of groups) {
|
|
@@ -34242,7 +34869,8 @@ function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE
|
|
|
34242
34869
|
})), /* @__PURE__ */ React.createElement(ConditionCard, {
|
|
34243
34870
|
condition: c,
|
|
34244
34871
|
onChange: /* @__PURE__ */ __name((updated) => updateCondition(group.id, c.id, updated), "onChange"),
|
|
34245
|
-
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove")
|
|
34872
|
+
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove"),
|
|
34873
|
+
eventsOn
|
|
34246
34874
|
})))), /* @__PURE__ */ React.createElement(Button, {
|
|
34247
34875
|
type: "button",
|
|
34248
34876
|
variant: "outline",
|
|
@@ -34305,6 +34933,7 @@ var init_DiscountsConditionsBuilder = __esm({
|
|
|
34305
34933
|
"use client";
|
|
34306
34934
|
init_button();
|
|
34307
34935
|
init_select();
|
|
34936
|
+
init_admin_config_context();
|
|
34308
34937
|
__name(uid, "uid");
|
|
34309
34938
|
__name(conditionToRule, "conditionToRule");
|
|
34310
34939
|
__name(rewardToRule, "rewardToRule");
|
|
@@ -36486,7 +37115,7 @@ function AdminPageResolver({ slug }) {
|
|
|
36486
37115
|
const searchParams = navigation.useSearchParams();
|
|
36487
37116
|
const { data: session } = react.useSession();
|
|
36488
37117
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
36489
|
-
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
37118
|
+
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval, requireEventApproval } = React26.useContext(exports.AdminConfigContext);
|
|
36490
37119
|
const key = slug?.[0] || "dashboard";
|
|
36491
37120
|
const [vendorOptions, setVendorOptions] = React26.useState([]);
|
|
36492
37121
|
React26.useEffect(() => {
|
|
@@ -36520,6 +37149,11 @@ function AdminPageResolver({ slug }) {
|
|
|
36520
37149
|
columns = columns.filter((column) => column.field !== "eventId" && column.field !== "eventName");
|
|
36521
37150
|
filters = filters.filter((filter) => filter.param !== "eventId");
|
|
36522
37151
|
}
|
|
37152
|
+
const showApprovalColumn = key === "products" && requireProductApproval === true || key === "events" && requireEventApproval === true;
|
|
37153
|
+
if (!showApprovalColumn) {
|
|
37154
|
+
columns = columns.filter((column) => column.field !== "approvalStatus");
|
|
37155
|
+
filters = filters.filter((filter) => filter.param !== "approvalStatus");
|
|
37156
|
+
}
|
|
36523
37157
|
if (multiVendorEnabled !== false && !vendorPortal && STORE_VENDOR_RESOURCES.has(key) && key !== "event_products" && !columns.some((c) => c.field === "vendorId")) {
|
|
36524
37158
|
columns = [
|
|
36525
37159
|
VENDOR_COLUMN2,
|
|
@@ -36557,7 +37191,9 @@ function AdminPageResolver({ slug }) {
|
|
|
36557
37191
|
vendorOptions,
|
|
36558
37192
|
vendorPortal,
|
|
36559
37193
|
multiVendorEnabled,
|
|
36560
|
-
eventsEnabled
|
|
37194
|
+
eventsEnabled,
|
|
37195
|
+
requireProductApproval,
|
|
37196
|
+
requireEventApproval
|
|
36561
37197
|
]);
|
|
36562
37198
|
const isContactsWithStore = key === "contacts" && storeEnabled;
|
|
36563
37199
|
const extraListParams = React26.useMemo(() => isContactsWithStore ? {
|
|
@@ -36671,7 +37307,27 @@ function AdminPageResolver({ slug }) {
|
|
|
36671
37307
|
className: "ml-2"
|
|
36672
37308
|
}, "Redirecting\u2026"));
|
|
36673
37309
|
}
|
|
36674
|
-
if (vendorPortal && key === "product_categories") {
|
|
37310
|
+
if (vendorPortal && key === "product_categories" && vendorCanCreateCategories !== true) {
|
|
37311
|
+
router.replace("/admin/products");
|
|
37312
|
+
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37313
|
+
className: "flex justify-center py-8"
|
|
37314
|
+
}, /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37315
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37316
|
+
}), /* @__PURE__ */ React26__namespace.default.createElement("span", {
|
|
37317
|
+
className: "ml-2"
|
|
37318
|
+
}, "Redirecting\u2026"));
|
|
37319
|
+
}
|
|
37320
|
+
if (vendorPortal && key === "collections" && vendorCanCreateCollections !== true) {
|
|
37321
|
+
router.replace("/admin/products");
|
|
37322
|
+
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37323
|
+
className: "flex justify-center py-8"
|
|
37324
|
+
}, /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37325
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37326
|
+
}), /* @__PURE__ */ React26__namespace.default.createElement("span", {
|
|
37327
|
+
className: "ml-2"
|
|
37328
|
+
}, "Redirecting\u2026"));
|
|
37329
|
+
}
|
|
37330
|
+
if (vendorPortal && key === "brands" && vendorCanCreateBrands !== true) {
|
|
36675
37331
|
router.replace("/admin/products");
|
|
36676
37332
|
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
36677
37333
|
className: "flex justify-center py-8"
|