@infuro/cms-core 1.0.41 → 1.0.43

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