@infuro/cms-core 1.0.41 → 1.0.42
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 +903 -287
- package/dist/admin.d.cts +11 -1
- package/dist/admin.d.ts +11 -1
- package/dist/admin.js +903 -287
- 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-QU554KAZ.cjs → chunk-GINYYPXK.cjs} +796 -149
- package/dist/{chunk-4BG7XYSH.js → chunk-I6NH72MO.js} +1 -1
- package/dist/{chunk-ETZN4OQI.cjs → chunk-TAHX46EI.cjs} +6 -6
- package/dist/{chunk-EG4ICILK.js → chunk-W2NL365N.js} +745 -110
- 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.42" ;
|
|
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
|
});
|
|
@@ -7736,6 +7768,155 @@ var init_ComponentSettings = __esm({
|
|
|
7736
7768
|
__name(ComponentSettings, "ComponentSettings");
|
|
7737
7769
|
}
|
|
7738
7770
|
});
|
|
7771
|
+
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 }) {
|
|
7772
|
+
const fileInputRef = React26.useRef(null);
|
|
7773
|
+
const [isUploading, setIsUploading] = React26.useState(false);
|
|
7774
|
+
const [error, setError] = React26.useState(null);
|
|
7775
|
+
const [lightboxOpen, setLightboxOpen] = React26.useState(false);
|
|
7776
|
+
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";
|
|
7777
|
+
const closeLightbox = React26.useCallback(() => setLightboxOpen(false), []);
|
|
7778
|
+
React26.useEffect(() => {
|
|
7779
|
+
if (!lightboxOpen) return;
|
|
7780
|
+
const onKeyDown = /* @__PURE__ */ __name((e) => {
|
|
7781
|
+
if (e.key === "Escape") closeLightbox();
|
|
7782
|
+
}, "onKeyDown");
|
|
7783
|
+
window.addEventListener("keydown", onKeyDown);
|
|
7784
|
+
const prevOverflow = document.body.style.overflow;
|
|
7785
|
+
document.body.style.overflow = "hidden";
|
|
7786
|
+
return () => {
|
|
7787
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
7788
|
+
document.body.style.overflow = prevOverflow;
|
|
7789
|
+
};
|
|
7790
|
+
}, [
|
|
7791
|
+
lightboxOpen,
|
|
7792
|
+
closeLightbox
|
|
7793
|
+
]);
|
|
7794
|
+
const handleUpload = React26.useCallback(async (file) => {
|
|
7795
|
+
setError(null);
|
|
7796
|
+
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
7797
|
+
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
7798
|
+
return;
|
|
7799
|
+
}
|
|
7800
|
+
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
7801
|
+
setError(`File must be under ${maxSizeMb}MB`);
|
|
7802
|
+
return;
|
|
7803
|
+
}
|
|
7804
|
+
setIsUploading(true);
|
|
7805
|
+
try {
|
|
7806
|
+
const formData = new FormData();
|
|
7807
|
+
formData.append("file", file);
|
|
7808
|
+
const response = await fetch("/api/upload", {
|
|
7809
|
+
method: "POST",
|
|
7810
|
+
body: formData
|
|
7811
|
+
});
|
|
7812
|
+
const data = await response.json();
|
|
7813
|
+
if (!response.ok) {
|
|
7814
|
+
throw new Error(data.error || data.details || "Upload failed");
|
|
7815
|
+
}
|
|
7816
|
+
onChange(data.filePath ?? "");
|
|
7817
|
+
} catch (err) {
|
|
7818
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
7819
|
+
} finally {
|
|
7820
|
+
setIsUploading(false);
|
|
7821
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
7822
|
+
}
|
|
7823
|
+
}, [
|
|
7824
|
+
maxSizeMb,
|
|
7825
|
+
onChange
|
|
7826
|
+
]);
|
|
7827
|
+
const onFileChange = React26.useCallback((e) => {
|
|
7828
|
+
const file = e.target.files?.[0];
|
|
7829
|
+
if (file) void handleUpload(file);
|
|
7830
|
+
}, [
|
|
7831
|
+
handleUpload
|
|
7832
|
+
]);
|
|
7833
|
+
const trimmed = value.trim();
|
|
7834
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
7835
|
+
className: "space-y-2"
|
|
7836
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
7837
|
+
className: labelClassName
|
|
7838
|
+
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7839
|
+
className: "flex items-start gap-3"
|
|
7840
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7841
|
+
src: trimmed,
|
|
7842
|
+
alt: label,
|
|
7843
|
+
className: previewCls,
|
|
7844
|
+
role: "button",
|
|
7845
|
+
tabIndex: 0,
|
|
7846
|
+
title: "Click to enlarge",
|
|
7847
|
+
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
7848
|
+
onKeyDown: /* @__PURE__ */ __name((e) => {
|
|
7849
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7850
|
+
e.preventDefault();
|
|
7851
|
+
setLightboxOpen(true);
|
|
7852
|
+
}
|
|
7853
|
+
}, "onKeyDown"),
|
|
7854
|
+
onError: /* @__PURE__ */ __name((e) => {
|
|
7855
|
+
e.currentTarget.style.display = "none";
|
|
7856
|
+
}, "onError")
|
|
7857
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7858
|
+
type: "button",
|
|
7859
|
+
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
7860
|
+
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"
|
|
7861
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
7862
|
+
className: "h-3 w-3"
|
|
7863
|
+
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
7864
|
+
className: "flex flex-wrap gap-2"
|
|
7865
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
7866
|
+
type: "url",
|
|
7867
|
+
value,
|
|
7868
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
7869
|
+
setError(null);
|
|
7870
|
+
onChange(e.target.value);
|
|
7871
|
+
}, "onChange"),
|
|
7872
|
+
placeholder,
|
|
7873
|
+
className: `${inputClassName} min-w-0 flex-1`
|
|
7874
|
+
}), /* @__PURE__ */ React.createElement("input", {
|
|
7875
|
+
ref: fileInputRef,
|
|
7876
|
+
type: "file",
|
|
7877
|
+
accept: ACCEPTED_TYPES.join(","),
|
|
7878
|
+
onChange: onFileChange,
|
|
7879
|
+
className: "hidden",
|
|
7880
|
+
disabled: isUploading
|
|
7881
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7882
|
+
type: "button",
|
|
7883
|
+
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
7884
|
+
disabled: isUploading,
|
|
7885
|
+
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"
|
|
7886
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
|
|
7887
|
+
className: "h-3.5 w-3.5"
|
|
7888
|
+
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
7889
|
+
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
7890
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.AlertCircle, {
|
|
7891
|
+
className: "h-3.5 w-3.5 shrink-0"
|
|
7892
|
+
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
7893
|
+
className: "text-xs text-gray-500"
|
|
7894
|
+
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"), lightboxOpen && trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7895
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4",
|
|
7896
|
+
role: "dialog",
|
|
7897
|
+
"aria-modal": "true",
|
|
7898
|
+
"aria-label": `${label} preview`,
|
|
7899
|
+
onClick: closeLightbox
|
|
7900
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7901
|
+
src: trimmed,
|
|
7902
|
+
alt: label,
|
|
7903
|
+
className: "max-h-[90vh] max-w-[90vw] rounded-md object-contain shadow-lg",
|
|
7904
|
+
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
7905
|
+
})) : null);
|
|
7906
|
+
}
|
|
7907
|
+
var ACCEPTED_TYPES;
|
|
7908
|
+
var init_ImageOrUrlField = __esm({
|
|
7909
|
+
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
7910
|
+
"use client";
|
|
7911
|
+
ACCEPTED_TYPES = [
|
|
7912
|
+
"image/jpeg",
|
|
7913
|
+
"image/png",
|
|
7914
|
+
"image/gif",
|
|
7915
|
+
"image/webp"
|
|
7916
|
+
];
|
|
7917
|
+
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
7918
|
+
}
|
|
7919
|
+
});
|
|
7739
7920
|
function generateId() {
|
|
7740
7921
|
return `nav_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
7741
7922
|
}
|
|
@@ -8038,22 +8219,16 @@ function NavbarEditor({ config, onChange }) {
|
|
|
8038
8219
|
className: "space-y-6"
|
|
8039
8220
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8040
8221
|
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, {
|
|
8222
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
8223
|
+
label: "Logo",
|
|
8050
8224
|
value: config.logo || "",
|
|
8051
|
-
onChange: /* @__PURE__ */ __name((
|
|
8225
|
+
onChange: /* @__PURE__ */ __name((logo) => onChange({
|
|
8052
8226
|
...config,
|
|
8053
|
-
logo
|
|
8227
|
+
logo
|
|
8054
8228
|
}), "onChange"),
|
|
8229
|
+
previewVariant: "logo",
|
|
8055
8230
|
placeholder: "Logo image URL"
|
|
8056
|
-
}))
|
|
8231
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
8057
8232
|
className: "space-y-3"
|
|
8058
8233
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8059
8234
|
className: "flex items-center justify-between"
|
|
@@ -8092,6 +8267,7 @@ var init_NavbarEditor = __esm({
|
|
|
8092
8267
|
init_button();
|
|
8093
8268
|
init_switch();
|
|
8094
8269
|
init_label();
|
|
8270
|
+
init_ImageOrUrlField();
|
|
8095
8271
|
__name(generateId, "generateId");
|
|
8096
8272
|
__name(NavItemEditor, "NavItemEditor");
|
|
8097
8273
|
__name(updateItemInTree, "updateItemInTree");
|
|
@@ -11040,10 +11216,32 @@ function renderListPrice(value, item) {
|
|
|
11040
11216
|
return `${formatted} ${currency}`;
|
|
11041
11217
|
}
|
|
11042
11218
|
}
|
|
11219
|
+
function renderListThumbnail(url) {
|
|
11220
|
+
const trimmed = url.trim();
|
|
11221
|
+
if (!trimmed) return "\u2014";
|
|
11222
|
+
return React26.createElement("img", {
|
|
11223
|
+
src: trimmed,
|
|
11224
|
+
alt: "",
|
|
11225
|
+
className: "h-10 w-10 rounded border border-gray-200 bg-white object-contain"
|
|
11226
|
+
});
|
|
11227
|
+
}
|
|
11228
|
+
function productListImageUrl(item) {
|
|
11229
|
+
const meta = item.metadata;
|
|
11230
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return "";
|
|
11231
|
+
const images = meta.images;
|
|
11232
|
+
if (!Array.isArray(images)) return "";
|
|
11233
|
+
const rows = images;
|
|
11234
|
+
const def = rows.find((r) => r?.isDefault && typeof r.url === "string" && r.url.trim());
|
|
11235
|
+
if (def && typeof def.url === "string") return def.url.trim();
|
|
11236
|
+
const first = rows.find((r) => typeof r.url === "string" && r.url.trim());
|
|
11237
|
+
return first && typeof first.url === "string" ? first.url.trim() : "";
|
|
11238
|
+
}
|
|
11043
11239
|
exports.STORE_CRUD_CONFIGS = void 0;
|
|
11044
11240
|
var init_store_crud_configs = __esm({
|
|
11045
11241
|
"src/admin/store-crud-configs.ts"() {
|
|
11046
11242
|
__name(renderListPrice, "renderListPrice");
|
|
11243
|
+
__name(renderListThumbnail, "renderListThumbnail");
|
|
11244
|
+
__name(productListImageUrl, "productListImageUrl");
|
|
11047
11245
|
exports.STORE_CRUD_CONFIGS = {
|
|
11048
11246
|
products: {
|
|
11049
11247
|
title: "Products",
|
|
@@ -11069,9 +11267,40 @@ var init_store_crud_configs = __esm({
|
|
|
11069
11267
|
label: "Out of stock"
|
|
11070
11268
|
}
|
|
11071
11269
|
]
|
|
11270
|
+
},
|
|
11271
|
+
{
|
|
11272
|
+
param: "approvalStatus",
|
|
11273
|
+
label: "Approval",
|
|
11274
|
+
type: "select",
|
|
11275
|
+
options: [
|
|
11276
|
+
{
|
|
11277
|
+
value: "",
|
|
11278
|
+
label: "All"
|
|
11279
|
+
},
|
|
11280
|
+
{
|
|
11281
|
+
value: "pending",
|
|
11282
|
+
label: "Pending"
|
|
11283
|
+
},
|
|
11284
|
+
{
|
|
11285
|
+
value: "approved",
|
|
11286
|
+
label: "Approved"
|
|
11287
|
+
},
|
|
11288
|
+
{
|
|
11289
|
+
value: "rejected",
|
|
11290
|
+
label: "Rejected"
|
|
11291
|
+
}
|
|
11292
|
+
]
|
|
11072
11293
|
}
|
|
11073
11294
|
],
|
|
11074
11295
|
columns: [
|
|
11296
|
+
{
|
|
11297
|
+
field: "metadata",
|
|
11298
|
+
displayName: "Image",
|
|
11299
|
+
hideInCreate: true,
|
|
11300
|
+
hideInEdit: true,
|
|
11301
|
+
listFilter: false,
|
|
11302
|
+
render: /* @__PURE__ */ __name((_value, item) => renderListThumbnail(productListImageUrl(item)), "render")
|
|
11303
|
+
},
|
|
11075
11304
|
{
|
|
11076
11305
|
field: "name",
|
|
11077
11306
|
displayName: "Name"
|
|
@@ -11133,6 +11362,33 @@ var init_store_crud_configs = __esm({
|
|
|
11133
11362
|
}
|
|
11134
11363
|
]
|
|
11135
11364
|
},
|
|
11365
|
+
{
|
|
11366
|
+
field: "approvalStatus",
|
|
11367
|
+
displayName: "Approval",
|
|
11368
|
+
type: "select",
|
|
11369
|
+
listFilter: false,
|
|
11370
|
+
options: [
|
|
11371
|
+
{
|
|
11372
|
+
value: "pending",
|
|
11373
|
+
label: "Pending"
|
|
11374
|
+
},
|
|
11375
|
+
{
|
|
11376
|
+
value: "approved",
|
|
11377
|
+
label: "Approved"
|
|
11378
|
+
},
|
|
11379
|
+
{
|
|
11380
|
+
value: "rejected",
|
|
11381
|
+
label: "Rejected"
|
|
11382
|
+
}
|
|
11383
|
+
],
|
|
11384
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
11385
|
+
const v = value == null || value === "" ? null : String(value);
|
|
11386
|
+
if (v === "pending") return "Pending";
|
|
11387
|
+
if (v === "approved") return "Approved";
|
|
11388
|
+
if (v === "rejected") return "Rejected";
|
|
11389
|
+
return "\u2014";
|
|
11390
|
+
}, "render")
|
|
11391
|
+
},
|
|
11136
11392
|
{
|
|
11137
11393
|
field: "featured",
|
|
11138
11394
|
displayName: "Featured",
|
|
@@ -11256,6 +11512,14 @@ var init_store_crud_configs = __esm({
|
|
|
11256
11512
|
title: "Collections",
|
|
11257
11513
|
apiEndpoint: "/api/collections",
|
|
11258
11514
|
columns: [
|
|
11515
|
+
{
|
|
11516
|
+
field: "image",
|
|
11517
|
+
displayName: "Image",
|
|
11518
|
+
hideInCreate: true,
|
|
11519
|
+
hideInEdit: true,
|
|
11520
|
+
listFilter: false,
|
|
11521
|
+
render: /* @__PURE__ */ __name((value) => renderListThumbnail(typeof value === "string" ? value : ""), "render")
|
|
11522
|
+
},
|
|
11259
11523
|
{
|
|
11260
11524
|
field: "name",
|
|
11261
11525
|
displayName: "Name"
|
|
@@ -11652,6 +11916,13 @@ var init_store_crud_configs = __esm({
|
|
|
11652
11916
|
field: "slug",
|
|
11653
11917
|
displayName: "Slug"
|
|
11654
11918
|
},
|
|
11919
|
+
{
|
|
11920
|
+
field: "isCatalog",
|
|
11921
|
+
displayName: "Catalog",
|
|
11922
|
+
type: "boolean",
|
|
11923
|
+
hideInCreate: true,
|
|
11924
|
+
hideInEdit: true
|
|
11925
|
+
},
|
|
11655
11926
|
{
|
|
11656
11927
|
field: "active",
|
|
11657
11928
|
displayName: "Active",
|
|
@@ -11954,6 +12225,29 @@ var init_store_crud_configs = __esm({
|
|
|
11954
12225
|
label: "Paid"
|
|
11955
12226
|
}
|
|
11956
12227
|
]
|
|
12228
|
+
},
|
|
12229
|
+
{
|
|
12230
|
+
param: "approvalStatus",
|
|
12231
|
+
label: "Approval",
|
|
12232
|
+
type: "select",
|
|
12233
|
+
options: [
|
|
12234
|
+
{
|
|
12235
|
+
value: "",
|
|
12236
|
+
label: "All"
|
|
12237
|
+
},
|
|
12238
|
+
{
|
|
12239
|
+
value: "pending",
|
|
12240
|
+
label: "Pending"
|
|
12241
|
+
},
|
|
12242
|
+
{
|
|
12243
|
+
value: "approved",
|
|
12244
|
+
label: "Approved"
|
|
12245
|
+
},
|
|
12246
|
+
{
|
|
12247
|
+
value: "rejected",
|
|
12248
|
+
label: "Rejected"
|
|
12249
|
+
}
|
|
12250
|
+
]
|
|
11957
12251
|
}
|
|
11958
12252
|
],
|
|
11959
12253
|
columns: [
|
|
@@ -11985,6 +12279,33 @@ var init_store_crud_configs = __esm({
|
|
|
11985
12279
|
displayName: "Active",
|
|
11986
12280
|
type: "boolean"
|
|
11987
12281
|
},
|
|
12282
|
+
{
|
|
12283
|
+
field: "approvalStatus",
|
|
12284
|
+
displayName: "Approval",
|
|
12285
|
+
type: "select",
|
|
12286
|
+
listFilter: false,
|
|
12287
|
+
options: [
|
|
12288
|
+
{
|
|
12289
|
+
value: "pending",
|
|
12290
|
+
label: "Pending"
|
|
12291
|
+
},
|
|
12292
|
+
{
|
|
12293
|
+
value: "approved",
|
|
12294
|
+
label: "Approved"
|
|
12295
|
+
},
|
|
12296
|
+
{
|
|
12297
|
+
value: "rejected",
|
|
12298
|
+
label: "Rejected"
|
|
12299
|
+
}
|
|
12300
|
+
],
|
|
12301
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
12302
|
+
const v = value == null || value === "" ? null : String(value);
|
|
12303
|
+
if (v === "pending") return "Pending";
|
|
12304
|
+
if (v === "approved") return "Approved";
|
|
12305
|
+
if (v === "rejected") return "Rejected";
|
|
12306
|
+
return "\u2014";
|
|
12307
|
+
}, "render")
|
|
12308
|
+
},
|
|
11988
12309
|
{
|
|
11989
12310
|
field: "comingSoon",
|
|
11990
12311
|
displayName: "Coming soon",
|
|
@@ -12271,7 +12592,12 @@ function SettingsPage() {
|
|
|
12271
12592
|
const [themeSettingsLoading, setThemeSettingsLoading] = React26.useState(true);
|
|
12272
12593
|
const [storeEnabled, setStoreEnabled] = React26.useState(false);
|
|
12273
12594
|
const [multiVendorEnabled, setMultiVendorEnabled] = React26.useState(true);
|
|
12595
|
+
const [vendorCanCreateCategories, setVendorCanCreateCategories] = React26.useState(false);
|
|
12596
|
+
const [vendorCanCreateCollections, setVendorCanCreateCollections] = React26.useState(false);
|
|
12597
|
+
const [vendorCanCreateBrands, setVendorCanCreateBrands] = React26.useState(false);
|
|
12598
|
+
const [requireProductApproval, setRequireProductApproval] = React26.useState(false);
|
|
12274
12599
|
const [eventsEnabled, setEventsEnabled] = React26.useState(true);
|
|
12600
|
+
const [requireEventApproval, setRequireEventApproval] = React26.useState(false);
|
|
12275
12601
|
const [storeSettingsLoading, setStoreSettingsLoading] = React26.useState(true);
|
|
12276
12602
|
const [currency, setCurrency] = React26.useState(DEFAULT_CURRENCY);
|
|
12277
12603
|
const [currencies, setCurrencies] = React26.useState([]);
|
|
@@ -12331,10 +12657,15 @@ function SettingsPage() {
|
|
|
12331
12657
|
}).finally(() => setStoreSettingsLoading(false));
|
|
12332
12658
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12333
12659
|
setMultiVendorEnabled(data.enabled !== "false");
|
|
12660
|
+
setVendorCanCreateCategories(data.vendorCanCreateCategories === "true");
|
|
12661
|
+
setVendorCanCreateCollections(data.vendorCanCreateCollections === "true");
|
|
12662
|
+
setVendorCanCreateBrands(data.vendorCanCreateBrands === "true");
|
|
12663
|
+
setRequireProductApproval(data.requireProductApproval === "true");
|
|
12334
12664
|
}).catch(() => {
|
|
12335
12665
|
});
|
|
12336
12666
|
fetch("/api/settings/events").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12337
12667
|
setEventsEnabled(data.enabled !== "false");
|
|
12668
|
+
setRequireEventApproval(data.requireEventApproval === "true");
|
|
12338
12669
|
}).catch(() => {
|
|
12339
12670
|
});
|
|
12340
12671
|
fetch("/api/currencies/exchange-rates").then((r) => r.ok ? r.json() : []).then((data) => {
|
|
@@ -12462,6 +12793,22 @@ function SettingsPage() {
|
|
|
12462
12793
|
enabled: {
|
|
12463
12794
|
value: multiVendorEnabled ? "true" : "false",
|
|
12464
12795
|
type: "public"
|
|
12796
|
+
},
|
|
12797
|
+
vendorCanCreateCategories: {
|
|
12798
|
+
value: vendorCanCreateCategories ? "true" : "false",
|
|
12799
|
+
type: "public"
|
|
12800
|
+
},
|
|
12801
|
+
vendorCanCreateCollections: {
|
|
12802
|
+
value: vendorCanCreateCollections ? "true" : "false",
|
|
12803
|
+
type: "public"
|
|
12804
|
+
},
|
|
12805
|
+
vendorCanCreateBrands: {
|
|
12806
|
+
value: vendorCanCreateBrands ? "true" : "false",
|
|
12807
|
+
type: "public"
|
|
12808
|
+
},
|
|
12809
|
+
requireProductApproval: {
|
|
12810
|
+
value: requireProductApproval ? "true" : "false",
|
|
12811
|
+
type: "public"
|
|
12465
12812
|
}
|
|
12466
12813
|
})
|
|
12467
12814
|
});
|
|
@@ -12474,6 +12821,10 @@ function SettingsPage() {
|
|
|
12474
12821
|
enabled: {
|
|
12475
12822
|
value: eventsEnabled ? "true" : "false",
|
|
12476
12823
|
type: "public"
|
|
12824
|
+
},
|
|
12825
|
+
requireEventApproval: {
|
|
12826
|
+
value: requireEventApproval ? "true" : "false",
|
|
12827
|
+
type: "public"
|
|
12477
12828
|
}
|
|
12478
12829
|
})
|
|
12479
12830
|
});
|
|
@@ -12745,8 +13096,46 @@ function SettingsPage() {
|
|
|
12745
13096
|
className: "text-sm font-medium text-gray-700"
|
|
12746
13097
|
}, "Multi Vendor"), /* @__PURE__ */ React.createElement("p", {
|
|
12747
13098
|
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
|
|
13099
|
+
}, "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", {
|
|
13100
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13101
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
13102
|
+
className: "text-sm font-medium text-gray-700"
|
|
13103
|
+
}, "Vendor catalog permissions"), /* @__PURE__ */ React.createElement("p", {
|
|
13104
|
+
className: "text-xs text-gray-500 mb-3"
|
|
13105
|
+
}, "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", {
|
|
13106
|
+
className: "flex items-center gap-3"
|
|
13107
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13108
|
+
checked: vendorCanCreateCategories,
|
|
13109
|
+
onCheckedChange: setVendorCanCreateCategories
|
|
13110
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13111
|
+
className: "text-sm font-medium text-gray-700"
|
|
13112
|
+
}, "Can vendors create categories")), /* @__PURE__ */ React.createElement("div", {
|
|
13113
|
+
className: "flex items-center gap-3"
|
|
13114
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13115
|
+
checked: vendorCanCreateCollections,
|
|
13116
|
+
onCheckedChange: setVendorCanCreateCollections
|
|
13117
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13118
|
+
className: "text-sm font-medium text-gray-700"
|
|
13119
|
+
}, "Can vendors create collections")), /* @__PURE__ */ React.createElement("div", {
|
|
13120
|
+
className: "flex items-center gap-3"
|
|
13121
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13122
|
+
checked: vendorCanCreateBrands,
|
|
13123
|
+
onCheckedChange: setVendorCanCreateBrands
|
|
13124
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13125
|
+
className: "text-sm font-medium text-gray-700"
|
|
13126
|
+
}, "Can vendors create brands")), /* @__PURE__ */ React.createElement("div", {
|
|
13127
|
+
className: "flex items-start gap-3"
|
|
13128
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13129
|
+
checked: requireProductApproval,
|
|
13130
|
+
onCheckedChange: setRequireProductApproval
|
|
13131
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13132
|
+
className: "text-sm font-medium text-gray-700"
|
|
13133
|
+
}, "Require product approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13134
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13135
|
+
}, "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", {
|
|
13136
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13137
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13138
|
+
className: "flex items-center gap-3"
|
|
12750
13139
|
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
12751
13140
|
checked: eventsEnabled,
|
|
12752
13141
|
onCheckedChange: setEventsEnabled
|
|
@@ -12754,7 +13143,16 @@ function SettingsPage() {
|
|
|
12754
13143
|
className: "text-sm font-medium text-gray-700"
|
|
12755
13144
|
}, "Events"), /* @__PURE__ */ React.createElement("p", {
|
|
12756
13145
|
className: "text-xs text-gray-500"
|
|
12757
|
-
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))),
|
|
13146
|
+
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))), eventsEnabled && multiVendorEnabled ? /* @__PURE__ */ React.createElement("div", {
|
|
13147
|
+
className: "flex items-start gap-3 pl-1 border-t border-gray-200 pt-3"
|
|
13148
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13149
|
+
checked: requireEventApproval,
|
|
13150
|
+
onCheckedChange: setRequireEventApproval
|
|
13151
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13152
|
+
className: "text-sm font-medium text-gray-700"
|
|
13153
|
+
}, "Require event approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13154
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13155
|
+
}, "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
13156
|
className: "flex items-center justify-between mb-1"
|
|
12759
13157
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
12760
13158
|
className: "block text-sm font-semibold text-gray-700"
|
|
@@ -17024,15 +17422,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17024
17422
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17025
17423
|
}, "Layout below merges with Branding settings; values here override branding when set. Use absolute URLs for logos in email."), /* @__PURE__ */ React.createElement("div", {
|
|
17026
17424
|
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`,
|
|
17425
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17426
|
+
label: "Logo (optional override)",
|
|
17032
17427
|
value: logoUrl,
|
|
17033
|
-
onChange:
|
|
17428
|
+
onChange: setLogoUrl,
|
|
17429
|
+
previewVariant: "logo",
|
|
17034
17430
|
placeholder: "https://\u2026",
|
|
17035
|
-
|
|
17431
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17036
17432
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17037
17433
|
className: "space-y-1"
|
|
17038
17434
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17085,22 +17481,23 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17085
17481
|
className: "flex flex-wrap items-end gap-2 border-b border-border/60 pb-3 dark:border-gray-600"
|
|
17086
17482
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
17087
17483
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17088
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17089
|
-
|
|
17090
|
-
}, "Icon image URL"), /* @__PURE__ */ React.createElement(Input, {
|
|
17484
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17485
|
+
label: "Icon image",
|
|
17091
17486
|
value: row.iconUrl,
|
|
17092
|
-
onChange: /* @__PURE__ */ __name((
|
|
17487
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
17093
17488
|
const next = [
|
|
17094
17489
|
...socialLinkRows
|
|
17095
17490
|
];
|
|
17096
17491
|
next[i] = {
|
|
17097
17492
|
...next[i],
|
|
17098
|
-
iconUrl:
|
|
17493
|
+
iconUrl: v
|
|
17099
17494
|
};
|
|
17100
17495
|
setSocialLinkRows(next);
|
|
17101
17496
|
}, "onChange"),
|
|
17497
|
+
previewVariant: "logo",
|
|
17102
17498
|
placeholder: "https://\u2026",
|
|
17103
|
-
|
|
17499
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3",
|
|
17500
|
+
labelClassName: "text-xs text-muted-foreground"
|
|
17104
17501
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17105
17502
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17106
17503
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17747,15 +18144,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17747
18144
|
className: "h-8 text-sm"
|
|
17748
18145
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17749
18146
|
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`,
|
|
18147
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
18148
|
+
label: "Icon image",
|
|
17755
18149
|
value: iconImageUrl,
|
|
17756
|
-
onChange:
|
|
18150
|
+
onChange: setIconImageUrl,
|
|
18151
|
+
previewVariant: "logo",
|
|
17757
18152
|
placeholder: "https://\u2026 or /images/chat-icon.png",
|
|
17758
|
-
|
|
18153
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17759
18154
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
17760
18155
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17761
18156
|
}, "PNG or image URL. Leave empty to use emoji below.")), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -18315,6 +18710,7 @@ var init_PluginsPage = __esm({
|
|
|
18315
18710
|
init_checkbox();
|
|
18316
18711
|
init_select();
|
|
18317
18712
|
init_EventNotificationsPluginSettings();
|
|
18713
|
+
init_ImageOrUrlField();
|
|
18318
18714
|
init_chat_email_intent();
|
|
18319
18715
|
init_llm_agent_scope();
|
|
18320
18716
|
__name(normalizeLinkedInOrganizations, "normalizeLinkedInOrganizations");
|
|
@@ -19998,7 +20394,7 @@ var init_VendorPortalProfilePage = __esm({
|
|
|
19998
20394
|
__name(VendorPortalProfilePage, "VendorPortalProfilePage");
|
|
19999
20395
|
}
|
|
20000
20396
|
});
|
|
20001
|
-
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, menuItems = [] }) {
|
|
20397
|
+
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, headerExtra, menuItems = [] }) {
|
|
20002
20398
|
const router = navigation.useRouter();
|
|
20003
20399
|
const handleClose = /* @__PURE__ */ __name(() => {
|
|
20004
20400
|
if (onClose) onClose();
|
|
@@ -20022,7 +20418,7 @@ function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", close
|
|
|
20022
20418
|
className: "text-sm text-gray-400 truncate"
|
|
20023
20419
|
}, subtitle))), /* @__PURE__ */ React.createElement("div", {
|
|
20024
20420
|
className: "flex items-center gap-2 shrink-0"
|
|
20025
|
-
}, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
20421
|
+
}, headerExtra, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
20026
20422
|
className: "flex items-center gap-2 md:hidden"
|
|
20027
20423
|
}, menuItems.map((item, i) => {
|
|
20028
20424
|
const Icon2 = item.icon;
|
|
@@ -25202,15 +25598,11 @@ function SeoTabContent(props) {
|
|
|
25202
25598
|
onChange: /* @__PURE__ */ __name((e) => props.setSeoOgDescription(e.target.value), "onChange"),
|
|
25203
25599
|
placeholder: "Open Graph description",
|
|
25204
25600
|
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, {
|
|
25601
|
+
})), /* @__PURE__ */ React26__namespace.default.createElement(ImageOrUrlField, {
|
|
25602
|
+
label: "OG Image",
|
|
25210
25603
|
value: props.seoOgImage,
|
|
25211
|
-
onChange:
|
|
25212
|
-
|
|
25213
|
-
})));
|
|
25604
|
+
onChange: props.setSeoOgImage
|
|
25605
|
+
}));
|
|
25214
25606
|
}
|
|
25215
25607
|
function CollapsibleSection({ title, icon: Icon2, open, onToggle, children }) {
|
|
25216
25608
|
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
@@ -25596,6 +25988,7 @@ var init_PageBuilderPage = __esm({
|
|
|
25596
25988
|
init_ComponentSettings();
|
|
25597
25989
|
init_admin_config_context();
|
|
25598
25990
|
init_registry();
|
|
25991
|
+
init_ImageOrUrlField();
|
|
25599
25992
|
__name(createSelectable, "createSelectable");
|
|
25600
25993
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
25601
25994
|
__name(getIcon, "getIcon");
|
|
@@ -25658,15 +26051,12 @@ function SeoSection({ values, onChange }) {
|
|
|
25658
26051
|
onChange: /* @__PURE__ */ __name((e) => onChange("seoOgDescription", e.target.value), "onChange"),
|
|
25659
26052
|
className: textareaCls,
|
|
25660
26053
|
rows: 2
|
|
25661
|
-
})), /* @__PURE__ */ React.createElement(
|
|
25662
|
-
|
|
25663
|
-
}, "OG Image"), /* @__PURE__ */ React.createElement("input", {
|
|
25664
|
-
type: "url",
|
|
26054
|
+
})), /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26055
|
+
label: "OG Image",
|
|
25665
26056
|
value: values.seoOgImage,
|
|
25666
|
-
onChange: /* @__PURE__ */ __name((
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
}))));
|
|
26057
|
+
onChange: /* @__PURE__ */ __name((v) => onChange("seoOgImage", v), "onChange"),
|
|
26058
|
+
inputClassName: inputCls9
|
|
26059
|
+
})));
|
|
25670
26060
|
}
|
|
25671
26061
|
async function saveSeo(seo, slug, existingSeoId) {
|
|
25672
26062
|
const hasSeo = seo.seoTitle || seo.seoDescription || seo.seoKeywords || seo.seoOgTitle || seo.seoOgDescription || seo.seoOgImage;
|
|
@@ -25731,6 +26121,7 @@ async function fetchSeo(seoId) {
|
|
|
25731
26121
|
var init_SeoSection = __esm({
|
|
25732
26122
|
"src/components/Admin/SeoSection.tsx"() {
|
|
25733
26123
|
"use client";
|
|
26124
|
+
init_ImageOrUrlField();
|
|
25734
26125
|
__name(SeoSection, "SeoSection");
|
|
25735
26126
|
__name(saveSeo, "saveSeo");
|
|
25736
26127
|
__name(fetchSeo, "fetchSeo");
|
|
@@ -25745,6 +26136,8 @@ __export(BrandEditPage_exports, {
|
|
|
25745
26136
|
function BrandEditPage({ brandId }) {
|
|
25746
26137
|
const router = navigation.useRouter();
|
|
25747
26138
|
const searchParams = navigation.useSearchParams();
|
|
26139
|
+
const { data: session } = react.useSession();
|
|
26140
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
25748
26141
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/brands";
|
|
25749
26142
|
const create = isCreate(brandId);
|
|
25750
26143
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -25846,6 +26239,9 @@ function BrandEditPage({ brandId }) {
|
|
|
25846
26239
|
active,
|
|
25847
26240
|
sortOrder
|
|
25848
26241
|
};
|
|
26242
|
+
if (!vendorPortal) {
|
|
26243
|
+
payload.isCatalog = true;
|
|
26244
|
+
}
|
|
25849
26245
|
if (savedSeoId) payload.seoId = savedSeoId;
|
|
25850
26246
|
const res = await fetch(create ? "/api/brands" : `/api/brands/${brandId}`, {
|
|
25851
26247
|
method: create ? "POST" : "PUT",
|
|
@@ -25937,13 +26333,12 @@ function BrandEditPage({ brandId }) {
|
|
|
25937
26333
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
25938
26334
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm min-h-[80px]",
|
|
25939
26335
|
rows: 3
|
|
25940
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
25941
|
-
|
|
25942
|
-
}, "Logo URL"), /* @__PURE__ */ React.createElement("input", {
|
|
25943
|
-
type: "url",
|
|
26336
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26337
|
+
label: "Logo",
|
|
25944
26338
|
value: logo,
|
|
25945
|
-
onChange:
|
|
25946
|
-
|
|
26339
|
+
onChange: setLogo,
|
|
26340
|
+
previewVariant: "logo",
|
|
26341
|
+
inputClassName: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
25947
26342
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
25948
26343
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
25949
26344
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -25986,6 +26381,8 @@ var init_BrandEditPage = __esm({
|
|
|
25986
26381
|
init_SeoSection();
|
|
25987
26382
|
init_DetailPageLayout();
|
|
25988
26383
|
init_DetailPageHeader();
|
|
26384
|
+
init_vendor_scope();
|
|
26385
|
+
init_ImageOrUrlField();
|
|
25989
26386
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
25990
26387
|
__name(BrandEditPage, "BrandEditPage");
|
|
25991
26388
|
}
|
|
@@ -26893,16 +27290,38 @@ function ProductVariantsSection({ hasVariants, onHasVariantsChange, variantOptio
|
|
|
26893
27290
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
26894
27291
|
value: "sold"
|
|
26895
27292
|
}, "Sold"))), /* @__PURE__ */ React.createElement("td", {
|
|
26896
|
-
className: "px-3 py-2 align-top"
|
|
26897
|
-
},
|
|
26898
|
-
|
|
26899
|
-
|
|
26900
|
-
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
27293
|
+
className: "px-3 py-2 align-top min-w-[240px]"
|
|
27294
|
+
}, (() => {
|
|
27295
|
+
const lines = row.imageUrlsText === "" ? [
|
|
27296
|
+
""
|
|
27297
|
+
] : row.imageUrlsText.split("\n");
|
|
27298
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
27299
|
+
className: "space-y-2"
|
|
27300
|
+
}, lines.map((url, ui) => /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
27301
|
+
key: ui,
|
|
27302
|
+
label: ui === 0 ? "Images" : `Image ${ui + 1}`,
|
|
27303
|
+
value: url,
|
|
27304
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
27305
|
+
const next = [
|
|
27306
|
+
...lines
|
|
27307
|
+
];
|
|
27308
|
+
next[ui] = v;
|
|
27309
|
+
setVariantField(i, "imageUrlsText", next.join("\n"));
|
|
27310
|
+
}, "onChange"),
|
|
27311
|
+
inputClassName: inputCls2
|
|
27312
|
+
})), /* @__PURE__ */ React.createElement("button", {
|
|
27313
|
+
type: "button",
|
|
27314
|
+
onClick: /* @__PURE__ */ __name(() => setVariantField(i, "imageUrlsText", [
|
|
27315
|
+
...lines,
|
|
27316
|
+
""
|
|
27317
|
+
].join("\n")), "onClick"),
|
|
27318
|
+
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"
|
|
27319
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Plus, {
|
|
27320
|
+
className: "h-3 w-3"
|
|
27321
|
+
}), " Add image"), /* @__PURE__ */ React.createElement("p", {
|
|
27322
|
+
className: "text-[11px] text-gray-500"
|
|
27323
|
+
}, "First image is used on PDP."));
|
|
27324
|
+
})()), /* @__PURE__ */ React.createElement("td", {
|
|
26906
27325
|
className: "px-3 py-2 align-top text-right"
|
|
26907
27326
|
}, /* @__PURE__ */ React.createElement("button", {
|
|
26908
27327
|
type: "button",
|
|
@@ -26917,6 +27336,7 @@ var init_ProductVariantsSection = __esm({
|
|
|
26917
27336
|
"src/admin/pages/ProductVariantsSection.tsx"() {
|
|
26918
27337
|
"use client";
|
|
26919
27338
|
init_product_variants();
|
|
27339
|
+
init_ImageOrUrlField();
|
|
26920
27340
|
labelCls2 = "block text-xs font-medium text-gray-600 mb-1";
|
|
26921
27341
|
inputCls2 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
26922
27342
|
sectionCls2 = "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50";
|
|
@@ -27112,13 +27532,16 @@ function ProductEditPage({ productId }) {
|
|
|
27112
27532
|
const router = navigation.useRouter();
|
|
27113
27533
|
const searchParams = navigation.useSearchParams();
|
|
27114
27534
|
const { data: session } = react.useSession();
|
|
27535
|
+
const { eventsEnabled, requireProductApproval } = React26.useContext(exports.AdminConfigContext);
|
|
27536
|
+
const eventsOn = eventsEnabled !== false;
|
|
27537
|
+
const approvalOn = requireProductApproval === true;
|
|
27115
27538
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
27116
27539
|
const categoryIdParam = searchParams.get("categoryId")?.trim() ?? "";
|
|
27117
27540
|
const collectionIdParam = searchParams.get("collectionId")?.trim() ?? "";
|
|
27118
27541
|
const eventIdParam = searchParams.get("eventId")?.trim() ?? "";
|
|
27119
27542
|
const lockCategoryFromStore = isCreate2(productId) && /^\d+$/.test(categoryIdParam);
|
|
27120
27543
|
const lockCollectionFromQuery = isCreate2(productId) && /^\d+$/.test(collectionIdParam);
|
|
27121
|
-
const lockEventFromQuery = isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27544
|
+
const lockEventFromQuery = eventsOn && isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27122
27545
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? (lockCategoryFromStore ? `/admin/products?categoryId=${categoryIdParam}` : "/admin/products");
|
|
27123
27546
|
const create = isCreate2(productId);
|
|
27124
27547
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -27149,6 +27572,11 @@ function ProductEditPage({ productId }) {
|
|
|
27149
27572
|
const [compareAtPrice, setCompareAtPrice] = React26.useState(0);
|
|
27150
27573
|
const [quantity, setQuantity] = React26.useState(1);
|
|
27151
27574
|
const [status, setStatus] = React26.useState("draft");
|
|
27575
|
+
const [approvalStatus, setApprovalStatus] = React26.useState("pending");
|
|
27576
|
+
const [rejectionReason, setRejectionReason] = React26.useState("");
|
|
27577
|
+
const [rejectModalOpen, setRejectModalOpen] = React26.useState(false);
|
|
27578
|
+
const [rejectDraft, setRejectDraft] = React26.useState("");
|
|
27579
|
+
const approvalBeforeRejectRef = React26.useRef("pending");
|
|
27152
27580
|
const [featured, setFeatured] = React26.useState(false);
|
|
27153
27581
|
const [description, setDescription] = React26.useState("");
|
|
27154
27582
|
const [images, setImages] = React26.useState([
|
|
@@ -27253,7 +27681,7 @@ function ProductEditPage({ productId }) {
|
|
|
27253
27681
|
try {
|
|
27254
27682
|
const [brandRes, catRes, attrRes, taxesRes, eventsRes, formsRes, refundRes] = await Promise.all([
|
|
27255
27683
|
fetch("/api/brands?limit=500"),
|
|
27256
|
-
fetch("/api/product_categories?limit=500&isCatalog=true"),
|
|
27684
|
+
fetch(vendorPortal ? "/api/product_categories?limit=500" : "/api/product_categories?limit=500&isCatalog=true"),
|
|
27257
27685
|
fetch("/api/attributes?limit=500"),
|
|
27258
27686
|
fetch("/api/taxes?limit=200&sortField=name&sortOrder=asc"),
|
|
27259
27687
|
fetch("/api/events?limit=200&sortField=startDate&sortOrder=desc"),
|
|
@@ -27366,6 +27794,8 @@ function ProductEditPage({ productId }) {
|
|
|
27366
27794
|
setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
|
|
27367
27795
|
setQuantity(product.quantity ?? 1);
|
|
27368
27796
|
setStatus(product.status ?? "draft");
|
|
27797
|
+
setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
|
|
27798
|
+
setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
|
|
27369
27799
|
setFeatured(product.featured ?? false);
|
|
27370
27800
|
setDescription((m && typeof m.description === "string" ? m.description : "") ?? "");
|
|
27371
27801
|
const rawImages = m?.images;
|
|
@@ -27501,6 +27931,16 @@ function ProductEditPage({ productId }) {
|
|
|
27501
27931
|
create,
|
|
27502
27932
|
eventId
|
|
27503
27933
|
]);
|
|
27934
|
+
React26.useEffect(() => {
|
|
27935
|
+
if (create && vendorPortal && approvalOn) {
|
|
27936
|
+
setApprovalStatus("pending");
|
|
27937
|
+
setStatus((s) => s === "available" ? "draft" : s);
|
|
27938
|
+
}
|
|
27939
|
+
}, [
|
|
27940
|
+
create,
|
|
27941
|
+
vendorPortal,
|
|
27942
|
+
approvalOn
|
|
27943
|
+
]);
|
|
27504
27944
|
React26.useEffect(() => {
|
|
27505
27945
|
if (!create || !name.trim()) return;
|
|
27506
27946
|
setProductSlug(slugifyProductName(name));
|
|
@@ -27535,6 +27975,33 @@ function ProductEditPage({ productId }) {
|
|
|
27535
27975
|
}
|
|
27536
27976
|
setter(num);
|
|
27537
27977
|
}, "handleNumberChange");
|
|
27978
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
27979
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
27980
|
+
setRejectDraft(rejectionReason);
|
|
27981
|
+
setApprovalStatus("rejected");
|
|
27982
|
+
setRejectModalOpen(true);
|
|
27983
|
+
}, "openRejectModal");
|
|
27984
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
27985
|
+
const reason = rejectDraft.trim();
|
|
27986
|
+
if (!reason) return;
|
|
27987
|
+
setRejectionReason(reason);
|
|
27988
|
+
setRejectModalOpen(false);
|
|
27989
|
+
}, "confirmRejectReason");
|
|
27990
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
27991
|
+
if (!rejectionReason.trim()) {
|
|
27992
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
27993
|
+
}
|
|
27994
|
+
setRejectDraft(rejectionReason);
|
|
27995
|
+
setRejectModalOpen(false);
|
|
27996
|
+
}, "cancelRejectModal");
|
|
27997
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
27998
|
+
if (value === "rejected") {
|
|
27999
|
+
openRejectModal(approvalStatus);
|
|
28000
|
+
return;
|
|
28001
|
+
}
|
|
28002
|
+
setApprovalStatus(value);
|
|
28003
|
+
if (value !== "rejected") setRejectionReason("");
|
|
28004
|
+
}, "handleApprovalSelect");
|
|
27538
28005
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
27539
28006
|
setErrors([]);
|
|
27540
28007
|
if (!name.trim()) {
|
|
@@ -27543,6 +28010,14 @@ function ProductEditPage({ productId }) {
|
|
|
27543
28010
|
]);
|
|
27544
28011
|
return;
|
|
27545
28012
|
}
|
|
28013
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
28014
|
+
setRejectDraft("");
|
|
28015
|
+
setRejectModalOpen(true);
|
|
28016
|
+
setErrors([
|
|
28017
|
+
"Rejection reason is required"
|
|
28018
|
+
]);
|
|
28019
|
+
return;
|
|
28020
|
+
}
|
|
27546
28021
|
if (!defaultPriceStr.trim()) {
|
|
27547
28022
|
setErrors([
|
|
27548
28023
|
`${pricingConfig.defaultCurrency} price is required`
|
|
@@ -27605,11 +28080,21 @@ function ProductEditPage({ productId }) {
|
|
|
27605
28080
|
currencyPrices: null,
|
|
27606
28081
|
compareAtPrice: compareAtPriceValue,
|
|
27607
28082
|
quantity: resolvedQuantity,
|
|
27608
|
-
status,
|
|
28083
|
+
status: create && vendorPortal && approvalOn && status === "available" ? "draft" : status,
|
|
27609
28084
|
featured,
|
|
27610
28085
|
contactFormId,
|
|
27611
28086
|
metadata
|
|
27612
28087
|
};
|
|
28088
|
+
if (approvalOn) {
|
|
28089
|
+
if (vendorPortal && create) {
|
|
28090
|
+
productPayload.approvalStatus = "pending";
|
|
28091
|
+
} else if (!vendorPortal) {
|
|
28092
|
+
productPayload.approvalStatus = approvalStatus;
|
|
28093
|
+
if (approvalStatus === "rejected") {
|
|
28094
|
+
productPayload.rejectionReason = rejectionReason.trim();
|
|
28095
|
+
}
|
|
28096
|
+
}
|
|
28097
|
+
}
|
|
27613
28098
|
const res = await fetch(create ? "/api/products" : `/api/products/${productId}`, {
|
|
27614
28099
|
method: create ? "POST" : "PUT",
|
|
27615
28100
|
headers: {
|
|
@@ -27631,6 +28116,15 @@ function ProductEditPage({ productId }) {
|
|
|
27631
28116
|
if (typeof savedProduct.slug === "string") {
|
|
27632
28117
|
setProductSlug(savedProduct.slug);
|
|
27633
28118
|
}
|
|
28119
|
+
if (typeof savedProduct.status === "string") {
|
|
28120
|
+
setStatus(savedProduct.status);
|
|
28121
|
+
}
|
|
28122
|
+
if (typeof savedProduct.approvalStatus === "string") {
|
|
28123
|
+
setApprovalStatus(savedProduct.approvalStatus);
|
|
28124
|
+
}
|
|
28125
|
+
if (approvalStatus === "approved") {
|
|
28126
|
+
setRejectionReason("");
|
|
28127
|
+
}
|
|
27634
28128
|
const savedId = create ? savedProduct.id : productId;
|
|
27635
28129
|
const savedSeoId = await saveSeo(seo, productSlugValue, seoId);
|
|
27636
28130
|
const linkedSeoId = savedSeoId ?? savedProduct.seoId ?? null;
|
|
@@ -27831,7 +28325,7 @@ function ProductEditPage({ productId }) {
|
|
|
27831
28325
|
}
|
|
27832
28326
|
setVariantRows([]);
|
|
27833
28327
|
}
|
|
27834
|
-
if (create && eventId != null) {
|
|
28328
|
+
if (create && eventsOn && eventId != null) {
|
|
27835
28329
|
const attachRes = await fetch("/api/event_products", {
|
|
27836
28330
|
method: "POST",
|
|
27837
28331
|
headers: {
|
|
@@ -27936,6 +28430,27 @@ function ProductEditPage({ productId }) {
|
|
|
27936
28430
|
title: pageTitle,
|
|
27937
28431
|
subtitle: pageSubtitle,
|
|
27938
28432
|
closeHref: listReturnUrl,
|
|
28433
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
28434
|
+
className: "flex items-center gap-2"
|
|
28435
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
28436
|
+
value: approvalStatus,
|
|
28437
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
28438
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
28439
|
+
"aria-label": "Approval status"
|
|
28440
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
28441
|
+
value: "pending"
|
|
28442
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
28443
|
+
value: "approved"
|
|
28444
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
28445
|
+
value: "rejected"
|
|
28446
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
28447
|
+
type: "button",
|
|
28448
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
28449
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
28450
|
+
title: rejectionReason || "Add rejection reason"
|
|
28451
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
28452
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
28453
|
+
}, approvalStatus.replace(/_/g, " ")) : null,
|
|
27939
28454
|
menuItems: [
|
|
27940
28455
|
{
|
|
27941
28456
|
label: saving ? "Saving..." : "Save",
|
|
@@ -27948,7 +28463,31 @@ function ProductEditPage({ productId }) {
|
|
|
27948
28463
|
onClick: /* @__PURE__ */ __name(() => setFeatured(!featured), "onClick")
|
|
27949
28464
|
}
|
|
27950
28465
|
]
|
|
27951
|
-
}),
|
|
28466
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
28467
|
+
open: rejectModalOpen,
|
|
28468
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
28469
|
+
if (!open) cancelRejectModal();
|
|
28470
|
+
}, "onOpenChange")
|
|
28471
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
28472
|
+
className: "max-w-md"
|
|
28473
|
+
}, /* @__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", {
|
|
28474
|
+
value: rejectDraft,
|
|
28475
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
28476
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
28477
|
+
placeholder: "Explain what needs to change\u2026",
|
|
28478
|
+
autoFocus: true
|
|
28479
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
28480
|
+
className: "gap-2 sm:gap-0"
|
|
28481
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
28482
|
+
type: "button",
|
|
28483
|
+
variant: "outline",
|
|
28484
|
+
onClick: cancelRejectModal
|
|
28485
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
28486
|
+
type: "button",
|
|
28487
|
+
variant: "destructive",
|
|
28488
|
+
disabled: !rejectDraft.trim(),
|
|
28489
|
+
onClick: confirmRejectReason
|
|
28490
|
+
}, "Confirm reject")))), errors.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
27952
28491
|
className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4"
|
|
27953
28492
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
27954
28493
|
className: "flex"
|
|
@@ -28071,7 +28610,7 @@ function ProductEditPage({ productId }) {
|
|
|
28071
28610
|
className: "mt-1 text-xs text-gray-500"
|
|
28072
28611
|
}, "Only collections in the selected category are listed.") : /* @__PURE__ */ React.createElement("p", {
|
|
28073
28612
|
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", {
|
|
28613
|
+
}, "Select a category to load collections.")), create && eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28075
28614
|
className: labelCls3
|
|
28076
28615
|
}, "Event"), /* @__PURE__ */ React.createElement("select", {
|
|
28077
28616
|
value: eventId ?? "",
|
|
@@ -28167,7 +28706,8 @@ function ProductEditPage({ productId }) {
|
|
|
28167
28706
|
}, "Status"), /* @__PURE__ */ React.createElement("select", {
|
|
28168
28707
|
value: status,
|
|
28169
28708
|
onChange: /* @__PURE__ */ __name((e) => setStatus(e.target.value), "onChange"),
|
|
28170
|
-
className: inputCls3
|
|
28709
|
+
className: inputCls3,
|
|
28710
|
+
disabled: approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected")
|
|
28171
28711
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28172
28712
|
value: "draft"
|
|
28173
28713
|
}, "Draft"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28176,7 +28716,11 @@ function ProductEditPage({ productId }) {
|
|
|
28176
28716
|
value: "reserved"
|
|
28177
28717
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
28178
28718
|
value: "sold"
|
|
28179
|
-
}, "Sold"))
|
|
28719
|
+
}, "Sold")), approvalOn && vendorPortal && approvalStatus === "pending" ? /* @__PURE__ */ React.createElement("p", {
|
|
28720
|
+
className: "mt-1 text-xs text-gray-500"
|
|
28721
|
+
}, "Waiting for admin approval. The product goes live when approved.") : null, approvalOn && vendorPortal && approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("p", {
|
|
28722
|
+
className: "mt-1 text-xs text-red-600"
|
|
28723
|
+
}, "Rejected", rejectionReason ? `: ${rejectionReason}` : "", ".") : null)))), /* @__PURE__ */ React.createElement(ProductVariantsSection, {
|
|
28180
28724
|
hasVariants,
|
|
28181
28725
|
onHasVariantsChange: setHasVariants,
|
|
28182
28726
|
variantOptionRows,
|
|
@@ -28303,14 +28847,11 @@ function ProductEditPage({ productId }) {
|
|
|
28303
28847
|
className: "flex flex-wrap items-start gap-2 p-2 bg-white rounded border border-gray-200"
|
|
28304
28848
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
28305
28849
|
className: "flex-1 min-w-[200px]"
|
|
28306
|
-
}, /* @__PURE__ */ React.createElement(
|
|
28307
|
-
|
|
28308
|
-
}, "Image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28309
|
-
type: "url",
|
|
28850
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
28851
|
+
label: "Image",
|
|
28310
28852
|
value: row.url,
|
|
28311
|
-
onChange: /* @__PURE__ */ __name((
|
|
28312
|
-
|
|
28313
|
-
placeholder: "https://..."
|
|
28853
|
+
onChange: /* @__PURE__ */ __name((v) => setImage(i, "url", v), "onChange"),
|
|
28854
|
+
inputClassName: inputCls3
|
|
28314
28855
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
28315
28856
|
className: "flex-1 min-w-[120px]"
|
|
28316
28857
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -28389,6 +28930,8 @@ var init_ProductEditPage = __esm({
|
|
|
28389
28930
|
"use client";
|
|
28390
28931
|
init_vendor_scope();
|
|
28391
28932
|
init_admin_list_return_url();
|
|
28933
|
+
init_dialog();
|
|
28934
|
+
init_button();
|
|
28392
28935
|
init_SeoSection();
|
|
28393
28936
|
init_DetailPageLayout();
|
|
28394
28937
|
init_DetailPageHeader();
|
|
@@ -28399,6 +28942,8 @@ var init_ProductEditPage = __esm({
|
|
|
28399
28942
|
init_inventory_validation();
|
|
28400
28943
|
init_category_item_label();
|
|
28401
28944
|
init_use_category_collections();
|
|
28945
|
+
init_ImageOrUrlField();
|
|
28946
|
+
init_admin_config_context();
|
|
28402
28947
|
init_ProductVariantsSection();
|
|
28403
28948
|
init_product_variants();
|
|
28404
28949
|
__name(parseCategoryIdFromReturnUrl, "parseCategoryIdFromReturnUrl");
|
|
@@ -28668,12 +29213,6 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28668
29213
|
]);
|
|
28669
29214
|
return;
|
|
28670
29215
|
}
|
|
28671
|
-
if (create && !categoryId) {
|
|
28672
|
-
setErrors([
|
|
28673
|
-
"Category is required"
|
|
28674
|
-
]);
|
|
28675
|
-
return;
|
|
28676
|
-
}
|
|
28677
29216
|
setSaving(true);
|
|
28678
29217
|
try {
|
|
28679
29218
|
const savedSeoId = await saveSeo(seo, slug.trim(), seoId);
|
|
@@ -28894,14 +29433,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28894
29433
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
28895
29434
|
className: `${inputCls4} min-h-[80px]`,
|
|
28896
29435
|
rows: 3
|
|
28897
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
28898
|
-
|
|
28899
|
-
}, "Cover image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28900
|
-
type: "url",
|
|
29436
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29437
|
+
label: "Cover image",
|
|
28901
29438
|
value: image,
|
|
28902
|
-
onChange:
|
|
28903
|
-
|
|
28904
|
-
placeholder: "https://..."
|
|
29439
|
+
onChange: setImage,
|
|
29440
|
+
inputClassName: inputCls4
|
|
28905
29441
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28906
29442
|
className: labelCls4
|
|
28907
29443
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -28928,17 +29464,24 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28928
29464
|
className: "text-xs font-medium text-gray-700 mb-2"
|
|
28929
29465
|
}, "Hero carousel"), heroSlides.map((slide, i) => /* @__PURE__ */ React.createElement("div", {
|
|
28930
29466
|
key: i,
|
|
28931
|
-
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border"
|
|
28932
|
-
}, /* @__PURE__ */ React.createElement("
|
|
29467
|
+
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border items-start"
|
|
29468
|
+
}, slide.type === "image" ? /* @__PURE__ */ React.createElement("div", {
|
|
29469
|
+
className: "flex-1 min-w-[200px]"
|
|
29470
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29471
|
+
label: "Slide image",
|
|
29472
|
+
value: slide.url,
|
|
29473
|
+
onChange: /* @__PURE__ */ __name((v) => updateHeroSlide(i, "url", v), "onChange"),
|
|
29474
|
+
inputClassName: inputCls4
|
|
29475
|
+
})) : /* @__PURE__ */ React.createElement("input", {
|
|
28933
29476
|
type: "url",
|
|
28934
29477
|
value: slide.url,
|
|
28935
29478
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "url", e.target.value), "onChange"),
|
|
28936
|
-
placeholder: "
|
|
29479
|
+
placeholder: "Video URL",
|
|
28937
29480
|
className: `${inputCls4} flex-1 min-w-[200px]`
|
|
28938
29481
|
}), /* @__PURE__ */ React.createElement("select", {
|
|
28939
29482
|
value: slide.type,
|
|
28940
29483
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "type", e.target.value), "onChange"),
|
|
28941
|
-
className: `${inputCls4} w-24`
|
|
29484
|
+
className: `${inputCls4} w-24 mt-6`
|
|
28942
29485
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28943
29486
|
value: "image"
|
|
28944
29487
|
}, "Image"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28948,11 +29491,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28948
29491
|
value: slide.caption,
|
|
28949
29492
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "caption", e.target.value), "onChange"),
|
|
28950
29493
|
placeholder: "Caption",
|
|
28951
|
-
className: `${inputCls4} flex-1 min-w-[120px]`
|
|
29494
|
+
className: `${inputCls4} flex-1 min-w-[120px] mt-6`
|
|
28952
29495
|
}), /* @__PURE__ */ React.createElement("button", {
|
|
28953
29496
|
type: "button",
|
|
28954
29497
|
onClick: /* @__PURE__ */ __name(() => removeHeroSlide(i), "onClick"),
|
|
28955
|
-
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0"
|
|
29498
|
+
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mt-6"
|
|
28956
29499
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Trash2, {
|
|
28957
29500
|
className: "h-4 w-4"
|
|
28958
29501
|
})))), /* @__PURE__ */ React.createElement("button", {
|
|
@@ -29079,6 +29622,7 @@ var init_CollectionEditPage = __esm({
|
|
|
29079
29622
|
init_use_catalog_categories();
|
|
29080
29623
|
init_admin_config_context();
|
|
29081
29624
|
init_category_related_product_labels();
|
|
29625
|
+
init_ImageOrUrlField();
|
|
29082
29626
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
29083
29627
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
29084
29628
|
url: "",
|
|
@@ -29096,116 +29640,6 @@ var init_CollectionEditPage = __esm({
|
|
|
29096
29640
|
__name(CollectionEditPage, "CollectionEditPage");
|
|
29097
29641
|
}
|
|
29098
29642
|
});
|
|
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
29643
|
function AttachProductModal({ open, onOpenChange, categoryId, categoryName, excludeProductIds = [], onAttach }) {
|
|
29210
29644
|
const [attachingId, setAttachingId] = React26.useState(null);
|
|
29211
29645
|
const [error, setError] = React26.useState(null);
|
|
@@ -30150,6 +30584,10 @@ function combineCityCountry(city, country) {
|
|
|
30150
30584
|
function EventEditPage({ eventId }) {
|
|
30151
30585
|
const router = navigation.useRouter();
|
|
30152
30586
|
const searchParams = navigation.useSearchParams();
|
|
30587
|
+
const { data: session } = react.useSession();
|
|
30588
|
+
const { requireEventApproval } = React26.useContext(exports.AdminConfigContext);
|
|
30589
|
+
const approvalOn = requireEventApproval === true;
|
|
30590
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
30153
30591
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/events";
|
|
30154
30592
|
const create = isCreate4(eventId);
|
|
30155
30593
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -30162,6 +30600,11 @@ function EventEditPage({ eventId }) {
|
|
|
30162
30600
|
const [slug, setSlug] = React26.useState("");
|
|
30163
30601
|
const [description, setDescription] = React26.useState("");
|
|
30164
30602
|
const [isActive, setIsActive] = React26.useState(true);
|
|
30603
|
+
const [approvalStatus, setApprovalStatus] = React26.useState("pending");
|
|
30604
|
+
const [rejectionReason, setRejectionReason] = React26.useState("");
|
|
30605
|
+
const [rejectModalOpen, setRejectModalOpen] = React26.useState(false);
|
|
30606
|
+
const [rejectDraft, setRejectDraft] = React26.useState("");
|
|
30607
|
+
const approvalBeforeRejectRef = React26.useRef("pending");
|
|
30165
30608
|
const [comingSoon, setComingSoon] = React26.useState(false);
|
|
30166
30609
|
const [bannerImageUrl, setBannerImageUrl] = React26.useState("");
|
|
30167
30610
|
const [logoUrl, setLogoUrl] = React26.useState("");
|
|
@@ -30247,6 +30690,16 @@ function EventEditPage({ eventId }) {
|
|
|
30247
30690
|
cancelled = true;
|
|
30248
30691
|
};
|
|
30249
30692
|
}, []);
|
|
30693
|
+
React26.useEffect(() => {
|
|
30694
|
+
if (create && vendorPortal && approvalOn) {
|
|
30695
|
+
setApprovalStatus("pending");
|
|
30696
|
+
setIsActive(false);
|
|
30697
|
+
}
|
|
30698
|
+
}, [
|
|
30699
|
+
create,
|
|
30700
|
+
vendorPortal,
|
|
30701
|
+
approvalOn
|
|
30702
|
+
]);
|
|
30250
30703
|
React26.useEffect(() => {
|
|
30251
30704
|
let cancelled = false;
|
|
30252
30705
|
(async () => {
|
|
@@ -30268,6 +30721,8 @@ function EventEditPage({ eventId }) {
|
|
|
30268
30721
|
setSlug(data.slug ?? "");
|
|
30269
30722
|
setDescription(data.description ?? "");
|
|
30270
30723
|
setIsActive(data.isActive ?? true);
|
|
30724
|
+
setApprovalStatus(typeof data.approvalStatus === "string" && data.approvalStatus ? data.approvalStatus : "pending");
|
|
30725
|
+
setRejectionReason(typeof data.rejectionReason === "string" ? data.rejectionReason : "");
|
|
30271
30726
|
setComingSoon(data.comingSoon ?? false);
|
|
30272
30727
|
setBannerImageUrl(data.bannerImageUrl ?? "");
|
|
30273
30728
|
setLogoUrl(data.logoUrl ?? "");
|
|
@@ -30379,11 +30834,11 @@ function EventEditPage({ eventId }) {
|
|
|
30379
30834
|
setErrors(nextErrors);
|
|
30380
30835
|
return null;
|
|
30381
30836
|
}
|
|
30382
|
-
|
|
30837
|
+
const payload = {
|
|
30383
30838
|
name: name.trim(),
|
|
30384
30839
|
slug: slug.trim(),
|
|
30385
30840
|
description: description.trim() || null,
|
|
30386
|
-
isActive,
|
|
30841
|
+
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
30387
30842
|
comingSoon,
|
|
30388
30843
|
bannerImageUrl: bannerImageUrl.trim() || null,
|
|
30389
30844
|
logoUrl: logoUrl.trim() || null,
|
|
@@ -30421,9 +30876,55 @@ function EventEditPage({ eventId }) {
|
|
|
30421
30876
|
sortOrder,
|
|
30422
30877
|
contactFormId
|
|
30423
30878
|
};
|
|
30879
|
+
if (approvalOn) {
|
|
30880
|
+
if (vendorPortal && create) {
|
|
30881
|
+
payload.approvalStatus = "pending";
|
|
30882
|
+
} else if (!vendorPortal) {
|
|
30883
|
+
payload.approvalStatus = approvalStatus;
|
|
30884
|
+
if (approvalStatus === "rejected") {
|
|
30885
|
+
payload.rejectionReason = rejectionReason.trim();
|
|
30886
|
+
}
|
|
30887
|
+
}
|
|
30888
|
+
}
|
|
30889
|
+
return payload;
|
|
30424
30890
|
}, "buildPayload");
|
|
30891
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
30892
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
30893
|
+
setRejectDraft(rejectionReason);
|
|
30894
|
+
setApprovalStatus("rejected");
|
|
30895
|
+
setRejectModalOpen(true);
|
|
30896
|
+
}, "openRejectModal");
|
|
30897
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
30898
|
+
const reason = rejectDraft.trim();
|
|
30899
|
+
if (!reason) return;
|
|
30900
|
+
setRejectionReason(reason);
|
|
30901
|
+
setRejectModalOpen(false);
|
|
30902
|
+
}, "confirmRejectReason");
|
|
30903
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
30904
|
+
if (!rejectionReason.trim()) {
|
|
30905
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
30906
|
+
}
|
|
30907
|
+
setRejectDraft(rejectionReason);
|
|
30908
|
+
setRejectModalOpen(false);
|
|
30909
|
+
}, "cancelRejectModal");
|
|
30910
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
30911
|
+
if (value === "rejected") {
|
|
30912
|
+
openRejectModal(approvalStatus);
|
|
30913
|
+
return;
|
|
30914
|
+
}
|
|
30915
|
+
setApprovalStatus(value);
|
|
30916
|
+
if (value !== "rejected") setRejectionReason("");
|
|
30917
|
+
}, "handleApprovalSelect");
|
|
30425
30918
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
30426
30919
|
setErrors([]);
|
|
30920
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
30921
|
+
setRejectDraft("");
|
|
30922
|
+
setRejectModalOpen(true);
|
|
30923
|
+
setErrors([
|
|
30924
|
+
"Rejection reason is required"
|
|
30925
|
+
]);
|
|
30926
|
+
return;
|
|
30927
|
+
}
|
|
30427
30928
|
const payload = buildPayload();
|
|
30428
30929
|
if (!payload) return;
|
|
30429
30930
|
setSaving(true);
|
|
@@ -30443,6 +30944,9 @@ function EventEditPage({ eventId }) {
|
|
|
30443
30944
|
return;
|
|
30444
30945
|
}
|
|
30445
30946
|
const saved = await res.json();
|
|
30947
|
+
if (typeof saved.isActive === "boolean") setIsActive(saved.isActive);
|
|
30948
|
+
if (typeof saved.approvalStatus === "string") setApprovalStatus(saved.approvalStatus);
|
|
30949
|
+
if (approvalStatus === "approved") setRejectionReason("");
|
|
30446
30950
|
const savedId = create ? saved.id : Number(eventId);
|
|
30447
30951
|
if (savedId != null && !Number.isNaN(savedId)) {
|
|
30448
30952
|
router.push(`/admin/events/${savedId}/edit?from=${encodeURIComponent(listReturnUrl)}`);
|
|
@@ -30505,19 +31009,66 @@ function EventEditPage({ eventId }) {
|
|
|
30505
31009
|
title: create ? "Add event" : "Edit event",
|
|
30506
31010
|
subtitle: create ? "Create a new event" : "Update event details and tickets",
|
|
30507
31011
|
closeHref: listReturnUrl,
|
|
31012
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
31013
|
+
className: "flex items-center gap-2"
|
|
31014
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
31015
|
+
value: approvalStatus,
|
|
31016
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
31017
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
31018
|
+
"aria-label": "Approval status"
|
|
31019
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
31020
|
+
value: "pending"
|
|
31021
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
31022
|
+
value: "approved"
|
|
31023
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
31024
|
+
value: "rejected"
|
|
31025
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
31026
|
+
type: "button",
|
|
31027
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
31028
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
31029
|
+
title: rejectionReason || "Add rejection reason"
|
|
31030
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
31031
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
31032
|
+
}, approvalStatus.replace(/_/g, " "), approvalStatus === "rejected" && rejectionReason ? ` \u2014 ${rejectionReason}` : "") : null,
|
|
30508
31033
|
menuItems: [
|
|
30509
31034
|
{
|
|
30510
31035
|
label: saving ? "Saving..." : "Save",
|
|
30511
31036
|
icon: LucideIcons.Save,
|
|
30512
31037
|
onClick: handleSave
|
|
30513
31038
|
},
|
|
30514
|
-
|
|
30515
|
-
|
|
30516
|
-
|
|
30517
|
-
|
|
30518
|
-
|
|
31039
|
+
...approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected") ? [] : [
|
|
31040
|
+
{
|
|
31041
|
+
label: isActive ? "Deactivate" : "Activate",
|
|
31042
|
+
icon: LucideIcons.Power,
|
|
31043
|
+
onClick: /* @__PURE__ */ __name(() => setIsActive(!isActive), "onClick")
|
|
31044
|
+
}
|
|
31045
|
+
]
|
|
30519
31046
|
]
|
|
30520
|
-
}),
|
|
31047
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
31048
|
+
open: rejectModalOpen,
|
|
31049
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
31050
|
+
if (!open) cancelRejectModal();
|
|
31051
|
+
}, "onOpenChange")
|
|
31052
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
31053
|
+
className: "max-w-md"
|
|
31054
|
+
}, /* @__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", {
|
|
31055
|
+
value: rejectDraft,
|
|
31056
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
31057
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31058
|
+
placeholder: "Explain what needs to change\u2026",
|
|
31059
|
+
autoFocus: true
|
|
31060
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
31061
|
+
className: "gap-2 sm:gap-0"
|
|
31062
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
31063
|
+
type: "button",
|
|
31064
|
+
variant: "outline",
|
|
31065
|
+
onClick: cancelRejectModal
|
|
31066
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
31067
|
+
type: "button",
|
|
31068
|
+
variant: "destructive",
|
|
31069
|
+
disabled: !rejectDraft.trim(),
|
|
31070
|
+
onClick: confirmRejectReason
|
|
31071
|
+
}, "Confirm reject")))), !create && logoUrl.trim() ? /* @__PURE__ */ React.createElement("div", {
|
|
30521
31072
|
className: "flex items-center gap-3 px-4 sm:px-6 py-3 border-b border-gray-100 bg-gray-50/80"
|
|
30522
31073
|
}, /* @__PURE__ */ React.createElement("img", {
|
|
30523
31074
|
src: logoUrl.trim(),
|
|
@@ -30876,11 +31427,15 @@ var init_EventEditPage = __esm({
|
|
|
30876
31427
|
"src/admin/pages/EventEditPage.tsx"() {
|
|
30877
31428
|
"use client";
|
|
30878
31429
|
init_admin_list_return_url();
|
|
31430
|
+
init_vendor_scope();
|
|
30879
31431
|
init_DetailPageLayout();
|
|
30880
31432
|
init_DetailPageHeader();
|
|
30881
31433
|
init_ImageOrUrlField();
|
|
30882
31434
|
init_JoditRichText();
|
|
30883
31435
|
init_EventProductsSection();
|
|
31436
|
+
init_admin_config_context();
|
|
31437
|
+
init_dialog();
|
|
31438
|
+
init_button();
|
|
30884
31439
|
init_EventManagementFields();
|
|
30885
31440
|
init_event_named_lists();
|
|
30886
31441
|
init_social_media_links();
|
|
@@ -30956,6 +31511,8 @@ async function validateComboProductInventory(productIds) {
|
|
|
30956
31511
|
function ComboEditPage({ comboId }) {
|
|
30957
31512
|
const router = navigation.useRouter();
|
|
30958
31513
|
const searchParams = navigation.useSearchParams();
|
|
31514
|
+
const { eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
31515
|
+
const eventsOn = eventsEnabled !== false;
|
|
30959
31516
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/combos";
|
|
30960
31517
|
const create = isCreate5(comboId);
|
|
30961
31518
|
const duplicateFrom = searchParams.get("duplicateFrom")?.trim();
|
|
@@ -30977,8 +31534,9 @@ function ComboEditPage({ comboId }) {
|
|
|
30977
31534
|
const [productOptions, setProductOptions] = React26.useState([]);
|
|
30978
31535
|
const [fixedItems, setFixedItems] = React26.useState([]);
|
|
30979
31536
|
const [addonItems, setAddonItems] = React26.useState([]);
|
|
31537
|
+
const canPickProducts = !eventsOn || Boolean(eventId);
|
|
30980
31538
|
React26.useEffect(() => {
|
|
30981
|
-
if (!eventId) {
|
|
31539
|
+
if (!eventsOn || !eventId) {
|
|
30982
31540
|
setDefaultCurrency("INR");
|
|
30983
31541
|
return;
|
|
30984
31542
|
}
|
|
@@ -30990,9 +31548,14 @@ function ComboEditPage({ comboId }) {
|
|
|
30990
31548
|
cancelled = true;
|
|
30991
31549
|
};
|
|
30992
31550
|
}, [
|
|
30993
|
-
eventId
|
|
31551
|
+
eventId,
|
|
31552
|
+
eventsOn
|
|
30994
31553
|
]);
|
|
30995
31554
|
React26.useEffect(() => {
|
|
31555
|
+
if (!eventsOn) {
|
|
31556
|
+
setEventOptions([]);
|
|
31557
|
+
return;
|
|
31558
|
+
}
|
|
30996
31559
|
let cancelled = false;
|
|
30997
31560
|
(async () => {
|
|
30998
31561
|
try {
|
|
@@ -31012,45 +31575,63 @@ function ComboEditPage({ comboId }) {
|
|
|
31012
31575
|
return () => {
|
|
31013
31576
|
cancelled = true;
|
|
31014
31577
|
};
|
|
31015
|
-
}, [
|
|
31578
|
+
}, [
|
|
31579
|
+
eventsOn
|
|
31580
|
+
]);
|
|
31016
31581
|
React26.useEffect(() => {
|
|
31017
|
-
if (!eventId) {
|
|
31018
|
-
setProductOptions([]);
|
|
31019
|
-
return;
|
|
31020
|
-
}
|
|
31021
31582
|
let cancelled = false;
|
|
31022
31583
|
(async () => {
|
|
31023
31584
|
try {
|
|
31024
|
-
|
|
31025
|
-
|
|
31585
|
+
if (eventsOn) {
|
|
31586
|
+
if (!eventId) {
|
|
31587
|
+
setProductOptions([]);
|
|
31588
|
+
return;
|
|
31589
|
+
}
|
|
31590
|
+
const res2 = await fetch(`/api/event_products?eventId=${eventId}&limit=500`);
|
|
31591
|
+
if (!res2.ok) return;
|
|
31026
31592
|
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
|
-
}
|
|
31593
|
+
if (cancelled || !Array.isArray(data.data)) return;
|
|
31594
|
+
const productIds = data.data.map((ep) => ep.productId).filter(Boolean);
|
|
31595
|
+
if (productIds.length === 0) {
|
|
31596
|
+
setProductOptions([]);
|
|
31597
|
+
return;
|
|
31052
31598
|
}
|
|
31599
|
+
const prodRes2 = await fetch(`/api/products?ids=${productIds.join(",")}&limit=500`);
|
|
31600
|
+
if (!prodRes2.ok) return;
|
|
31601
|
+
const prodData2 = await prodRes2.json();
|
|
31602
|
+
if (cancelled || !Array.isArray(prodData2.data)) return;
|
|
31603
|
+
const fetched2 = prodData2.data.map((p) => ({
|
|
31604
|
+
value: String(p.id),
|
|
31605
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31606
|
+
}));
|
|
31607
|
+
setProductOptions((prev) => {
|
|
31608
|
+
const merged = [
|
|
31609
|
+
...fetched2
|
|
31610
|
+
];
|
|
31611
|
+
for (const p of prev) {
|
|
31612
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31613
|
+
}
|
|
31614
|
+
return merged;
|
|
31615
|
+
});
|
|
31616
|
+
return;
|
|
31053
31617
|
}
|
|
31618
|
+
const prodRes = await fetch("/api/products?limit=500&sortField=name&sortOrder=asc");
|
|
31619
|
+
if (!prodRes.ok) return;
|
|
31620
|
+
const prodData = await prodRes.json();
|
|
31621
|
+
if (cancelled || !Array.isArray(prodData.data)) return;
|
|
31622
|
+
const fetched = prodData.data.map((p) => ({
|
|
31623
|
+
value: String(p.id),
|
|
31624
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31625
|
+
}));
|
|
31626
|
+
setProductOptions((prev) => {
|
|
31627
|
+
const merged = [
|
|
31628
|
+
...fetched
|
|
31629
|
+
];
|
|
31630
|
+
for (const p of prev) {
|
|
31631
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31632
|
+
}
|
|
31633
|
+
return merged;
|
|
31634
|
+
});
|
|
31054
31635
|
} catch {
|
|
31055
31636
|
}
|
|
31056
31637
|
})();
|
|
@@ -31058,7 +31639,8 @@ function ComboEditPage({ comboId }) {
|
|
|
31058
31639
|
cancelled = true;
|
|
31059
31640
|
};
|
|
31060
31641
|
}, [
|
|
31061
|
-
eventId
|
|
31642
|
+
eventId,
|
|
31643
|
+
eventsOn
|
|
31062
31644
|
]);
|
|
31063
31645
|
React26.useEffect(() => {
|
|
31064
31646
|
let cancelled = false;
|
|
@@ -31169,7 +31751,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31169
31751
|
]);
|
|
31170
31752
|
return;
|
|
31171
31753
|
}
|
|
31172
|
-
if (!trimmedEventId || !/^\d+$/.test(trimmedEventId)) {
|
|
31754
|
+
if (eventsOn && (!trimmedEventId || !/^\d+$/.test(trimmedEventId))) {
|
|
31173
31755
|
setErrors([
|
|
31174
31756
|
"Event is required"
|
|
31175
31757
|
]);
|
|
@@ -31228,10 +31810,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31228
31810
|
}
|
|
31229
31811
|
setSaving(true);
|
|
31230
31812
|
try {
|
|
31813
|
+
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
31231
31814
|
const payload = {
|
|
31232
31815
|
name: trimmedName,
|
|
31233
31816
|
desc: desc || null,
|
|
31234
|
-
eventId:
|
|
31817
|
+
eventId: resolvedEventId,
|
|
31235
31818
|
price,
|
|
31236
31819
|
currencyPrices: null,
|
|
31237
31820
|
minSelectableItems,
|
|
@@ -31325,7 +31908,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31325
31908
|
value: desc,
|
|
31326
31909
|
onChange: /* @__PURE__ */ __name((e) => setDesc(e.target.value), "onChange"),
|
|
31327
31910
|
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", {
|
|
31911
|
+
})), eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31329
31912
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
31330
31913
|
}, "Event *"), /* @__PURE__ */ React.createElement("select", {
|
|
31331
31914
|
value: eventId,
|
|
@@ -31340,7 +31923,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31340
31923
|
}, "Select event"), eventOptions.map((o) => /* @__PURE__ */ React.createElement("option", {
|
|
31341
31924
|
key: o.value,
|
|
31342
31925
|
value: o.value
|
|
31343
|
-
}, o.label)))))),
|
|
31926
|
+
}, o.label)))) : null)), canPickProducts && /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
|
|
31344
31927
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
31345
31928
|
}, "Combo items"), /* @__PURE__ */ React.createElement("div", {
|
|
31346
31929
|
className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-5"
|
|
@@ -31408,11 +31991,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31408
31991
|
step: "0.01",
|
|
31409
31992
|
value: priceStr,
|
|
31410
31993
|
onChange: /* @__PURE__ */ __name((e) => setPriceStr(e.target.value), "onChange"),
|
|
31411
|
-
disabled: !
|
|
31994
|
+
disabled: !canPickProducts,
|
|
31412
31995
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm disabled:bg-gray-100"
|
|
31413
31996
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
31414
31997
|
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", {
|
|
31998
|
+
}, 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
31999
|
className: "grid grid-cols-2 gap-4"
|
|
31417
32000
|
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31418
32001
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -31478,6 +32061,7 @@ var init_ComboEditPage = __esm({
|
|
|
31478
32061
|
init_DetailPageLayout();
|
|
31479
32062
|
init_DetailPageHeader();
|
|
31480
32063
|
init_inventory_validation();
|
|
32064
|
+
init_admin_config_context();
|
|
31481
32065
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31482
32066
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
31483
32067
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -32312,9 +32896,9 @@ var VendorCategoryWorkspacePage_exports = {};
|
|
|
32312
32896
|
__export(VendorCategoryWorkspacePage_exports, {
|
|
32313
32897
|
default: () => VendorCategoryWorkspacePage
|
|
32314
32898
|
});
|
|
32315
|
-
function buildAdminProductColumns(base,
|
|
32899
|
+
function buildAdminProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32316
32900
|
const extraColumns = [
|
|
32317
|
-
...
|
|
32901
|
+
...showVendorColumn ? [
|
|
32318
32902
|
VENDOR_EXTRA_COLUMN
|
|
32319
32903
|
] : [],
|
|
32320
32904
|
...eventsEnabled ? [
|
|
@@ -32333,9 +32917,9 @@ function buildAdminProductColumns(base, multiVendorEnabled, eventsEnabled) {
|
|
|
32333
32917
|
...cols.slice(nameIdx + 1)
|
|
32334
32918
|
];
|
|
32335
32919
|
}
|
|
32336
|
-
function buildAllProductColumns(base,
|
|
32920
|
+
function buildAllProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32337
32921
|
const extraColumns = [
|
|
32338
|
-
...
|
|
32922
|
+
...showVendorColumn ? [
|
|
32339
32923
|
VENDOR_EXTRA_COLUMN
|
|
32340
32924
|
] : [],
|
|
32341
32925
|
...eventsEnabled ? [
|
|
@@ -32385,7 +32969,8 @@ function VendorCategoryWorkspacePage() {
|
|
|
32385
32969
|
const workspaceFrom = activeCategoryId ? `/admin/products?categoryId=${activeCategoryId}` : "/admin/products";
|
|
32386
32970
|
const itemLabel = activeCategory ? categorySingularName(activeCategory.name) : "Product";
|
|
32387
32971
|
const productColumns2 = React26.useMemo(() => {
|
|
32388
|
-
const
|
|
32972
|
+
const showVendorColumn = multiVendorEnabled !== false && !vendorPortal;
|
|
32973
|
+
const base = showAllProducts ? buildAllProductColumns(exports.STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false) : buildAdminProductColumns(exports.STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false);
|
|
32389
32974
|
return withCollectionRelationApi(base, activeCategoryId, vendorPortal);
|
|
32390
32975
|
}, [
|
|
32391
32976
|
vendorPortal,
|
|
@@ -33711,7 +34296,7 @@ function CustomerPicker({ value, label, onChange }) {
|
|
|
33711
34296
|
className: "text-xs text-gray-400 shrink-0 truncate"
|
|
33712
34297
|
}, c.email ?? ""))))));
|
|
33713
34298
|
}
|
|
33714
|
-
function ConditionCard({ condition, onChange, onRemove }) {
|
|
34299
|
+
function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
33715
34300
|
const iconMap = {
|
|
33716
34301
|
minAmount: /* @__PURE__ */ React.createElement(LucideIcons.DollarSign, {
|
|
33717
34302
|
className: "h-3.5 w-3.5 text-blue-500"
|
|
@@ -33767,7 +34352,7 @@ function ConditionCard({ condition, onChange, onRemove }) {
|
|
|
33767
34352
|
value: "minQuantity"
|
|
33768
34353
|
}, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33769
34354
|
value: "productMinQuantity"
|
|
33770
|
-
}, "Product"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34355
|
+
}, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33771
34356
|
value: "events"
|
|
33772
34357
|
}, "Events"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33773
34358
|
value: "nthOrder"
|
|
@@ -34001,11 +34586,13 @@ function RewardCard({ reward, onChange, onRemove, discountType, discountValue, e
|
|
|
34001
34586
|
})));
|
|
34002
34587
|
}
|
|
34003
34588
|
function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE", discountValue = "" }) {
|
|
34589
|
+
const { eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
34590
|
+
const eventsOn = eventsEnabled !== false;
|
|
34004
34591
|
const parsed = ruleTreeToFriendly(rules);
|
|
34005
34592
|
const [groups, setGroups] = React26.useState(parsed.groups);
|
|
34006
34593
|
const [groupOperator, setGroupOperator] = React26.useState(parsed.groupOperator);
|
|
34007
34594
|
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;
|
|
34595
|
+
const rewardEventId = eventsOn ? groups.flatMap((g) => g.conditions).find((c) => c.kind === "events" && c.eventId != null)?.eventId ?? null : null;
|
|
34009
34596
|
React26.useEffect(() => {
|
|
34010
34597
|
const missingNameIds = [];
|
|
34011
34598
|
for (const g of groups) {
|
|
@@ -34242,7 +34829,8 @@ function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE
|
|
|
34242
34829
|
})), /* @__PURE__ */ React.createElement(ConditionCard, {
|
|
34243
34830
|
condition: c,
|
|
34244
34831
|
onChange: /* @__PURE__ */ __name((updated) => updateCondition(group.id, c.id, updated), "onChange"),
|
|
34245
|
-
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove")
|
|
34832
|
+
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove"),
|
|
34833
|
+
eventsOn
|
|
34246
34834
|
})))), /* @__PURE__ */ React.createElement(Button, {
|
|
34247
34835
|
type: "button",
|
|
34248
34836
|
variant: "outline",
|
|
@@ -34305,6 +34893,7 @@ var init_DiscountsConditionsBuilder = __esm({
|
|
|
34305
34893
|
"use client";
|
|
34306
34894
|
init_button();
|
|
34307
34895
|
init_select();
|
|
34896
|
+
init_admin_config_context();
|
|
34308
34897
|
__name(uid, "uid");
|
|
34309
34898
|
__name(conditionToRule, "conditionToRule");
|
|
34310
34899
|
__name(rewardToRule, "rewardToRule");
|
|
@@ -36486,7 +37075,7 @@ function AdminPageResolver({ slug }) {
|
|
|
36486
37075
|
const searchParams = navigation.useSearchParams();
|
|
36487
37076
|
const { data: session } = react.useSession();
|
|
36488
37077
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
36489
|
-
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled } = React26.useContext(exports.AdminConfigContext);
|
|
37078
|
+
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval, requireEventApproval } = React26.useContext(exports.AdminConfigContext);
|
|
36490
37079
|
const key = slug?.[0] || "dashboard";
|
|
36491
37080
|
const [vendorOptions, setVendorOptions] = React26.useState([]);
|
|
36492
37081
|
React26.useEffect(() => {
|
|
@@ -36520,6 +37109,11 @@ function AdminPageResolver({ slug }) {
|
|
|
36520
37109
|
columns = columns.filter((column) => column.field !== "eventId" && column.field !== "eventName");
|
|
36521
37110
|
filters = filters.filter((filter) => filter.param !== "eventId");
|
|
36522
37111
|
}
|
|
37112
|
+
const showApprovalColumn = key === "products" && requireProductApproval === true || key === "events" && requireEventApproval === true;
|
|
37113
|
+
if (!showApprovalColumn) {
|
|
37114
|
+
columns = columns.filter((column) => column.field !== "approvalStatus");
|
|
37115
|
+
filters = filters.filter((filter) => filter.param !== "approvalStatus");
|
|
37116
|
+
}
|
|
36523
37117
|
if (multiVendorEnabled !== false && !vendorPortal && STORE_VENDOR_RESOURCES.has(key) && key !== "event_products" && !columns.some((c) => c.field === "vendorId")) {
|
|
36524
37118
|
columns = [
|
|
36525
37119
|
VENDOR_COLUMN2,
|
|
@@ -36557,7 +37151,9 @@ function AdminPageResolver({ slug }) {
|
|
|
36557
37151
|
vendorOptions,
|
|
36558
37152
|
vendorPortal,
|
|
36559
37153
|
multiVendorEnabled,
|
|
36560
|
-
eventsEnabled
|
|
37154
|
+
eventsEnabled,
|
|
37155
|
+
requireProductApproval,
|
|
37156
|
+
requireEventApproval
|
|
36561
37157
|
]);
|
|
36562
37158
|
const isContactsWithStore = key === "contacts" && storeEnabled;
|
|
36563
37159
|
const extraListParams = React26.useMemo(() => isContactsWithStore ? {
|
|
@@ -36671,7 +37267,27 @@ function AdminPageResolver({ slug }) {
|
|
|
36671
37267
|
className: "ml-2"
|
|
36672
37268
|
}, "Redirecting\u2026"));
|
|
36673
37269
|
}
|
|
36674
|
-
if (vendorPortal && key === "product_categories") {
|
|
37270
|
+
if (vendorPortal && key === "product_categories" && vendorCanCreateCategories !== true) {
|
|
37271
|
+
router.replace("/admin/products");
|
|
37272
|
+
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37273
|
+
className: "flex justify-center py-8"
|
|
37274
|
+
}, /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37275
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37276
|
+
}), /* @__PURE__ */ React26__namespace.default.createElement("span", {
|
|
37277
|
+
className: "ml-2"
|
|
37278
|
+
}, "Redirecting\u2026"));
|
|
37279
|
+
}
|
|
37280
|
+
if (vendorPortal && key === "collections" && vendorCanCreateCollections !== true) {
|
|
37281
|
+
router.replace("/admin/products");
|
|
37282
|
+
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37283
|
+
className: "flex justify-center py-8"
|
|
37284
|
+
}, /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
37285
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37286
|
+
}), /* @__PURE__ */ React26__namespace.default.createElement("span", {
|
|
37287
|
+
className: "ml-2"
|
|
37288
|
+
}, "Redirecting\u2026"));
|
|
37289
|
+
}
|
|
37290
|
+
if (vendorPortal && key === "brands" && vendorCanCreateBrands !== true) {
|
|
36675
37291
|
router.replace("/admin/products");
|
|
36676
37292
|
return /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
36677
37293
|
className: "flex justify-center py-8"
|