@infuro/cms-core 1.0.45 → 1.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist/admin.cjs +873 -214
- package/dist/admin.d.cts +7 -1
- package/dist/admin.d.ts +7 -1
- package/dist/admin.js +874 -215
- package/dist/api.cjs +40 -37
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +8 -5
- package/dist/auth.cjs +23 -23
- package/dist/auth.d.cts +1 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +2 -2
- package/dist/chunk-2KUCQVAQ.js +936 -0
- package/dist/chunk-3K5AIEIZ.js +256 -0
- package/dist/{chunk-I6NH72MO.js → chunk-57O3HZPG.js} +9 -1
- package/dist/chunk-7PMHZRF3.cjs +872 -0
- package/dist/chunk-BXYZDMTZ.cjs +953 -0
- package/dist/chunk-CQHXW4TR.js +107 -0
- package/dist/{chunk-NNQBWDCI.js → chunk-CRYKVJTO.js} +55 -959
- package/dist/chunk-DBPSJYLZ.js +47 -0
- package/dist/{chunk-TAHX46EI.cjs → chunk-FBKDMRQL.cjs} +9 -1
- package/dist/chunk-GUSHM5HN.js +862 -0
- package/dist/chunk-HY3AXLOI.cjs +265 -0
- package/dist/chunk-L3525VXI.js +243 -0
- package/dist/{chunk-ZMRQ72F6.cjs → chunk-NBY4NVTY.cjs} +3 -3
- package/dist/chunk-OY2YTBMP.cjs +50 -0
- package/dist/{chunk-BLR6H5GL.js → chunk-SF2XKMCI.js} +3 -3
- package/dist/{chunk-22FFHLTO.cjs → chunk-TCSGFAWB.cjs} +56 -970
- package/dist/{chunk-F5XFHYTG.cjs → chunk-UDVLFVEC.cjs} +661 -382
- package/dist/{chunk-MVXLRANV.js → chunk-UKC3HYXI.js} +591 -323
- package/dist/chunk-V25RDUOU.cjs +248 -0
- package/dist/chunk-XMRMZ6NQ.cjs +109 -0
- package/dist/cli.cjs +9 -4
- package/dist/cli.js +9 -4
- package/dist/{email-queue-TPZWLTIV.cjs → email-queue-6NJY6HNM.cjs} +7 -5
- package/dist/email-queue-OI2HSCGE.js +4 -0
- package/dist/erp-order-invoice-3GXDE443.js +3 -0
- package/dist/erp-order-invoice-EYYNR526.cjs +24 -0
- package/dist/index.cjs +391 -364
- package/dist/index.d.cts +267 -14
- package/dist/index.d.ts +267 -14
- package/dist/index.js +12 -110
- package/dist/migrations/1781810000000-OrderItemsVariantId.ts +44 -0
- package/dist/migrations/1781900000000-UserInviteStatusToken.ts +46 -0
- package/dist/migrations/1782000000000-ComboSlug.ts +58 -0
- package/dist/migrations/1782100000000-DiscountDeviceType.ts +20 -0
- package/dist/order-inventory-2MYRKEPC.cjs +39 -0
- package/dist/order-inventory-76V4XIGW.js +2 -0
- package/dist/order-notification-dispatcher-SBQAMM5V.cjs +21 -0
- package/dist/{order-notification-dispatcher-XWIJYDGA.js → order-notification-dispatcher-ZJZB2HUN.js} +5 -2
- package/dist/retire-soft-deleted-unique-5VXBA5XX.cjs +15 -0
- package/dist/retire-soft-deleted-unique-NXQIH4BC.js +2 -0
- package/dist/send-order-placed-emails-B5ZVJT7T.cjs +15 -0
- package/dist/send-order-placed-emails-WPI37KRZ.js +6 -0
- package/package.json +5 -4
- package/src/admin/admin.css +27 -24
- package/dist/chunk-5ELSW2UP.js +0 -150
- package/dist/chunk-PNOKNSG2.cjs +0 -222
- package/dist/chunk-WEMDMVHQ.js +0 -216
- package/dist/chunk-XW5ATPRW.cjs +0 -155
- package/dist/email-queue-CGZBVVAS.js +0 -2
- package/dist/erp-order-invoice-3YISSOWW.js +0 -3
- package/dist/erp-order-invoice-ZR5F2KTA.cjs +0 -12
- package/dist/order-notification-dispatcher-RU7BTGGX.cjs +0 -18
package/dist/admin.cjs
CHANGED
|
@@ -494,7 +494,7 @@ var init_admin_config_context = __esm({
|
|
|
494
494
|
var CMS_VERSION;
|
|
495
495
|
var init_cms_version = __esm({
|
|
496
496
|
"src/lib/cms-version.ts"() {
|
|
497
|
-
CMS_VERSION = "1.0.
|
|
497
|
+
CMS_VERSION = "1.0.46" ;
|
|
498
498
|
}
|
|
499
499
|
});
|
|
500
500
|
function useCatalogCategories(enabled = true) {
|
|
@@ -1096,8 +1096,10 @@ var init_admin_theme = __esm({
|
|
|
1096
1096
|
"src/admin/admin-theme.ts"() {
|
|
1097
1097
|
ADMIN_THEME_CSS = `
|
|
1098
1098
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
|
|
1099
|
+
html{color-scheme:light}
|
|
1099
1100
|
:root{--background:0 0% 100%;--foreground:0 0% 9%;--card:0 0% 100%;--card-foreground:0 0% 9%;--popover:0 0% 100%;--popover-foreground:0 0% 9%;--primary:0 0% 9%;--primary-foreground:0 0% 98%;--secondary:0 0% 96.1%;--secondary-foreground:0 0% 9%;--muted:0 0% 96.1%;--muted-foreground:0 0% 45.1%;--accent:0 0% 96.1%;--accent-foreground:0 0% 9%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:0 0% 89.8%;--input:0 0% 89.8%;--ring:0 0% 3.9%;--chart-1:0 0% 40%;--chart-2:0 0% 55%;--chart-3:0 0% 70%;--chart-4:0 0% 45%;--chart-5:0 0% 60%;--radius:0.375rem;--sidebar-background:0 0% 100%;--sidebar-foreground:0 0% 9%;--sidebar-primary:0 0% 9%;--sidebar-primary-foreground:0 0% 98%;--sidebar-accent:0 0% 96.1%;--sidebar-accent-foreground:0 0% 9%;--sidebar-border:0 0% 89.8%;--sidebar-ring:0 0% 9%}
|
|
1100
|
-
|
|
1101
|
+
/* Dark mode disabled: keep light tokens even if a host app adds .dark on <html> */
|
|
1102
|
+
.dark{--background:0 0% 100%;--foreground:0 0% 9%;--card:0 0% 100%;--card-foreground:0 0% 9%;--popover:0 0% 100%;--popover-foreground:0 0% 9%;--primary:0 0% 9%;--primary-foreground:0 0% 98%;--secondary:0 0% 96.1%;--secondary-foreground:0 0% 9%;--muted:0 0% 96.1%;--muted-foreground:0 0% 45.1%;--accent:0 0% 96.1%;--accent-foreground:0 0% 9%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:0 0% 89.8%;--input:0 0% 89.8%;--ring:0 0% 3.9%;--sidebar-background:0 0% 100%;--sidebar-foreground:0 0% 9%;--sidebar-primary:0 0% 9%;--sidebar-primary-foreground:0 0% 98%;--sidebar-accent:0 0% 96.1%;--sidebar-accent-foreground:0 0% 9%;--sidebar-border:0 0% 89.8%;--sidebar-ring:0 0% 9%}
|
|
1101
1103
|
*,*::before,*::after{border-color:hsl(var(--border))}
|
|
1102
1104
|
body{font-family:"Inter",ui-sans-serif,system-ui,sans-serif;font-size:14px}
|
|
1103
1105
|
.admin-app{font-size:13.5px}
|
|
@@ -1212,6 +1214,39 @@ var init_auth_debug = __esm({
|
|
|
1212
1214
|
__name(summarizeSessionUserForLog, "summarizeSessionUserForLog");
|
|
1213
1215
|
}
|
|
1214
1216
|
});
|
|
1217
|
+
function useForceAdminLightMode() {
|
|
1218
|
+
React26.useEffect(() => {
|
|
1219
|
+
const root = document.documentElement;
|
|
1220
|
+
const hadDark = root.classList.contains("dark");
|
|
1221
|
+
const prevColorScheme = root.style.colorScheme;
|
|
1222
|
+
const applyLight = /* @__PURE__ */ __name(() => {
|
|
1223
|
+
root.classList.remove("dark");
|
|
1224
|
+
root.style.colorScheme = "light";
|
|
1225
|
+
}, "applyLight");
|
|
1226
|
+
applyLight();
|
|
1227
|
+
const observer = new MutationObserver(() => {
|
|
1228
|
+
if (root.classList.contains("dark")) applyLight();
|
|
1229
|
+
});
|
|
1230
|
+
observer.observe(root, {
|
|
1231
|
+
attributes: true,
|
|
1232
|
+
attributeFilter: [
|
|
1233
|
+
"class"
|
|
1234
|
+
]
|
|
1235
|
+
});
|
|
1236
|
+
return () => {
|
|
1237
|
+
observer.disconnect();
|
|
1238
|
+
root.style.colorScheme = prevColorScheme;
|
|
1239
|
+
if (hadDark) root.classList.add("dark");
|
|
1240
|
+
else root.classList.remove("dark");
|
|
1241
|
+
};
|
|
1242
|
+
}, []);
|
|
1243
|
+
}
|
|
1244
|
+
var init_force_light_mode = __esm({
|
|
1245
|
+
"src/admin/force-light-mode.ts"() {
|
|
1246
|
+
"use client";
|
|
1247
|
+
__name(useForceAdminLightMode, "useForceAdminLightMode");
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1215
1250
|
function useAdminViewSettings() {
|
|
1216
1251
|
React26.useEffect(() => {
|
|
1217
1252
|
fetch("/api/settings/admin_view").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
@@ -1233,6 +1268,7 @@ function AdminLayoutInner({ children }) {
|
|
|
1233
1268
|
const isMobile = useIsMobile();
|
|
1234
1269
|
const vendorBlockRedirectRef = React26.useRef(false);
|
|
1235
1270
|
useAdminViewSettings();
|
|
1271
|
+
useForceAdminLightMode();
|
|
1236
1272
|
const isPublicPath = PUBLIC_ADMIN_PATHS.includes(pathname);
|
|
1237
1273
|
const isVendorOnlySession = session?.user?.isVendorPortal === true && session?.user?.isRBACAdmin !== true;
|
|
1238
1274
|
React26.useEffect(() => {
|
|
@@ -1521,6 +1557,7 @@ var init_AdminLayout = __esm({
|
|
|
1521
1557
|
init_admin_config_context();
|
|
1522
1558
|
init_builtin_plugin_descriptors();
|
|
1523
1559
|
init_auth_debug();
|
|
1560
|
+
init_force_light_mode();
|
|
1524
1561
|
PUBLIC_ADMIN_PATHS = [
|
|
1525
1562
|
"/admin/signin",
|
|
1526
1563
|
"/admin/forgot-password",
|
|
@@ -1771,6 +1808,225 @@ var init_file_upload = __esm({
|
|
|
1771
1808
|
__name(FileUpload, "FileUpload");
|
|
1772
1809
|
}
|
|
1773
1810
|
});
|
|
1811
|
+
|
|
1812
|
+
// src/lib/media-url.ts
|
|
1813
|
+
function isVideoUploadMime(mime) {
|
|
1814
|
+
const t = String(mime ?? "").trim().toLowerCase();
|
|
1815
|
+
return VIDEO_UPLOAD_MIME_TYPES.includes(t);
|
|
1816
|
+
}
|
|
1817
|
+
function isVideoMediaUrl(url) {
|
|
1818
|
+
if (!url || typeof url !== "string") return false;
|
|
1819
|
+
const trimmed = url.trim();
|
|
1820
|
+
if (!trimmed) return false;
|
|
1821
|
+
try {
|
|
1822
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
1823
|
+
return VIDEO_EXT_RE.test(new URL(trimmed).pathname);
|
|
1824
|
+
}
|
|
1825
|
+
} catch {
|
|
1826
|
+
}
|
|
1827
|
+
const path = trimmed.split("?")[0]?.split("#")[0] ?? trimmed;
|
|
1828
|
+
return VIDEO_EXT_RE.test(path);
|
|
1829
|
+
}
|
|
1830
|
+
var VIDEO_EXT_RE, IMAGE_UPLOAD_MIME_TYPES, VIDEO_UPLOAD_MIME_TYPES, MEDIA_UPLOAD_MIME_TYPES, VIDEO_UPLOAD_MAX_BYTES;
|
|
1831
|
+
var init_media_url = __esm({
|
|
1832
|
+
"src/lib/media-url.ts"() {
|
|
1833
|
+
VIDEO_EXT_RE = /\.(mp4|webm|mov)(?:$|[?#])/i;
|
|
1834
|
+
IMAGE_UPLOAD_MIME_TYPES = [
|
|
1835
|
+
"image/jpeg",
|
|
1836
|
+
"image/png",
|
|
1837
|
+
"image/gif",
|
|
1838
|
+
"image/webp"
|
|
1839
|
+
];
|
|
1840
|
+
VIDEO_UPLOAD_MIME_TYPES = [
|
|
1841
|
+
"video/mp4",
|
|
1842
|
+
"video/webm",
|
|
1843
|
+
"video/quicktime"
|
|
1844
|
+
];
|
|
1845
|
+
MEDIA_UPLOAD_MIME_TYPES = [
|
|
1846
|
+
...IMAGE_UPLOAD_MIME_TYPES,
|
|
1847
|
+
...VIDEO_UPLOAD_MIME_TYPES
|
|
1848
|
+
];
|
|
1849
|
+
VIDEO_UPLOAD_MAX_BYTES = 100 * 1024 * 1024;
|
|
1850
|
+
__name(isVideoUploadMime, "isVideoUploadMime");
|
|
1851
|
+
__name(isVideoMediaUrl, "isVideoMediaUrl");
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1854
|
+
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 }) {
|
|
1855
|
+
const fileInputRef = React26.useRef(null);
|
|
1856
|
+
const [isUploading, setIsUploading] = React26.useState(false);
|
|
1857
|
+
const [error, setError] = React26.useState(null);
|
|
1858
|
+
const [lightboxOpen, setLightboxOpen] = React26.useState(false);
|
|
1859
|
+
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";
|
|
1860
|
+
const closeLightbox = React26.useCallback(() => setLightboxOpen(false), []);
|
|
1861
|
+
React26.useEffect(() => {
|
|
1862
|
+
if (!lightboxOpen) return;
|
|
1863
|
+
const onKeyDown = /* @__PURE__ */ __name((e) => {
|
|
1864
|
+
if (e.key === "Escape") closeLightbox();
|
|
1865
|
+
}, "onKeyDown");
|
|
1866
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1867
|
+
const prevOverflow = document.body.style.overflow;
|
|
1868
|
+
document.body.style.overflow = "hidden";
|
|
1869
|
+
return () => {
|
|
1870
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
1871
|
+
document.body.style.overflow = prevOverflow;
|
|
1872
|
+
};
|
|
1873
|
+
}, [
|
|
1874
|
+
lightboxOpen,
|
|
1875
|
+
closeLightbox
|
|
1876
|
+
]);
|
|
1877
|
+
const handleUpload = React26.useCallback(async (file) => {
|
|
1878
|
+
setError(null);
|
|
1879
|
+
if (!MEDIA_UPLOAD_MIME_TYPES.includes(file.type)) {
|
|
1880
|
+
setError("Unsupported type. Use JPG, PNG, GIF, WEBP, or video MP4 / WebM / MOV");
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
const isVideo2 = isVideoUploadMime(file.type);
|
|
1884
|
+
const limit = isVideo2 ? VIDEO_UPLOAD_MAX_BYTES : maxSizeMb * 1024 * 1024;
|
|
1885
|
+
if (file.size > limit) {
|
|
1886
|
+
setError(isVideo2 ? `Video must be under ${VIDEO_UPLOAD_MAX_BYTES / (1024 * 1024)}MB` : `File must be under ${Math.round(limit / (1024 * 1024))}MB`);
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
setIsUploading(true);
|
|
1890
|
+
try {
|
|
1891
|
+
const formData = new FormData();
|
|
1892
|
+
formData.append("file", file);
|
|
1893
|
+
const response = await fetch("/api/upload", {
|
|
1894
|
+
method: "POST",
|
|
1895
|
+
body: formData
|
|
1896
|
+
});
|
|
1897
|
+
const data = await response.json();
|
|
1898
|
+
if (!response.ok) {
|
|
1899
|
+
throw new Error(data.error || data.details || "Upload failed");
|
|
1900
|
+
}
|
|
1901
|
+
onChange(data.filePath ?? "");
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
1904
|
+
} finally {
|
|
1905
|
+
setIsUploading(false);
|
|
1906
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
1907
|
+
}
|
|
1908
|
+
}, [
|
|
1909
|
+
maxSizeMb,
|
|
1910
|
+
onChange
|
|
1911
|
+
]);
|
|
1912
|
+
const onFileChange = React26.useCallback((e) => {
|
|
1913
|
+
const file = e.target.files?.[0];
|
|
1914
|
+
if (file) void handleUpload(file);
|
|
1915
|
+
}, [
|
|
1916
|
+
handleUpload
|
|
1917
|
+
]);
|
|
1918
|
+
const trimmed = value.trim();
|
|
1919
|
+
const isVideo = isVideoMediaUrl(trimmed);
|
|
1920
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1921
|
+
className: "space-y-2"
|
|
1922
|
+
}, /* @__PURE__ */ React.createElement("label", {
|
|
1923
|
+
className: labelClassName
|
|
1924
|
+
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
1925
|
+
className: "flex items-start gap-3"
|
|
1926
|
+
}, isVideo ? (
|
|
1927
|
+
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
1928
|
+
/* @__PURE__ */ React.createElement("video", {
|
|
1929
|
+
src: trimmed,
|
|
1930
|
+
className: previewCls,
|
|
1931
|
+
muted: true,
|
|
1932
|
+
playsInline: true,
|
|
1933
|
+
preload: "metadata",
|
|
1934
|
+
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
1935
|
+
title: "Click to enlarge"
|
|
1936
|
+
})
|
|
1937
|
+
) : (
|
|
1938
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
1939
|
+
/* @__PURE__ */ React.createElement("img", {
|
|
1940
|
+
src: trimmed,
|
|
1941
|
+
alt: label,
|
|
1942
|
+
className: previewCls,
|
|
1943
|
+
role: "button",
|
|
1944
|
+
tabIndex: 0,
|
|
1945
|
+
title: "Click to enlarge",
|
|
1946
|
+
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
1947
|
+
onKeyDown: /* @__PURE__ */ __name((e) => {
|
|
1948
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1949
|
+
e.preventDefault();
|
|
1950
|
+
setLightboxOpen(true);
|
|
1951
|
+
}
|
|
1952
|
+
}, "onKeyDown"),
|
|
1953
|
+
onError: /* @__PURE__ */ __name((e) => {
|
|
1954
|
+
e.currentTarget.style.display = "none";
|
|
1955
|
+
}, "onError")
|
|
1956
|
+
})
|
|
1957
|
+
), /* @__PURE__ */ React.createElement("button", {
|
|
1958
|
+
type: "button",
|
|
1959
|
+
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
1960
|
+
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"
|
|
1961
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
1962
|
+
className: "h-3 w-3"
|
|
1963
|
+
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
1964
|
+
className: "flex flex-wrap gap-2"
|
|
1965
|
+
}, /* @__PURE__ */ React.createElement("input", {
|
|
1966
|
+
type: "text",
|
|
1967
|
+
inputMode: "url",
|
|
1968
|
+
autoComplete: "off",
|
|
1969
|
+
value,
|
|
1970
|
+
onChange: /* @__PURE__ */ __name((e) => {
|
|
1971
|
+
setError(null);
|
|
1972
|
+
onChange(e.target.value);
|
|
1973
|
+
}, "onChange"),
|
|
1974
|
+
placeholder,
|
|
1975
|
+
className: `${inputClassName} min-w-0 flex-1`
|
|
1976
|
+
}), /* @__PURE__ */ React.createElement("input", {
|
|
1977
|
+
ref: fileInputRef,
|
|
1978
|
+
type: "file",
|
|
1979
|
+
accept: ACCEPT,
|
|
1980
|
+
onChange: onFileChange,
|
|
1981
|
+
className: "hidden",
|
|
1982
|
+
disabled: isUploading
|
|
1983
|
+
}), /* @__PURE__ */ React.createElement("button", {
|
|
1984
|
+
type: "button",
|
|
1985
|
+
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
1986
|
+
disabled: isUploading,
|
|
1987
|
+
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"
|
|
1988
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
|
|
1989
|
+
className: "h-3.5 w-3.5"
|
|
1990
|
+
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
1991
|
+
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
1992
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.AlertCircle, {
|
|
1993
|
+
className: "h-3.5 w-3.5 shrink-0"
|
|
1994
|
+
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
1995
|
+
className: "text-xs text-gray-500"
|
|
1996
|
+
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB) or video (MP4, WebM, MOV up to", " ", VIDEO_UPLOAD_MAX_BYTES / (1024 * 1024), "MB)"), lightboxOpen && trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
1997
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4",
|
|
1998
|
+
role: "dialog",
|
|
1999
|
+
"aria-modal": "true",
|
|
2000
|
+
"aria-label": `${label} preview`,
|
|
2001
|
+
onClick: closeLightbox
|
|
2002
|
+
}, isVideo ? (
|
|
2003
|
+
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
2004
|
+
/* @__PURE__ */ React.createElement("video", {
|
|
2005
|
+
src: trimmed,
|
|
2006
|
+
controls: true,
|
|
2007
|
+
autoPlay: true,
|
|
2008
|
+
className: "max-h-[90vh] max-w-[90vw] rounded-md shadow-lg",
|
|
2009
|
+
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
2010
|
+
})
|
|
2011
|
+
) : (
|
|
2012
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
2013
|
+
/* @__PURE__ */ React.createElement("img", {
|
|
2014
|
+
src: trimmed,
|
|
2015
|
+
alt: label,
|
|
2016
|
+
className: "max-h-[90vh] max-w-[90vw] rounded-md object-contain shadow-lg",
|
|
2017
|
+
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
2018
|
+
})
|
|
2019
|
+
)) : null);
|
|
2020
|
+
}
|
|
2021
|
+
var ACCEPT;
|
|
2022
|
+
var init_ImageOrUrlField = __esm({
|
|
2023
|
+
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
2024
|
+
"use client";
|
|
2025
|
+
init_media_url();
|
|
2026
|
+
ACCEPT = MEDIA_UPLOAD_MIME_TYPES.join(",");
|
|
2027
|
+
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
1774
2030
|
function RelationAutocomplete({ apiEndpoint, value, onChange, labelField = "name", valueField = "id", placeholder = "Select\u2026", disabled = false, queryParams }) {
|
|
1775
2031
|
const [open, setOpen] = React26.useState(false);
|
|
1776
2032
|
const [search, setSearch] = React26.useState("");
|
|
@@ -2359,12 +2615,7 @@ function CreateEditForm({ isOpen, onClose, onSaveSuccess, apiEndpoint, columns,
|
|
|
2359
2615
|
return;
|
|
2360
2616
|
}
|
|
2361
2617
|
const result = await res.json();
|
|
2362
|
-
|
|
2363
|
-
sonner.toast.success(`User invite created! Invite link: ${result.inviteLink}
|
|
2364
|
-
|
|
2365
|
-
Note: ${result.note}`);
|
|
2366
|
-
}
|
|
2367
|
-
onSaveSuccess?.();
|
|
2618
|
+
onSaveSuccess?.(result);
|
|
2368
2619
|
onClose();
|
|
2369
2620
|
} catch (error) {
|
|
2370
2621
|
console.error("Error saving data:", error);
|
|
@@ -2374,6 +2625,7 @@ Note: ${result.note}`);
|
|
|
2374
2625
|
const visibleColumns = columns.filter((col) => {
|
|
2375
2626
|
if (col.hideInForm) return false;
|
|
2376
2627
|
if (!existingData && col.hideInCreate) return false;
|
|
2628
|
+
if (existingData && col.hideInEdit) return false;
|
|
2377
2629
|
return true;
|
|
2378
2630
|
});
|
|
2379
2631
|
const isFieldDisabled = /* @__PURE__ */ __name((path) => {
|
|
@@ -2410,7 +2662,7 @@ Note: ${result.note}`);
|
|
|
2410
2662
|
const selectValue = optionSet && rawStr !== "" && optionSet.has(rawStr) ? rawStr : void 0;
|
|
2411
2663
|
return /* @__PURE__ */ React26__namespace.default.createElement(React26__namespace.default.Fragment, {
|
|
2412
2664
|
key: path
|
|
2413
|
-
}, /* @__PURE__ */ React26__namespace.default.createElement("div", null, /* @__PURE__ */ React26__namespace.default.createElement(Label3, {
|
|
2665
|
+
}, /* @__PURE__ */ React26__namespace.default.createElement("div", null, col.type !== "image" && /* @__PURE__ */ React26__namespace.default.createElement(Label3, {
|
|
2414
2666
|
className: "block text-sm font-medium mb-1"
|
|
2415
2667
|
}, col.displayName, [
|
|
2416
2668
|
"subtotal",
|
|
@@ -2537,6 +2789,16 @@ Note: ${result.note}`);
|
|
|
2537
2789
|
disabled
|
|
2538
2790
|
}) : col.type === "file" ? /* @__PURE__ */ React26__namespace.default.createElement(FileUpload, {
|
|
2539
2791
|
onUploadSuccess: /* @__PURE__ */ __name((url) => handleFileUpload(path, url), "onUploadSuccess")
|
|
2792
|
+
}) : col.type === "image" ? /* @__PURE__ */ React26__namespace.default.createElement(ImageOrUrlField, {
|
|
2793
|
+
label: col.displayName,
|
|
2794
|
+
value: typeof formData[path] === "string" ? formData[path] : "",
|
|
2795
|
+
onChange: /* @__PURE__ */ __name((url) => setFormData((prev) => ({
|
|
2796
|
+
...prev,
|
|
2797
|
+
[path]: url
|
|
2798
|
+
})), "onChange"),
|
|
2799
|
+
placeholder: col.placeholder ?? "Image URL or upload",
|
|
2800
|
+
previewVariant: "logo",
|
|
2801
|
+
labelClassName: "block text-sm font-medium mb-1"
|
|
2540
2802
|
}) : /* @__PURE__ */ React26__namespace.default.createElement(Input, {
|
|
2541
2803
|
type: col.type || "text",
|
|
2542
2804
|
placeholder: [
|
|
@@ -2582,6 +2844,7 @@ var init_CreateEditForm = __esm({
|
|
|
2582
2844
|
init_switch();
|
|
2583
2845
|
init_label();
|
|
2584
2846
|
init_file_upload();
|
|
2847
|
+
init_ImageOrUrlField();
|
|
2585
2848
|
init_RelationAutocomplete();
|
|
2586
2849
|
init_RelationMultiSelect();
|
|
2587
2850
|
init_use_mobile();
|
|
@@ -3933,11 +4196,7 @@ function formatArrayListCell(value, col) {
|
|
|
3933
4196
|
return items.join(", ");
|
|
3934
4197
|
}
|
|
3935
4198
|
function formatCellValue(value, col) {
|
|
3936
|
-
if (col.type === "boolean") return value ?
|
|
3937
|
-
className: "h-4 w-4 text-green-600"
|
|
3938
|
-
}) : /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
3939
|
-
className: "h-4 w-4 text-red-600"
|
|
3940
|
-
});
|
|
4199
|
+
if (col.type === "boolean") return value ? "Yes" : "No";
|
|
3941
4200
|
if (col.type === "date") return formatDate(value);
|
|
3942
4201
|
if (col.type === "datetime") return formatDateTime(value);
|
|
3943
4202
|
if (col.type === "multiSelect" || Array.isArray(value) || typeof value === "string" && value.trim().startsWith("[")) {
|
|
@@ -4118,6 +4377,9 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4118
4377
|
const [bulkDialogOpen, setBulkDialogOpen] = React26.useState(false);
|
|
4119
4378
|
const [isRefetching, setIsRefetching] = React26.useState(false);
|
|
4120
4379
|
const [roleOptions, setRoleOptions] = React26.useState([]);
|
|
4380
|
+
const [inviteDialog, setInviteDialog] = React26.useState(null);
|
|
4381
|
+
const [sendingInviteEmail, setSendingInviteEmail] = React26.useState(false);
|
|
4382
|
+
const [linkCopied, setLinkCopied] = React26.useState(false);
|
|
4121
4383
|
const hasLoadedRef = React26.useRef(false);
|
|
4122
4384
|
const isMobile = useIsMobile();
|
|
4123
4385
|
const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
|
|
@@ -4576,22 +4838,77 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4576
4838
|
method: "POST",
|
|
4577
4839
|
headers: {
|
|
4578
4840
|
"Content-Type": "application/json"
|
|
4579
|
-
}
|
|
4841
|
+
},
|
|
4842
|
+
body: JSON.stringify({
|
|
4843
|
+
sendEmail: true,
|
|
4844
|
+
rotate: true
|
|
4845
|
+
})
|
|
4580
4846
|
});
|
|
4581
4847
|
if (!res.ok) {
|
|
4582
4848
|
const error2 = await res.json();
|
|
4583
|
-
throw new Error(error2.error || "Failed to
|
|
4849
|
+
throw new Error(error2.error || "Failed to resend invite");
|
|
4584
4850
|
}
|
|
4585
4851
|
const result = await res.json();
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
sonner.toast.success(`New invite link generated.${note}`);
|
|
4589
|
-
}
|
|
4852
|
+
sonner.toast.success(result.message || "Invite resent. Previous invite link is no longer valid.");
|
|
4853
|
+
setRefreshKey((k) => k + 1);
|
|
4590
4854
|
} catch (error2) {
|
|
4591
4855
|
console.error("Error regenerating invite:", error2);
|
|
4592
|
-
sonner.toast.error(error2 instanceof Error ? error2.message : "Failed to
|
|
4856
|
+
sonner.toast.error(error2 instanceof Error ? error2.message : "Failed to resend invite");
|
|
4593
4857
|
}
|
|
4594
4858
|
}, "handleRegenerateInvite");
|
|
4859
|
+
const copyInviteLink = /* @__PURE__ */ __name(async () => {
|
|
4860
|
+
const inviteLink = inviteDialog?.inviteLink;
|
|
4861
|
+
if (!inviteLink) {
|
|
4862
|
+
sonner.toast.error("Invite link is not available");
|
|
4863
|
+
return;
|
|
4864
|
+
}
|
|
4865
|
+
try {
|
|
4866
|
+
await navigator.clipboard.writeText(inviteLink);
|
|
4867
|
+
setLinkCopied(true);
|
|
4868
|
+
sonner.toast.success("Invite link copied");
|
|
4869
|
+
window.setTimeout(() => setLinkCopied(false), 2e3);
|
|
4870
|
+
} catch {
|
|
4871
|
+
sonner.toast.error("Could not copy invite link");
|
|
4872
|
+
}
|
|
4873
|
+
}, "copyInviteLink");
|
|
4874
|
+
const sendInviteEmailFromDialog = /* @__PURE__ */ __name(async () => {
|
|
4875
|
+
if (!inviteDialog) return;
|
|
4876
|
+
setSendingInviteEmail(true);
|
|
4877
|
+
try {
|
|
4878
|
+
const res = await fetch(`/api/users/${inviteDialog.userId}/regenerate-invite`, {
|
|
4879
|
+
method: "POST",
|
|
4880
|
+
headers: {
|
|
4881
|
+
"Content-Type": "application/json"
|
|
4882
|
+
},
|
|
4883
|
+
body: JSON.stringify({
|
|
4884
|
+
sendEmail: true,
|
|
4885
|
+
rotate: false
|
|
4886
|
+
})
|
|
4887
|
+
});
|
|
4888
|
+
const data2 = await res.json();
|
|
4889
|
+
if (!res.ok) {
|
|
4890
|
+
sonner.toast.error(data2.error || data2.message || "Failed to send invite email");
|
|
4891
|
+
return;
|
|
4892
|
+
}
|
|
4893
|
+
const nextLink = typeof data2.inviteLink === "string" ? data2.inviteLink.trim() : "";
|
|
4894
|
+
if (nextLink) {
|
|
4895
|
+
setInviteDialog((prev) => prev ? {
|
|
4896
|
+
...prev,
|
|
4897
|
+
inviteLink: nextLink
|
|
4898
|
+
} : prev);
|
|
4899
|
+
}
|
|
4900
|
+
sonner.toast.success(data2.emailSent ? "Invite email sent" : data2.message || "Invite email queued");
|
|
4901
|
+
} catch {
|
|
4902
|
+
sonner.toast.error("Failed to send invite email");
|
|
4903
|
+
} finally {
|
|
4904
|
+
setSendingInviteEmail(false);
|
|
4905
|
+
}
|
|
4906
|
+
}, "sendInviteEmailFromDialog");
|
|
4907
|
+
const closeInviteDialog = /* @__PURE__ */ __name(() => {
|
|
4908
|
+
setInviteDialog(null);
|
|
4909
|
+
setLinkCopied(false);
|
|
4910
|
+
setRefreshKey((k) => k + 1);
|
|
4911
|
+
}, "closeInviteDialog");
|
|
4595
4912
|
const addEditPage = addEditPageUrl && addEditPageUrl.length > 0;
|
|
4596
4913
|
const createQueryString = React26.useMemo(() => {
|
|
4597
4914
|
if (!createQueryParams || Object.keys(createQueryParams).length === 0) return "";
|
|
@@ -4869,11 +5186,11 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4869
5186
|
onClick: /* @__PURE__ */ __name(() => handleDuplicateClick(item), "onClick")
|
|
4870
5187
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Copy, {
|
|
4871
5188
|
className: "h-4 w-4 mr-2"
|
|
4872
|
-
}), "Duplicate")), resourceName === "users" && /* @__PURE__ */ React.createElement(DropdownMenuItem, {
|
|
5189
|
+
}), "Duplicate")), resourceName === "users" && String(item.inviteStatus ?? "") === "pending" && /* @__PURE__ */ React.createElement(DropdownMenuItem, {
|
|
4873
5190
|
onClick: /* @__PURE__ */ __name(() => handleRegenerateInvite(Number(item.id)), "onClick")
|
|
4874
5191
|
}, /* @__PURE__ */ React.createElement(LucideIcons.RefreshCw, {
|
|
4875
5192
|
className: "h-4 w-4 mr-2"
|
|
4876
|
-
}), "
|
|
5193
|
+
}), "Resend invite"), /* @__PURE__ */ React.createElement(DropdownMenuItem, {
|
|
4877
5194
|
onClick: /* @__PURE__ */ __name(() => handleDeleteClick(item), "onClick"),
|
|
4878
5195
|
className: "text-red-600 focus:text-red-600"
|
|
4879
5196
|
}, /* @__PURE__ */ React.createElement(LucideIcons.Trash2, {
|
|
@@ -4977,17 +5294,17 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
4977
5294
|
className: "h-3.5 w-3.5"
|
|
4978
5295
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
4979
5296
|
className: "sr-only"
|
|
4980
|
-
}, "Duplicate")), resourceName === "users" && /* @__PURE__ */ React.createElement(Button, {
|
|
5297
|
+
}, "Duplicate")), resourceName === "users" && String(item.inviteStatus ?? "") === "pending" && /* @__PURE__ */ React.createElement(Button, {
|
|
4981
5298
|
variant: "outline",
|
|
4982
5299
|
size: "icon",
|
|
4983
5300
|
className: "h-7 w-7",
|
|
4984
5301
|
onClick: /* @__PURE__ */ __name(() => handleRegenerateInvite(Number(item.id)), "onClick"),
|
|
4985
|
-
title: "
|
|
5302
|
+
title: "Resend invite"
|
|
4986
5303
|
}, /* @__PURE__ */ React.createElement(LucideIcons.RefreshCw, {
|
|
4987
5304
|
className: "h-3.5 w-3.5"
|
|
4988
5305
|
}), /* @__PURE__ */ React.createElement("span", {
|
|
4989
5306
|
className: "sr-only"
|
|
4990
|
-
}, "
|
|
5307
|
+
}, "Resend invite")), /* @__PURE__ */ React.createElement(Button, {
|
|
4991
5308
|
variant: "outline",
|
|
4992
5309
|
size: "icon",
|
|
4993
5310
|
className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
|
|
@@ -5036,12 +5353,55 @@ function AdminCRUD({ title, apiEndpoint, columns, addEditPageUrl, customViewPage
|
|
|
5036
5353
|
setIsFormOpen(false);
|
|
5037
5354
|
setRefreshKey((k) => k + 1);
|
|
5038
5355
|
}, "onClose"),
|
|
5356
|
+
onSaveSuccess: /* @__PURE__ */ __name((result) => {
|
|
5357
|
+
if (resourceName === "users" && result && typeof result.inviteLink === "string" && result.inviteLink.trim() && !editingItem) {
|
|
5358
|
+
const userId = Number(result.user?.id ?? result.id);
|
|
5359
|
+
const inviteLink = result.inviteLink.trim();
|
|
5360
|
+
if (Number.isFinite(userId) && userId > 0) {
|
|
5361
|
+
setLinkCopied(false);
|
|
5362
|
+
setInviteDialog({
|
|
5363
|
+
userId,
|
|
5364
|
+
inviteLink
|
|
5365
|
+
});
|
|
5366
|
+
sonner.toast.success(typeof result.message === "string" ? result.message : "User created successfully");
|
|
5367
|
+
return;
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
setRefreshKey((k) => k + 1);
|
|
5371
|
+
}, "onSaveSuccess"),
|
|
5039
5372
|
apiEndpoint,
|
|
5040
5373
|
columns,
|
|
5041
5374
|
existingData: editingItem,
|
|
5042
5375
|
duplicateSeed,
|
|
5043
5376
|
resource: resourceName
|
|
5044
5377
|
}), /* @__PURE__ */ React.createElement(Dialog, {
|
|
5378
|
+
open: inviteDialog != null,
|
|
5379
|
+
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
5380
|
+
if (!open) closeInviteDialog();
|
|
5381
|
+
}, "onOpenChange")
|
|
5382
|
+
}, /* @__PURE__ */ React.createElement(DialogContent, {
|
|
5383
|
+
className: "max-w-md"
|
|
5384
|
+
}, /* @__PURE__ */ React.createElement(DialogHeader, null, /* @__PURE__ */ React.createElement(DialogTitle, null, "User created"), /* @__PURE__ */ React.createElement(DialogDescription, null, "Send an invite email, or copy the invite link to share another way. The link is not shown here.")), /* @__PURE__ */ React.createElement("div", {
|
|
5385
|
+
className: "flex flex-col gap-2 py-2"
|
|
5386
|
+
}, /* @__PURE__ */ React.createElement(Button, {
|
|
5387
|
+
type: "button",
|
|
5388
|
+
disabled: sendingInviteEmail,
|
|
5389
|
+
onClick: /* @__PURE__ */ __name(() => void sendInviteEmailFromDialog(), "onClick")
|
|
5390
|
+
}, /* @__PURE__ */ React.createElement(LucideIcons.Mail, {
|
|
5391
|
+
className: "h-4 w-4 mr-2"
|
|
5392
|
+
}), sendingInviteEmail ? "Sending\u2026" : "Send email"), /* @__PURE__ */ React.createElement(Button, {
|
|
5393
|
+
type: "button",
|
|
5394
|
+
variant: "outline",
|
|
5395
|
+
onClick: /* @__PURE__ */ __name(() => void copyInviteLink(), "onClick")
|
|
5396
|
+
}, linkCopied ? /* @__PURE__ */ React.createElement(LucideIcons.Check, {
|
|
5397
|
+
className: "h-4 w-4 mr-2"
|
|
5398
|
+
}) : /* @__PURE__ */ React.createElement(LucideIcons.Copy, {
|
|
5399
|
+
className: "h-4 w-4 mr-2"
|
|
5400
|
+
}), linkCopied ? "Copied" : "Copy invite link")), /* @__PURE__ */ React.createElement(DialogFooter, null, /* @__PURE__ */ React.createElement(Button, {
|
|
5401
|
+
type: "button",
|
|
5402
|
+
variant: "secondary",
|
|
5403
|
+
onClick: closeInviteDialog
|
|
5404
|
+
}, "Done")))), /* @__PURE__ */ React.createElement(Dialog, {
|
|
5045
5405
|
open: deleteDialogOpen,
|
|
5046
5406
|
onOpenChange: /* @__PURE__ */ __name((open) => {
|
|
5047
5407
|
console.debug("[CRUD][delete] dialog onOpenChange", {
|
|
@@ -7767,155 +8127,6 @@ var init_ComponentSettings = __esm({
|
|
|
7767
8127
|
__name(ComponentSettings, "ComponentSettings");
|
|
7768
8128
|
}
|
|
7769
8129
|
});
|
|
7770
|
-
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 }) {
|
|
7771
|
-
const fileInputRef = React26.useRef(null);
|
|
7772
|
-
const [isUploading, setIsUploading] = React26.useState(false);
|
|
7773
|
-
const [error, setError] = React26.useState(null);
|
|
7774
|
-
const [lightboxOpen, setLightboxOpen] = React26.useState(false);
|
|
7775
|
-
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";
|
|
7776
|
-
const closeLightbox = React26.useCallback(() => setLightboxOpen(false), []);
|
|
7777
|
-
React26.useEffect(() => {
|
|
7778
|
-
if (!lightboxOpen) return;
|
|
7779
|
-
const onKeyDown = /* @__PURE__ */ __name((e) => {
|
|
7780
|
-
if (e.key === "Escape") closeLightbox();
|
|
7781
|
-
}, "onKeyDown");
|
|
7782
|
-
window.addEventListener("keydown", onKeyDown);
|
|
7783
|
-
const prevOverflow = document.body.style.overflow;
|
|
7784
|
-
document.body.style.overflow = "hidden";
|
|
7785
|
-
return () => {
|
|
7786
|
-
window.removeEventListener("keydown", onKeyDown);
|
|
7787
|
-
document.body.style.overflow = prevOverflow;
|
|
7788
|
-
};
|
|
7789
|
-
}, [
|
|
7790
|
-
lightboxOpen,
|
|
7791
|
-
closeLightbox
|
|
7792
|
-
]);
|
|
7793
|
-
const handleUpload = React26.useCallback(async (file) => {
|
|
7794
|
-
setError(null);
|
|
7795
|
-
if (!ACCEPTED_TYPES.includes(file.type)) {
|
|
7796
|
-
setError(`Unsupported file type. Use: ${ACCEPTED_TYPES.map((t) => t.replace("image/", "")).join(", ")}`);
|
|
7797
|
-
return;
|
|
7798
|
-
}
|
|
7799
|
-
if (file.size > maxSizeMb * 1024 * 1024) {
|
|
7800
|
-
setError(`File must be under ${maxSizeMb}MB`);
|
|
7801
|
-
return;
|
|
7802
|
-
}
|
|
7803
|
-
setIsUploading(true);
|
|
7804
|
-
try {
|
|
7805
|
-
const formData = new FormData();
|
|
7806
|
-
formData.append("file", file);
|
|
7807
|
-
const response = await fetch("/api/upload", {
|
|
7808
|
-
method: "POST",
|
|
7809
|
-
body: formData
|
|
7810
|
-
});
|
|
7811
|
-
const data = await response.json();
|
|
7812
|
-
if (!response.ok) {
|
|
7813
|
-
throw new Error(data.error || data.details || "Upload failed");
|
|
7814
|
-
}
|
|
7815
|
-
onChange(data.filePath ?? "");
|
|
7816
|
-
} catch (err) {
|
|
7817
|
-
setError(err instanceof Error ? err.message : "Upload failed");
|
|
7818
|
-
} finally {
|
|
7819
|
-
setIsUploading(false);
|
|
7820
|
-
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
7821
|
-
}
|
|
7822
|
-
}, [
|
|
7823
|
-
maxSizeMb,
|
|
7824
|
-
onChange
|
|
7825
|
-
]);
|
|
7826
|
-
const onFileChange = React26.useCallback((e) => {
|
|
7827
|
-
const file = e.target.files?.[0];
|
|
7828
|
-
if (file) void handleUpload(file);
|
|
7829
|
-
}, [
|
|
7830
|
-
handleUpload
|
|
7831
|
-
]);
|
|
7832
|
-
const trimmed = value.trim();
|
|
7833
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
7834
|
-
className: "space-y-2"
|
|
7835
|
-
}, /* @__PURE__ */ React.createElement("label", {
|
|
7836
|
-
className: labelClassName
|
|
7837
|
-
}, label), trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7838
|
-
className: "flex items-start gap-3"
|
|
7839
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
7840
|
-
src: trimmed,
|
|
7841
|
-
alt: label,
|
|
7842
|
-
className: previewCls,
|
|
7843
|
-
role: "button",
|
|
7844
|
-
tabIndex: 0,
|
|
7845
|
-
title: "Click to enlarge",
|
|
7846
|
-
onClick: /* @__PURE__ */ __name(() => setLightboxOpen(true), "onClick"),
|
|
7847
|
-
onKeyDown: /* @__PURE__ */ __name((e) => {
|
|
7848
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
7849
|
-
e.preventDefault();
|
|
7850
|
-
setLightboxOpen(true);
|
|
7851
|
-
}
|
|
7852
|
-
}, "onKeyDown"),
|
|
7853
|
-
onError: /* @__PURE__ */ __name((e) => {
|
|
7854
|
-
e.currentTarget.style.display = "none";
|
|
7855
|
-
}, "onError")
|
|
7856
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
7857
|
-
type: "button",
|
|
7858
|
-
onClick: /* @__PURE__ */ __name(() => onChange(""), "onClick"),
|
|
7859
|
-
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"
|
|
7860
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.X, {
|
|
7861
|
-
className: "h-3 w-3"
|
|
7862
|
-
}), "Clear")) : null, /* @__PURE__ */ React.createElement("div", {
|
|
7863
|
-
className: "flex flex-wrap gap-2"
|
|
7864
|
-
}, /* @__PURE__ */ React.createElement("input", {
|
|
7865
|
-
type: "url",
|
|
7866
|
-
value,
|
|
7867
|
-
onChange: /* @__PURE__ */ __name((e) => {
|
|
7868
|
-
setError(null);
|
|
7869
|
-
onChange(e.target.value);
|
|
7870
|
-
}, "onChange"),
|
|
7871
|
-
placeholder,
|
|
7872
|
-
className: `${inputClassName} min-w-0 flex-1`
|
|
7873
|
-
}), /* @__PURE__ */ React.createElement("input", {
|
|
7874
|
-
ref: fileInputRef,
|
|
7875
|
-
type: "file",
|
|
7876
|
-
accept: ACCEPTED_TYPES.join(","),
|
|
7877
|
-
onChange: onFileChange,
|
|
7878
|
-
className: "hidden",
|
|
7879
|
-
disabled: isUploading
|
|
7880
|
-
}), /* @__PURE__ */ React.createElement("button", {
|
|
7881
|
-
type: "button",
|
|
7882
|
-
onClick: /* @__PURE__ */ __name(() => fileInputRef.current?.click(), "onClick"),
|
|
7883
|
-
disabled: isUploading,
|
|
7884
|
-
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"
|
|
7885
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.Upload, {
|
|
7886
|
-
className: "h-3.5 w-3.5"
|
|
7887
|
-
}), isUploading ? "Uploading\u2026" : "Upload")), error ? /* @__PURE__ */ React.createElement("p", {
|
|
7888
|
-
className: "flex items-center gap-1.5 text-xs text-red-600"
|
|
7889
|
-
}, /* @__PURE__ */ React.createElement(LucideIcons.AlertCircle, {
|
|
7890
|
-
className: "h-3.5 w-3.5 shrink-0"
|
|
7891
|
-
}), error) : /* @__PURE__ */ React.createElement("p", {
|
|
7892
|
-
className: "text-xs text-gray-500"
|
|
7893
|
-
}, "Paste a URL or upload an image (PNG, JPG, GIF, WEBP up to ", maxSizeMb, "MB)"), lightboxOpen && trimmed ? /* @__PURE__ */ React.createElement("div", {
|
|
7894
|
-
className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4",
|
|
7895
|
-
role: "dialog",
|
|
7896
|
-
"aria-modal": "true",
|
|
7897
|
-
"aria-label": `${label} preview`,
|
|
7898
|
-
onClick: closeLightbox
|
|
7899
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
7900
|
-
src: trimmed,
|
|
7901
|
-
alt: label,
|
|
7902
|
-
className: "max-h-[90vh] max-w-[90vw] rounded-md object-contain shadow-lg",
|
|
7903
|
-
onClick: /* @__PURE__ */ __name((e) => e.stopPropagation(), "onClick")
|
|
7904
|
-
})) : null);
|
|
7905
|
-
}
|
|
7906
|
-
var ACCEPTED_TYPES;
|
|
7907
|
-
var init_ImageOrUrlField = __esm({
|
|
7908
|
-
"src/components/Admin/ImageOrUrlField.tsx"() {
|
|
7909
|
-
"use client";
|
|
7910
|
-
ACCEPTED_TYPES = [
|
|
7911
|
-
"image/jpeg",
|
|
7912
|
-
"image/png",
|
|
7913
|
-
"image/gif",
|
|
7914
|
-
"image/webp"
|
|
7915
|
-
];
|
|
7916
|
-
__name(ImageOrUrlField, "ImageOrUrlField");
|
|
7917
|
-
}
|
|
7918
|
-
});
|
|
7919
8130
|
function generateId() {
|
|
7920
8131
|
return `nav_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
7921
8132
|
}
|
|
@@ -8915,7 +9126,7 @@ var init_AuthFooter = __esm({
|
|
|
8915
9126
|
});
|
|
8916
9127
|
function AuthPageLayout({ children }) {
|
|
8917
9128
|
return /* @__PURE__ */ React.createElement("div", {
|
|
8918
|
-
className: "min-h-screen flex flex-col bg-gray-50
|
|
9129
|
+
className: "min-h-screen flex flex-col bg-gray-50"
|
|
8919
9130
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
8920
9131
|
className: "flex-1 flex items-center justify-center px-4 py-6"
|
|
8921
9132
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -8956,6 +9167,7 @@ var init_SignInPage = __esm({
|
|
|
8956
9167
|
const [email, setEmail] = React26.useState("");
|
|
8957
9168
|
const [password, setPassword] = React26.useState("");
|
|
8958
9169
|
const [error, setError] = React26.useState("");
|
|
9170
|
+
const [success, setSuccess] = React26.useState("");
|
|
8959
9171
|
const [loading, setLoading] = React26.useState(false);
|
|
8960
9172
|
const { status } = react.useSession();
|
|
8961
9173
|
const searchParams = navigation.useSearchParams();
|
|
@@ -8963,6 +9175,9 @@ var init_SignInPage = __esm({
|
|
|
8963
9175
|
if (searchParams.get("error")) {
|
|
8964
9176
|
setError("Invalid email or password");
|
|
8965
9177
|
}
|
|
9178
|
+
if (searchParams.get("activated") === "1") {
|
|
9179
|
+
setSuccess("Account activated. Sign in with your new password.");
|
|
9180
|
+
}
|
|
8966
9181
|
}, [
|
|
8967
9182
|
searchParams
|
|
8968
9183
|
]);
|
|
@@ -9023,41 +9238,45 @@ var init_SignInPage = __esm({
|
|
|
9023
9238
|
}
|
|
9024
9239
|
}, "handleSubmit");
|
|
9025
9240
|
return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
|
|
9026
|
-
className: "w-full shadow-md rounded-lg bg-white
|
|
9241
|
+
className: "w-full shadow-md rounded-lg bg-white px-4 py-6 sm:px-6 sm:py-8"
|
|
9027
9242
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
9028
|
-
className: "mb-1 text-center text-xl font-bold text-black
|
|
9243
|
+
className: "mb-1 text-center text-xl font-bold text-black sm:text-2xl"
|
|
9029
9244
|
}, "Sign in"), /* @__PURE__ */ React.createElement("p", {
|
|
9030
9245
|
className: "mb-6 text-center text-sm text-body-color"
|
|
9031
|
-
}, "Enter your credentials"),
|
|
9032
|
-
className: "mb-4 rounded-md bg-
|
|
9246
|
+
}, "Enter your credentials"), success && /* @__PURE__ */ React.createElement("div", {
|
|
9247
|
+
className: "mb-4 rounded-md bg-green-50 p-3 text-sm text-green-700"
|
|
9248
|
+
}, success), error && /* @__PURE__ */ React.createElement("div", {
|
|
9249
|
+
className: "mb-4 rounded-md bg-red-50 p-3 text-sm text-red-600"
|
|
9033
9250
|
}, error), /* @__PURE__ */ React.createElement("form", {
|
|
9034
9251
|
onSubmit: handleSubmit
|
|
9035
9252
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
9036
9253
|
className: "mb-4"
|
|
9037
9254
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
9038
9255
|
htmlFor: "email",
|
|
9039
|
-
className: "mb-1.5 block text-sm text-
|
|
9256
|
+
className: "mb-1.5 block text-sm text-gray-900"
|
|
9040
9257
|
}, "Email"), /* @__PURE__ */ React.createElement("input", {
|
|
9041
9258
|
type: "email",
|
|
9042
9259
|
name: "email",
|
|
9260
|
+
id: "email",
|
|
9043
9261
|
value: email,
|
|
9044
9262
|
onChange: /* @__PURE__ */ __name((e) => setEmail(e.target.value), "onChange"),
|
|
9045
9263
|
placeholder: "Enter email",
|
|
9046
9264
|
required: true,
|
|
9047
|
-
className: "border border-gray-300 w-full rounded bg-gray-50 px-3 py-2 text-sm text-gray-900 outline-none focus:border-gray-500 focus:ring-1 focus:ring-gray-500
|
|
9265
|
+
className: "border border-gray-300 w-full rounded bg-gray-50 px-3 py-2 text-sm text-gray-900 outline-none focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
|
9048
9266
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
9049
9267
|
className: "mb-4"
|
|
9050
9268
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
9051
9269
|
htmlFor: "password",
|
|
9052
|
-
className: "mb-1.5 block text-sm text-
|
|
9270
|
+
className: "mb-1.5 block text-sm text-gray-900"
|
|
9053
9271
|
}, "Password"), /* @__PURE__ */ React.createElement("input", {
|
|
9054
9272
|
type: "password",
|
|
9055
9273
|
name: "password",
|
|
9274
|
+
id: "password",
|
|
9056
9275
|
value: password,
|
|
9057
9276
|
onChange: /* @__PURE__ */ __name((e) => setPassword(e.target.value), "onChange"),
|
|
9058
9277
|
placeholder: "Enter password",
|
|
9059
9278
|
required: true,
|
|
9060
|
-
className: "border border-gray-300 w-full rounded bg-gray-50 px-3 py-2 text-sm text-gray-900 outline-none focus:border-gray-500 focus:ring-1 focus:ring-gray-500
|
|
9279
|
+
className: "border border-gray-300 w-full rounded bg-gray-50 px-3 py-2 text-sm text-gray-900 outline-none focus:border-gray-500 focus:ring-1 focus:ring-gray-500"
|
|
9061
9280
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
9062
9281
|
className: "mb-4 flex items-center justify-between"
|
|
9063
9282
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
@@ -9071,7 +9290,7 @@ var init_SignInPage = __esm({
|
|
|
9071
9290
|
}, "Forgot password?")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", {
|
|
9072
9291
|
type: "submit",
|
|
9073
9292
|
disabled: loading,
|
|
9074
|
-
className: "w-full rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed
|
|
9293
|
+
className: "w-full rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
9075
9294
|
}, loading ? "Signing in..." : "Sign in")))));
|
|
9076
9295
|
}, "SigninPage");
|
|
9077
9296
|
exports.AdminSignInPage = SigninPage;
|
|
@@ -9115,11 +9334,11 @@ function ForgotPasswordPage() {
|
|
|
9115
9334
|
}
|
|
9116
9335
|
}, "handleSubmit");
|
|
9117
9336
|
return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
|
|
9118
|
-
className: "w-full space-y-4 p-5 sm:p-6 bg-white
|
|
9337
|
+
className: "w-full space-y-4 p-5 sm:p-6 bg-white rounded-lg shadow-md"
|
|
9119
9338
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
9120
9339
|
className: "text-center"
|
|
9121
9340
|
}, /* @__PURE__ */ React.createElement("h2", {
|
|
9122
|
-
className: "text-xl font-bold text-gray-900
|
|
9341
|
+
className: "text-xl font-bold text-gray-900"
|
|
9123
9342
|
}, "Forgot password"), /* @__PURE__ */ React.createElement("p", {
|
|
9124
9343
|
className: "mt-1 text-sm text-body-color"
|
|
9125
9344
|
}, "Enter your email for a reset link.")), /* @__PURE__ */ React.createElement("form", {
|
|
@@ -9137,7 +9356,7 @@ function ForgotPasswordPage() {
|
|
|
9137
9356
|
placeholder: "you@example.com",
|
|
9138
9357
|
className: "mt-1 h-9 text-sm"
|
|
9139
9358
|
})), message && /* @__PURE__ */ React.createElement("div", {
|
|
9140
|
-
className: `p-2.5 rounded-md text-sm ${message.includes("receive") ? "bg-green-50
|
|
9359
|
+
className: `p-2.5 rounded-md text-sm ${message.includes("receive") ? "bg-green-50 text-green-700" : "bg-red-50 text-red-700"}`
|
|
9141
9360
|
}, message), /* @__PURE__ */ React.createElement(Button, {
|
|
9142
9361
|
type: "submit",
|
|
9143
9362
|
disabled: loading,
|
|
@@ -9220,9 +9439,9 @@ function ResetPasswordPageContent() {
|
|
|
9220
9439
|
}, "handleSubmit");
|
|
9221
9440
|
if (!token) {
|
|
9222
9441
|
return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
|
|
9223
|
-
className: "w-full space-y-4 p-5 sm:p-6 bg-white
|
|
9442
|
+
className: "w-full space-y-4 p-5 sm:p-6 bg-white rounded-lg shadow-md text-center"
|
|
9224
9443
|
}, /* @__PURE__ */ React.createElement("h2", {
|
|
9225
|
-
className: "text-xl font-bold text-gray-900
|
|
9444
|
+
className: "text-xl font-bold text-gray-900"
|
|
9226
9445
|
}, "Invalid reset link"), /* @__PURE__ */ React.createElement("p", {
|
|
9227
9446
|
className: "text-sm text-body-color"
|
|
9228
9447
|
}, message), /* @__PURE__ */ React.createElement(Link2__default.default, {
|
|
@@ -9231,11 +9450,11 @@ function ResetPasswordPageContent() {
|
|
|
9231
9450
|
}, "Request a new link")));
|
|
9232
9451
|
}
|
|
9233
9452
|
return /* @__PURE__ */ React.createElement(AuthPageLayout, null, /* @__PURE__ */ React.createElement("div", {
|
|
9234
|
-
className: "w-full space-y-4 p-5 sm:p-6 bg-white
|
|
9453
|
+
className: "w-full space-y-4 p-5 sm:p-6 bg-white rounded-lg shadow-md"
|
|
9235
9454
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
9236
9455
|
className: "text-center"
|
|
9237
9456
|
}, /* @__PURE__ */ React.createElement("h2", {
|
|
9238
|
-
className: "text-xl font-bold text-gray-900
|
|
9457
|
+
className: "text-xl font-bold text-gray-900"
|
|
9239
9458
|
}, "Set new password"), /* @__PURE__ */ React.createElement("p", {
|
|
9240
9459
|
className: "mt-1 text-sm text-body-color"
|
|
9241
9460
|
}, "Enter your new password.")), /* @__PURE__ */ React.createElement("form", {
|
|
@@ -9270,7 +9489,7 @@ function ResetPasswordPageContent() {
|
|
|
9270
9489
|
}), "onChange"),
|
|
9271
9490
|
className: "mt-1 h-9 text-sm"
|
|
9272
9491
|
})), message && /* @__PURE__ */ React.createElement("div", {
|
|
9273
|
-
className: `p-2.5 rounded-md text-sm ${message.includes("successfully") || message.includes("now sign in") ? "bg-green-50
|
|
9492
|
+
className: `p-2.5 rounded-md text-sm ${message.includes("successfully") || message.includes("now sign in") ? "bg-green-50 text-green-700" : "bg-red-50 text-red-700"}`
|
|
9274
9493
|
}, message), /* @__PURE__ */ React.createElement(Button, {
|
|
9275
9494
|
type: "submit",
|
|
9276
9495
|
disabled: loading,
|
|
@@ -9285,7 +9504,7 @@ function ResetPasswordPageContent() {
|
|
|
9285
9504
|
function ResetPasswordPage() {
|
|
9286
9505
|
return /* @__PURE__ */ React.createElement(React26.Suspense, {
|
|
9287
9506
|
fallback: /* @__PURE__ */ React.createElement("div", {
|
|
9288
|
-
className: "min-h-screen flex items-center justify-center bg-gray-50
|
|
9507
|
+
className: "min-h-screen flex items-center justify-center bg-gray-50"
|
|
9289
9508
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
9290
9509
|
className: "max-w-[400px] w-full p-6 text-center"
|
|
9291
9510
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
@@ -9313,6 +9532,7 @@ __export(InvitePage_exports, {
|
|
|
9313
9532
|
default: () => InvitePage
|
|
9314
9533
|
});
|
|
9315
9534
|
function InvitePageContent() {
|
|
9535
|
+
const router = navigation.useRouter();
|
|
9316
9536
|
const searchParams = navigation.useSearchParams();
|
|
9317
9537
|
const token = searchParams.get("token");
|
|
9318
9538
|
const [formData, setFormData] = React26.useState({
|
|
@@ -9339,8 +9559,13 @@ function InvitePageContent() {
|
|
|
9339
9559
|
setInviteValid(true);
|
|
9340
9560
|
setMessage("");
|
|
9341
9561
|
} else {
|
|
9562
|
+
const err = typeof result.error === "string" ? result.error : "";
|
|
9563
|
+
if (/already (active|accepted)/i.test(err)) {
|
|
9564
|
+
router.replace("/admin/signin?activated=1");
|
|
9565
|
+
return;
|
|
9566
|
+
}
|
|
9342
9567
|
setInviteValid(false);
|
|
9343
|
-
setMessage(
|
|
9568
|
+
setMessage(err || "This invitation is no longer valid.");
|
|
9344
9569
|
}
|
|
9345
9570
|
} catch {
|
|
9346
9571
|
if (!cancelled) {
|
|
@@ -9382,11 +9607,8 @@ function InvitePageContent() {
|
|
|
9382
9607
|
});
|
|
9383
9608
|
const result = await res.json();
|
|
9384
9609
|
if (res.ok) {
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
password: "",
|
|
9388
|
-
confirmPassword: ""
|
|
9389
|
-
});
|
|
9610
|
+
router.replace("/admin/signin?activated=1");
|
|
9611
|
+
return;
|
|
9390
9612
|
} else {
|
|
9391
9613
|
setMessage(result.error || "Failed to activate account");
|
|
9392
9614
|
}
|
|
@@ -9452,7 +9674,7 @@ function InvitePageContent() {
|
|
|
9452
9674
|
}), "onChange"),
|
|
9453
9675
|
className: "mt-1 h-9 text-sm"
|
|
9454
9676
|
})), message && /* @__PURE__ */ React26__namespace.default.createElement("div", {
|
|
9455
|
-
className:
|
|
9677
|
+
className: "p-2.5 rounded-md text-sm bg-red-50 text-red-700"
|
|
9456
9678
|
}, message), /* @__PURE__ */ React26__namespace.default.createElement(Button, {
|
|
9457
9679
|
type: "submit",
|
|
9458
9680
|
disabled: loading,
|
|
@@ -11218,6 +11440,15 @@ function renderListPrice(value, item) {
|
|
|
11218
11440
|
function renderListThumbnail(url) {
|
|
11219
11441
|
const trimmed = url.trim();
|
|
11220
11442
|
if (!trimmed) return "\u2014";
|
|
11443
|
+
if (isVideoMediaUrl(trimmed)) {
|
|
11444
|
+
return React26.createElement("video", {
|
|
11445
|
+
src: trimmed,
|
|
11446
|
+
muted: true,
|
|
11447
|
+
playsInline: true,
|
|
11448
|
+
preload: "metadata",
|
|
11449
|
+
className: "h-10 w-10 rounded border border-gray-200 bg-black object-cover"
|
|
11450
|
+
});
|
|
11451
|
+
}
|
|
11221
11452
|
return React26.createElement("img", {
|
|
11222
11453
|
src: trimmed,
|
|
11223
11454
|
alt: "",
|
|
@@ -11238,6 +11469,7 @@ function productListImageUrl(item) {
|
|
|
11238
11469
|
exports.STORE_CRUD_CONFIGS = void 0;
|
|
11239
11470
|
var init_store_crud_configs = __esm({
|
|
11240
11471
|
"src/admin/store-crud-configs.ts"() {
|
|
11472
|
+
init_media_url();
|
|
11241
11473
|
__name(renderListPrice, "renderListPrice");
|
|
11242
11474
|
__name(renderListThumbnail, "renderListThumbnail");
|
|
11243
11475
|
__name(productListImageUrl, "productListImageUrl");
|
|
@@ -11450,6 +11682,10 @@ var init_store_crud_configs = __esm({
|
|
|
11450
11682
|
field: "name",
|
|
11451
11683
|
displayName: "Name"
|
|
11452
11684
|
},
|
|
11685
|
+
{
|
|
11686
|
+
field: "slug",
|
|
11687
|
+
displayName: "Slug"
|
|
11688
|
+
},
|
|
11453
11689
|
{
|
|
11454
11690
|
field: "eventId",
|
|
11455
11691
|
displayName: "Event",
|
|
@@ -11943,6 +12179,14 @@ var init_store_crud_configs = __esm({
|
|
|
11943
12179
|
title: "Categories",
|
|
11944
12180
|
apiEndpoint: "/api/product_categories",
|
|
11945
12181
|
columns: [
|
|
12182
|
+
{
|
|
12183
|
+
field: "image",
|
|
12184
|
+
displayName: "Image",
|
|
12185
|
+
type: "image",
|
|
12186
|
+
listFilter: false,
|
|
12187
|
+
placeholder: "Image URL or upload",
|
|
12188
|
+
render: /* @__PURE__ */ __name((value) => renderListThumbnail(typeof value === "string" ? value : ""), "render")
|
|
12189
|
+
},
|
|
11946
12190
|
{
|
|
11947
12191
|
field: "name",
|
|
11948
12192
|
displayName: "Name"
|
|
@@ -12549,6 +12793,134 @@ var SettingsPage_exports = {};
|
|
|
12549
12793
|
__export(SettingsPage_exports, {
|
|
12550
12794
|
default: () => SettingsPage
|
|
12551
12795
|
});
|
|
12796
|
+
function InvoiceTemplatePreview({ id }) {
|
|
12797
|
+
if (id === "modern") {
|
|
12798
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
12799
|
+
className: "h-28 rounded-md border border-slate-200 bg-white overflow-hidden shadow-sm"
|
|
12800
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12801
|
+
className: "h-7 bg-teal-700 px-2 flex flex-col justify-center"
|
|
12802
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12803
|
+
className: "h-1.5 w-14 rounded bg-white/90"
|
|
12804
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12805
|
+
className: "h-1 w-10 rounded bg-white/50 mt-1"
|
|
12806
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12807
|
+
className: "p-2 space-y-1.5"
|
|
12808
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12809
|
+
className: "flex gap-2"
|
|
12810
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12811
|
+
className: "flex-1 space-y-1"
|
|
12812
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12813
|
+
className: "h-1 w-8 bg-teal-600/70 rounded"
|
|
12814
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12815
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12816
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12817
|
+
className: "h-1 w-4/5 bg-slate-200 rounded"
|
|
12818
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12819
|
+
className: "flex-1 space-y-1"
|
|
12820
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12821
|
+
className: "h-1 w-8 bg-teal-600/70 rounded"
|
|
12822
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12823
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12824
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
12825
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
12826
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12827
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
12828
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12829
|
+
className: "ml-auto h-1.5 w-12 bg-teal-700/80 rounded"
|
|
12830
|
+
})));
|
|
12831
|
+
}
|
|
12832
|
+
if (id === "compact") {
|
|
12833
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
12834
|
+
className: "h-28 rounded-md border border-slate-300 bg-white overflow-hidden shadow-sm p-1.5 space-y-1"
|
|
12835
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12836
|
+
className: "h-4 bg-slate-100 rounded flex items-center px-1"
|
|
12837
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12838
|
+
className: "h-1 w-10 bg-slate-700 rounded"
|
|
12839
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12840
|
+
className: "grid grid-cols-4 gap-0.5"
|
|
12841
|
+
}, Array.from({
|
|
12842
|
+
length: 8
|
|
12843
|
+
}).map((_, i) => /* @__PURE__ */ React.createElement("div", {
|
|
12844
|
+
key: i,
|
|
12845
|
+
className: "h-1 bg-slate-200 rounded"
|
|
12846
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
12847
|
+
className: "space-y-0.5"
|
|
12848
|
+
}, Array.from({
|
|
12849
|
+
length: 5
|
|
12850
|
+
}).map((_, i) => /* @__PURE__ */ React.createElement("div", {
|
|
12851
|
+
key: i,
|
|
12852
|
+
className: "flex gap-0.5"
|
|
12853
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12854
|
+
className: "h-1 flex-[3] bg-slate-200 rounded"
|
|
12855
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12856
|
+
className: "h-1 flex-1 bg-slate-300 rounded"
|
|
12857
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12858
|
+
className: "h-1 flex-1 bg-slate-300 rounded"
|
|
12859
|
+
})))), /* @__PURE__ */ React.createElement("div", {
|
|
12860
|
+
className: "ml-auto h-1 w-10 bg-slate-800 rounded"
|
|
12861
|
+
}));
|
|
12862
|
+
}
|
|
12863
|
+
if (id === "bold") {
|
|
12864
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
12865
|
+
className: "h-28 rounded-md border border-red-200 bg-white overflow-hidden shadow-sm"
|
|
12866
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12867
|
+
className: "h-9 bg-red-800 px-2 flex flex-col justify-center gap-1"
|
|
12868
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12869
|
+
className: "h-2 w-16 rounded bg-white"
|
|
12870
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12871
|
+
className: "h-1 w-12 rounded bg-white/60"
|
|
12872
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12873
|
+
className: "p-2 space-y-1.5"
|
|
12874
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12875
|
+
className: "h-1 w-20 bg-slate-300 rounded"
|
|
12876
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12877
|
+
className: "border border-red-100 rounded p-1 space-y-0.5 bg-red-50/40"
|
|
12878
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12879
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12880
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12881
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12882
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12883
|
+
className: "h-1.5 w-14 ml-auto bg-red-800 rounded"
|
|
12884
|
+
}))));
|
|
12885
|
+
}
|
|
12886
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
12887
|
+
className: "h-28 rounded-md border border-slate-200 bg-white overflow-hidden shadow-sm p-2 space-y-1.5"
|
|
12888
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12889
|
+
className: "h-5 rounded bg-slate-100 px-1.5 flex items-center justify-between"
|
|
12890
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12891
|
+
className: "h-1.5 w-12 bg-slate-700 rounded"
|
|
12892
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12893
|
+
className: "h-1 w-8 bg-slate-400 rounded"
|
|
12894
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12895
|
+
className: "flex gap-2"
|
|
12896
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12897
|
+
className: "flex-1 space-y-0.5"
|
|
12898
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12899
|
+
className: "h-1 w-6 bg-slate-600 rounded"
|
|
12900
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12901
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12902
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12903
|
+
className: "h-1 w-3/4 bg-slate-200 rounded"
|
|
12904
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12905
|
+
className: "flex-1 space-y-0.5"
|
|
12906
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12907
|
+
className: "h-1 w-6 bg-slate-600 rounded"
|
|
12908
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12909
|
+
className: "h-1 w-full bg-slate-200 rounded"
|
|
12910
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
12911
|
+
className: "border-t border-slate-200 pt-1 space-y-0.5"
|
|
12912
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12913
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
12914
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12915
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
12916
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
12917
|
+
className: "h-1 w-full bg-slate-100 rounded"
|
|
12918
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
12919
|
+
className: "flex justify-end"
|
|
12920
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
12921
|
+
className: "h-1.5 w-10 bg-slate-700 rounded"
|
|
12922
|
+
})));
|
|
12923
|
+
}
|
|
12552
12924
|
function hexToHsl(hex) {
|
|
12553
12925
|
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
12554
12926
|
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
@@ -12599,6 +12971,25 @@ function SettingsPage() {
|
|
|
12599
12971
|
const [requireEventApproval, setRequireEventApproval] = React26.useState(false);
|
|
12600
12972
|
const [storeSettingsLoading, setStoreSettingsLoading] = React26.useState(true);
|
|
12601
12973
|
const [currency, setCurrency] = React26.useState(DEFAULT_CURRENCY);
|
|
12974
|
+
const [invoiceTemplate, setInvoiceTemplate] = React26.useState("classic");
|
|
12975
|
+
const [companyLogo, setCompanyLogo] = React26.useState("");
|
|
12976
|
+
const [companyName, setCompanyName] = React26.useState("");
|
|
12977
|
+
const [companyEmail, setCompanyEmail] = React26.useState("");
|
|
12978
|
+
const [companyPhone, setCompanyPhone] = React26.useState("");
|
|
12979
|
+
const [companyAddress, setCompanyAddress] = React26.useState("");
|
|
12980
|
+
const handleStoreLogoChange = /* @__PURE__ */ __name((newLogo) => {
|
|
12981
|
+
setCompanyLogo(newLogo);
|
|
12982
|
+
setNavbarConfig((prev) => ({
|
|
12983
|
+
...prev,
|
|
12984
|
+
logo: newLogo
|
|
12985
|
+
}));
|
|
12986
|
+
}, "handleStoreLogoChange");
|
|
12987
|
+
const handleNavbarConfigChange = /* @__PURE__ */ __name((newConfig) => {
|
|
12988
|
+
setNavbarConfig(newConfig);
|
|
12989
|
+
if (newConfig.logo !== companyLogo) {
|
|
12990
|
+
setCompanyLogo(newConfig.logo || "");
|
|
12991
|
+
}
|
|
12992
|
+
}, "handleNavbarConfigChange");
|
|
12602
12993
|
const [currencies, setCurrencies] = React26.useState([]);
|
|
12603
12994
|
const [currenciesLoading, setCurrenciesLoading] = React26.useState(true);
|
|
12604
12995
|
const [showAddCurrency, setShowAddCurrency] = React26.useState(false);
|
|
@@ -12643,7 +13034,11 @@ function SettingsPage() {
|
|
|
12643
13034
|
setActiveThemeId((data.activeThemeId || "").trim());
|
|
12644
13035
|
if (data.navbar) {
|
|
12645
13036
|
try {
|
|
12646
|
-
|
|
13037
|
+
const parsed = JSON.parse(data.navbar);
|
|
13038
|
+
setNavbarConfig(parsed);
|
|
13039
|
+
if (parsed.logo) {
|
|
13040
|
+
setCompanyLogo((prev) => prev || parsed.logo);
|
|
13041
|
+
}
|
|
12647
13042
|
} catch {
|
|
12648
13043
|
}
|
|
12649
13044
|
}
|
|
@@ -12653,6 +13048,19 @@ function SettingsPage() {
|
|
|
12653
13048
|
fetch("/api/settings/store").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12654
13049
|
setStoreEnabled(data.enabled === "true");
|
|
12655
13050
|
setCurrency(data.currency || DEFAULT_CURRENCY);
|
|
13051
|
+
setInvoiceTemplate(data.invoiceTemplate || "classic");
|
|
13052
|
+
const logo = data.invoiceLogoUrl || data.companyLogo || data.logo || "";
|
|
13053
|
+
setCompanyLogo((prev) => logo || prev);
|
|
13054
|
+
setCompanyName(data.companyName || data.invoiceCompanyName || data.name || "");
|
|
13055
|
+
setCompanyEmail(data.companyEmail || data.invoiceEmail || data.email || "");
|
|
13056
|
+
setCompanyPhone(data.companyPhone || data.invoicePhone || data.phone || "");
|
|
13057
|
+
setCompanyAddress(data.companyAddress || data.invoiceAddress || data.address || "");
|
|
13058
|
+
if (logo) {
|
|
13059
|
+
setNavbarConfig((prev) => ({
|
|
13060
|
+
...prev,
|
|
13061
|
+
logo: prev.logo || logo
|
|
13062
|
+
}));
|
|
13063
|
+
}
|
|
12656
13064
|
}).finally(() => setStoreSettingsLoading(false));
|
|
12657
13065
|
fetch("/api/settings/multi_vendor").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
12658
13066
|
setMultiVendorEnabled(data.enabled !== "false");
|
|
@@ -12757,6 +13165,27 @@ function SettingsPage() {
|
|
|
12757
13165
|
}
|
|
12758
13166
|
})
|
|
12759
13167
|
});
|
|
13168
|
+
await fetch("/api/settings/store", {
|
|
13169
|
+
method: "PUT",
|
|
13170
|
+
headers: {
|
|
13171
|
+
"Content-Type": "application/json"
|
|
13172
|
+
},
|
|
13173
|
+
body: JSON.stringify({
|
|
13174
|
+
invoiceLogoUrl: {
|
|
13175
|
+
value: navbarConfig.logo || "",
|
|
13176
|
+
type: "public"
|
|
13177
|
+
},
|
|
13178
|
+
companyLogo: {
|
|
13179
|
+
value: navbarConfig.logo || "",
|
|
13180
|
+
type: "public"
|
|
13181
|
+
},
|
|
13182
|
+
logo: {
|
|
13183
|
+
value: navbarConfig.logo || "",
|
|
13184
|
+
type: "public"
|
|
13185
|
+
}
|
|
13186
|
+
})
|
|
13187
|
+
}).catch(() => {
|
|
13188
|
+
});
|
|
12760
13189
|
sonner.toast.success("Navbar saved");
|
|
12761
13190
|
} catch {
|
|
12762
13191
|
sonner.toast.error("Failed to save");
|
|
@@ -12780,8 +13209,57 @@ function SettingsPage() {
|
|
|
12780
13209
|
currency: {
|
|
12781
13210
|
value: currency,
|
|
12782
13211
|
type: "public"
|
|
13212
|
+
},
|
|
13213
|
+
invoiceTemplate: {
|
|
13214
|
+
value: invoiceTemplate || "classic",
|
|
13215
|
+
type: "public"
|
|
13216
|
+
},
|
|
13217
|
+
invoiceLogoUrl: {
|
|
13218
|
+
value: companyLogo,
|
|
13219
|
+
type: "public"
|
|
13220
|
+
},
|
|
13221
|
+
companyLogo: {
|
|
13222
|
+
value: companyLogo,
|
|
13223
|
+
type: "public"
|
|
13224
|
+
},
|
|
13225
|
+
logo: {
|
|
13226
|
+
value: companyLogo,
|
|
13227
|
+
type: "public"
|
|
13228
|
+
},
|
|
13229
|
+
companyName: {
|
|
13230
|
+
value: companyName,
|
|
13231
|
+
type: "public"
|
|
13232
|
+
},
|
|
13233
|
+
companyEmail: {
|
|
13234
|
+
value: companyEmail,
|
|
13235
|
+
type: "public"
|
|
13236
|
+
},
|
|
13237
|
+
companyPhone: {
|
|
13238
|
+
value: companyPhone,
|
|
13239
|
+
type: "public"
|
|
13240
|
+
},
|
|
13241
|
+
companyAddress: {
|
|
13242
|
+
value: companyAddress,
|
|
13243
|
+
type: "public"
|
|
13244
|
+
}
|
|
13245
|
+
})
|
|
13246
|
+
});
|
|
13247
|
+
const updatedNav = {
|
|
13248
|
+
...navbarConfig,
|
|
13249
|
+
logo: companyLogo
|
|
13250
|
+
};
|
|
13251
|
+
await fetch("/api/settings/theme", {
|
|
13252
|
+
method: "PUT",
|
|
13253
|
+
headers: {
|
|
13254
|
+
"Content-Type": "application/json"
|
|
13255
|
+
},
|
|
13256
|
+
body: JSON.stringify({
|
|
13257
|
+
navbar: {
|
|
13258
|
+
value: JSON.stringify(updatedNav),
|
|
13259
|
+
type: "public"
|
|
12783
13260
|
}
|
|
12784
13261
|
})
|
|
13262
|
+
}).catch(() => {
|
|
12785
13263
|
});
|
|
12786
13264
|
await fetch("/api/settings/multi_vendor", {
|
|
12787
13265
|
method: "PUT",
|
|
@@ -13065,7 +13543,7 @@ function SettingsPage() {
|
|
|
13065
13543
|
className: "text-sm text-gray-500 mb-4"
|
|
13066
13544
|
}, "Set logo and menu items."), /* @__PURE__ */ React.createElement(NavbarEditor, {
|
|
13067
13545
|
config: navbarConfig,
|
|
13068
|
-
onChange:
|
|
13546
|
+
onChange: handleNavbarConfigChange
|
|
13069
13547
|
})), /* @__PURE__ */ React.createElement(Button, {
|
|
13070
13548
|
onClick: handleSaveNavbar,
|
|
13071
13549
|
disabled: saving
|
|
@@ -13151,7 +13629,80 @@ function SettingsPage() {
|
|
|
13151
13629
|
className: "text-sm font-medium text-gray-700"
|
|
13152
13630
|
}, "Require event approval"), /* @__PURE__ */ React.createElement("p", {
|
|
13153
13631
|
className: "text-xs text-gray-500 mt-0.5"
|
|
13154
|
-
}, "When on, new vendor events start as pending. Admins approve or reject; approval activates the event (live)."))) : null), /* @__PURE__ */ React.createElement("div",
|
|
13632
|
+
}, "When on, new vendor events start as pending. Admins approve or reject; approval activates the event (live)."))) : null), /* @__PURE__ */ React.createElement("div", {
|
|
13633
|
+
className: "space-y-4 p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
13634
|
+
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", {
|
|
13635
|
+
className: "text-base font-semibold text-gray-900"
|
|
13636
|
+
}, "Invoice Header & Store Details"), /* @__PURE__ */ React.createElement("p", {
|
|
13637
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13638
|
+
}, "Configure company logo, name, email, phone, and address displayed on generated invoice headers.")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ImageOrUrlField, {
|
|
13639
|
+
label: "Company Logo",
|
|
13640
|
+
value: companyLogo,
|
|
13641
|
+
onChange: handleStoreLogoChange,
|
|
13642
|
+
previewVariant: "logo",
|
|
13643
|
+
placeholder: "Logo image URL or upload image"
|
|
13644
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13645
|
+
className: "grid grid-cols-1 sm:grid-cols-2 gap-4"
|
|
13646
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
13647
|
+
className: "space-y-1.5"
|
|
13648
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
13649
|
+
className: "text-xs font-medium text-gray-700"
|
|
13650
|
+
}, "Company Name"), /* @__PURE__ */ React.createElement(Input, {
|
|
13651
|
+
value: companyName,
|
|
13652
|
+
onChange: /* @__PURE__ */ __name((e) => setCompanyName(e.target.value), "onChange"),
|
|
13653
|
+
placeholder: "e.g. Acme Corp",
|
|
13654
|
+
className: "h-9 text-sm bg-white"
|
|
13655
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13656
|
+
className: "space-y-1.5"
|
|
13657
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
13658
|
+
className: "text-xs font-medium text-gray-700"
|
|
13659
|
+
}, "Company Email"), /* @__PURE__ */ React.createElement(Input, {
|
|
13660
|
+
type: "email",
|
|
13661
|
+
value: companyEmail,
|
|
13662
|
+
onChange: /* @__PURE__ */ __name((e) => setCompanyEmail(e.target.value), "onChange"),
|
|
13663
|
+
placeholder: "e.g. support@acme.com",
|
|
13664
|
+
className: "h-9 text-sm bg-white"
|
|
13665
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13666
|
+
className: "space-y-1.5"
|
|
13667
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
13668
|
+
className: "text-xs font-medium text-gray-700"
|
|
13669
|
+
}, "Company Phone"), /* @__PURE__ */ React.createElement(Input, {
|
|
13670
|
+
type: "tel",
|
|
13671
|
+
value: companyPhone,
|
|
13672
|
+
onChange: /* @__PURE__ */ __name((e) => setCompanyPhone(e.target.value), "onChange"),
|
|
13673
|
+
placeholder: "e.g. +1 555-0199",
|
|
13674
|
+
className: "h-9 text-sm bg-white"
|
|
13675
|
+
})), /* @__PURE__ */ React.createElement("div", {
|
|
13676
|
+
className: "space-y-1.5 sm:col-span-2"
|
|
13677
|
+
}, /* @__PURE__ */ React.createElement(Label3, {
|
|
13678
|
+
className: "text-xs font-medium text-gray-700"
|
|
13679
|
+
}, "Company Address"), /* @__PURE__ */ React.createElement(Textarea, {
|
|
13680
|
+
value: companyAddress,
|
|
13681
|
+
onChange: /* @__PURE__ */ __name((e) => setCompanyAddress(e.target.value), "onChange"),
|
|
13682
|
+
placeholder: "e.g. 123 Business Rd, Suite 100\nNew York, NY 10001",
|
|
13683
|
+
rows: 3,
|
|
13684
|
+
className: "text-sm bg-white"
|
|
13685
|
+
})))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
13686
|
+
className: "block text-sm font-semibold text-gray-700 mb-1"
|
|
13687
|
+
}, "Invoice PDF template"), /* @__PURE__ */ React.createElement("p", {
|
|
13688
|
+
className: "text-xs text-gray-500 mb-3"
|
|
13689
|
+
}, "Used when generating invoices in the app (and as fallback when ERP invoice is unavailable). Company logo, name, email, phone, and address set above are shown in the invoice header."), /* @__PURE__ */ React.createElement("div", {
|
|
13690
|
+
className: "grid gap-3 sm:grid-cols-2"
|
|
13691
|
+
}, INVOICE_TEMPLATE_CARDS.map((opt) => {
|
|
13692
|
+
const selected = invoiceTemplate === opt.id;
|
|
13693
|
+
return /* @__PURE__ */ React.createElement("button", {
|
|
13694
|
+
key: opt.id,
|
|
13695
|
+
type: "button",
|
|
13696
|
+
onClick: /* @__PURE__ */ __name(() => setInvoiceTemplate(opt.id), "onClick"),
|
|
13697
|
+
className: `text-left rounded-lg border p-3 transition ${selected ? "border-blue-500 bg-blue-50 ring-1 ring-blue-500" : "border-gray-200 bg-white hover:border-gray-300"}`
|
|
13698
|
+
}, /* @__PURE__ */ React.createElement(InvoiceTemplatePreview, {
|
|
13699
|
+
id: opt.id
|
|
13700
|
+
}), /* @__PURE__ */ React.createElement("div", {
|
|
13701
|
+
className: "text-sm font-semibold text-gray-800 mt-2"
|
|
13702
|
+
}, opt.label), /* @__PURE__ */ React.createElement("div", {
|
|
13703
|
+
className: "text-xs text-gray-500 mt-0.5"
|
|
13704
|
+
}, opt.description));
|
|
13705
|
+
}))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
13155
13706
|
className: "flex items-center justify-between mb-1"
|
|
13156
13707
|
}, /* @__PURE__ */ React.createElement("label", {
|
|
13157
13708
|
className: "block text-sm font-semibold text-gray-700"
|
|
@@ -13339,7 +13890,7 @@ function SettingsPage() {
|
|
|
13339
13890
|
className: "h-4 w-4 mr-2"
|
|
13340
13891
|
}), saving ? "Saving..." : "Save")));
|
|
13341
13892
|
}
|
|
13342
|
-
var DEFAULT_CURRENCY, DEFAULTS;
|
|
13893
|
+
var DEFAULT_CURRENCY, DEFAULTS, INVOICE_TEMPLATE_CARDS;
|
|
13343
13894
|
var init_SettingsPage = __esm({
|
|
13344
13895
|
"src/admin/pages/SettingsPage.tsx"() {
|
|
13345
13896
|
"use client";
|
|
@@ -13348,6 +13899,8 @@ var init_SettingsPage = __esm({
|
|
|
13348
13899
|
init_switch();
|
|
13349
13900
|
init_label();
|
|
13350
13901
|
init_admin_config_context();
|
|
13902
|
+
init_textarea();
|
|
13903
|
+
init_ImageOrUrlField();
|
|
13351
13904
|
init_NavbarEditor();
|
|
13352
13905
|
DEFAULT_CURRENCY = "INR";
|
|
13353
13906
|
DEFAULTS = {
|
|
@@ -13355,6 +13908,29 @@ var init_SettingsPage = __esm({
|
|
|
13355
13908
|
secondaryColor: "#6366f1",
|
|
13356
13909
|
compactView: "false"
|
|
13357
13910
|
};
|
|
13911
|
+
INVOICE_TEMPLATE_CARDS = [
|
|
13912
|
+
{
|
|
13913
|
+
id: "classic",
|
|
13914
|
+
label: "Classic",
|
|
13915
|
+
description: "Traditional invoice with lined items and totals footer."
|
|
13916
|
+
},
|
|
13917
|
+
{
|
|
13918
|
+
id: "modern",
|
|
13919
|
+
label: "Modern",
|
|
13920
|
+
description: "Clean layout with accent header and open spacing."
|
|
13921
|
+
},
|
|
13922
|
+
{
|
|
13923
|
+
id: "compact",
|
|
13924
|
+
label: "Compact",
|
|
13925
|
+
description: "Dense layout suited to longer item lists."
|
|
13926
|
+
},
|
|
13927
|
+
{
|
|
13928
|
+
id: "bold",
|
|
13929
|
+
label: "Bold",
|
|
13930
|
+
description: "Strong header band and clear payment summary."
|
|
13931
|
+
}
|
|
13932
|
+
];
|
|
13933
|
+
__name(InvoiceTemplatePreview, "InvoiceTemplatePreview");
|
|
13358
13934
|
__name(hexToHsl, "hexToHsl");
|
|
13359
13935
|
__name(genId, "genId");
|
|
13360
13936
|
__name(SettingsPage, "SettingsPage");
|
|
@@ -31680,6 +32256,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31680
32256
|
const [saving, setSaving] = React26.useState(false);
|
|
31681
32257
|
const [errors, setErrors] = React26.useState([]);
|
|
31682
32258
|
const [name, setName] = React26.useState("");
|
|
32259
|
+
const [slug, setSlug] = React26.useState("");
|
|
31683
32260
|
const [desc, setDesc] = React26.useState("");
|
|
31684
32261
|
const [eventId, setEventId] = React26.useState("");
|
|
31685
32262
|
const [priceStr, setPriceStr] = React26.useState("");
|
|
@@ -31819,6 +32396,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31819
32396
|
const data = await res.json();
|
|
31820
32397
|
if (cancelled) return;
|
|
31821
32398
|
setName(data.name ?? "");
|
|
32399
|
+
setSlug(typeof data.slug === "string" ? data.slug : "");
|
|
31822
32400
|
setDesc(data.desc ?? "");
|
|
31823
32401
|
setEventId(data.eventId != null ? String(data.eventId) : "");
|
|
31824
32402
|
if (data.eventId != null) {
|
|
@@ -31859,6 +32437,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31859
32437
|
if (create && duplicateSourceId) {
|
|
31860
32438
|
const rawName = (data.name ?? "").trim() || "Combo";
|
|
31861
32439
|
setName(rawName.toLowerCase().startsWith("copy of ") ? `${rawName} (copy)` : `Copy of ${rawName}`);
|
|
32440
|
+
setSlug("");
|
|
31862
32441
|
}
|
|
31863
32442
|
} catch {
|
|
31864
32443
|
if (!cancelled) setErrors([
|
|
@@ -31944,15 +32523,15 @@ function ComboEditPage({ comboId }) {
|
|
|
31944
32523
|
]);
|
|
31945
32524
|
return;
|
|
31946
32525
|
}
|
|
31947
|
-
if (addonItems.length
|
|
32526
|
+
if (addonItems.length < minSelectableItems) {
|
|
31948
32527
|
setErrors([
|
|
31949
|
-
`Please add at least ${minSelectableItems} add-on item(s). Currently ${addonItems.length} selected.`
|
|
32528
|
+
`Please add at least ${minSelectableItems} add-on item(s) so customers can meet the minimum. Currently ${addonItems.length} selected.`
|
|
31950
32529
|
]);
|
|
31951
32530
|
return;
|
|
31952
32531
|
}
|
|
31953
|
-
if (addonItems.length
|
|
32532
|
+
if (maxSelectableItems > addonItems.length) {
|
|
31954
32533
|
setErrors([
|
|
31955
|
-
`
|
|
32534
|
+
`Max add-on items (${maxSelectableItems}) cannot be greater than the number of add-on items selected (${addonItems.length}).`
|
|
31956
32535
|
]);
|
|
31957
32536
|
return;
|
|
31958
32537
|
}
|
|
@@ -31972,6 +32551,7 @@ function ComboEditPage({ comboId }) {
|
|
|
31972
32551
|
const resolvedEventId = trimmedEventId && /^\d+$/.test(trimmedEventId) ? Number(trimmedEventId) : null;
|
|
31973
32552
|
const payload = {
|
|
31974
32553
|
name: trimmedName,
|
|
32554
|
+
slug: slug.trim() || void 0,
|
|
31975
32555
|
desc: desc || null,
|
|
31976
32556
|
eventId: resolvedEventId,
|
|
31977
32557
|
price,
|
|
@@ -32063,6 +32643,14 @@ function ComboEditPage({ comboId }) {
|
|
|
32063
32643
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
32064
32644
|
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
32065
32645
|
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
32646
|
+
}, "Slug"), /* @__PURE__ */ React.createElement("input", {
|
|
32647
|
+
type: "text",
|
|
32648
|
+
value: slug,
|
|
32649
|
+
onChange: /* @__PURE__ */ __name((e) => setSlug(e.target.value), "onChange"),
|
|
32650
|
+
placeholder: "Auto generate from name",
|
|
32651
|
+
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
32652
|
+
})), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", {
|
|
32653
|
+
className: "block text-xs font-medium text-gray-600 mb-1"
|
|
32066
32654
|
}, "Description"), /* @__PURE__ */ React.createElement("textarea", {
|
|
32067
32655
|
value: desc,
|
|
32068
32656
|
onChange: /* @__PURE__ */ __name((e) => setDesc(e.target.value), "onChange"),
|
|
@@ -32176,7 +32764,7 @@ function ComboEditPage({ comboId }) {
|
|
|
32176
32764
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
|
|
32177
32765
|
}))), /* @__PURE__ */ React.createElement("p", {
|
|
32178
32766
|
className: "text-xs text-gray-400 mt-1"
|
|
32179
|
-
}, "Customers
|
|
32767
|
+
}, "List all available add-ons above. Customers choose between ", minSelectableItems, " and", " ", maxSelectableItems, " of those items (max cannot exceed the add-on list size)."))), /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", {
|
|
32180
32768
|
className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2"
|
|
32181
32769
|
}, "Availability"), /* @__PURE__ */ React.createElement("div", {
|
|
32182
32770
|
className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-4"
|
|
@@ -33675,6 +34263,7 @@ var init_DiscountDetailsPage = __esm({
|
|
|
33675
34263
|
category: "Category",
|
|
33676
34264
|
cart: "Cart",
|
|
33677
34265
|
paymentMethod: "Payment method",
|
|
34266
|
+
deviceType: "Device type",
|
|
33678
34267
|
order: "Order",
|
|
33679
34268
|
quantity: "Quantity",
|
|
33680
34269
|
time: "Time",
|
|
@@ -33766,6 +34355,15 @@ function conditionToRule(c) {
|
|
|
33766
34355
|
v: c.paymentMethod ?? ""
|
|
33767
34356
|
}
|
|
33768
34357
|
};
|
|
34358
|
+
case "deviceType":
|
|
34359
|
+
return {
|
|
34360
|
+
...base,
|
|
34361
|
+
type: "deviceType",
|
|
34362
|
+
comparisonOperator: "eq",
|
|
34363
|
+
value: {
|
|
34364
|
+
v: c.deviceType ?? ""
|
|
34365
|
+
}
|
|
34366
|
+
};
|
|
33769
34367
|
case "category":
|
|
33770
34368
|
return {
|
|
33771
34369
|
...base,
|
|
@@ -33938,6 +34536,12 @@ function ruleTreeToFriendly(rules) {
|
|
|
33938
34536
|
kind: "paymentMethod",
|
|
33939
34537
|
paymentMethod: v
|
|
33940
34538
|
};
|
|
34539
|
+
case "deviceType":
|
|
34540
|
+
return {
|
|
34541
|
+
id: rule._clientId ?? uid(),
|
|
34542
|
+
kind: "deviceType",
|
|
34543
|
+
deviceType: v
|
|
34544
|
+
};
|
|
33941
34545
|
case "category":
|
|
33942
34546
|
return {
|
|
33943
34547
|
id: rule._clientId ?? uid(),
|
|
@@ -34500,6 +35104,9 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
|
34500
35104
|
paymentMethod: /* @__PURE__ */ React.createElement(LucideIcons.DollarSign, {
|
|
34501
35105
|
className: "h-3.5 w-3.5 text-indigo-500"
|
|
34502
35106
|
}),
|
|
35107
|
+
deviceType: /* @__PURE__ */ React.createElement(LucideIcons.DollarSign, {
|
|
35108
|
+
className: "h-3.5 w-3.5 text-cyan-500"
|
|
35109
|
+
}),
|
|
34503
35110
|
category: /* @__PURE__ */ React.createElement(LucideIcons.Package, {
|
|
34504
35111
|
className: "h-3.5 w-3.5 text-teal-500"
|
|
34505
35112
|
}),
|
|
@@ -34542,6 +35149,8 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
|
34542
35149
|
}, "User type"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34543
35150
|
value: "paymentMethod"
|
|
34544
35151
|
}, "Payment method"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35152
|
+
value: "deviceType"
|
|
35153
|
+
}, "Device type"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34545
35154
|
value: "category"
|
|
34546
35155
|
}, "Category"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34547
35156
|
value: "customers"
|
|
@@ -34695,7 +35304,21 @@ function ConditionCard({ condition, onChange, onRemove, eventsOn }) {
|
|
|
34695
35304
|
value: "cod"
|
|
34696
35305
|
}, "Cash on Delivery"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
34697
35306
|
value: "wallet"
|
|
34698
|
-
}, "Wallet"))), condition.kind === "
|
|
35307
|
+
}, "Wallet"))), condition.kind === "deviceType" && /* @__PURE__ */ React.createElement(Select, {
|
|
35308
|
+
value: condition.deviceType ?? "",
|
|
35309
|
+
onValueChange: /* @__PURE__ */ __name((v) => onChange({
|
|
35310
|
+
...condition,
|
|
35311
|
+
deviceType: v
|
|
35312
|
+
}), "onValueChange")
|
|
35313
|
+
}, /* @__PURE__ */ React.createElement(SelectTrigger, {
|
|
35314
|
+
className: "h-8 text-xs"
|
|
35315
|
+
}, /* @__PURE__ */ React.createElement(SelectValue, {
|
|
35316
|
+
placeholder: "Select device type\u2026"
|
|
35317
|
+
})), /* @__PURE__ */ React.createElement(SelectContent, null, /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35318
|
+
value: "web"
|
|
35319
|
+
}, "Web"), /* @__PURE__ */ React.createElement(SelectItem, {
|
|
35320
|
+
value: "mobile"
|
|
35321
|
+
}, "Mobile"))), condition.kind === "category" && /* @__PURE__ */ React.createElement(CategoryPicker, {
|
|
34699
35322
|
value: condition.categoryId,
|
|
34700
35323
|
label: condition.categoryName,
|
|
34701
35324
|
onChange: /* @__PURE__ */ __name((id, name) => onChange({
|
|
@@ -37920,6 +38543,11 @@ var init_AdminPageResolver = __esm({
|
|
|
37920
38543
|
field: "email",
|
|
37921
38544
|
displayName: "Email"
|
|
37922
38545
|
},
|
|
38546
|
+
{
|
|
38547
|
+
field: "inviteStatus",
|
|
38548
|
+
displayName: "Invite status",
|
|
38549
|
+
hideInForm: true
|
|
38550
|
+
},
|
|
37923
38551
|
{
|
|
37924
38552
|
field: "blocked",
|
|
37925
38553
|
displayName: "Blocked",
|
|
@@ -38125,6 +38753,29 @@ function LayoutSettingsPage() {
|
|
|
38125
38753
|
footer: JSON.stringify(footerValues)
|
|
38126
38754
|
})
|
|
38127
38755
|
});
|
|
38756
|
+
if (navbarConfig.logo) {
|
|
38757
|
+
await fetch("/api/settings/store", {
|
|
38758
|
+
method: "PUT",
|
|
38759
|
+
headers: {
|
|
38760
|
+
"Content-Type": "application/json"
|
|
38761
|
+
},
|
|
38762
|
+
body: JSON.stringify({
|
|
38763
|
+
invoiceLogoUrl: {
|
|
38764
|
+
value: navbarConfig.logo,
|
|
38765
|
+
type: "public"
|
|
38766
|
+
},
|
|
38767
|
+
companyLogo: {
|
|
38768
|
+
value: navbarConfig.logo,
|
|
38769
|
+
type: "public"
|
|
38770
|
+
},
|
|
38771
|
+
logo: {
|
|
38772
|
+
value: navbarConfig.logo,
|
|
38773
|
+
type: "public"
|
|
38774
|
+
}
|
|
38775
|
+
})
|
|
38776
|
+
}).catch(() => {
|
|
38777
|
+
});
|
|
38778
|
+
}
|
|
38128
38779
|
} finally {
|
|
38129
38780
|
setSaving(false);
|
|
38130
38781
|
}
|
|
@@ -38551,18 +39202,26 @@ var init_config = __esm({
|
|
|
38551
39202
|
];
|
|
38552
39203
|
}
|
|
38553
39204
|
});
|
|
39205
|
+
function ForceAdminLightMode() {
|
|
39206
|
+
useForceAdminLightMode();
|
|
39207
|
+
return null;
|
|
39208
|
+
}
|
|
38554
39209
|
function CmsProviders({ children }) {
|
|
38555
39210
|
return /* @__PURE__ */ React.createElement(nextThemes.ThemeProvider, {
|
|
38556
39211
|
attribute: "class",
|
|
38557
|
-
defaultTheme: "
|
|
38558
|
-
|
|
38559
|
-
|
|
39212
|
+
defaultTheme: "light",
|
|
39213
|
+
forcedTheme: "light",
|
|
39214
|
+
enableSystem: false,
|
|
39215
|
+
enableColorScheme: true
|
|
39216
|
+
}, /* @__PURE__ */ React.createElement(ForceAdminLightMode, null), children, /* @__PURE__ */ React.createElement(sonner.Toaster, {
|
|
38560
39217
|
position: "top-right"
|
|
38561
39218
|
}));
|
|
38562
39219
|
}
|
|
38563
39220
|
var init_CmsProviders = __esm({
|
|
38564
39221
|
"src/admin/CmsProviders.tsx"() {
|
|
38565
39222
|
"use client";
|
|
39223
|
+
init_force_light_mode();
|
|
39224
|
+
__name(ForceAdminLightMode, "ForceAdminLightMode");
|
|
38566
39225
|
__name(CmsProviders, "CmsProviders");
|
|
38567
39226
|
}
|
|
38568
39227
|
});
|