@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.js
CHANGED
|
@@ -457,7 +457,7 @@ var init_admin_config_context = __esm({
|
|
|
457
457
|
var CMS_VERSION;
|
|
458
458
|
var init_cms_version = __esm({
|
|
459
459
|
"src/lib/cms-version.ts"() {
|
|
460
|
-
CMS_VERSION = "1.0.
|
|
460
|
+
CMS_VERSION = "1.0.42" ;
|
|
461
461
|
}
|
|
462
462
|
});
|
|
463
463
|
function useCatalogCategories(enabled = true) {
|
|
@@ -567,9 +567,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
567
567
|
const sessionUser = session?.user;
|
|
568
568
|
const showVendorOnboard = canOnboardVendors(sessionUser);
|
|
569
569
|
const vendorPortal = isVendorPortalUser(sessionUser);
|
|
570
|
-
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled } = useContext(AdminConfigContext);
|
|
570
|
+
const { customNavItems, customNavSections = [], storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands } = useContext(AdminConfigContext);
|
|
571
571
|
const showStoreNav = storeEnabled || vendorPortal;
|
|
572
572
|
const showPlatformNav = !vendorPortal;
|
|
573
|
+
const showVendorCategories = !vendorPortal || vendorCanCreateCategories === true;
|
|
574
|
+
const showVendorCollections = !vendorPortal || vendorCanCreateCollections === true;
|
|
575
|
+
const showVendorBrands = !vendorPortal || vendorCanCreateBrands === true;
|
|
573
576
|
const isDrawer = variant === "drawer";
|
|
574
577
|
const { categories: catalogCategories } = useCatalogCategories(showStoreNav);
|
|
575
578
|
searchParams.get("categoryId")?.trim() ?? "";
|
|
@@ -678,17 +681,17 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
678
681
|
className: `${linkCls} ${isActive("/admin/vendors") ? linkActive : linkInactive}`
|
|
679
682
|
}, /* @__PURE__ */ React.createElement(Store, {
|
|
680
683
|
className: `h-4 w-4 mr-2 ${isActive("/admin/vendors") ? iconActive : iconInactive}`
|
|
681
|
-
}), "Vendors")),
|
|
684
|
+
}), "Vendors")), showVendorCategories && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
682
685
|
href: "/admin/product_categories",
|
|
683
686
|
className: `${linkCls} ${isActive("/admin/product_categories") ? linkActive : linkInactive}`
|
|
684
687
|
}, /* @__PURE__ */ React.createElement(FolderTree, {
|
|
685
688
|
className: `h-4 w-4 mr-2 ${isActive("/admin/product_categories") ? iconActive : iconInactive}`
|
|
686
|
-
}), "Categories")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
689
|
+
}), "Categories")), showVendorCollections && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
687
690
|
href: "/admin/collections",
|
|
688
691
|
className: `${linkCls} ${isActive("/admin/collections") ? linkActive : linkInactive}`
|
|
689
692
|
}, /* @__PURE__ */ React.createElement(Layers, {
|
|
690
693
|
className: `h-4 w-4 mr-2 ${isActive("/admin/collections") ? iconActive : iconInactive}`
|
|
691
|
-
}), "Collections")), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
694
|
+
}), "Collections")), showVendorBrands && /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(Link2, {
|
|
692
695
|
href: "/admin/brands",
|
|
693
696
|
className: `${linkCls} ${isActive("/admin/brands") ? linkActive : linkInactive}`
|
|
694
697
|
}, /* @__PURE__ */ React.createElement(Building2, {
|
|
@@ -1371,33 +1374,52 @@ function parseBooleanSetting(value) {
|
|
|
1371
1374
|
}
|
|
1372
1375
|
return null;
|
|
1373
1376
|
}
|
|
1374
|
-
function
|
|
1377
|
+
function useMultiVendorSettings() {
|
|
1375
1378
|
const [multiVendorEnabled, setMultiVendorEnabled] = useState(true);
|
|
1379
|
+
const [vendorCanCreateCategories, setVendorCanCreateCategories] = useState(false);
|
|
1380
|
+
const [vendorCanCreateCollections, setVendorCanCreateCollections] = useState(false);
|
|
1381
|
+
const [vendorCanCreateBrands, setVendorCanCreateBrands] = useState(false);
|
|
1382
|
+
const [requireProductApproval, setRequireProductApproval] = useState(false);
|
|
1376
1383
|
useEffect(() => {
|
|
1377
1384
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
1378
1385
|
const parsed = parseBooleanSetting(data?.enabled ?? data?.value ?? data?.isEnabled ?? data?.active ?? data?.state);
|
|
1379
1386
|
setMultiVendorEnabled(parsed ?? true);
|
|
1387
|
+
setVendorCanCreateCategories(parseBooleanSetting(data?.vendorCanCreateCategories) === true);
|
|
1388
|
+
setVendorCanCreateCollections(parseBooleanSetting(data?.vendorCanCreateCollections) === true);
|
|
1389
|
+
setVendorCanCreateBrands(parseBooleanSetting(data?.vendorCanCreateBrands) === true);
|
|
1390
|
+
setRequireProductApproval(parseBooleanSetting(data?.requireProductApproval) === true);
|
|
1380
1391
|
}).catch(() => {
|
|
1381
1392
|
});
|
|
1382
1393
|
}, []);
|
|
1383
|
-
return
|
|
1394
|
+
return {
|
|
1395
|
+
multiVendorEnabled,
|
|
1396
|
+
vendorCanCreateCategories,
|
|
1397
|
+
vendorCanCreateCollections,
|
|
1398
|
+
vendorCanCreateBrands,
|
|
1399
|
+
requireProductApproval
|
|
1400
|
+
};
|
|
1384
1401
|
}
|
|
1385
|
-
function
|
|
1402
|
+
function useEventsSettings() {
|
|
1386
1403
|
const [eventsEnabled, setEventsEnabled] = useState(true);
|
|
1404
|
+
const [requireEventApproval, setRequireEventApproval] = useState(false);
|
|
1387
1405
|
useEffect(() => {
|
|
1388
1406
|
fetch("/api/settings/events").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
1389
1407
|
const parsed = parseBooleanSetting(data?.enabled ?? data?.value ?? data?.isEnabled ?? data?.active ?? data?.state);
|
|
1390
1408
|
setEventsEnabled(parsed ?? true);
|
|
1409
|
+
setRequireEventApproval(parseBooleanSetting(data?.requireEventApproval) === true);
|
|
1391
1410
|
}).catch(() => {
|
|
1392
1411
|
});
|
|
1393
1412
|
}, []);
|
|
1394
|
-
return
|
|
1413
|
+
return {
|
|
1414
|
+
eventsEnabled,
|
|
1415
|
+
requireEventApproval
|
|
1416
|
+
};
|
|
1395
1417
|
}
|
|
1396
1418
|
function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, categoryRelatedProductLabels = {}, renderOrderDetailFooter, theme, themeRegistry, pluginDescriptors = [] }) {
|
|
1397
1419
|
const resolvedTheme = useResolvedTheme(theme, themeRegistry);
|
|
1398
1420
|
const { storeEnabled, currency } = useStoreEnabled();
|
|
1399
|
-
const multiVendorEnabled =
|
|
1400
|
-
const eventsEnabled =
|
|
1421
|
+
const { multiVendorEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval } = useMultiVendorSettings();
|
|
1422
|
+
const { eventsEnabled, requireEventApproval } = useEventsSettings();
|
|
1401
1423
|
const mergedPluginDescriptors = useMemo(() => {
|
|
1402
1424
|
const seen = new Set(pluginDescriptors.map((p) => p.name));
|
|
1403
1425
|
const extra = BUILTIN_PLUGIN_DESCRIPTORS.filter((p) => !seen.has(p.name));
|
|
@@ -1420,6 +1442,11 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1420
1442
|
storeEnabled,
|
|
1421
1443
|
currency,
|
|
1422
1444
|
multiVendorEnabled,
|
|
1445
|
+
vendorCanCreateCategories,
|
|
1446
|
+
vendorCanCreateCollections,
|
|
1447
|
+
vendorCanCreateBrands,
|
|
1448
|
+
requireProductApproval,
|
|
1449
|
+
requireEventApproval,
|
|
1423
1450
|
eventsEnabled
|
|
1424
1451
|
}), [
|
|
1425
1452
|
customNavItems,
|
|
@@ -1433,6 +1460,11 @@ function AdminLayout({ children, customNavItems = [], customNavSections = [], cu
|
|
|
1433
1460
|
storeEnabled,
|
|
1434
1461
|
currency,
|
|
1435
1462
|
multiVendorEnabled,
|
|
1463
|
+
vendorCanCreateCategories,
|
|
1464
|
+
vendorCanCreateCollections,
|
|
1465
|
+
vendorCanCreateBrands,
|
|
1466
|
+
requireProductApproval,
|
|
1467
|
+
requireEventApproval,
|
|
1436
1468
|
eventsEnabled
|
|
1437
1469
|
]);
|
|
1438
1470
|
return /* @__PURE__ */ React.createElement(AdminConfigContext.Provider, {
|
|
@@ -1469,8 +1501,8 @@ var init_AdminLayout = __esm({
|
|
|
1469
1501
|
__name(useResolvedTheme, "useResolvedTheme");
|
|
1470
1502
|
__name(useStoreEnabled, "useStoreEnabled");
|
|
1471
1503
|
__name(parseBooleanSetting, "parseBooleanSetting");
|
|
1472
|
-
__name(
|
|
1473
|
-
__name(
|
|
1504
|
+
__name(useMultiVendorSettings, "useMultiVendorSettings");
|
|
1505
|
+
__name(useEventsSettings, "useEventsSettings");
|
|
1474
1506
|
__name(AdminLayout, "AdminLayout");
|
|
1475
1507
|
}
|
|
1476
1508
|
});
|
|
@@ -7701,6 +7733,155 @@ var init_ComponentSettings = __esm({
|
|
|
7701
7733
|
__name(ComponentSettings, "ComponentSettings");
|
|
7702
7734
|
}
|
|
7703
7735
|
});
|
|
7736
|
+
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 }) {
|
|
7737
|
+
const fileInputRef = useRef(null);
|
|
7738
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
7739
|
+
const [error, setError] = useState(null);
|
|
7740
|
+
const [lightboxOpen, setLightboxOpen] = useState(false);
|
|
7741
|
+
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";
|
|
7742
|
+
const closeLightbox = useCallback(() => setLightboxOpen(false), []);
|
|
7743
|
+
useEffect(() => {
|
|
7744
|
+
if (!lightboxOpen) return;
|
|
7745
|
+
const onKeyDown = /* @__PURE__ */ __name((e) => {
|
|
7746
|
+
if (e.key === "Escape") closeLightbox();
|
|
7747
|
+
}, "onKeyDown");
|
|
7748
|
+
window.addEventListener("keydown", onKeyDown);
|
|
7749
|
+
const prevOverflow = document.body.style.overflow;
|
|
7750
|
+
document.body.style.overflow = "hidden";
|
|
7751
|
+
return () => {
|
|
7752
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
7753
|
+
document.body.style.overflow = prevOverflow;
|
|
7754
|
+
};
|
|
7755
|
+
}, [
|
|
7756
|
+
lightboxOpen,
|
|
7757
|
+
closeLightbox
|
|
7758
|
+
]);
|
|
7759
|
+
const handleUpload = useCallback(async (file) => {
|
|
7760
|
+
setError(null);
|
|
7761
|
+
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
7762
|
+
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
7763
|
+
return;
|
|
7764
|
+
}
|
|
7765
|
+
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
7766
|
+
setError(`File must be under ${maxSizeMb}MB`);
|
|
7767
|
+
return;
|
|
7768
|
+
}
|
|
7769
|
+
setIsUploading(true);
|
|
7770
|
+
try {
|
|
7771
|
+
const formData = new FormData();
|
|
7772
|
+
formData.append("file", file);
|
|
7773
|
+
const response = await fetch("/api/upload", {
|
|
7774
|
+
method: "POST",
|
|
7775
|
+
body: formData
|
|
7776
|
+
});
|
|
7777
|
+
const data = await response.json();
|
|
7778
|
+
if (!response.ok) {
|
|
7779
|
+
throw new Error(data.error || data.details || "Upload failed");
|
|
7780
|
+
}
|
|
7781
|
+
onChange(data.filePath ?? "");
|
|
7782
|
+
} catch (err) {
|
|
7783
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
7784
|
+
} finally {
|
|
7785
|
+
setIsUploading(false);
|
|
7786
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
7787
|
+
}
|
|
7788
|
+
}, [
|
|
7789
|
+
maxSizeMb,
|
|
7790
|
+
onChange
|
|
7791
|
+
]);
|
|
7792
|
+
const onFileChange = useCallback((e) => {
|
|
7793
|
+
const file = e.target.files?.[0];
|
|
7794
|
+
if (file) void handleUpload(file);
|
|
7795
|
+
}, [
|
|
7796
|
+
handleUpload
|
|
7797
|
+
]);
|
|
7798
|
+
const trimmed = value.trim();
|
|
7799
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
7800
|
+
className: "space-y-2"
|
|
7801
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
7802
|
+
className: labelClassName
|
|
7803
|
+
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7804
|
+
className: "flex items-start gap-3"
|
|
7805
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7806
|
+
src: trimmed,
|
|
7807
|
+
alt: label,
|
|
7808
|
+
className: previewCls,
|
|
7809
|
+
role: "button",
|
|
7810
|
+
tabIndex: 0,
|
|
7811
|
+
title: "Click to enlarge",
|
|
7812
|
+
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
7813
|
+
onKeyDown: /* @__PURE__ */ __name((e) => {
|
|
7814
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7815
|
+
e.preventDefault();
|
|
7816
|
+
setLightboxOpen(true);
|
|
7817
|
+
}
|
|
7818
|
+
}, "onKeyDown"),
|
|
7819
|
+
onError: /* @__PURE__ */ __name((e) => {
|
|
7820
|
+
e.currentTarget.style.display = "none";
|
|
7821
|
+
}, "onError")
|
|
7822
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7823
|
+
type: "button",
|
|
7824
|
+
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
7825
|
+
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"
|
|
7826
|
+
}, /* @__PURE__ */ React.createElement(X, {
|
|
7827
|
+
className: "h-3 w-3"
|
|
7828
|
+
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
7829
|
+
className: "flex flex-wrap gap-2"
|
|
7830
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
7831
|
+
type: "url",
|
|
7832
|
+
value,
|
|
7833
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
7834
|
+
setError(null);
|
|
7835
|
+
onChange(e.target.value);
|
|
7836
|
+
}, "onChange"),
|
|
7837
|
+
placeholder,
|
|
7838
|
+
className: `${inputClassName} min-w-0 flex-1`
|
|
7839
|
+
}), /* @__PURE__ */ React.createElement("input", {
|
|
7840
|
+
ref: fileInputRef,
|
|
7841
|
+
type: "file",
|
|
7842
|
+
accept: ACCEPTED_TYPES.join(","),
|
|
7843
|
+
onChange: onFileChange,
|
|
7844
|
+
className: "hidden",
|
|
7845
|
+
disabled: isUploading
|
|
7846
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
7847
|
+
type: "button",
|
|
7848
|
+
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
7849
|
+
disabled: isUploading,
|
|
7850
|
+
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"
|
|
7851
|
+
}, /* @__PURE__ */ React.createElement(Upload, {
|
|
7852
|
+
className: "h-3.5 w-3.5"
|
|
7853
|
+
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
7854
|
+
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
7855
|
+
}, /* @__PURE__ */ React.createElement(AlertCircle, {
|
|
7856
|
+
className: "h-3.5 w-3.5 shrink-0"
|
|
7857
|
+
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
7858
|
+
className: "text-xs text-gray-500"
|
|
7859
|
+
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"), lightboxOpen && trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7860
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4",
|
|
7861
|
+
role: "dialog",
|
|
7862
|
+
"aria-modal": "true",
|
|
7863
|
+
"aria-label": `${label} preview`,
|
|
7864
|
+
onClick: closeLightbox
|
|
7865
|
+
}, /* @__PURE__ */ React.createElement("img", {
|
|
7866
|
+
src: trimmed,
|
|
7867
|
+
alt: label,
|
|
7868
|
+
className: "max-h-[90vh] max-w-[90vw] rounded-md object-contain shadow-lg",
|
|
7869
|
+
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
7870
|
+
})) : null);
|
|
7871
|
+
}
|
|
7872
|
+
var ACCEPTED_TYPES;
|
|
7873
|
+
var init_ImageOrUrlField = __esm({
|
|
7874
|
+
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
7875
|
+
"use client";
|
|
7876
|
+
ACCEPTED_TYPES = [
|
|
7877
|
+
"image/jpeg",
|
|
7878
|
+
"image/png",
|
|
7879
|
+
"image/gif",
|
|
7880
|
+
"image/webp"
|
|
7881
|
+
];
|
|
7882
|
+
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
7883
|
+
}
|
|
7884
|
+
});
|
|
7704
7885
|
function generateId() {
|
|
7705
7886
|
return `nav_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
7706
7887
|
}
|
|
@@ -8003,22 +8184,16 @@ function NavbarEditor({ config, onChange }) {
|
|
|
8003
8184
|
className: "space-y-6"
|
|
8004
8185
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8005
8186
|
className: "space-y-3"
|
|
8006
|
-
}, /* @__PURE__ */ React.createElement(
|
|
8007
|
-
|
|
8008
|
-
}, "Logo"), /* @__PURE__ */ React.createElement("div", {
|
|
8009
|
-
className: "flex items-center gap-3"
|
|
8010
|
-
}, config.logo && /* @__PURE__ */ React.createElement("img", {
|
|
8011
|
-
src: config.logo,
|
|
8012
|
-
alt: "Logo",
|
|
8013
|
-
className: "h-10 rounded border object-contain"
|
|
8014
|
-
}), /* @__PURE__ */ React.createElement(Input, {
|
|
8187
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
8188
|
+
label: "Logo",
|
|
8015
8189
|
value: config.logo || "",
|
|
8016
|
-
onChange: /* @__PURE__ */ __name((
|
|
8190
|
+
onChange: /* @__PURE__ */ __name((logo) => onChange({
|
|
8017
8191
|
...config,
|
|
8018
|
-
logo
|
|
8192
|
+
logo
|
|
8019
8193
|
}), "onChange"),
|
|
8194
|
+
previewVariant: "logo",
|
|
8020
8195
|
placeholder: "Logo image URL"
|
|
8021
|
-
}))
|
|
8196
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
8022
8197
|
className: "space-y-3"
|
|
8023
8198
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8024
8199
|
className: "flex items-center justify-between"
|
|
@@ -8057,6 +8232,7 @@ var init_NavbarEditor = __esm({
|
|
|
8057
8232
|
init_button();
|
|
8058
8233
|
init_switch();
|
|
8059
8234
|
init_label();
|
|
8235
|
+
init_ImageOrUrlField();
|
|
8060
8236
|
__name(generateId, "generateId");
|
|
8061
8237
|
__name(NavItemEditor, "NavItemEditor");
|
|
8062
8238
|
__name(updateItemInTree, "updateItemInTree");
|
|
@@ -11005,10 +11181,32 @@ function renderListPrice(value, item) {
|
|
|
11005
11181
|
return `${formatted} ${currency}`;
|
|
11006
11182
|
}
|
|
11007
11183
|
}
|
|
11184
|
+
function renderListThumbnail(url) {
|
|
11185
|
+
const trimmed = url.trim();
|
|
11186
|
+
if (!trimmed) return "\u2014";
|
|
11187
|
+
return createElement("img", {
|
|
11188
|
+
src: trimmed,
|
|
11189
|
+
alt: "",
|
|
11190
|
+
className: "h-10 w-10 rounded border border-gray-200 bg-white object-contain"
|
|
11191
|
+
});
|
|
11192
|
+
}
|
|
11193
|
+
function productListImageUrl(item) {
|
|
11194
|
+
const meta = item.metadata;
|
|
11195
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return "";
|
|
11196
|
+
const images = meta.images;
|
|
11197
|
+
if (!Array.isArray(images)) return "";
|
|
11198
|
+
const rows = images;
|
|
11199
|
+
const def = rows.find((r) => r?.isDefault && typeof r.url === "string" && r.url.trim());
|
|
11200
|
+
if (def && typeof def.url === "string") return def.url.trim();
|
|
11201
|
+
const first = rows.find((r) => typeof r.url === "string" && r.url.trim());
|
|
11202
|
+
return first && typeof first.url === "string" ? first.url.trim() : "";
|
|
11203
|
+
}
|
|
11008
11204
|
var STORE_CRUD_CONFIGS;
|
|
11009
11205
|
var init_store_crud_configs = __esm({
|
|
11010
11206
|
"src/admin/store-crud-configs.ts"() {
|
|
11011
11207
|
__name(renderListPrice, "renderListPrice");
|
|
11208
|
+
__name(renderListThumbnail, "renderListThumbnail");
|
|
11209
|
+
__name(productListImageUrl, "productListImageUrl");
|
|
11012
11210
|
STORE_CRUD_CONFIGS = {
|
|
11013
11211
|
products: {
|
|
11014
11212
|
title: "Products",
|
|
@@ -11034,9 +11232,40 @@ var init_store_crud_configs = __esm({
|
|
|
11034
11232
|
label: "Out of stock"
|
|
11035
11233
|
}
|
|
11036
11234
|
]
|
|
11235
|
+
},
|
|
11236
|
+
{
|
|
11237
|
+
param: "approvalStatus",
|
|
11238
|
+
label: "Approval",
|
|
11239
|
+
type: "select",
|
|
11240
|
+
options: [
|
|
11241
|
+
{
|
|
11242
|
+
value: "",
|
|
11243
|
+
label: "All"
|
|
11244
|
+
},
|
|
11245
|
+
{
|
|
11246
|
+
value: "pending",
|
|
11247
|
+
label: "Pending"
|
|
11248
|
+
},
|
|
11249
|
+
{
|
|
11250
|
+
value: "approved",
|
|
11251
|
+
label: "Approved"
|
|
11252
|
+
},
|
|
11253
|
+
{
|
|
11254
|
+
value: "rejected",
|
|
11255
|
+
label: "Rejected"
|
|
11256
|
+
}
|
|
11257
|
+
]
|
|
11037
11258
|
}
|
|
11038
11259
|
],
|
|
11039
11260
|
columns: [
|
|
11261
|
+
{
|
|
11262
|
+
field: "metadata",
|
|
11263
|
+
displayName: "Image",
|
|
11264
|
+
hideInCreate: true,
|
|
11265
|
+
hideInEdit: true,
|
|
11266
|
+
listFilter: false,
|
|
11267
|
+
render: /* @__PURE__ */ __name((_value, item) => renderListThumbnail(productListImageUrl(item)), "render")
|
|
11268
|
+
},
|
|
11040
11269
|
{
|
|
11041
11270
|
field: "name",
|
|
11042
11271
|
displayName: "Name"
|
|
@@ -11098,6 +11327,33 @@ var init_store_crud_configs = __esm({
|
|
|
11098
11327
|
}
|
|
11099
11328
|
]
|
|
11100
11329
|
},
|
|
11330
|
+
{
|
|
11331
|
+
field: "approvalStatus",
|
|
11332
|
+
displayName: "Approval",
|
|
11333
|
+
type: "select",
|
|
11334
|
+
listFilter: false,
|
|
11335
|
+
options: [
|
|
11336
|
+
{
|
|
11337
|
+
value: "pending",
|
|
11338
|
+
label: "Pending"
|
|
11339
|
+
},
|
|
11340
|
+
{
|
|
11341
|
+
value: "approved",
|
|
11342
|
+
label: "Approved"
|
|
11343
|
+
},
|
|
11344
|
+
{
|
|
11345
|
+
value: "rejected",
|
|
11346
|
+
label: "Rejected"
|
|
11347
|
+
}
|
|
11348
|
+
],
|
|
11349
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
11350
|
+
const v = value == null || value === "" ? null : String(value);
|
|
11351
|
+
if (v === "pending") return "Pending";
|
|
11352
|
+
if (v === "approved") return "Approved";
|
|
11353
|
+
if (v === "rejected") return "Rejected";
|
|
11354
|
+
return "\u2014";
|
|
11355
|
+
}, "render")
|
|
11356
|
+
},
|
|
11101
11357
|
{
|
|
11102
11358
|
field: "featured",
|
|
11103
11359
|
displayName: "Featured",
|
|
@@ -11221,6 +11477,14 @@ var init_store_crud_configs = __esm({
|
|
|
11221
11477
|
title: "Collections",
|
|
11222
11478
|
apiEndpoint: "/api/collections",
|
|
11223
11479
|
columns: [
|
|
11480
|
+
{
|
|
11481
|
+
field: "image",
|
|
11482
|
+
displayName: "Image",
|
|
11483
|
+
hideInCreate: true,
|
|
11484
|
+
hideInEdit: true,
|
|
11485
|
+
listFilter: false,
|
|
11486
|
+
render: /* @__PURE__ */ __name((value) => renderListThumbnail(typeof value === "string" ? value : ""), "render")
|
|
11487
|
+
},
|
|
11224
11488
|
{
|
|
11225
11489
|
field: "name",
|
|
11226
11490
|
displayName: "Name"
|
|
@@ -11617,6 +11881,13 @@ var init_store_crud_configs = __esm({
|
|
|
11617
11881
|
field: "slug",
|
|
11618
11882
|
displayName: "Slug"
|
|
11619
11883
|
},
|
|
11884
|
+
{
|
|
11885
|
+
field: "isCatalog",
|
|
11886
|
+
displayName: "Catalog",
|
|
11887
|
+
type: "boolean",
|
|
11888
|
+
hideInCreate: true,
|
|
11889
|
+
hideInEdit: true
|
|
11890
|
+
},
|
|
11620
11891
|
{
|
|
11621
11892
|
field: "active",
|
|
11622
11893
|
displayName: "Active",
|
|
@@ -11919,6 +12190,29 @@ var init_store_crud_configs = __esm({
|
|
|
11919
12190
|
label: "Paid"
|
|
11920
12191
|
}
|
|
11921
12192
|
]
|
|
12193
|
+
},
|
|
12194
|
+
{
|
|
12195
|
+
param: "approvalStatus",
|
|
12196
|
+
label: "Approval",
|
|
12197
|
+
type: "select",
|
|
12198
|
+
options: [
|
|
12199
|
+
{
|
|
12200
|
+
value: "",
|
|
12201
|
+
label: "All"
|
|
12202
|
+
},
|
|
12203
|
+
{
|
|
12204
|
+
value: "pending",
|
|
12205
|
+
label: "Pending"
|
|
12206
|
+
},
|
|
12207
|
+
{
|
|
12208
|
+
value: "approved",
|
|
12209
|
+
label: "Approved"
|
|
12210
|
+
},
|
|
12211
|
+
{
|
|
12212
|
+
value: "rejected",
|
|
12213
|
+
label: "Rejected"
|
|
12214
|
+
}
|
|
12215
|
+
]
|
|
11922
12216
|
}
|
|
11923
12217
|
],
|
|
11924
12218
|
columns: [
|
|
@@ -11950,6 +12244,33 @@ var init_store_crud_configs = __esm({
|
|
|
11950
12244
|
displayName: "Active",
|
|
11951
12245
|
type: "boolean"
|
|
11952
12246
|
},
|
|
12247
|
+
{
|
|
12248
|
+
field: "approvalStatus",
|
|
12249
|
+
displayName: "Approval",
|
|
12250
|
+
type: "select",
|
|
12251
|
+
listFilter: false,
|
|
12252
|
+
options: [
|
|
12253
|
+
{
|
|
12254
|
+
value: "pending",
|
|
12255
|
+
label: "Pending"
|
|
12256
|
+
},
|
|
12257
|
+
{
|
|
12258
|
+
value: "approved",
|
|
12259
|
+
label: "Approved"
|
|
12260
|
+
},
|
|
12261
|
+
{
|
|
12262
|
+
value: "rejected",
|
|
12263
|
+
label: "Rejected"
|
|
12264
|
+
}
|
|
12265
|
+
],
|
|
12266
|
+
render: /* @__PURE__ */ __name((value) => {
|
|
12267
|
+
const v = value == null || value === "" ? null : String(value);
|
|
12268
|
+
if (v === "pending") return "Pending";
|
|
12269
|
+
if (v === "approved") return "Approved";
|
|
12270
|
+
if (v === "rejected") return "Rejected";
|
|
12271
|
+
return "\u2014";
|
|
12272
|
+
}, "render")
|
|
12273
|
+
},
|
|
11953
12274
|
{
|
|
11954
12275
|
field: "comingSoon",
|
|
11955
12276
|
displayName: "Coming soon",
|
|
@@ -12236,7 +12557,12 @@ function SettingsPage() {
|
|
|
12236
12557
|
const [themeSettingsLoading, setThemeSettingsLoading] = useState(true);
|
|
12237
12558
|
const [storeEnabled, setStoreEnabled] = useState(false);
|
|
12238
12559
|
const [multiVendorEnabled, setMultiVendorEnabled] = useState(true);
|
|
12560
|
+
const [vendorCanCreateCategories, setVendorCanCreateCategories] = useState(false);
|
|
12561
|
+
const [vendorCanCreateCollections, setVendorCanCreateCollections] = useState(false);
|
|
12562
|
+
const [vendorCanCreateBrands, setVendorCanCreateBrands] = useState(false);
|
|
12563
|
+
const [requireProductApproval, setRequireProductApproval] = useState(false);
|
|
12239
12564
|
const [eventsEnabled, setEventsEnabled] = useState(true);
|
|
12565
|
+
const [requireEventApproval, setRequireEventApproval] = useState(false);
|
|
12240
12566
|
const [storeSettingsLoading, setStoreSettingsLoading] = useState(true);
|
|
12241
12567
|
const [currency, setCurrency] = useState(DEFAULT_CURRENCY);
|
|
12242
12568
|
const [currencies, setCurrencies] = useState([]);
|
|
@@ -12296,10 +12622,15 @@ function SettingsPage() {
|
|
|
12296
12622
|
}).finally(() => setStoreSettingsLoading(false));
|
|
12297
12623
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12298
12624
|
setMultiVendorEnabled(data.enabled !== "false");
|
|
12625
|
+
setVendorCanCreateCategories(data.vendorCanCreateCategories === "true");
|
|
12626
|
+
setVendorCanCreateCollections(data.vendorCanCreateCollections === "true");
|
|
12627
|
+
setVendorCanCreateBrands(data.vendorCanCreateBrands === "true");
|
|
12628
|
+
setRequireProductApproval(data.requireProductApproval === "true");
|
|
12299
12629
|
}).catch(() => {
|
|
12300
12630
|
});
|
|
12301
12631
|
fetch("/api/settings/events").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12302
12632
|
setEventsEnabled(data.enabled !== "false");
|
|
12633
|
+
setRequireEventApproval(data.requireEventApproval === "true");
|
|
12303
12634
|
}).catch(() => {
|
|
12304
12635
|
});
|
|
12305
12636
|
fetch("/api/currencies/exchange-rates").then((r) => r.ok ? r.json() : []).then((data) => {
|
|
@@ -12427,6 +12758,22 @@ function SettingsPage() {
|
|
|
12427
12758
|
enabled: {
|
|
12428
12759
|
value: multiVendorEnabled ? "true" : "false",
|
|
12429
12760
|
type: "public"
|
|
12761
|
+
},
|
|
12762
|
+
vendorCanCreateCategories: {
|
|
12763
|
+
value: vendorCanCreateCategories ? "true" : "false",
|
|
12764
|
+
type: "public"
|
|
12765
|
+
},
|
|
12766
|
+
vendorCanCreateCollections: {
|
|
12767
|
+
value: vendorCanCreateCollections ? "true" : "false",
|
|
12768
|
+
type: "public"
|
|
12769
|
+
},
|
|
12770
|
+
vendorCanCreateBrands: {
|
|
12771
|
+
value: vendorCanCreateBrands ? "true" : "false",
|
|
12772
|
+
type: "public"
|
|
12773
|
+
},
|
|
12774
|
+
requireProductApproval: {
|
|
12775
|
+
value: requireProductApproval ? "true" : "false",
|
|
12776
|
+
type: "public"
|
|
12430
12777
|
}
|
|
12431
12778
|
})
|
|
12432
12779
|
});
|
|
@@ -12439,6 +12786,10 @@ function SettingsPage() {
|
|
|
12439
12786
|
enabled: {
|
|
12440
12787
|
value: eventsEnabled ? "true" : "false",
|
|
12441
12788
|
type: "public"
|
|
12789
|
+
},
|
|
12790
|
+
requireEventApproval: {
|
|
12791
|
+
value: requireEventApproval ? "true" : "false",
|
|
12792
|
+
type: "public"
|
|
12442
12793
|
}
|
|
12443
12794
|
})
|
|
12444
12795
|
});
|
|
@@ -12710,8 +13061,46 @@ function SettingsPage() {
|
|
|
12710
13061
|
className: "text-sm font-medium text-gray-700"
|
|
12711
13062
|
}, "Multi Vendor"), /* @__PURE__ */ React.createElement("p", {
|
|
12712
13063
|
className: "text-xs text-gray-500"
|
|
12713
|
-
}, "Enable vendor portals, vendor logins, and vendor-scoped store data. Disabling this blocks vendor-only accounts from signing in."))), storeEnabled && /* @__PURE__ */ React.createElement("div", {
|
|
12714
|
-
className: "mt-4
|
|
13064
|
+
}, "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", {
|
|
13065
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13066
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", {
|
|
13067
|
+
className: "text-sm font-medium text-gray-700"
|
|
13068
|
+
}, "Vendor catalog permissions"), /* @__PURE__ */ React.createElement("p", {
|
|
13069
|
+
className: "text-xs text-gray-500 mb-3"
|
|
13070
|
+
}, "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", {
|
|
13071
|
+
className: "flex items-center gap-3"
|
|
13072
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13073
|
+
checked: vendorCanCreateCategories,
|
|
13074
|
+
onCheckedChange: setVendorCanCreateCategories
|
|
13075
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13076
|
+
className: "text-sm font-medium text-gray-700"
|
|
13077
|
+
}, "Can vendors create categories")), /* @__PURE__ */ React.createElement("div", {
|
|
13078
|
+
className: "flex items-center gap-3"
|
|
13079
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13080
|
+
checked: vendorCanCreateCollections,
|
|
13081
|
+
onCheckedChange: setVendorCanCreateCollections
|
|
13082
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13083
|
+
className: "text-sm font-medium text-gray-700"
|
|
13084
|
+
}, "Can vendors create collections")), /* @__PURE__ */ React.createElement("div", {
|
|
13085
|
+
className: "flex items-center gap-3"
|
|
13086
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13087
|
+
checked: vendorCanCreateBrands,
|
|
13088
|
+
onCheckedChange: setVendorCanCreateBrands
|
|
13089
|
+
}), /* @__PURE__ */ React.createElement(Label3, {
|
|
13090
|
+
className: "text-sm font-medium text-gray-700"
|
|
13091
|
+
}, "Can vendors create brands")), /* @__PURE__ */ React.createElement("div", {
|
|
13092
|
+
className: "flex items-start gap-3"
|
|
13093
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13094
|
+
checked: requireProductApproval,
|
|
13095
|
+
onCheckedChange: setRequireProductApproval
|
|
13096
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13097
|
+
className: "text-sm font-medium text-gray-700"
|
|
13098
|
+
}, "Require product approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13099
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13100
|
+
}, "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", {
|
|
13101
|
+
className: "mt-4 space-y-3 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13102
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13103
|
+
className: "flex items-center gap-3"
|
|
12715
13104
|
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
12716
13105
|
checked: eventsEnabled,
|
|
12717
13106
|
onCheckedChange: setEventsEnabled
|
|
@@ -12719,7 +13108,16 @@ function SettingsPage() {
|
|
|
12719
13108
|
className: "text-sm font-medium text-gray-700"
|
|
12720
13109
|
}, "Events"), /* @__PURE__ */ React.createElement("p", {
|
|
12721
13110
|
className: "text-xs text-gray-500"
|
|
12722
|
-
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))),
|
|
13111
|
+
}, "Enable event management. Disabling this hides the Events tab and blocks all event-related APIs."))), eventsEnabled && multiVendorEnabled ? /* @__PURE__ */ React.createElement("div", {
|
|
13112
|
+
className: "flex items-start gap-3 pl-1 border-t border-gray-200 pt-3"
|
|
13113
|
+
}, /* @__PURE__ */ React.createElement(Switch, {
|
|
13114
|
+
checked: requireEventApproval,
|
|
13115
|
+
onCheckedChange: setRequireEventApproval
|
|
13116
|
+
}), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Label3, {
|
|
13117
|
+
className: "text-sm font-medium text-gray-700"
|
|
13118
|
+
}, "Require event approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13119
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13120
|
+
}, "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", {
|
|
12723
13121
|
className: "flex items-center justify-between mb-1"
|
|
12724
13122
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
12725
13123
|
className: "block text-sm font-semibold text-gray-700"
|
|
@@ -16989,15 +17387,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
16989
17387
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
16990
17388
|
}, "Layout below merges with Branding settings; values here override branding when set. Use absolute URLs for logos in email."), /* @__PURE__ */ React.createElement("div", {
|
|
16991
17389
|
className: "space-y-1"
|
|
16992
|
-
}, /* @__PURE__ */ React.createElement(
|
|
16993
|
-
|
|
16994
|
-
className: "text-sm"
|
|
16995
|
-
}, "Logo URL (optional override)"), /* @__PURE__ */ React.createElement(Input, {
|
|
16996
|
-
id: `${settingsGroup}-logoUrl`,
|
|
17390
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17391
|
+
label: "Logo (optional override)",
|
|
16997
17392
|
value: logoUrl,
|
|
16998
|
-
onChange:
|
|
17393
|
+
onChange: setLogoUrl,
|
|
17394
|
+
previewVariant: "logo",
|
|
16999
17395
|
placeholder: "https://\u2026",
|
|
17000
|
-
|
|
17396
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17001
17397
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17002
17398
|
className: "space-y-1"
|
|
17003
17399
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17050,22 +17446,23 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17050
17446
|
className: "flex flex-wrap items-end gap-2 border-b border-border/60 pb-3 dark:border-gray-600"
|
|
17051
17447
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
17052
17448
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17053
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17054
|
-
|
|
17055
|
-
}, "Icon image URL"), /* @__PURE__ */ React.createElement(Input, {
|
|
17449
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
17450
|
+
label: "Icon image",
|
|
17056
17451
|
value: row.iconUrl,
|
|
17057
|
-
onChange: /* @__PURE__ */ __name((
|
|
17452
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
17058
17453
|
const next = [
|
|
17059
17454
|
...socialLinkRows
|
|
17060
17455
|
];
|
|
17061
17456
|
next[i] = {
|
|
17062
17457
|
...next[i],
|
|
17063
|
-
iconUrl:
|
|
17458
|
+
iconUrl: v
|
|
17064
17459
|
};
|
|
17065
17460
|
setSocialLinkRows(next);
|
|
17066
17461
|
}, "onChange"),
|
|
17462
|
+
previewVariant: "logo",
|
|
17067
17463
|
placeholder: "https://\u2026",
|
|
17068
|
-
|
|
17464
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3",
|
|
17465
|
+
labelClassName: "text-xs text-muted-foreground"
|
|
17069
17466
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17070
17467
|
className: "min-w-[160px] flex-1 space-y-1"
|
|
17071
17468
|
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
@@ -17712,15 +18109,13 @@ function PluginSettingsPanel({ descriptor, onSaved }) {
|
|
|
17712
18109
|
className: "h-8 text-sm"
|
|
17713
18110
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
17714
18111
|
className: "space-y-1"
|
|
17715
|
-
}, /* @__PURE__ */ React.createElement(
|
|
17716
|
-
|
|
17717
|
-
className: "text-sm"
|
|
17718
|
-
}, "Icon image URL"), /* @__PURE__ */ React.createElement(Input, {
|
|
17719
|
-
id: `${settingsGroup}-iconImageUrl`,
|
|
18112
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
18113
|
+
label: "Icon image",
|
|
17720
18114
|
value: iconImageUrl,
|
|
17721
|
-
onChange:
|
|
18115
|
+
onChange: setIconImageUrl,
|
|
18116
|
+
previewVariant: "logo",
|
|
17722
18117
|
placeholder: "https://\u2026 or /images/chat-icon.png",
|
|
17723
|
-
|
|
18118
|
+
inputClassName: "h-8 text-sm w-full rounded-md border border-input bg-background px-3"
|
|
17724
18119
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
17725
18120
|
className: "text-xs text-gray-500 dark:text-gray-400"
|
|
17726
18121
|
}, "PNG or image URL. Leave empty to use emoji below.")), /* @__PURE__ */ React.createElement("div", {
|
|
@@ -18280,6 +18675,7 @@ var init_PluginsPage = __esm({
|
|
|
18280
18675
|
init_checkbox();
|
|
18281
18676
|
init_select();
|
|
18282
18677
|
init_EventNotificationsPluginSettings();
|
|
18678
|
+
init_ImageOrUrlField();
|
|
18283
18679
|
init_chat_email_intent();
|
|
18284
18680
|
init_llm_agent_scope();
|
|
18285
18681
|
__name(normalizeLinkedInOrganizations, "normalizeLinkedInOrganizations");
|
|
@@ -19963,7 +20359,7 @@ var init_VendorPortalProfilePage = __esm({
|
|
|
19963
20359
|
__name(VendorPortalProfilePage, "VendorPortalProfilePage");
|
|
19964
20360
|
}
|
|
19965
20361
|
});
|
|
19966
|
-
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, menuItems = [] }) {
|
|
20362
|
+
function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", closeHref, onClose, headerExtra, menuItems = [] }) {
|
|
19967
20363
|
const router = useRouter();
|
|
19968
20364
|
const handleClose = /* @__PURE__ */ __name(() => {
|
|
19969
20365
|
if (onClose) onClose();
|
|
@@ -19987,7 +20383,7 @@ function DetailPageHeader({ title, subtitle, backHref, backLabel = "Back", close
|
|
|
19987
20383
|
className: "text-sm text-gray-400 truncate"
|
|
19988
20384
|
}, subtitle))), /* @__PURE__ */ React.createElement("div", {
|
|
19989
20385
|
className: "flex items-center gap-2 shrink-0"
|
|
19990
|
-
}, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
20386
|
+
}, headerExtra, menuItems.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
|
|
19991
20387
|
className: "flex items-center gap-2 md:hidden"
|
|
19992
20388
|
}, menuItems.map((item, i) => {
|
|
19993
20389
|
const Icon2 = item.icon;
|
|
@@ -25167,15 +25563,11 @@ function SeoTabContent(props) {
|
|
|
25167
25563
|
onChange: /* @__PURE__ */ __name((e) => props.setSeoOgDescription(e.target.value), "onChange"),
|
|
25168
25564
|
placeholder: "Open Graph description",
|
|
25169
25565
|
rows: 2
|
|
25170
|
-
})), /* @__PURE__ */ React26__default.createElement(
|
|
25171
|
-
|
|
25172
|
-
}, /* @__PURE__ */ React26__default.createElement(Label3, {
|
|
25173
|
-
className: "text-xs"
|
|
25174
|
-
}, "OG Image"), /* @__PURE__ */ React26__default.createElement(Input, {
|
|
25566
|
+
})), /* @__PURE__ */ React26__default.createElement(ImageOrUrlField, {
|
|
25567
|
+
label: "OG Image",
|
|
25175
25568
|
value: props.seoOgImage,
|
|
25176
|
-
onChange:
|
|
25177
|
-
|
|
25178
|
-
})));
|
|
25569
|
+
onChange: props.setSeoOgImage
|
|
25570
|
+
}));
|
|
25179
25571
|
}
|
|
25180
25572
|
function CollapsibleSection({ title, icon: Icon2, open, onToggle, children }) {
|
|
25181
25573
|
return /* @__PURE__ */ React26__default.createElement("div", {
|
|
@@ -25561,6 +25953,7 @@ var init_PageBuilderPage = __esm({
|
|
|
25561
25953
|
init_ComponentSettings();
|
|
25562
25954
|
init_admin_config_context();
|
|
25563
25955
|
init_registry();
|
|
25956
|
+
init_ImageOrUrlField();
|
|
25564
25957
|
__name(createSelectable, "createSelectable");
|
|
25565
25958
|
__name(buildEditorResolver, "buildEditorResolver");
|
|
25566
25959
|
__name(getIcon, "getIcon");
|
|
@@ -25623,15 +26016,12 @@ function SeoSection({ values, onChange }) {
|
|
|
25623
26016
|
onChange: /* @__PURE__ */ __name((e) => onChange("seoOgDescription", e.target.value), "onChange"),
|
|
25624
26017
|
className: textareaCls,
|
|
25625
26018
|
rows: 2
|
|
25626
|
-
})), /* @__PURE__ */ React.createElement(
|
|
25627
|
-
|
|
25628
|
-
}, "OG Image"), /* @__PURE__ */ React.createElement("input", {
|
|
25629
|
-
type: "url",
|
|
26019
|
+
})), /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26020
|
+
label: "OG Image",
|
|
25630
26021
|
value: values.seoOgImage,
|
|
25631
|
-
onChange: /* @__PURE__ */ __name((
|
|
25632
|
-
|
|
25633
|
-
|
|
25634
|
-
}))));
|
|
26022
|
+
onChange: /* @__PURE__ */ __name((v) => onChange("seoOgImage", v), "onChange"),
|
|
26023
|
+
inputClassName: inputCls9
|
|
26024
|
+
})));
|
|
25635
26025
|
}
|
|
25636
26026
|
async function saveSeo(seo, slug, existingSeoId) {
|
|
25637
26027
|
const hasSeo = seo.seoTitle || seo.seoDescription || seo.seoKeywords || seo.seoOgTitle || seo.seoOgDescription || seo.seoOgImage;
|
|
@@ -25696,6 +26086,7 @@ async function fetchSeo(seoId) {
|
|
|
25696
26086
|
var init_SeoSection = __esm({
|
|
25697
26087
|
"src/components/Admin/SeoSection.tsx"() {
|
|
25698
26088
|
"use client";
|
|
26089
|
+
init_ImageOrUrlField();
|
|
25699
26090
|
__name(SeoSection, "SeoSection");
|
|
25700
26091
|
__name(saveSeo, "saveSeo");
|
|
25701
26092
|
__name(fetchSeo, "fetchSeo");
|
|
@@ -25710,6 +26101,8 @@ __export(BrandEditPage_exports, {
|
|
|
25710
26101
|
function BrandEditPage({ brandId }) {
|
|
25711
26102
|
const router = useRouter();
|
|
25712
26103
|
const searchParams = useSearchParams();
|
|
26104
|
+
const { data: session } = useSession();
|
|
26105
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
25713
26106
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/brands";
|
|
25714
26107
|
const create = isCreate(brandId);
|
|
25715
26108
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -25811,6 +26204,9 @@ function BrandEditPage({ brandId }) {
|
|
|
25811
26204
|
active,
|
|
25812
26205
|
sortOrder
|
|
25813
26206
|
};
|
|
26207
|
+
if (!vendorPortal) {
|
|
26208
|
+
payload.isCatalog = true;
|
|
26209
|
+
}
|
|
25814
26210
|
if (savedSeoId) payload.seoId = savedSeoId;
|
|
25815
26211
|
const res = await fetch(create ? "/api/brands" : `/api/brands/${brandId}`, {
|
|
25816
26212
|
method: create ? "POST" : "PUT",
|
|
@@ -25902,13 +26298,12 @@ function BrandEditPage({ brandId }) {
|
|
|
25902
26298
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
25903
26299
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm min-h-[80px]",
|
|
25904
26300
|
rows: 3
|
|
25905
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
25906
|
-
|
|
25907
|
-
}, "Logo URL"), /* @__PURE__ */ React.createElement("input", {
|
|
25908
|
-
type: "url",
|
|
26301
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
26302
|
+
label: "Logo",
|
|
25909
26303
|
value: logo,
|
|
25910
|
-
onChange:
|
|
25911
|
-
|
|
26304
|
+
onChange: setLogo,
|
|
26305
|
+
previewVariant: "logo",
|
|
26306
|
+
inputClassName: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
25912
26307
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
25913
26308
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
25914
26309
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -25951,6 +26346,8 @@ var init_BrandEditPage = __esm({
|
|
|
25951
26346
|
init_SeoSection();
|
|
25952
26347
|
init_DetailPageLayout();
|
|
25953
26348
|
init_DetailPageHeader();
|
|
26349
|
+
init_vendor_scope();
|
|
26350
|
+
init_ImageOrUrlField();
|
|
25954
26351
|
isCreate = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
25955
26352
|
__name(BrandEditPage, "BrandEditPage");
|
|
25956
26353
|
}
|
|
@@ -26858,16 +27255,38 @@ function ProductVariantsSection({ hasVariants, onHasVariantsChange, variantOptio
|
|
|
26858
27255
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
26859
27256
|
value: "sold"
|
|
26860
27257
|
}, "Sold"))), /* @__PURE__ */ React.createElement("td", {
|
|
26861
|
-
className: "px-3 py-2 align-top"
|
|
26862
|
-
},
|
|
26863
|
-
|
|
26864
|
-
|
|
26865
|
-
|
|
26866
|
-
|
|
26867
|
-
|
|
26868
|
-
|
|
26869
|
-
|
|
26870
|
-
|
|
27258
|
+
className: "px-3 py-2 align-top min-w-[240px]"
|
|
27259
|
+
}, (() => {
|
|
27260
|
+
const lines = row.imageUrlsText === "" ? [
|
|
27261
|
+
""
|
|
27262
|
+
] : row.imageUrlsText.split("\n");
|
|
27263
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
27264
|
+
className: "space-y-2"
|
|
27265
|
+
}, lines.map((url, ui) => /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
27266
|
+
key: ui,
|
|
27267
|
+
label: ui === 0 ? "Images" : `Image ${ui + 1}`,
|
|
27268
|
+
value: url,
|
|
27269
|
+
onChange: /* @__PURE__ */ __name((v) => {
|
|
27270
|
+
const next = [
|
|
27271
|
+
...lines
|
|
27272
|
+
];
|
|
27273
|
+
next[ui] = v;
|
|
27274
|
+
setVariantField(i, "imageUrlsText", next.join("\n"));
|
|
27275
|
+
}, "onChange"),
|
|
27276
|
+
inputClassName: inputCls2
|
|
27277
|
+
})), /* @__PURE__ */ React.createElement("button", {
|
|
27278
|
+
type: "button",
|
|
27279
|
+
onClick: /* @__PURE__ */ __name(() => setVariantField(i, "imageUrlsText", [
|
|
27280
|
+
...lines,
|
|
27281
|
+
""
|
|
27282
|
+
].join("\n")), "onClick"),
|
|
27283
|
+
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"
|
|
27284
|
+
}, /* @__PURE__ */ React.createElement(Plus, {
|
|
27285
|
+
className: "h-3 w-3"
|
|
27286
|
+
}), " Add image"), /* @__PURE__ */ React.createElement("p", {
|
|
27287
|
+
className: "text-[11px] text-gray-500"
|
|
27288
|
+
}, "First image is used on PDP."));
|
|
27289
|
+
})()), /* @__PURE__ */ React.createElement("td", {
|
|
26871
27290
|
className: "px-3 py-2 align-top text-right"
|
|
26872
27291
|
}, /* @__PURE__ */ React.createElement("button", {
|
|
26873
27292
|
type: "button",
|
|
@@ -26882,6 +27301,7 @@ var init_ProductVariantsSection = __esm({
|
|
|
26882
27301
|
"src/admin/pages/ProductVariantsSection.tsx"() {
|
|
26883
27302
|
"use client";
|
|
26884
27303
|
init_product_variants();
|
|
27304
|
+
init_ImageOrUrlField();
|
|
26885
27305
|
labelCls2 = "block text-xs font-medium text-gray-600 mb-1";
|
|
26886
27306
|
inputCls2 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
26887
27307
|
sectionCls2 = "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50";
|
|
@@ -27077,13 +27497,16 @@ function ProductEditPage({ productId }) {
|
|
|
27077
27497
|
const router = useRouter();
|
|
27078
27498
|
const searchParams = useSearchParams();
|
|
27079
27499
|
const { data: session } = useSession();
|
|
27500
|
+
const { eventsEnabled, requireProductApproval } = useContext(AdminConfigContext);
|
|
27501
|
+
const eventsOn = eventsEnabled !== false;
|
|
27502
|
+
const approvalOn = requireProductApproval === true;
|
|
27080
27503
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
27081
27504
|
const categoryIdParam = searchParams.get("categoryId")?.trim() ?? "";
|
|
27082
27505
|
const collectionIdParam = searchParams.get("collectionId")?.trim() ?? "";
|
|
27083
27506
|
const eventIdParam = searchParams.get("eventId")?.trim() ?? "";
|
|
27084
27507
|
const lockCategoryFromStore = isCreate2(productId) && /^\d+$/.test(categoryIdParam);
|
|
27085
27508
|
const lockCollectionFromQuery = isCreate2(productId) && /^\d+$/.test(collectionIdParam);
|
|
27086
|
-
const lockEventFromQuery = isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27509
|
+
const lockEventFromQuery = eventsOn && isCreate2(productId) && /^\d+$/.test(eventIdParam);
|
|
27087
27510
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? (lockCategoryFromStore ? `/admin/products?categoryId=${categoryIdParam}` : "/admin/products");
|
|
27088
27511
|
const create = isCreate2(productId);
|
|
27089
27512
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -27114,6 +27537,11 @@ function ProductEditPage({ productId }) {
|
|
|
27114
27537
|
const [compareAtPrice, setCompareAtPrice] = useState(0);
|
|
27115
27538
|
const [quantity, setQuantity] = useState(1);
|
|
27116
27539
|
const [status, setStatus] = useState("draft");
|
|
27540
|
+
const [approvalStatus, setApprovalStatus] = useState("pending");
|
|
27541
|
+
const [rejectionReason, setRejectionReason] = useState("");
|
|
27542
|
+
const [rejectModalOpen, setRejectModalOpen] = useState(false);
|
|
27543
|
+
const [rejectDraft, setRejectDraft] = useState("");
|
|
27544
|
+
const approvalBeforeRejectRef = useRef("pending");
|
|
27117
27545
|
const [featured, setFeatured] = useState(false);
|
|
27118
27546
|
const [description, setDescription] = useState("");
|
|
27119
27547
|
const [images, setImages] = useState([
|
|
@@ -27218,7 +27646,7 @@ function ProductEditPage({ productId }) {
|
|
|
27218
27646
|
try {
|
|
27219
27647
|
const [brandRes, catRes, attrRes, taxesRes, eventsRes, formsRes, refundRes] = await Promise.all([
|
|
27220
27648
|
fetch("/api/brands?limit=500"),
|
|
27221
|
-
fetch("/api/product_categories?limit=500&isCatalog=true"),
|
|
27649
|
+
fetch(vendorPortal ? "/api/product_categories?limit=500" : "/api/product_categories?limit=500&isCatalog=true"),
|
|
27222
27650
|
fetch("/api/attributes?limit=500"),
|
|
27223
27651
|
fetch("/api/taxes?limit=200&sortField=name&sortOrder=asc"),
|
|
27224
27652
|
fetch("/api/events?limit=200&sortField=startDate&sortOrder=desc"),
|
|
@@ -27331,6 +27759,8 @@ function ProductEditPage({ productId }) {
|
|
|
27331
27759
|
setCompareAtPrice(Number.isFinite(rawCompare) ? rawCompare : 0);
|
|
27332
27760
|
setQuantity(product.quantity ?? 1);
|
|
27333
27761
|
setStatus(product.status ?? "draft");
|
|
27762
|
+
setApprovalStatus(typeof product.approvalStatus === "string" && product.approvalStatus ? product.approvalStatus : "pending");
|
|
27763
|
+
setRejectionReason(typeof product.rejectionReason === "string" ? product.rejectionReason : "");
|
|
27334
27764
|
setFeatured(product.featured ?? false);
|
|
27335
27765
|
setDescription((m && typeof m.description === "string" ? m.description : "") ?? "");
|
|
27336
27766
|
const rawImages = m?.images;
|
|
@@ -27466,6 +27896,16 @@ function ProductEditPage({ productId }) {
|
|
|
27466
27896
|
create,
|
|
27467
27897
|
eventId
|
|
27468
27898
|
]);
|
|
27899
|
+
useEffect(() => {
|
|
27900
|
+
if (create && vendorPortal && approvalOn) {
|
|
27901
|
+
setApprovalStatus("pending");
|
|
27902
|
+
setStatus((s) => s === "available" ? "draft" : s);
|
|
27903
|
+
}
|
|
27904
|
+
}, [
|
|
27905
|
+
create,
|
|
27906
|
+
vendorPortal,
|
|
27907
|
+
approvalOn
|
|
27908
|
+
]);
|
|
27469
27909
|
useEffect(() => {
|
|
27470
27910
|
if (!create || !name.trim()) return;
|
|
27471
27911
|
setProductSlug(slugifyProductName(name));
|
|
@@ -27500,6 +27940,33 @@ function ProductEditPage({ productId }) {
|
|
|
27500
27940
|
}
|
|
27501
27941
|
setter(num);
|
|
27502
27942
|
}, "handleNumberChange");
|
|
27943
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
27944
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
27945
|
+
setRejectDraft(rejectionReason);
|
|
27946
|
+
setApprovalStatus("rejected");
|
|
27947
|
+
setRejectModalOpen(true);
|
|
27948
|
+
}, "openRejectModal");
|
|
27949
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
27950
|
+
const reason = rejectDraft.trim();
|
|
27951
|
+
if (!reason) return;
|
|
27952
|
+
setRejectionReason(reason);
|
|
27953
|
+
setRejectModalOpen(false);
|
|
27954
|
+
}, "confirmRejectReason");
|
|
27955
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
27956
|
+
if (!rejectionReason.trim()) {
|
|
27957
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
27958
|
+
}
|
|
27959
|
+
setRejectDraft(rejectionReason);
|
|
27960
|
+
setRejectModalOpen(false);
|
|
27961
|
+
}, "cancelRejectModal");
|
|
27962
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
27963
|
+
if (value === "rejected") {
|
|
27964
|
+
openRejectModal(approvalStatus);
|
|
27965
|
+
return;
|
|
27966
|
+
}
|
|
27967
|
+
setApprovalStatus(value);
|
|
27968
|
+
if (value !== "rejected") setRejectionReason("");
|
|
27969
|
+
}, "handleApprovalSelect");
|
|
27503
27970
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
27504
27971
|
setErrors([]);
|
|
27505
27972
|
if (!name.trim()) {
|
|
@@ -27508,6 +27975,14 @@ function ProductEditPage({ productId }) {
|
|
|
27508
27975
|
]);
|
|
27509
27976
|
return;
|
|
27510
27977
|
}
|
|
27978
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
27979
|
+
setRejectDraft("");
|
|
27980
|
+
setRejectModalOpen(true);
|
|
27981
|
+
setErrors([
|
|
27982
|
+
"Rejection reason is required"
|
|
27983
|
+
]);
|
|
27984
|
+
return;
|
|
27985
|
+
}
|
|
27511
27986
|
if (!defaultPriceStr.trim()) {
|
|
27512
27987
|
setErrors([
|
|
27513
27988
|
`${pricingConfig.defaultCurrency} price is required`
|
|
@@ -27570,11 +28045,21 @@ function ProductEditPage({ productId }) {
|
|
|
27570
28045
|
currencyPrices: null,
|
|
27571
28046
|
compareAtPrice: compareAtPriceValue,
|
|
27572
28047
|
quantity: resolvedQuantity,
|
|
27573
|
-
status,
|
|
28048
|
+
status: create && vendorPortal && approvalOn && status === "available" ? "draft" : status,
|
|
27574
28049
|
featured,
|
|
27575
28050
|
contactFormId,
|
|
27576
28051
|
metadata
|
|
27577
28052
|
};
|
|
28053
|
+
if (approvalOn) {
|
|
28054
|
+
if (vendorPortal && create) {
|
|
28055
|
+
productPayload.approvalStatus = "pending";
|
|
28056
|
+
} else if (!vendorPortal) {
|
|
28057
|
+
productPayload.approvalStatus = approvalStatus;
|
|
28058
|
+
if (approvalStatus === "rejected") {
|
|
28059
|
+
productPayload.rejectionReason = rejectionReason.trim();
|
|
28060
|
+
}
|
|
28061
|
+
}
|
|
28062
|
+
}
|
|
27578
28063
|
const res = await fetch(create ? "/api/products" : `/api/products/${productId}`, {
|
|
27579
28064
|
method: create ? "POST" : "PUT",
|
|
27580
28065
|
headers: {
|
|
@@ -27596,6 +28081,15 @@ function ProductEditPage({ productId }) {
|
|
|
27596
28081
|
if (typeof savedProduct.slug === "string") {
|
|
27597
28082
|
setProductSlug(savedProduct.slug);
|
|
27598
28083
|
}
|
|
28084
|
+
if (typeof savedProduct.status === "string") {
|
|
28085
|
+
setStatus(savedProduct.status);
|
|
28086
|
+
}
|
|
28087
|
+
if (typeof savedProduct.approvalStatus === "string") {
|
|
28088
|
+
setApprovalStatus(savedProduct.approvalStatus);
|
|
28089
|
+
}
|
|
28090
|
+
if (approvalStatus === "approved") {
|
|
28091
|
+
setRejectionReason("");
|
|
28092
|
+
}
|
|
27599
28093
|
const savedId = create ? savedProduct.id : productId;
|
|
27600
28094
|
const savedSeoId = await saveSeo(seo, productSlugValue, seoId);
|
|
27601
28095
|
const linkedSeoId = savedSeoId ?? savedProduct.seoId ?? null;
|
|
@@ -27796,7 +28290,7 @@ function ProductEditPage({ productId }) {
|
|
|
27796
28290
|
}
|
|
27797
28291
|
setVariantRows([]);
|
|
27798
28292
|
}
|
|
27799
|
-
if (create && eventId != null) {
|
|
28293
|
+
if (create && eventsOn && eventId != null) {
|
|
27800
28294
|
const attachRes = await fetch("/api/event_products", {
|
|
27801
28295
|
method: "POST",
|
|
27802
28296
|
headers: {
|
|
@@ -27901,6 +28395,27 @@ function ProductEditPage({ productId }) {
|
|
|
27901
28395
|
title: pageTitle,
|
|
27902
28396
|
subtitle: pageSubtitle,
|
|
27903
28397
|
closeHref: listReturnUrl,
|
|
28398
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
28399
|
+
className: "flex items-center gap-2"
|
|
28400
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
28401
|
+
value: approvalStatus,
|
|
28402
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
28403
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
28404
|
+
"aria-label": "Approval status"
|
|
28405
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
28406
|
+
value: "pending"
|
|
28407
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
28408
|
+
value: "approved"
|
|
28409
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
28410
|
+
value: "rejected"
|
|
28411
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
28412
|
+
type: "button",
|
|
28413
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
28414
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
28415
|
+
title: rejectionReason || "Add rejection reason"
|
|
28416
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
28417
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
28418
|
+
}, approvalStatus.replace(/_/g, " ")) : null,
|
|
27904
28419
|
menuItems: [
|
|
27905
28420
|
{
|
|
27906
28421
|
label: saving ? "Saving..." : "Save",
|
|
@@ -27913,7 +28428,31 @@ function ProductEditPage({ productId }) {
|
|
|
27913
28428
|
onClick: /* @__PURE__ */ __name(() => setFeatured(!featured), "onClick")
|
|
27914
28429
|
}
|
|
27915
28430
|
]
|
|
27916
|
-
}),
|
|
28431
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
28432
|
+
open: rejectModalOpen,
|
|
28433
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
28434
|
+
if (!open) cancelRejectModal();
|
|
28435
|
+
}, "onOpenChange")
|
|
28436
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
28437
|
+
className: "max-w-md"
|
|
28438
|
+
}, /* @__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", {
|
|
28439
|
+
value: rejectDraft,
|
|
28440
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
28441
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
28442
|
+
placeholder: "Explain what needs to change\u2026",
|
|
28443
|
+
autoFocus: true
|
|
28444
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
28445
|
+
className: "gap-2 sm:gap-0"
|
|
28446
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
28447
|
+
type: "button",
|
|
28448
|
+
variant: "outline",
|
|
28449
|
+
onClick: cancelRejectModal
|
|
28450
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
28451
|
+
type: "button",
|
|
28452
|
+
variant: "destructive",
|
|
28453
|
+
disabled: !rejectDraft.trim(),
|
|
28454
|
+
onClick: confirmRejectReason
|
|
28455
|
+
}, "Confirm reject")))), errors.length > 0 && /* @__PURE__ */ React.createElement("div", {
|
|
27917
28456
|
className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4"
|
|
27918
28457
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
27919
28458
|
className: "flex"
|
|
@@ -28036,7 +28575,7 @@ function ProductEditPage({ productId }) {
|
|
|
28036
28575
|
className: "mt-1 text-xs text-gray-500"
|
|
28037
28576
|
}, "Only collections in the selected category are listed.") : /* @__PURE__ */ React.createElement("p", {
|
|
28038
28577
|
className: "mt-1 text-xs text-gray-500"
|
|
28039
|
-
}, "Select a category to load collections.")), create ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28578
|
+
}, "Select a category to load collections.")), create && eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28040
28579
|
className: labelCls3
|
|
28041
28580
|
}, "Event"), /* @__PURE__ */ React.createElement("select", {
|
|
28042
28581
|
value: eventId ?? "",
|
|
@@ -28132,7 +28671,8 @@ function ProductEditPage({ productId }) {
|
|
|
28132
28671
|
}, "Status"), /* @__PURE__ */ React.createElement("select", {
|
|
28133
28672
|
value: status,
|
|
28134
28673
|
onChange: /* @__PURE__ */ __name((e) => setStatus(e.target.value), "onChange"),
|
|
28135
|
-
className: inputCls3
|
|
28674
|
+
className: inputCls3,
|
|
28675
|
+
disabled: approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected")
|
|
28136
28676
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28137
28677
|
value: "draft"
|
|
28138
28678
|
}, "Draft"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28141,7 +28681,11 @@ function ProductEditPage({ productId }) {
|
|
|
28141
28681
|
value: "reserved"
|
|
28142
28682
|
}, "Reserved"), /* @__PURE__ */ React.createElement("option", {
|
|
28143
28683
|
value: "sold"
|
|
28144
|
-
}, "Sold"))
|
|
28684
|
+
}, "Sold")), approvalOn && vendorPortal && approvalStatus === "pending" ? /* @__PURE__ */ React.createElement("p", {
|
|
28685
|
+
className: "mt-1 text-xs text-gray-500"
|
|
28686
|
+
}, "Waiting for admin approval. The product goes live when approved.") : null, approvalOn && vendorPortal && approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("p", {
|
|
28687
|
+
className: "mt-1 text-xs text-red-600"
|
|
28688
|
+
}, "Rejected", rejectionReason ? `: ${rejectionReason}` : "", ".") : null)))), /* @__PURE__ */ React.createElement(ProductVariantsSection, {
|
|
28145
28689
|
hasVariants,
|
|
28146
28690
|
onHasVariantsChange: setHasVariants,
|
|
28147
28691
|
variantOptionRows,
|
|
@@ -28268,14 +28812,11 @@ function ProductEditPage({ productId }) {
|
|
|
28268
28812
|
className: "flex flex-wrap items-start gap-2 p-2 bg-white rounded border border-gray-200"
|
|
28269
28813
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
28270
28814
|
className: "flex-1 min-w-[200px]"
|
|
28271
|
-
}, /* @__PURE__ */ React.createElement(
|
|
28272
|
-
|
|
28273
|
-
}, "Image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28274
|
-
type: "url",
|
|
28815
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
28816
|
+
label: "Image",
|
|
28275
28817
|
value: row.url,
|
|
28276
|
-
onChange: /* @__PURE__ */ __name((
|
|
28277
|
-
|
|
28278
|
-
placeholder: "https://..."
|
|
28818
|
+
onChange: /* @__PURE__ */ __name((v) => setImage(i, "url", v), "onChange"),
|
|
28819
|
+
inputClassName: inputCls3
|
|
28279
28820
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
28280
28821
|
className: "flex-1 min-w-[120px]"
|
|
28281
28822
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -28354,6 +28895,8 @@ var init_ProductEditPage = __esm({
|
|
|
28354
28895
|
"use client";
|
|
28355
28896
|
init_vendor_scope();
|
|
28356
28897
|
init_admin_list_return_url();
|
|
28898
|
+
init_dialog();
|
|
28899
|
+
init_button();
|
|
28357
28900
|
init_SeoSection();
|
|
28358
28901
|
init_DetailPageLayout();
|
|
28359
28902
|
init_DetailPageHeader();
|
|
@@ -28364,6 +28907,8 @@ var init_ProductEditPage = __esm({
|
|
|
28364
28907
|
init_inventory_validation();
|
|
28365
28908
|
init_category_item_label();
|
|
28366
28909
|
init_use_category_collections();
|
|
28910
|
+
init_ImageOrUrlField();
|
|
28911
|
+
init_admin_config_context();
|
|
28367
28912
|
init_ProductVariantsSection();
|
|
28368
28913
|
init_product_variants();
|
|
28369
28914
|
__name(parseCategoryIdFromReturnUrl, "parseCategoryIdFromReturnUrl");
|
|
@@ -28633,12 +29178,6 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28633
29178
|
]);
|
|
28634
29179
|
return;
|
|
28635
29180
|
}
|
|
28636
|
-
if (create && !categoryId) {
|
|
28637
|
-
setErrors([
|
|
28638
|
-
"Category is required"
|
|
28639
|
-
]);
|
|
28640
|
-
return;
|
|
28641
|
-
}
|
|
28642
29181
|
setSaving(true);
|
|
28643
29182
|
try {
|
|
28644
29183
|
const savedSeoId = await saveSeo(seo, slug.trim(), seoId);
|
|
@@ -28859,14 +29398,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28859
29398
|
onChange: /* @__PURE__ */ __name((e) => setDescription(e.target.value), "onChange"),
|
|
28860
29399
|
className: `${inputCls4} min-h-[80px]`,
|
|
28861
29400
|
rows: 3
|
|
28862
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
28863
|
-
|
|
28864
|
-
}, "Cover image URL"), /* @__PURE__ */ React.createElement("input", {
|
|
28865
|
-
type: "url",
|
|
29401
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29402
|
+
label: "Cover image",
|
|
28866
29403
|
value: image,
|
|
28867
|
-
onChange:
|
|
28868
|
-
|
|
28869
|
-
placeholder: "https://..."
|
|
29404
|
+
onChange: setImage,
|
|
29405
|
+
inputClassName: inputCls4
|
|
28870
29406
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
28871
29407
|
className: labelCls4
|
|
28872
29408
|
}, "Sort order"), /* @__PURE__ */ React.createElement("input", {
|
|
@@ -28893,17 +29429,24 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28893
29429
|
className: "text-xs font-medium text-gray-700 mb-2"
|
|
28894
29430
|
}, "Hero carousel"), heroSlides.map((slide, i) => /* @__PURE__ */ React.createElement("div", {
|
|
28895
29431
|
key: i,
|
|
28896
|
-
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border"
|
|
28897
|
-
}, /* @__PURE__ */ React.createElement("
|
|
29432
|
+
className: "flex flex-wrap gap-2 mb-3 p-2 bg-white rounded border items-start"
|
|
29433
|
+
}, slide.type === "image" ? /* @__PURE__ */ React.createElement("div", {
|
|
29434
|
+
className: "flex-1 min-w-[200px]"
|
|
29435
|
+
}, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
29436
|
+
label: "Slide image",
|
|
29437
|
+
value: slide.url,
|
|
29438
|
+
onChange: /* @__PURE__ */ __name((v) => updateHeroSlide(i, "url", v), "onChange"),
|
|
29439
|
+
inputClassName: inputCls4
|
|
29440
|
+
})) : /* @__PURE__ */ React.createElement("input", {
|
|
28898
29441
|
type: "url",
|
|
28899
29442
|
value: slide.url,
|
|
28900
29443
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "url", e.target.value), "onChange"),
|
|
28901
|
-
placeholder: "
|
|
29444
|
+
placeholder: "Video URL",
|
|
28902
29445
|
className: `${inputCls4} flex-1 min-w-[200px]`
|
|
28903
29446
|
}), /* @__PURE__ */ React.createElement("select", {
|
|
28904
29447
|
value: slide.type,
|
|
28905
29448
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "type", e.target.value), "onChange"),
|
|
28906
|
-
className: `${inputCls4} w-24`
|
|
29449
|
+
className: `${inputCls4} w-24 mt-6`
|
|
28907
29450
|
}, /* @__PURE__ */ React.createElement("option", {
|
|
28908
29451
|
value: "image"
|
|
28909
29452
|
}, "Image"), /* @__PURE__ */ React.createElement("option", {
|
|
@@ -28913,11 +29456,11 @@ function CollectionEditPage({ collectionId }) {
|
|
|
28913
29456
|
value: slide.caption,
|
|
28914
29457
|
onChange: /* @__PURE__ */ __name((e) => updateHeroSlide(i, "caption", e.target.value), "onChange"),
|
|
28915
29458
|
placeholder: "Caption",
|
|
28916
|
-
className: `${inputCls4} flex-1 min-w-[120px]`
|
|
29459
|
+
className: `${inputCls4} flex-1 min-w-[120px] mt-6`
|
|
28917
29460
|
}), /* @__PURE__ */ React.createElement("button", {
|
|
28918
29461
|
type: "button",
|
|
28919
29462
|
onClick: /* @__PURE__ */ __name(() => removeHeroSlide(i), "onClick"),
|
|
28920
|
-
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0"
|
|
29463
|
+
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mt-6"
|
|
28921
29464
|
}, /* @__PURE__ */ React.createElement(Trash2, {
|
|
28922
29465
|
className: "h-4 w-4"
|
|
28923
29466
|
})))), /* @__PURE__ */ React.createElement("button", {
|
|
@@ -29044,6 +29587,7 @@ var init_CollectionEditPage = __esm({
|
|
|
29044
29587
|
init_use_catalog_categories();
|
|
29045
29588
|
init_admin_config_context();
|
|
29046
29589
|
init_category_related_product_labels();
|
|
29590
|
+
init_ImageOrUrlField();
|
|
29047
29591
|
isCreate3 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
29048
29592
|
emptySlide = /* @__PURE__ */ __name(() => ({
|
|
29049
29593
|
url: "",
|
|
@@ -29061,116 +29605,6 @@ var init_CollectionEditPage = __esm({
|
|
|
29061
29605
|
__name(CollectionEditPage, "CollectionEditPage");
|
|
29062
29606
|
}
|
|
29063
29607
|
});
|
|
29064
|
-
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 }) {
|
|
29065
|
-
const fileInputRef = useRef(null);
|
|
29066
|
-
const [isUploading, setIsUploading] = useState(false);
|
|
29067
|
-
const [error, setError] = useState(null);
|
|
29068
|
-
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";
|
|
29069
|
-
const handleUpload = useCallback(async (file) => {
|
|
29070
|
-
setError(null);
|
|
29071
|
-
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
29072
|
-
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
29073
|
-
return;
|
|
29074
|
-
}
|
|
29075
|
-
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
29076
|
-
setError(`File must be under ${maxSizeMb}MB`);
|
|
29077
|
-
return;
|
|
29078
|
-
}
|
|
29079
|
-
setIsUploading(true);
|
|
29080
|
-
try {
|
|
29081
|
-
const formData = new FormData();
|
|
29082
|
-
formData.append("file", file);
|
|
29083
|
-
const response = await fetch("/api/upload", {
|
|
29084
|
-
method: "POST",
|
|
29085
|
-
body: formData
|
|
29086
|
-
});
|
|
29087
|
-
const data = await response.json();
|
|
29088
|
-
if (!response.ok) {
|
|
29089
|
-
throw new Error(data.error || data.details || "Upload failed");
|
|
29090
|
-
}
|
|
29091
|
-
onChange(data.filePath ?? "");
|
|
29092
|
-
} catch (err) {
|
|
29093
|
-
setError(err instanceof Error ? err.message : "Upload failed");
|
|
29094
|
-
} finally {
|
|
29095
|
-
setIsUploading(false);
|
|
29096
|
-
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
29097
|
-
}
|
|
29098
|
-
}, [
|
|
29099
|
-
maxSizeMb,
|
|
29100
|
-
onChange
|
|
29101
|
-
]);
|
|
29102
|
-
const onFileChange = useCallback((e) => {
|
|
29103
|
-
const file = e.target.files?.[0];
|
|
29104
|
-
if (file) void handleUpload(file);
|
|
29105
|
-
}, [
|
|
29106
|
-
handleUpload
|
|
29107
|
-
]);
|
|
29108
|
-
const trimmed = value.trim();
|
|
29109
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
29110
|
-
className: "space-y-2"
|
|
29111
|
-
}, /* @__PURE__ */ React.createElement("label", {
|
|
29112
|
-
className: labelClassName
|
|
29113
|
-
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
29114
|
-
className: "flex items-start gap-3"
|
|
29115
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
29116
|
-
src: trimmed,
|
|
29117
|
-
alt: label,
|
|
29118
|
-
className: previewCls,
|
|
29119
|
-
onError: /* @__PURE__ */ __name((e) => {
|
|
29120
|
-
e.currentTarget.style.display = "none";
|
|
29121
|
-
}, "onError")
|
|
29122
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
29123
|
-
type: "button",
|
|
29124
|
-
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
29125
|
-
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"
|
|
29126
|
-
}, /* @__PURE__ */ React.createElement(X, {
|
|
29127
|
-
className: "h-3 w-3"
|
|
29128
|
-
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
29129
|
-
className: "flex flex-wrap gap-2"
|
|
29130
|
-
}, /* @__PURE__ */ React.createElement("input", {
|
|
29131
|
-
type: "url",
|
|
29132
|
-
value,
|
|
29133
|
-
onChange: /* @__PURE__ */ __name((e) => {
|
|
29134
|
-
setError(null);
|
|
29135
|
-
onChange(e.target.value);
|
|
29136
|
-
}, "onChange"),
|
|
29137
|
-
placeholder,
|
|
29138
|
-
className: `${inputClassName} min-w-0 flex-1`
|
|
29139
|
-
}), /* @__PURE__ */ React.createElement("input", {
|
|
29140
|
-
ref: fileInputRef,
|
|
29141
|
-
type: "file",
|
|
29142
|
-
accept: ACCEPTED_TYPES.join(","),
|
|
29143
|
-
onChange: onFileChange,
|
|
29144
|
-
className: "hidden",
|
|
29145
|
-
disabled: isUploading
|
|
29146
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
29147
|
-
type: "button",
|
|
29148
|
-
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
29149
|
-
disabled: isUploading,
|
|
29150
|
-
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"
|
|
29151
|
-
}, /* @__PURE__ */ React.createElement(Upload, {
|
|
29152
|
-
className: "h-3.5 w-3.5"
|
|
29153
|
-
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
29154
|
-
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
29155
|
-
}, /* @__PURE__ */ React.createElement(AlertCircle, {
|
|
29156
|
-
className: "h-3.5 w-3.5 shrink-0"
|
|
29157
|
-
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
29158
|
-
className: "text-xs text-gray-500"
|
|
29159
|
-
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"));
|
|
29160
|
-
}
|
|
29161
|
-
var ACCEPTED_TYPES;
|
|
29162
|
-
var init_ImageOrUrlField = __esm({
|
|
29163
|
-
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
29164
|
-
"use client";
|
|
29165
|
-
ACCEPTED_TYPES = [
|
|
29166
|
-
"image/jpeg",
|
|
29167
|
-
"image/png",
|
|
29168
|
-
"image/gif",
|
|
29169
|
-
"image/webp"
|
|
29170
|
-
];
|
|
29171
|
-
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
29172
|
-
}
|
|
29173
|
-
});
|
|
29174
29608
|
function AttachProductModal({ open, onOpenChange, categoryId, categoryName, excludeProductIds = [], onAttach }) {
|
|
29175
29609
|
const [attachingId, setAttachingId] = useState(null);
|
|
29176
29610
|
const [error, setError] = useState(null);
|
|
@@ -30115,6 +30549,10 @@ function combineCityCountry(city, country) {
|
|
|
30115
30549
|
function EventEditPage({ eventId }) {
|
|
30116
30550
|
const router = useRouter();
|
|
30117
30551
|
const searchParams = useSearchParams();
|
|
30552
|
+
const { data: session } = useSession();
|
|
30553
|
+
const { requireEventApproval } = useContext(AdminConfigContext);
|
|
30554
|
+
const approvalOn = requireEventApproval === true;
|
|
30555
|
+
const vendorPortal = isVendorPortalUser(session?.user);
|
|
30118
30556
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/events";
|
|
30119
30557
|
const create = isCreate4(eventId);
|
|
30120
30558
|
const dupParam = searchParams.get("duplicateFrom");
|
|
@@ -30127,6 +30565,11 @@ function EventEditPage({ eventId }) {
|
|
|
30127
30565
|
const [slug, setSlug] = useState("");
|
|
30128
30566
|
const [description, setDescription] = useState("");
|
|
30129
30567
|
const [isActive, setIsActive] = useState(true);
|
|
30568
|
+
const [approvalStatus, setApprovalStatus] = useState("pending");
|
|
30569
|
+
const [rejectionReason, setRejectionReason] = useState("");
|
|
30570
|
+
const [rejectModalOpen, setRejectModalOpen] = useState(false);
|
|
30571
|
+
const [rejectDraft, setRejectDraft] = useState("");
|
|
30572
|
+
const approvalBeforeRejectRef = useRef("pending");
|
|
30130
30573
|
const [comingSoon, setComingSoon] = useState(false);
|
|
30131
30574
|
const [bannerImageUrl, setBannerImageUrl] = useState("");
|
|
30132
30575
|
const [logoUrl, setLogoUrl] = useState("");
|
|
@@ -30212,6 +30655,16 @@ function EventEditPage({ eventId }) {
|
|
|
30212
30655
|
cancelled = true;
|
|
30213
30656
|
};
|
|
30214
30657
|
}, []);
|
|
30658
|
+
useEffect(() => {
|
|
30659
|
+
if (create && vendorPortal && approvalOn) {
|
|
30660
|
+
setApprovalStatus("pending");
|
|
30661
|
+
setIsActive(false);
|
|
30662
|
+
}
|
|
30663
|
+
}, [
|
|
30664
|
+
create,
|
|
30665
|
+
vendorPortal,
|
|
30666
|
+
approvalOn
|
|
30667
|
+
]);
|
|
30215
30668
|
useEffect(() => {
|
|
30216
30669
|
let cancelled = false;
|
|
30217
30670
|
(async () => {
|
|
@@ -30233,6 +30686,8 @@ function EventEditPage({ eventId }) {
|
|
|
30233
30686
|
setSlug(data.slug ?? "");
|
|
30234
30687
|
setDescription(data.description ?? "");
|
|
30235
30688
|
setIsActive(data.isActive ?? true);
|
|
30689
|
+
setApprovalStatus(typeof data.approvalStatus === "string" && data.approvalStatus ? data.approvalStatus : "pending");
|
|
30690
|
+
setRejectionReason(typeof data.rejectionReason === "string" ? data.rejectionReason : "");
|
|
30236
30691
|
setComingSoon(data.comingSoon ?? false);
|
|
30237
30692
|
setBannerImageUrl(data.bannerImageUrl ?? "");
|
|
30238
30693
|
setLogoUrl(data.logoUrl ?? "");
|
|
@@ -30344,11 +30799,11 @@ function EventEditPage({ eventId }) {
|
|
|
30344
30799
|
setErrors(nextErrors);
|
|
30345
30800
|
return null;
|
|
30346
30801
|
}
|
|
30347
|
-
|
|
30802
|
+
const payload = {
|
|
30348
30803
|
name: name.trim(),
|
|
30349
30804
|
slug: slug.trim(),
|
|
30350
30805
|
description: description.trim() || null,
|
|
30351
|
-
isActive,
|
|
30806
|
+
isActive: create && vendorPortal && approvalOn ? false : isActive,
|
|
30352
30807
|
comingSoon,
|
|
30353
30808
|
bannerImageUrl: bannerImageUrl.trim() || null,
|
|
30354
30809
|
logoUrl: logoUrl.trim() || null,
|
|
@@ -30386,9 +30841,55 @@ function EventEditPage({ eventId }) {
|
|
|
30386
30841
|
sortOrder,
|
|
30387
30842
|
contactFormId
|
|
30388
30843
|
};
|
|
30844
|
+
if (approvalOn) {
|
|
30845
|
+
if (vendorPortal && create) {
|
|
30846
|
+
payload.approvalStatus = "pending";
|
|
30847
|
+
} else if (!vendorPortal) {
|
|
30848
|
+
payload.approvalStatus = approvalStatus;
|
|
30849
|
+
if (approvalStatus === "rejected") {
|
|
30850
|
+
payload.rejectionReason = rejectionReason.trim();
|
|
30851
|
+
}
|
|
30852
|
+
}
|
|
30853
|
+
}
|
|
30854
|
+
return payload;
|
|
30389
30855
|
}, "buildPayload");
|
|
30856
|
+
const openRejectModal = /* @__PURE__ */ __name((fromStatus) => {
|
|
30857
|
+
approvalBeforeRejectRef.current = fromStatus === "rejected" ? "pending" : fromStatus;
|
|
30858
|
+
setRejectDraft(rejectionReason);
|
|
30859
|
+
setApprovalStatus("rejected");
|
|
30860
|
+
setRejectModalOpen(true);
|
|
30861
|
+
}, "openRejectModal");
|
|
30862
|
+
const confirmRejectReason = /* @__PURE__ */ __name(() => {
|
|
30863
|
+
const reason = rejectDraft.trim();
|
|
30864
|
+
if (!reason) return;
|
|
30865
|
+
setRejectionReason(reason);
|
|
30866
|
+
setRejectModalOpen(false);
|
|
30867
|
+
}, "confirmRejectReason");
|
|
30868
|
+
const cancelRejectModal = /* @__PURE__ */ __name(() => {
|
|
30869
|
+
if (!rejectionReason.trim()) {
|
|
30870
|
+
setApprovalStatus(approvalBeforeRejectRef.current || "pending");
|
|
30871
|
+
}
|
|
30872
|
+
setRejectDraft(rejectionReason);
|
|
30873
|
+
setRejectModalOpen(false);
|
|
30874
|
+
}, "cancelRejectModal");
|
|
30875
|
+
const handleApprovalSelect = /* @__PURE__ */ __name((value) => {
|
|
30876
|
+
if (value === "rejected") {
|
|
30877
|
+
openRejectModal(approvalStatus);
|
|
30878
|
+
return;
|
|
30879
|
+
}
|
|
30880
|
+
setApprovalStatus(value);
|
|
30881
|
+
if (value !== "rejected") setRejectionReason("");
|
|
30882
|
+
}, "handleApprovalSelect");
|
|
30390
30883
|
const handleSave = /* @__PURE__ */ __name(async () => {
|
|
30391
30884
|
setErrors([]);
|
|
30885
|
+
if (approvalOn && !vendorPortal && approvalStatus === "rejected" && !rejectionReason.trim()) {
|
|
30886
|
+
setRejectDraft("");
|
|
30887
|
+
setRejectModalOpen(true);
|
|
30888
|
+
setErrors([
|
|
30889
|
+
"Rejection reason is required"
|
|
30890
|
+
]);
|
|
30891
|
+
return;
|
|
30892
|
+
}
|
|
30392
30893
|
const payload = buildPayload();
|
|
30393
30894
|
if (!payload) return;
|
|
30394
30895
|
setSaving(true);
|
|
@@ -30408,6 +30909,9 @@ function EventEditPage({ eventId }) {
|
|
|
30408
30909
|
return;
|
|
30409
30910
|
}
|
|
30410
30911
|
const saved = await res.json();
|
|
30912
|
+
if (typeof saved.isActive === "boolean") setIsActive(saved.isActive);
|
|
30913
|
+
if (typeof saved.approvalStatus === "string") setApprovalStatus(saved.approvalStatus);
|
|
30914
|
+
if (approvalStatus === "approved") setRejectionReason("");
|
|
30411
30915
|
const savedId = create ? saved.id : Number(eventId);
|
|
30412
30916
|
if (savedId != null && !Number.isNaN(savedId)) {
|
|
30413
30917
|
router.push(`/admin/events/${savedId}/edit?from=${encodeURIComponent(listReturnUrl)}`);
|
|
@@ -30470,19 +30974,66 @@ function EventEditPage({ eventId }) {
|
|
|
30470
30974
|
title: create ? "Add event" : "Edit event",
|
|
30471
30975
|
subtitle: create ? "Create a new event" : "Update event details and tickets",
|
|
30472
30976
|
closeHref: listReturnUrl,
|
|
30977
|
+
headerExtra: approvalOn && !vendorPortal ? /* @__PURE__ */ React.createElement("div", {
|
|
30978
|
+
className: "flex items-center gap-2"
|
|
30979
|
+
}, /* @__PURE__ */ React.createElement("select", {
|
|
30980
|
+
value: approvalStatus,
|
|
30981
|
+
onChange: /* @__PURE__ */ __name((e) => handleApprovalSelect(e.target.value), "onChange"),
|
|
30982
|
+
className: "h-8 rounded-md border border-gray-600 bg-gray-900 text-white text-xs px-2 max-w-[10rem]",
|
|
30983
|
+
"aria-label": "Approval status"
|
|
30984
|
+
}, /* @__PURE__ */ React.createElement("option", {
|
|
30985
|
+
value: "pending"
|
|
30986
|
+
}, "Pending"), /* @__PURE__ */ React.createElement("option", {
|
|
30987
|
+
value: "approved"
|
|
30988
|
+
}, "Approve"), /* @__PURE__ */ React.createElement("option", {
|
|
30989
|
+
value: "rejected"
|
|
30990
|
+
}, "Reject")), approvalStatus === "rejected" ? /* @__PURE__ */ React.createElement("button", {
|
|
30991
|
+
type: "button",
|
|
30992
|
+
onClick: /* @__PURE__ */ __name(() => openRejectModal("rejected"), "onClick"),
|
|
30993
|
+
className: "text-xs text-amber-300 hover:text-amber-200 underline underline-offset-2 max-w-[9rem] truncate",
|
|
30994
|
+
title: rejectionReason || "Add rejection reason"
|
|
30995
|
+
}, rejectionReason.trim() ? "Edit reason" : "Add reason") : null) : approvalOn && vendorPortal ? /* @__PURE__ */ React.createElement("span", {
|
|
30996
|
+
className: "text-xs text-gray-300 capitalize hidden sm:inline"
|
|
30997
|
+
}, approvalStatus.replace(/_/g, " "), approvalStatus === "rejected" && rejectionReason ? ` \u2014 ${rejectionReason}` : "") : null,
|
|
30473
30998
|
menuItems: [
|
|
30474
30999
|
{
|
|
30475
31000
|
label: saving ? "Saving..." : "Save",
|
|
30476
31001
|
icon: Save,
|
|
30477
31002
|
onClick: handleSave
|
|
30478
31003
|
},
|
|
30479
|
-
|
|
30480
|
-
|
|
30481
|
-
|
|
30482
|
-
|
|
30483
|
-
|
|
31004
|
+
...approvalOn && vendorPortal && (approvalStatus === "pending" || approvalStatus === "rejected") ? [] : [
|
|
31005
|
+
{
|
|
31006
|
+
label: isActive ? "Deactivate" : "Activate",
|
|
31007
|
+
icon: Power,
|
|
31008
|
+
onClick: /* @__PURE__ */ __name(() => setIsActive(!isActive), "onClick")
|
|
31009
|
+
}
|
|
31010
|
+
]
|
|
30484
31011
|
]
|
|
30485
|
-
}),
|
|
31012
|
+
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
31013
|
+
open: rejectModalOpen,
|
|
31014
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
31015
|
+
if (!open) cancelRejectModal();
|
|
31016
|
+
}, "onOpenChange")
|
|
31017
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
31018
|
+
className: "max-w-md"
|
|
31019
|
+
}, /* @__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", {
|
|
31020
|
+
value: rejectDraft,
|
|
31021
|
+
onChange: /* @__PURE__ */ __name((e) => setRejectDraft(e.target.value), "onChange"),
|
|
31022
|
+
className: "w-full min-h-[100px] rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
31023
|
+
placeholder: "Explain what needs to change\u2026",
|
|
31024
|
+
autoFocus: true
|
|
31025
|
+
}), /* @__PURE__ */ React.createElement(DialogFooter, {
|
|
31026
|
+
className: "gap-2 sm:gap-0"
|
|
31027
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
31028
|
+
type: "button",
|
|
31029
|
+
variant: "outline",
|
|
31030
|
+
onClick: cancelRejectModal
|
|
31031
|
+
}, "Cancel"), /* @__PURE__ */ React.createElement(Button, {
|
|
31032
|
+
type: "button",
|
|
31033
|
+
variant: "destructive",
|
|
31034
|
+
disabled: !rejectDraft.trim(),
|
|
31035
|
+
onClick: confirmRejectReason
|
|
31036
|
+
}, "Confirm reject")))), !create && logoUrl.trim() ? /* @__PURE__ */ React.createElement("div", {
|
|
30486
31037
|
className: "flex items-center gap-3 px-4 sm:px-6 py-3 border-b border-gray-100 bg-gray-50/80"
|
|
30487
31038
|
}, /* @__PURE__ */ React.createElement("img", {
|
|
30488
31039
|
src: logoUrl.trim(),
|
|
@@ -30841,11 +31392,15 @@ var init_EventEditPage = __esm({
|
|
|
30841
31392
|
"src/admin/pages/EventEditPage.tsx"() {
|
|
30842
31393
|
"use client";
|
|
30843
31394
|
init_admin_list_return_url();
|
|
31395
|
+
init_vendor_scope();
|
|
30844
31396
|
init_DetailPageLayout();
|
|
30845
31397
|
init_DetailPageHeader();
|
|
30846
31398
|
init_ImageOrUrlField();
|
|
30847
31399
|
init_JoditRichText();
|
|
30848
31400
|
init_EventProductsSection();
|
|
31401
|
+
init_admin_config_context();
|
|
31402
|
+
init_dialog();
|
|
31403
|
+
init_button();
|
|
30849
31404
|
init_EventManagementFields();
|
|
30850
31405
|
init_event_named_lists();
|
|
30851
31406
|
init_social_media_links();
|
|
@@ -30921,6 +31476,8 @@ async function validateComboProductInventory(productIds) {
|
|
|
30921
31476
|
function ComboEditPage({ comboId }) {
|
|
30922
31477
|
const router = useRouter();
|
|
30923
31478
|
const searchParams = useSearchParams();
|
|
31479
|
+
const { eventsEnabled } = useContext(AdminConfigContext);
|
|
31480
|
+
const eventsOn = eventsEnabled !== false;
|
|
30924
31481
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/combos";
|
|
30925
31482
|
const create = isCreate5(comboId);
|
|
30926
31483
|
const duplicateFrom = searchParams.get("duplicateFrom")?.trim();
|
|
@@ -30942,8 +31499,9 @@ function ComboEditPage({ comboId }) {
|
|
|
30942
31499
|
const [productOptions, setProductOptions] = useState([]);
|
|
30943
31500
|
const [fixedItems, setFixedItems] = useState([]);
|
|
30944
31501
|
const [addonItems, setAddonItems] = useState([]);
|
|
31502
|
+
const canPickProducts = !eventsOn || Boolean(eventId);
|
|
30945
31503
|
useEffect(() => {
|
|
30946
|
-
if (!eventId) {
|
|
31504
|
+
if (!eventsOn || !eventId) {
|
|
30947
31505
|
setDefaultCurrency("INR");
|
|
30948
31506
|
return;
|
|
30949
31507
|
}
|
|
@@ -30955,9 +31513,14 @@ function ComboEditPage({ comboId }) {
|
|
|
30955
31513
|
cancelled = true;
|
|
30956
31514
|
};
|
|
30957
31515
|
}, [
|
|
30958
|
-
eventId
|
|
31516
|
+
eventId,
|
|
31517
|
+
eventsOn
|
|
30959
31518
|
]);
|
|
30960
31519
|
useEffect(() => {
|
|
31520
|
+
if (!eventsOn) {
|
|
31521
|
+
setEventOptions([]);
|
|
31522
|
+
return;
|
|
31523
|
+
}
|
|
30961
31524
|
let cancelled = false;
|
|
30962
31525
|
(async () => {
|
|
30963
31526
|
try {
|
|
@@ -30977,45 +31540,63 @@ function ComboEditPage({ comboId }) {
|
|
|
30977
31540
|
return () => {
|
|
30978
31541
|
cancelled = true;
|
|
30979
31542
|
};
|
|
30980
|
-
}, [
|
|
31543
|
+
}, [
|
|
31544
|
+
eventsOn
|
|
31545
|
+
]);
|
|
30981
31546
|
useEffect(() => {
|
|
30982
|
-
if (!eventId) {
|
|
30983
|
-
setProductOptions([]);
|
|
30984
|
-
return;
|
|
30985
|
-
}
|
|
30986
31547
|
let cancelled = false;
|
|
30987
31548
|
(async () => {
|
|
30988
31549
|
try {
|
|
30989
|
-
|
|
30990
|
-
|
|
31550
|
+
if (eventsOn) {
|
|
31551
|
+
if (!eventId) {
|
|
31552
|
+
setProductOptions([]);
|
|
31553
|
+
return;
|
|
31554
|
+
}
|
|
31555
|
+
const res2 = await fetch(`/api/event_products?eventId=${eventId}&limit=500`);
|
|
31556
|
+
if (!res2.ok) return;
|
|
30991
31557
|
const data = await res2.json();
|
|
30992
|
-
if (
|
|
30993
|
-
|
|
30994
|
-
|
|
30995
|
-
|
|
30996
|
-
|
|
30997
|
-
const prodData = await prodRes.json();
|
|
30998
|
-
if (!cancelled && Array.isArray(prodData.data)) {
|
|
30999
|
-
const fetched = prodData.data.map((p) => ({
|
|
31000
|
-
value: String(p.id),
|
|
31001
|
-
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31002
|
-
}));
|
|
31003
|
-
setProductOptions((prev) => {
|
|
31004
|
-
const merged = [
|
|
31005
|
-
...fetched
|
|
31006
|
-
];
|
|
31007
|
-
for (const p of prev) {
|
|
31008
|
-
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31009
|
-
}
|
|
31010
|
-
return merged;
|
|
31011
|
-
});
|
|
31012
|
-
}
|
|
31013
|
-
}
|
|
31014
|
-
} else {
|
|
31015
|
-
setProductOptions([]);
|
|
31016
|
-
}
|
|
31558
|
+
if (cancelled || !Array.isArray(data.data)) return;
|
|
31559
|
+
const productIds = data.data.map((ep) => ep.productId).filter(Boolean);
|
|
31560
|
+
if (productIds.length === 0) {
|
|
31561
|
+
setProductOptions([]);
|
|
31562
|
+
return;
|
|
31017
31563
|
}
|
|
31564
|
+
const prodRes2 = await fetch(`/api/products?ids=${productIds.join(",")}&limit=500`);
|
|
31565
|
+
if (!prodRes2.ok) return;
|
|
31566
|
+
const prodData2 = await prodRes2.json();
|
|
31567
|
+
if (cancelled || !Array.isArray(prodData2.data)) return;
|
|
31568
|
+
const fetched2 = prodData2.data.map((p) => ({
|
|
31569
|
+
value: String(p.id),
|
|
31570
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31571
|
+
}));
|
|
31572
|
+
setProductOptions((prev) => {
|
|
31573
|
+
const merged = [
|
|
31574
|
+
...fetched2
|
|
31575
|
+
];
|
|
31576
|
+
for (const p of prev) {
|
|
31577
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31578
|
+
}
|
|
31579
|
+
return merged;
|
|
31580
|
+
});
|
|
31581
|
+
return;
|
|
31018
31582
|
}
|
|
31583
|
+
const prodRes = await fetch("/api/products?limit=500&sortField=name&sortOrder=asc");
|
|
31584
|
+
if (!prodRes.ok) return;
|
|
31585
|
+
const prodData = await prodRes.json();
|
|
31586
|
+
if (cancelled || !Array.isArray(prodData.data)) return;
|
|
31587
|
+
const fetched = prodData.data.map((p) => ({
|
|
31588
|
+
value: String(p.id),
|
|
31589
|
+
label: p.name ?? p.title ?? `Product #${p.id}`
|
|
31590
|
+
}));
|
|
31591
|
+
setProductOptions((prev) => {
|
|
31592
|
+
const merged = [
|
|
31593
|
+
...fetched
|
|
31594
|
+
];
|
|
31595
|
+
for (const p of prev) {
|
|
31596
|
+
if (!merged.some((m) => m.value === p.value)) merged.push(p);
|
|
31597
|
+
}
|
|
31598
|
+
return merged;
|
|
31599
|
+
});
|
|
31019
31600
|
} catch {
|
|
31020
31601
|
}
|
|
31021
31602
|
})();
|
|
@@ -31023,7 +31604,8 @@ function ComboEditPage({ comboId }) {
|
|
|
31023
31604
|
cancelled = true;
|
|
31024
31605
|
};
|
|
31025
31606
|
}, [
|
|
31026
|
-
eventId
|
|
31607
|
+
eventId,
|
|
31608
|
+
eventsOn
|
|
31027
31609
|
]);
|
|
31028
31610
|
useEffect(() => {
|
|
31029
31611
|
let cancelled = false;
|
|
@@ -31134,7 +31716,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31134
31716
|
]);
|
|
31135
31717
|
return;
|
|
31136
31718
|
}
|
|
31137
|
-
if (!trimmedEventId || !/^\d+$/.test(trimmedEventId)) {
|
|
31719
|
+
if (eventsOn && (!trimmedEventId || !/^\d+$/.test(trimmedEventId))) {
|
|
31138
31720
|
setErrors([
|
|
31139
31721
|
"Event is required"
|
|
31140
31722
|
]);
|
|
@@ -31193,10 +31775,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31193
31775
|
}
|
|
31194
31776
|
setSaving(true);
|
|
31195
31777
|
try {
|
|
31778
|
+
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
31196
31779
|
const payload = {
|
|
31197
31780
|
name: trimmedName,
|
|
31198
31781
|
desc: desc || null,
|
|
31199
|
-
eventId:
|
|
31782
|
+
eventId: resolvedEventId,
|
|
31200
31783
|
price,
|
|
31201
31784
|
currencyPrices: null,
|
|
31202
31785
|
minSelectableItems,
|
|
@@ -31290,7 +31873,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31290
31873
|
value: desc,
|
|
31291
31874
|
onChange: /* @__PURE__ */ __name((e) => setDesc(e.target.value), "onChange"),
|
|
31292
31875
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm min-h-[100px]"
|
|
31293
|
-
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31876
|
+
})), eventsOn ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31294
31877
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
31295
31878
|
}, "Event *"), /* @__PURE__ */ React.createElement("select", {
|
|
31296
31879
|
value: eventId,
|
|
@@ -31305,7 +31888,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31305
31888
|
}, "Select event"), eventOptions.map((o) => /* @__PURE__ */ React.createElement("option", {
|
|
31306
31889
|
key: o.value,
|
|
31307
31890
|
value: o.value
|
|
31308
|
-
}, o.label)))))),
|
|
31891
|
+
}, o.label)))) : null)), canPickProducts && /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
|
|
31309
31892
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
31310
31893
|
}, "Combo items"), /* @__PURE__ */ React.createElement("div", {
|
|
31311
31894
|
className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-5"
|
|
@@ -31373,11 +31956,11 @@ function ComboEditPage({ comboId }) {
|
|
|
31373
31956
|
step: "0.01",
|
|
31374
31957
|
value: priceStr,
|
|
31375
31958
|
onChange: /* @__PURE__ */ __name((e) => setPriceStr(e.target.value), "onChange"),
|
|
31376
|
-
disabled: !
|
|
31959
|
+
disabled: !canPickProducts,
|
|
31377
31960
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm disabled:bg-gray-100"
|
|
31378
31961
|
}), /* @__PURE__ */ React.createElement("p", {
|
|
31379
31962
|
className: "text-xs text-gray-400 mt-1"
|
|
31380
|
-
}, eventId ? "Other currencies use the event\u2019s supported currencies and exchange rates." : "Select an event to set the combo price.")), /* @__PURE__ */ React.createElement("div", {
|
|
31963
|
+
}, 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", {
|
|
31381
31964
|
className: "grid grid-cols-2 gap-4"
|
|
31382
31965
|
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
31383
31966
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
@@ -31443,6 +32026,7 @@ var init_ComboEditPage = __esm({
|
|
|
31443
32026
|
init_DetailPageLayout();
|
|
31444
32027
|
init_DetailPageHeader();
|
|
31445
32028
|
init_inventory_validation();
|
|
32029
|
+
init_admin_config_context();
|
|
31446
32030
|
isCreate5 = /* @__PURE__ */ __name((id) => id === "create", "isCreate");
|
|
31447
32031
|
__name(formatDateTimeLocal, "formatDateTimeLocal");
|
|
31448
32032
|
__name(fetchEventDefaultCurrency, "fetchEventDefaultCurrency");
|
|
@@ -32277,9 +32861,9 @@ var VendorCategoryWorkspacePage_exports = {};
|
|
|
32277
32861
|
__export(VendorCategoryWorkspacePage_exports, {
|
|
32278
32862
|
default: () => VendorCategoryWorkspacePage
|
|
32279
32863
|
});
|
|
32280
|
-
function buildAdminProductColumns(base,
|
|
32864
|
+
function buildAdminProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32281
32865
|
const extraColumns = [
|
|
32282
|
-
...
|
|
32866
|
+
...showVendorColumn ? [
|
|
32283
32867
|
VENDOR_EXTRA_COLUMN
|
|
32284
32868
|
] : [],
|
|
32285
32869
|
...eventsEnabled ? [
|
|
@@ -32298,9 +32882,9 @@ function buildAdminProductColumns(base, multiVendorEnabled, eventsEnabled) {
|
|
|
32298
32882
|
...cols.slice(nameIdx + 1)
|
|
32299
32883
|
];
|
|
32300
32884
|
}
|
|
32301
|
-
function buildAllProductColumns(base,
|
|
32885
|
+
function buildAllProductColumns(base, showVendorColumn, eventsEnabled) {
|
|
32302
32886
|
const extraColumns = [
|
|
32303
|
-
...
|
|
32887
|
+
...showVendorColumn ? [
|
|
32304
32888
|
VENDOR_EXTRA_COLUMN
|
|
32305
32889
|
] : [],
|
|
32306
32890
|
...eventsEnabled ? [
|
|
@@ -32350,7 +32934,8 @@ function VendorCategoryWorkspacePage() {
|
|
|
32350
32934
|
const workspaceFrom = activeCategoryId ? `/admin/products?categoryId=${activeCategoryId}` : "/admin/products";
|
|
32351
32935
|
const itemLabel = activeCategory ? categorySingularName(activeCategory.name) : "Product";
|
|
32352
32936
|
const productColumns2 = useMemo(() => {
|
|
32353
|
-
const
|
|
32937
|
+
const showVendorColumn = multiVendorEnabled !== false && !vendorPortal;
|
|
32938
|
+
const base = showAllProducts ? buildAllProductColumns(STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false) : buildAdminProductColumns(STORE_CRUD_CONFIGS.products.columns, showVendorColumn, eventsEnabled !== false);
|
|
32354
32939
|
return withCollectionRelationApi(base, activeCategoryId, vendorPortal);
|
|
32355
32940
|
}, [
|
|
32356
32941
|
vendorPortal,
|
|
@@ -33676,7 +34261,7 @@ function CustomerPicker({ value, label, onChange }) {
|
|
|
33676
34261
|
className: "text-xs text-gray-400 shrink-0 truncate"
|
|
33677
34262
|
}, c.email ?? ""))))));
|
|
33678
34263
|
}
|
|
33679
|
-
function ConditionCard({ condition, onChange, onRemove }) {
|
|
34264
|
+
function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
33680
34265
|
const iconMap = {
|
|
33681
34266
|
minAmount: /* @__PURE__ */ React.createElement(DollarSign, {
|
|
33682
34267
|
className: "h-3.5 w-3.5 text-blue-500"
|
|
@@ -33732,7 +34317,7 @@ function ConditionCard({ condition, onChange, onRemove }) {
|
|
|
33732
34317
|
value: "minQuantity"
|
|
33733
34318
|
}, "Minimum quantity"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33734
34319
|
value: "productMinQuantity"
|
|
33735
|
-
}, "Product"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34320
|
+
}, "Product"), (eventsOn || condition.kind === "events") && /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33736
34321
|
value: "events"
|
|
33737
34322
|
}, "Events"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
33738
34323
|
value: "nthOrder"
|
|
@@ -33966,11 +34551,13 @@ function RewardCard({ reward, onChange, onRemove, discountType, discountValue, e
|
|
|
33966
34551
|
})));
|
|
33967
34552
|
}
|
|
33968
34553
|
function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE", discountValue = "" }) {
|
|
34554
|
+
const { eventsEnabled } = useContext(AdminConfigContext);
|
|
34555
|
+
const eventsOn = eventsEnabled !== false;
|
|
33969
34556
|
const parsed = ruleTreeToFriendly(rules);
|
|
33970
34557
|
const [groups, setGroups] = useState(parsed.groups);
|
|
33971
34558
|
const [groupOperator, setGroupOperator] = useState(parsed.groupOperator);
|
|
33972
34559
|
const [rewards, setRewards] = useState(parsed.rewards);
|
|
33973
|
-
const rewardEventId = groups.flatMap((g) => g.conditions).find((c) => c.kind === "events" && c.eventId != null)?.eventId ?? null;
|
|
34560
|
+
const rewardEventId = eventsOn ? groups.flatMap((g) => g.conditions).find((c) => c.kind === "events" && c.eventId != null)?.eventId ?? null : null;
|
|
33974
34561
|
useEffect(() => {
|
|
33975
34562
|
const missingNameIds = [];
|
|
33976
34563
|
for (const g of groups) {
|
|
@@ -34207,7 +34794,8 @@ function DiscountConditionsBuilder({ rules, onChange, discountType = "PERCENTAGE
|
|
|
34207
34794
|
})), /* @__PURE__ */ React.createElement(ConditionCard, {
|
|
34208
34795
|
condition: c,
|
|
34209
34796
|
onChange: /* @__PURE__ */ __name((updated) => updateCondition(group.id, c.id, updated), "onChange"),
|
|
34210
|
-
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove")
|
|
34797
|
+
onRemove: /* @__PURE__ */ __name(() => removeCondition(group.id, c.id), "onRemove"),
|
|
34798
|
+
eventsOn
|
|
34211
34799
|
})))), /* @__PURE__ */ React.createElement(Button, {
|
|
34212
34800
|
type: "button",
|
|
34213
34801
|
variant: "outline",
|
|
@@ -34270,6 +34858,7 @@ var init_DiscountsConditionsBuilder = __esm({
|
|
|
34270
34858
|
"use client";
|
|
34271
34859
|
init_button();
|
|
34272
34860
|
init_select();
|
|
34861
|
+
init_admin_config_context();
|
|
34273
34862
|
__name(uid, "uid");
|
|
34274
34863
|
__name(conditionToRule, "conditionToRule");
|
|
34275
34864
|
__name(rewardToRule, "rewardToRule");
|
|
@@ -36451,7 +37040,7 @@ function AdminPageResolver({ slug }) {
|
|
|
36451
37040
|
const searchParams = useSearchParams();
|
|
36452
37041
|
const { data: session } = useSession();
|
|
36453
37042
|
const vendorPortal = isVendorPortalUser(session?.user);
|
|
36454
|
-
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled } = useContext(AdminConfigContext);
|
|
37043
|
+
const { customCrudConfigs, storeEnabled, multiVendorEnabled, eventsEnabled, vendorCanCreateCategories, vendorCanCreateCollections, vendorCanCreateBrands, requireProductApproval, requireEventApproval } = useContext(AdminConfigContext);
|
|
36455
37044
|
const key = slug?.[0] || "dashboard";
|
|
36456
37045
|
const [vendorOptions, setVendorOptions] = useState([]);
|
|
36457
37046
|
useEffect(() => {
|
|
@@ -36485,6 +37074,11 @@ function AdminPageResolver({ slug }) {
|
|
|
36485
37074
|
columns = columns.filter((column) => column.field !== "eventId" && column.field !== "eventName");
|
|
36486
37075
|
filters = filters.filter((filter) => filter.param !== "eventId");
|
|
36487
37076
|
}
|
|
37077
|
+
const showApprovalColumn = key === "products" && requireProductApproval === true || key === "events" && requireEventApproval === true;
|
|
37078
|
+
if (!showApprovalColumn) {
|
|
37079
|
+
columns = columns.filter((column) => column.field !== "approvalStatus");
|
|
37080
|
+
filters = filters.filter((filter) => filter.param !== "approvalStatus");
|
|
37081
|
+
}
|
|
36488
37082
|
if (multiVendorEnabled !== false && !vendorPortal && STORE_VENDOR_RESOURCES.has(key) && key !== "event_products" && !columns.some((c) => c.field === "vendorId")) {
|
|
36489
37083
|
columns = [
|
|
36490
37084
|
VENDOR_COLUMN2,
|
|
@@ -36522,7 +37116,9 @@ function AdminPageResolver({ slug }) {
|
|
|
36522
37116
|
vendorOptions,
|
|
36523
37117
|
vendorPortal,
|
|
36524
37118
|
multiVendorEnabled,
|
|
36525
|
-
eventsEnabled
|
|
37119
|
+
eventsEnabled,
|
|
37120
|
+
requireProductApproval,
|
|
37121
|
+
requireEventApproval
|
|
36526
37122
|
]);
|
|
36527
37123
|
const isContactsWithStore = key === "contacts" && storeEnabled;
|
|
36528
37124
|
const extraListParams = useMemo(() => isContactsWithStore ? {
|
|
@@ -36636,7 +37232,27 @@ function AdminPageResolver({ slug }) {
|
|
|
36636
37232
|
className: "ml-2"
|
|
36637
37233
|
}, "Redirecting\u2026"));
|
|
36638
37234
|
}
|
|
36639
|
-
if (vendorPortal && key === "product_categories") {
|
|
37235
|
+
if (vendorPortal && key === "product_categories" && vendorCanCreateCategories !== true) {
|
|
37236
|
+
router.replace("/admin/products");
|
|
37237
|
+
return /* @__PURE__ */ React26__default.createElement("div", {
|
|
37238
|
+
className: "flex justify-center py-8"
|
|
37239
|
+
}, /* @__PURE__ */ React26__default.createElement("div", {
|
|
37240
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37241
|
+
}), /* @__PURE__ */ React26__default.createElement("span", {
|
|
37242
|
+
className: "ml-2"
|
|
37243
|
+
}, "Redirecting\u2026"));
|
|
37244
|
+
}
|
|
37245
|
+
if (vendorPortal && key === "collections" && vendorCanCreateCollections !== true) {
|
|
37246
|
+
router.replace("/admin/products");
|
|
37247
|
+
return /* @__PURE__ */ React26__default.createElement("div", {
|
|
37248
|
+
className: "flex justify-center py-8"
|
|
37249
|
+
}, /* @__PURE__ */ React26__default.createElement("div", {
|
|
37250
|
+
className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600"
|
|
37251
|
+
}), /* @__PURE__ */ React26__default.createElement("span", {
|
|
37252
|
+
className: "ml-2"
|
|
37253
|
+
}, "Redirecting\u2026"));
|
|
37254
|
+
}
|
|
37255
|
+
if (vendorPortal && key === "brands" && vendorCanCreateBrands !== true) {
|
|
36640
37256
|
router.replace("/admin/products");
|
|
36641
37257
|
return /* @__PURE__ */ React26__default.createElement("div", {
|
|
36642
37258
|
className: "flex justify-center py-8"
|