@infuro/cms-core 1.0.33 → 1.0.34
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 +9 -6
- package/dist/admin.cjs +980 -801
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +7 -2
- package/dist/admin.d.ts +7 -2
- package/dist/admin.js +1021 -842
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +1858 -848
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +1824 -815
- package/dist/api.js.map +1 -1
- package/dist/auth.cjs +94 -11
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +17 -1
- package/dist/auth.d.ts +17 -1
- package/dist/auth.js +89 -11
- package/dist/auth.js.map +1 -1
- package/dist/cli.cjs +39 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -1
- package/dist/{index-rZTnpK7y.d.cts → index-Bf5GO8fu.d.cts} +3 -2
- package/dist/{index-DWd5Gjc4.d.ts → index-DOiJuMQA.d.ts} +3 -2
- package/dist/index.cjs +2078 -841
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -8
- package/dist/index.d.ts +83 -8
- package/dist/index.js +2013 -786
- package/dist/index.js.map +1 -1
- package/dist/migrations/1778660000004-CreateCustomerAndCustomerContacts.ts +99 -0
- package/dist/migrations/1778660000005-AddCustomerIdToVendorCustomers.ts +39 -0
- package/dist/migrations/1778660000006-UpdateVendorCustomersContactNullable.ts +36 -0
- package/dist/migrations/1778660000007-VendorOwnerCustomerContactsPermission.ts +39 -0
- package/dist/migrations/1779300000000-VendorOwnerContactsPermission.ts +39 -0
- package/dist/migrations/1779400000000-AddCustomerContactIdToOrders.ts +33 -0
- package/package.json +4 -3
package/dist/admin.cjs
CHANGED
|
@@ -122,6 +122,30 @@ var init_permission_entities = __esm({
|
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
+
// src/auth/role-helpers.ts
|
|
126
|
+
function isSuperAdmin(user) {
|
|
127
|
+
if (!user) return false;
|
|
128
|
+
if (user.groupId === SUPER_ADMIN_GROUP_ID) return true;
|
|
129
|
+
if (user.isRBACAdmin === true) return true;
|
|
130
|
+
return isSuperAdminGroupName(user.groupName);
|
|
131
|
+
}
|
|
132
|
+
function isVendorAdmin(user) {
|
|
133
|
+
if (!user?.email || isSuperAdmin(user)) return false;
|
|
134
|
+
if (user.groupId === VENDOR_ADMIN_GROUP_ID) return true;
|
|
135
|
+
if ((user.vendorIds?.length ?? 0) > 0) return true;
|
|
136
|
+
return isVendorGroupName(user.groupName);
|
|
137
|
+
}
|
|
138
|
+
var SUPER_ADMIN_GROUP_ID, VENDOR_ADMIN_GROUP_ID;
|
|
139
|
+
var init_role_helpers = __esm({
|
|
140
|
+
"src/auth/role-helpers.ts"() {
|
|
141
|
+
"use strict";
|
|
142
|
+
init_permission_entities();
|
|
143
|
+
init_vendor_scope();
|
|
144
|
+
SUPER_ADMIN_GROUP_ID = 1;
|
|
145
|
+
VENDOR_ADMIN_GROUP_ID = 5;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
125
149
|
// src/auth/vendor-scope.ts
|
|
126
150
|
function isVendorGroupName(name) {
|
|
127
151
|
if (!name?.trim()) return false;
|
|
@@ -130,12 +154,12 @@ function isVendorGroupName(name) {
|
|
|
130
154
|
}
|
|
131
155
|
function isPlatformAdministrator(user) {
|
|
132
156
|
if (!user?.email) return false;
|
|
133
|
-
return
|
|
157
|
+
return isSuperAdmin(user);
|
|
134
158
|
}
|
|
135
159
|
function isVendorPortalUser(user) {
|
|
136
160
|
if (user?.isVendorPortal === true) return true;
|
|
137
161
|
if (!user?.email || isPlatformAdministrator(user)) return false;
|
|
138
|
-
if ((user
|
|
162
|
+
if (isVendorAdmin(user)) return true;
|
|
139
163
|
return isVendorGroupName(user.groupName);
|
|
140
164
|
}
|
|
141
165
|
function isVendorOwner(user) {
|
|
@@ -159,7 +183,7 @@ var VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, VENDOR_OWNER_GROUP
|
|
|
159
183
|
var init_vendor_scope = __esm({
|
|
160
184
|
"src/auth/vendor-scope.ts"() {
|
|
161
185
|
"use strict";
|
|
162
|
-
|
|
186
|
+
init_role_helpers();
|
|
163
187
|
VENDOR_SCOPED_STORE_ENTITIES = /* @__PURE__ */ new Set([
|
|
164
188
|
"products",
|
|
165
189
|
"collections",
|
|
@@ -821,8 +845,8 @@ function RequiredMark() {
|
|
|
821
845
|
] });
|
|
822
846
|
}
|
|
823
847
|
function ContactDetailPage({ contactId }) {
|
|
824
|
-
const router = (0,
|
|
825
|
-
const searchParams = (0,
|
|
848
|
+
const router = (0, import_navigation30.useRouter)();
|
|
849
|
+
const searchParams = (0, import_navigation30.useSearchParams)();
|
|
826
850
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/contacts";
|
|
827
851
|
const [contact, setContact] = (0, import_react67.useState)(null);
|
|
828
852
|
const [loading, setLoading] = (0, import_react67.useState)(true);
|
|
@@ -940,7 +964,7 @@ function ContactDetailPage({ contactId }) {
|
|
|
940
964
|
return /* @__PURE__ */ (0, import_jsx_runtime82.jsxs)("div", { className: "rounded-lg bg-white p-6 shadow-md", children: [
|
|
941
965
|
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)("p", { className: "text-red-600", children: error ?? "Not found" }),
|
|
942
966
|
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(import_link15.default, { href: listReturnUrl, children: /* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Button, { variant: "outline", className: "mt-4", children: [
|
|
943
|
-
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(import_lucide_react50.X, { className: "h-4 w-4 mr-2" }),
|
|
944
968
|
"Close"
|
|
945
969
|
] }) })
|
|
946
970
|
] });
|
|
@@ -1017,7 +1041,7 @@ function ContactDetailPage({ contactId }) {
|
|
|
1017
1041
|
},
|
|
1018
1042
|
className: "inline-flex items-center gap-1",
|
|
1019
1043
|
children: [
|
|
1020
|
-
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
|
|
1044
|
+
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(import_lucide_react50.Plus, { className: "h-3.5 w-3.5" }),
|
|
1021
1045
|
" Add address"
|
|
1022
1046
|
]
|
|
1023
1047
|
}
|
|
@@ -1042,7 +1066,7 @@ function ContactDetailPage({ contactId }) {
|
|
|
1042
1066
|
title: "Delete address",
|
|
1043
1067
|
"aria-label": `Delete address ${a.id}`,
|
|
1044
1068
|
onClick: () => setAddressPendingDelete(a),
|
|
1045
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
|
|
1069
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(import_lucide_react50.Trash2, { className: "h-4 w-4" })
|
|
1046
1070
|
}
|
|
1047
1071
|
) })
|
|
1048
1072
|
] }, a.id)) })
|
|
@@ -1245,16 +1269,16 @@ function ContactDetailPage({ contactId }) {
|
|
|
1245
1269
|
)
|
|
1246
1270
|
] });
|
|
1247
1271
|
}
|
|
1248
|
-
var import_react67, import_link15,
|
|
1272
|
+
var import_react67, import_link15, import_navigation30, import_lucide_react50, import_sonner13, import_country_state_city3, import_jsx_runtime82, ADDRESS_TAGS, CONTACT_TYPE_LABELS;
|
|
1249
1273
|
var init_ContactDetailPage = __esm({
|
|
1250
1274
|
"src/admin/pages/ContactDetailPage.tsx"() {
|
|
1251
1275
|
"use strict";
|
|
1252
1276
|
"use client";
|
|
1253
1277
|
import_react67 = require("react");
|
|
1254
1278
|
import_link15 = __toESM(require("next/link"), 1);
|
|
1255
|
-
|
|
1279
|
+
import_navigation30 = require("next/navigation");
|
|
1256
1280
|
init_admin_list_return_url();
|
|
1257
|
-
|
|
1281
|
+
import_lucide_react50 = require("lucide-react");
|
|
1258
1282
|
import_sonner13 = require("sonner");
|
|
1259
1283
|
init_button();
|
|
1260
1284
|
init_input();
|
|
@@ -1594,7 +1618,7 @@ var defaultValue = {
|
|
|
1594
1618
|
var AdminConfigContext = (0, import_react4.createContext)(defaultValue);
|
|
1595
1619
|
|
|
1596
1620
|
// src/lib/cms-version.ts
|
|
1597
|
-
var CMS_VERSION = true ? "1.0.
|
|
1621
|
+
var CMS_VERSION = true ? "1.0.34" : "0.0.0";
|
|
1598
1622
|
|
|
1599
1623
|
// src/components/Admin/Sidebar.tsx
|
|
1600
1624
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -1802,7 +1826,7 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
1802
1826
|
className: `${linkCls} ${isActive("/admin/vendor-onboard") ? linkActive : linkInactive}`,
|
|
1803
1827
|
children: [
|
|
1804
1828
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Store, { className: `h-4 w-4 mr-2 ${isActive("/admin/vendor-onboard") ? iconActive : iconInactive}` }),
|
|
1805
|
-
"
|
|
1829
|
+
"Vendors"
|
|
1806
1830
|
]
|
|
1807
1831
|
}
|
|
1808
1832
|
) }),
|
|
@@ -2787,7 +2811,8 @@ function CreateEditForm({
|
|
|
2787
2811
|
apiEndpoint,
|
|
2788
2812
|
columns,
|
|
2789
2813
|
existingData,
|
|
2790
|
-
duplicateSeed
|
|
2814
|
+
duplicateSeed,
|
|
2815
|
+
resource
|
|
2791
2816
|
}) {
|
|
2792
2817
|
const isMobile = useIsMobile();
|
|
2793
2818
|
const { currency } = (0, import_react13.useContext)(AdminConfigContext);
|
|
@@ -2795,6 +2820,9 @@ function CreateEditForm({
|
|
|
2795
2820
|
currency: currency || "INR"
|
|
2796
2821
|
});
|
|
2797
2822
|
const [errors, setErrors] = (0, import_react13.useState)({});
|
|
2823
|
+
const [password, setPassword] = (0, import_react13.useState)("");
|
|
2824
|
+
const [confirmPassword, setConfirmPassword] = (0, import_react13.useState)("");
|
|
2825
|
+
const isCustomerCreate = resource === "vendor_customers" && !existingData;
|
|
2798
2826
|
(0, import_react13.useEffect)(() => {
|
|
2799
2827
|
fetch("/api/settings/store").then((res) => res.json()).then((data) => {
|
|
2800
2828
|
setFormData((prev) => ({
|
|
@@ -2824,14 +2852,10 @@ function CreateEditForm({
|
|
|
2824
2852
|
};
|
|
2825
2853
|
}).filter(Boolean)
|
|
2826
2854
|
);
|
|
2827
|
-
}).catch(
|
|
2828
|
-
(err) => console.error("Failed to load customers", err)
|
|
2829
|
-
);
|
|
2855
|
+
}).catch((err) => console.error("Failed to load customers", err));
|
|
2830
2856
|
}, [columns]);
|
|
2831
2857
|
const handleCustomerSelect = (customerId) => {
|
|
2832
|
-
const customer = customers.find(
|
|
2833
|
-
(c) => String(c.id) === customerId
|
|
2834
|
-
);
|
|
2858
|
+
const customer = customers.find((c) => String(c.id) === customerId);
|
|
2835
2859
|
if (!customer) return;
|
|
2836
2860
|
setFormData((prev) => ({
|
|
2837
2861
|
...prev,
|
|
@@ -2845,23 +2869,14 @@ function CreateEditForm({
|
|
|
2845
2869
|
const [totalLoading, setTotalLoading] = (0, import_react13.useState)(false);
|
|
2846
2870
|
(0, import_react13.useEffect)(() => {
|
|
2847
2871
|
return () => {
|
|
2848
|
-
if (debounceRef.current)
|
|
2849
|
-
clearTimeout(debounceRef.current);
|
|
2850
|
-
}
|
|
2872
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2851
2873
|
};
|
|
2852
2874
|
}, []);
|
|
2853
2875
|
const recalculateTotal = (itemsSummaryValue) => {
|
|
2854
|
-
if (debounceRef.current)
|
|
2855
|
-
clearTimeout(debounceRef.current);
|
|
2856
|
-
}
|
|
2876
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2857
2877
|
const names = itemsSummaryValue.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2858
2878
|
if (names.length === 0) {
|
|
2859
|
-
setFormData((prev) => ({
|
|
2860
|
-
...prev,
|
|
2861
|
-
subtotal: "",
|
|
2862
|
-
tax: "",
|
|
2863
|
-
total: ""
|
|
2864
|
-
}));
|
|
2879
|
+
setFormData((prev) => ({ ...prev, subtotal: "", tax: "", total: "" }));
|
|
2865
2880
|
return;
|
|
2866
2881
|
}
|
|
2867
2882
|
debounceRef.current = setTimeout(async () => {
|
|
@@ -2869,12 +2884,8 @@ function CreateEditForm({
|
|
|
2869
2884
|
try {
|
|
2870
2885
|
const res = await fetch("/api/orders/calculate", {
|
|
2871
2886
|
method: "POST",
|
|
2872
|
-
headers: {
|
|
2873
|
-
|
|
2874
|
-
},
|
|
2875
|
-
body: JSON.stringify({
|
|
2876
|
-
itemsSummary: itemsSummaryValue
|
|
2877
|
-
})
|
|
2887
|
+
headers: { "Content-Type": "application/json" },
|
|
2888
|
+
body: JSON.stringify({ itemsSummary: itemsSummaryValue })
|
|
2878
2889
|
});
|
|
2879
2890
|
if (!res.ok) return;
|
|
2880
2891
|
const data = await res.json();
|
|
@@ -2899,13 +2910,9 @@ function CreateEditForm({
|
|
|
2899
2910
|
const path = col.field || col.key;
|
|
2900
2911
|
if (!path) return;
|
|
2901
2912
|
const v = getNestedValue(ex, path);
|
|
2902
|
-
if (v !== void 0)
|
|
2903
|
-
merged[path] = v === null ? "" : v;
|
|
2904
|
-
}
|
|
2913
|
+
if (v !== void 0) merged[path] = v === null ? "" : v;
|
|
2905
2914
|
});
|
|
2906
|
-
columns.filter(
|
|
2907
|
-
(col) => !col.hideInForm && isDateFormColumn(col.type)
|
|
2908
|
-
).forEach((col) => {
|
|
2915
|
+
columns.filter((col) => !col.hideInForm && isDateFormColumn(col.type)).forEach((col) => {
|
|
2909
2916
|
const path = col.field || col.key;
|
|
2910
2917
|
if (!path) return;
|
|
2911
2918
|
const raw = merged[path];
|
|
@@ -2921,9 +2928,7 @@ function CreateEditForm({
|
|
|
2921
2928
|
).forEach((col) => {
|
|
2922
2929
|
const path = col.field || col.key;
|
|
2923
2930
|
if (!path) return;
|
|
2924
|
-
const allowed = new Set(
|
|
2925
|
-
col.options.map((o) => String(o.value))
|
|
2926
|
-
);
|
|
2931
|
+
const allowed = new Set(col.options.map((o) => String(o.value)));
|
|
2927
2932
|
const raw = merged[path];
|
|
2928
2933
|
if (raw != null && raw !== "" && !allowed.has(String(raw))) {
|
|
2929
2934
|
merged[path] = "";
|
|
@@ -2931,6 +2936,8 @@ function CreateEditForm({
|
|
|
2931
2936
|
});
|
|
2932
2937
|
setFormData(merged);
|
|
2933
2938
|
} else {
|
|
2939
|
+
setPassword("");
|
|
2940
|
+
setConfirmPassword("");
|
|
2934
2941
|
setFormData(
|
|
2935
2942
|
columns.filter((col) => !col.hideInForm).reduce((acc, col) => {
|
|
2936
2943
|
const path = col.field || col.key;
|
|
@@ -2939,29 +2946,18 @@ function CreateEditForm({
|
|
|
2939
2946
|
if (isDateFormColumn(col.type) && (initial === "" || initial == null)) {
|
|
2940
2947
|
initial = todayLocalDateString();
|
|
2941
2948
|
}
|
|
2942
|
-
return {
|
|
2943
|
-
...acc,
|
|
2944
|
-
[path]: initial
|
|
2945
|
-
};
|
|
2949
|
+
return { ...acc, [path]: initial };
|
|
2946
2950
|
}, {})
|
|
2947
2951
|
);
|
|
2948
2952
|
}
|
|
2949
2953
|
}, [existingData, duplicateSeed, columns]);
|
|
2950
2954
|
const handleChange = (e, field) => {
|
|
2951
2955
|
const value = e.target.value;
|
|
2952
|
-
setFormData((prev) => ({
|
|
2953
|
-
|
|
2954
|
-
[field]: value
|
|
2955
|
-
}));
|
|
2956
|
-
if (field === "itemsSummary") {
|
|
2957
|
-
recalculateTotal(value);
|
|
2958
|
-
}
|
|
2956
|
+
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
2957
|
+
if (field === "itemsSummary") recalculateTotal(value);
|
|
2959
2958
|
};
|
|
2960
2959
|
const handleFileUpload = (field, url) => {
|
|
2961
|
-
setFormData({
|
|
2962
|
-
...formData,
|
|
2963
|
-
[field]: url
|
|
2964
|
-
});
|
|
2960
|
+
setFormData({ ...formData, [field]: url });
|
|
2965
2961
|
};
|
|
2966
2962
|
const isValidPhoneFieldValue = (value) => {
|
|
2967
2963
|
const v = String(value ?? "").trim();
|
|
@@ -2985,6 +2981,18 @@ function CreateEditForm({
|
|
|
2985
2981
|
}
|
|
2986
2982
|
}
|
|
2987
2983
|
});
|
|
2984
|
+
if (isCustomerCreate) {
|
|
2985
|
+
if (!password) {
|
|
2986
|
+
newErrors["_password"] = "Password is required";
|
|
2987
|
+
} else if (password.length < 6) {
|
|
2988
|
+
newErrors["_password"] = "Password must be at least 6 characters";
|
|
2989
|
+
}
|
|
2990
|
+
if (!confirmPassword) {
|
|
2991
|
+
newErrors["_confirmPassword"] = "Please confirm your password";
|
|
2992
|
+
} else if (password && confirmPassword !== password) {
|
|
2993
|
+
newErrors["_confirmPassword"] = "Passwords do not match";
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2988
2996
|
setErrors(newErrors);
|
|
2989
2997
|
return Object.keys(newErrors).length === 0;
|
|
2990
2998
|
};
|
|
@@ -3002,16 +3010,19 @@ function CreateEditForm({
|
|
|
3002
3010
|
if (!existingData) {
|
|
3003
3011
|
delete payload.id;
|
|
3004
3012
|
}
|
|
3013
|
+
if (isCustomerCreate) {
|
|
3014
|
+
payload._password = password;
|
|
3015
|
+
}
|
|
3005
3016
|
try {
|
|
3006
3017
|
const res = await fetch(url, {
|
|
3007
3018
|
method,
|
|
3008
|
-
headers: {
|
|
3009
|
-
"Content-Type": "application/json"
|
|
3010
|
-
},
|
|
3019
|
+
headers: { "Content-Type": "application/json" },
|
|
3011
3020
|
body: JSON.stringify(payload)
|
|
3012
3021
|
});
|
|
3013
3022
|
if (!res.ok) {
|
|
3014
|
-
|
|
3023
|
+
const errData = await res.json().catch(() => ({}));
|
|
3024
|
+
import_sonner3.toast.error(errData.error ?? "Failed to save data");
|
|
3025
|
+
return;
|
|
3015
3026
|
}
|
|
3016
3027
|
const result = await res.json();
|
|
3017
3028
|
if (result.inviteLink) {
|
|
@@ -3025,13 +3036,12 @@ Note: ${result.note}`
|
|
|
3025
3036
|
onClose();
|
|
3026
3037
|
} catch (error) {
|
|
3027
3038
|
console.error("Error saving data:", error);
|
|
3039
|
+
import_sonner3.toast.error("An unexpected error occurred. Please try again.");
|
|
3028
3040
|
}
|
|
3029
3041
|
};
|
|
3030
3042
|
const visibleColumns = columns.filter((col) => {
|
|
3031
3043
|
if (col.hideInForm) return false;
|
|
3032
|
-
if (!existingData && col.hideInCreate)
|
|
3033
|
-
return false;
|
|
3034
|
-
}
|
|
3044
|
+
if (!existingData && col.hideInCreate) return false;
|
|
3035
3045
|
return true;
|
|
3036
3046
|
});
|
|
3037
3047
|
const isFieldDisabled = (path) => {
|
|
@@ -3061,228 +3071,193 @@ Note: ${result.note}`
|
|
|
3061
3071
|
}
|
|
3062
3072
|
)
|
|
3063
3073
|
] }),
|
|
3064
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3065
|
-
"
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
) : null;
|
|
3079
|
-
const rawStr = formData[path] != null && formData[path] !== "" ? String(formData[path]) : "";
|
|
3080
|
-
const selectValue = optionSet ? rawStr !== "" && optionSet.has(rawStr) ? rawStr : col.defaultValue : void 0;
|
|
3081
|
-
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react13.default.Fragment, { children: [
|
|
3082
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
3083
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Label3, { className: "block text-sm font-medium mb-1", children: [
|
|
3084
|
-
col.displayName,
|
|
3085
|
-
["subtotal", "tax", "total"].includes(path) && totalLoading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ml-2 text-xs text-gray-400 font-normal", children: "Calculating\u2026" })
|
|
3086
|
-
] }),
|
|
3087
|
-
col.type === "customerDropdown" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3088
|
-
Select,
|
|
3089
|
-
{
|
|
3090
|
-
value: (() => {
|
|
3091
|
-
const match = customers.find(
|
|
3092
|
-
(c) => String(c.id) === String(
|
|
3093
|
-
formData["contactId"]
|
|
3094
|
-
) || c.name === formData["contact.name"]
|
|
3095
|
-
);
|
|
3096
|
-
return match ? String(match.id) : void 0;
|
|
3097
|
-
})(),
|
|
3098
|
-
onValueChange: handleCustomerSelect,
|
|
3099
|
-
children: [
|
|
3100
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectValue, { placeholder: "Select a customer" }) }),
|
|
3101
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectContent, { children: customers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3102
|
-
SelectItem,
|
|
3103
|
-
{
|
|
3104
|
-
value: "__loading__",
|
|
3105
|
-
disabled: true,
|
|
3106
|
-
children: "Loading\u2026"
|
|
3107
|
-
}
|
|
3108
|
-
) : customers.map((c) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3109
|
-
SelectItem,
|
|
3110
|
-
{
|
|
3111
|
-
value: String(
|
|
3112
|
-
c.id
|
|
3113
|
-
),
|
|
3114
|
-
children: c.name
|
|
3115
|
-
},
|
|
3116
|
-
c.id
|
|
3117
|
-
)) })
|
|
3118
|
-
]
|
|
3119
|
-
}
|
|
3120
|
-
) : col.relationApi ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3121
|
-
RelationAutocomplete,
|
|
3122
|
-
{
|
|
3123
|
-
apiEndpoint: col.relationApi,
|
|
3124
|
-
value: formData[path] ?? "",
|
|
3125
|
-
onChange: (v) => setFormData({
|
|
3126
|
-
...formData,
|
|
3127
|
-
[path]: v
|
|
3128
|
-
}),
|
|
3129
|
-
labelField: col.relationLabelField ?? "name",
|
|
3130
|
-
valueField: col.relationValueField ?? "id",
|
|
3131
|
-
queryParams: col.relationQueryParams,
|
|
3132
|
-
placeholder: `Select ${col.displayName}`
|
|
3133
|
-
}
|
|
3134
|
-
) : col.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3135
|
-
Textarea,
|
|
3136
|
-
{
|
|
3137
|
-
rows: typeof col.textareaRows === "number" ? col.textareaRows : 4,
|
|
3138
|
-
className: "min-h-[80px] font-mono text-sm",
|
|
3139
|
-
value: formData[path] ?? "",
|
|
3140
|
-
onChange: (e) => handleChange(e, path),
|
|
3141
|
-
placeholder: col.placeholder,
|
|
3142
|
-
disabled
|
|
3143
|
-
}
|
|
3144
|
-
) : col.type === "select" && Array.isArray(
|
|
3145
|
-
col.options
|
|
3146
|
-
) && col.options.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3147
|
-
Select,
|
|
3148
|
-
{
|
|
3149
|
-
value: selectValue,
|
|
3150
|
-
onValueChange: (value) => setFormData({
|
|
3151
|
-
...formData,
|
|
3152
|
-
[path]: value
|
|
3153
|
-
}),
|
|
3154
|
-
disabled,
|
|
3155
|
-
children: [
|
|
3156
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3157
|
-
SelectValue,
|
|
3158
|
-
{
|
|
3159
|
-
placeholder: selectValue ? void 0 : "Select an option"
|
|
3160
|
-
}
|
|
3161
|
-
) }),
|
|
3162
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectContent, { children: col.options.map(
|
|
3163
|
-
(option) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3164
|
-
SelectItem,
|
|
3165
|
-
{
|
|
3166
|
-
value: option.value,
|
|
3167
|
-
children: option.label
|
|
3168
|
-
},
|
|
3169
|
-
option.value
|
|
3170
|
-
)
|
|
3171
|
-
) })
|
|
3172
|
-
]
|
|
3173
|
-
}
|
|
3174
|
-
) : col.type === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3175
|
-
Switch,
|
|
3176
|
-
{
|
|
3177
|
-
checked: Boolean(
|
|
3178
|
-
formData[path]
|
|
3179
|
-
),
|
|
3180
|
-
onCheckedChange: (checked) => setFormData({
|
|
3181
|
-
...formData,
|
|
3182
|
-
[path]: checked
|
|
3183
|
-
}),
|
|
3184
|
-
disabled
|
|
3185
|
-
}
|
|
3186
|
-
) : isDateFormColumn(
|
|
3187
|
-
col.type
|
|
3188
|
-
) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3189
|
-
Input,
|
|
3190
|
-
{
|
|
3191
|
-
type: "date",
|
|
3192
|
-
value: formData[path] ?? "",
|
|
3193
|
-
onChange: (e) => handleChange(e, path),
|
|
3194
|
-
disabled
|
|
3195
|
-
}
|
|
3196
|
-
) : col.type === "password" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3197
|
-
Input,
|
|
3198
|
-
{
|
|
3199
|
-
type: "password",
|
|
3200
|
-
value: formData[path] || "",
|
|
3201
|
-
onChange: (e) => handleChange(e, path),
|
|
3202
|
-
disabled
|
|
3203
|
-
}
|
|
3204
|
-
) : col.type === "number" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3205
|
-
Input,
|
|
3206
|
-
{
|
|
3207
|
-
type: "number",
|
|
3208
|
-
placeholder: col.placeholder,
|
|
3209
|
-
value: formData[path] ?? "",
|
|
3210
|
-
onChange: (e) => handleChange(e, path),
|
|
3211
|
-
disabled
|
|
3212
|
-
}
|
|
3213
|
-
) : col.type === "phone" || col.type === "tel" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3214
|
-
Input,
|
|
3215
|
-
{
|
|
3216
|
-
type: "tel",
|
|
3217
|
-
inputMode: "tel",
|
|
3218
|
-
autoComplete: "tel",
|
|
3219
|
-
placeholder: col.placeholder ?? "e.g. +1 555 123 4567",
|
|
3220
|
-
value: formData[path] ?? "",
|
|
3221
|
-
onChange: (e) => handleChange(e, path),
|
|
3222
|
-
disabled
|
|
3223
|
-
}
|
|
3224
|
-
) : col.type === "file" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3225
|
-
FileUpload,
|
|
3226
|
-
{
|
|
3227
|
-
onUploadSuccess: (url) => handleFileUpload(
|
|
3228
|
-
path,
|
|
3229
|
-
url
|
|
3230
|
-
)
|
|
3231
|
-
}
|
|
3232
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3233
|
-
Input,
|
|
3234
|
-
{
|
|
3235
|
-
type: col.type || "text",
|
|
3236
|
-
placeholder: ["subtotal", "tax", "total"].includes(path) ? "Auto-calculated from items" : col.placeholder,
|
|
3237
|
-
value: formData[path] ?? "",
|
|
3238
|
-
onChange: (e) => handleChange(e, path),
|
|
3239
|
-
disabled
|
|
3240
|
-
}
|
|
3241
|
-
),
|
|
3242
|
-
errors[path] && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-red-500 text-sm mt-1", children: errors[path] })
|
|
3074
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0", children: [
|
|
3075
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex-1 min-h-0 overflow-y-auto p-6 space-y-4", children: [
|
|
3076
|
+
visibleColumns.map((col) => {
|
|
3077
|
+
const path = col.field || col.key;
|
|
3078
|
+
if (!path) return null;
|
|
3079
|
+
const disabled = isFieldDisabled(path);
|
|
3080
|
+
const optionSet = col.type === "select" && Array.isArray(col.options) ? new Set(col.options.map((o) => String(o.value))) : null;
|
|
3081
|
+
const rawStr = formData[path] != null && formData[path] !== "" ? String(formData[path]) : "";
|
|
3082
|
+
const selectValue = optionSet ? rawStr !== "" && optionSet.has(rawStr) ? rawStr : col.defaultValue : void 0;
|
|
3083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react13.default.Fragment, { children: [
|
|
3084
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
3085
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Label3, { className: "block text-sm font-medium mb-1", children: [
|
|
3086
|
+
col.displayName,
|
|
3087
|
+
["subtotal", "tax", "total"].includes(path) && totalLoading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ml-2 text-xs text-gray-400 font-normal", children: "Calculating\u2026" })
|
|
3243
3088
|
] }),
|
|
3244
|
-
col.type === "customerDropdown"
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3089
|
+
col.type === "customerDropdown" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3090
|
+
Select,
|
|
3091
|
+
{
|
|
3092
|
+
value: (() => {
|
|
3093
|
+
const match = customers.find(
|
|
3094
|
+
(c) => String(c.id) === String(formData["contactId"]) || c.name === formData["contact.name"]
|
|
3095
|
+
);
|
|
3096
|
+
return match ? String(match.id) : void 0;
|
|
3097
|
+
})(),
|
|
3098
|
+
onValueChange: handleCustomerSelect,
|
|
3099
|
+
children: [
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectValue, { placeholder: "Select a customer" }) }),
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectContent, { children: customers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectItem, { value: "__loading__", disabled: true, children: "Loading\u2026" }) : customers.map((c) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectItem, { value: String(c.id), children: c.name }, c.id)) })
|
|
3102
|
+
]
|
|
3103
|
+
}
|
|
3104
|
+
) : col.relationApi ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3105
|
+
RelationAutocomplete,
|
|
3106
|
+
{
|
|
3107
|
+
apiEndpoint: col.relationApi,
|
|
3108
|
+
value: formData[path] ?? "",
|
|
3109
|
+
onChange: (v) => setFormData({ ...formData, [path]: v }),
|
|
3110
|
+
labelField: col.relationLabelField ?? "name",
|
|
3111
|
+
valueField: col.relationValueField ?? "id",
|
|
3112
|
+
queryParams: col.relationQueryParams,
|
|
3113
|
+
placeholder: `Select ${col.displayName}`
|
|
3114
|
+
}
|
|
3115
|
+
) : col.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3116
|
+
Textarea,
|
|
3117
|
+
{
|
|
3118
|
+
rows: typeof col.textareaRows === "number" ? col.textareaRows : 4,
|
|
3119
|
+
className: "min-h-[80px] font-mono text-sm",
|
|
3120
|
+
value: formData[path] ?? "",
|
|
3121
|
+
onChange: (e) => handleChange(e, path),
|
|
3122
|
+
placeholder: col.placeholder,
|
|
3123
|
+
disabled
|
|
3124
|
+
}
|
|
3125
|
+
) : col.type === "select" && Array.isArray(col.options) && col.options.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3126
|
+
Select,
|
|
3127
|
+
{
|
|
3128
|
+
value: selectValue,
|
|
3129
|
+
onValueChange: (value) => setFormData({ ...formData, [path]: value }),
|
|
3130
|
+
disabled,
|
|
3131
|
+
children: [
|
|
3132
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3133
|
+
SelectValue,
|
|
3134
|
+
{
|
|
3135
|
+
placeholder: selectValue ? void 0 : "Select an option"
|
|
3136
|
+
}
|
|
3137
|
+
) }),
|
|
3138
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectContent, { children: col.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SelectItem, { value: option.value, children: option.label }, option.value)) })
|
|
3139
|
+
]
|
|
3140
|
+
}
|
|
3141
|
+
) : col.type === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3142
|
+
Switch,
|
|
3143
|
+
{
|
|
3144
|
+
checked: Boolean(formData[path]),
|
|
3145
|
+
onCheckedChange: (checked) => setFormData({ ...formData, [path]: checked }),
|
|
3146
|
+
disabled
|
|
3147
|
+
}
|
|
3148
|
+
) : isDateFormColumn(col.type) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3149
|
+
Input,
|
|
3150
|
+
{
|
|
3151
|
+
type: "date",
|
|
3152
|
+
value: formData[path] ?? "",
|
|
3153
|
+
onChange: (e) => handleChange(e, path),
|
|
3154
|
+
disabled
|
|
3155
|
+
}
|
|
3156
|
+
) : col.type === "password" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3157
|
+
Input,
|
|
3158
|
+
{
|
|
3159
|
+
type: "password",
|
|
3160
|
+
value: formData[path] || "",
|
|
3161
|
+
onChange: (e) => handleChange(e, path),
|
|
3162
|
+
disabled
|
|
3163
|
+
}
|
|
3164
|
+
) : col.type === "number" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3165
|
+
Input,
|
|
3166
|
+
{
|
|
3167
|
+
type: "number",
|
|
3168
|
+
placeholder: col.placeholder,
|
|
3169
|
+
value: formData[path] ?? "",
|
|
3170
|
+
onChange: (e) => handleChange(e, path),
|
|
3171
|
+
disabled
|
|
3172
|
+
}
|
|
3173
|
+
) : col.type === "phone" || col.type === "tel" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3174
|
+
Input,
|
|
3175
|
+
{
|
|
3176
|
+
type: "tel",
|
|
3177
|
+
inputMode: "tel",
|
|
3178
|
+
autoComplete: "tel",
|
|
3179
|
+
placeholder: col.placeholder ?? "e.g. +1 555 123 4567",
|
|
3180
|
+
value: formData[path] ?? "",
|
|
3181
|
+
onChange: (e) => handleChange(e, path),
|
|
3182
|
+
disabled
|
|
3183
|
+
}
|
|
3184
|
+
) : col.type === "file" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3185
|
+
FileUpload,
|
|
3186
|
+
{
|
|
3187
|
+
onUploadSuccess: (url) => handleFileUpload(path, url)
|
|
3188
|
+
}
|
|
3189
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3190
|
+
Input,
|
|
3191
|
+
{
|
|
3192
|
+
type: col.type || "text",
|
|
3193
|
+
placeholder: ["subtotal", "tax", "total"].includes(path) ? "Auto-calculated from items" : col.placeholder,
|
|
3194
|
+
value: formData[path] ?? "",
|
|
3195
|
+
onChange: (e) => handleChange(e, path),
|
|
3196
|
+
disabled
|
|
3197
|
+
}
|
|
3198
|
+
),
|
|
3199
|
+
errors[path] && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-red-500 text-sm mt-1", children: errors[path] })
|
|
3200
|
+
] }),
|
|
3201
|
+
col.type === "customerDropdown" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
3202
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label3, { className: "block text-sm font-medium mb-1", children: "Name" }),
|
|
3203
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3204
|
+
Input,
|
|
3205
|
+
{
|
|
3206
|
+
type: "text",
|
|
3207
|
+
placeholder: "Customer name",
|
|
3208
|
+
value: formData["contact.name"] ?? "",
|
|
3209
|
+
onChange: (e) => setFormData((prev) => ({
|
|
3210
|
+
...prev,
|
|
3211
|
+
"contact.name": e.target.value
|
|
3212
|
+
}))
|
|
3213
|
+
}
|
|
3214
|
+
)
|
|
3215
|
+
] })
|
|
3216
|
+
] }, path);
|
|
3217
|
+
}),
|
|
3218
|
+
isCustomerCreate && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
3219
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
3220
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label3, { className: "block text-sm font-medium mb-1", children: "Password" }),
|
|
3264
3221
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3265
|
-
|
|
3222
|
+
Input,
|
|
3266
3223
|
{
|
|
3267
|
-
type: "
|
|
3268
|
-
|
|
3269
|
-
|
|
3224
|
+
type: "password",
|
|
3225
|
+
value: password,
|
|
3226
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3227
|
+
placeholder: "Min. 6 characters"
|
|
3270
3228
|
}
|
|
3271
3229
|
),
|
|
3230
|
+
errors["_password"] && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-red-500 text-sm mt-1", children: errors["_password"] })
|
|
3231
|
+
] }),
|
|
3232
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
3233
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label3, { className: "block text-sm font-medium mb-1", children: "Confirm Password" }),
|
|
3272
3234
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3273
|
-
|
|
3235
|
+
Input,
|
|
3274
3236
|
{
|
|
3275
|
-
type: "
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
children: "Cancel"
|
|
3237
|
+
type: "password",
|
|
3238
|
+
value: confirmPassword,
|
|
3239
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
3240
|
+
placeholder: "Re-enter password"
|
|
3280
3241
|
}
|
|
3281
|
-
)
|
|
3242
|
+
),
|
|
3243
|
+
errors["_confirmPassword"] && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-red-500 text-sm mt-1", children: errors["_confirmPassword"] })
|
|
3282
3244
|
] })
|
|
3283
|
-
]
|
|
3284
|
-
}
|
|
3285
|
-
|
|
3245
|
+
] })
|
|
3246
|
+
] }),
|
|
3247
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-2 p-4 border-t border-gray-200 shrink-0 bg-white", children: [
|
|
3248
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { type: "submit", className: "flex-1", children: "Save" }),
|
|
3249
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3250
|
+
Button,
|
|
3251
|
+
{
|
|
3252
|
+
type: "button",
|
|
3253
|
+
variant: "outline",
|
|
3254
|
+
onClick: onClose,
|
|
3255
|
+
className: "flex-1",
|
|
3256
|
+
children: "Cancel"
|
|
3257
|
+
}
|
|
3258
|
+
)
|
|
3259
|
+
] })
|
|
3260
|
+
] })
|
|
3286
3261
|
]
|
|
3287
3262
|
}
|
|
3288
3263
|
) }) : null;
|
|
@@ -5528,7 +5503,8 @@ function AdminCRUD({
|
|
|
5528
5503
|
apiEndpoint,
|
|
5529
5504
|
columns,
|
|
5530
5505
|
existingData: editingItem,
|
|
5531
|
-
duplicateSeed
|
|
5506
|
+
duplicateSeed,
|
|
5507
|
+
resource: resourceName
|
|
5532
5508
|
}
|
|
5533
5509
|
),
|
|
5534
5510
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
@@ -10121,7 +10097,7 @@ function DashboardPage() {
|
|
|
10121
10097
|
// src/admin/pages/AdminPageResolver.tsx
|
|
10122
10098
|
var import_react68 = require("react");
|
|
10123
10099
|
var import_dynamic2 = __toESM(require("next/dynamic"), 1);
|
|
10124
|
-
var
|
|
10100
|
+
var import_navigation31 = require("next/navigation");
|
|
10125
10101
|
|
|
10126
10102
|
// src/admin/pages/SubmissionDetailPage.tsx
|
|
10127
10103
|
var import_react37 = require("react");
|
|
@@ -11085,6 +11061,7 @@ function formatMoney3(amount, currency) {
|
|
|
11085
11061
|
}).format(Number(amount));
|
|
11086
11062
|
}
|
|
11087
11063
|
var MANUAL_CUSTOMER = "__manual__";
|
|
11064
|
+
var MANUAL_CONTACT = "__manual__";
|
|
11088
11065
|
function OrderPlacementPage({ editOrderId }) {
|
|
11089
11066
|
const router = (0, import_navigation15.useRouter)();
|
|
11090
11067
|
const searchParams = (0, import_navigation15.useSearchParams)();
|
|
@@ -11092,25 +11069,23 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11092
11069
|
const [customers, setCustomers] = (0, import_react40.useState)([]);
|
|
11093
11070
|
const [customersLoading, setCustomersLoading] = (0, import_react40.useState)(true);
|
|
11094
11071
|
const [contactId, setContactId] = (0, import_react40.useState)(null);
|
|
11072
|
+
const [selectedCustomerId, setSelectedCustomerId] = (0, import_react40.useState)(null);
|
|
11095
11073
|
const [contactName, setContactName] = (0, import_react40.useState)("");
|
|
11096
11074
|
const [contactEmail, setContactEmail] = (0, import_react40.useState)("");
|
|
11097
11075
|
const [contactPhone, setContactPhone] = (0, import_react40.useState)("");
|
|
11076
|
+
const [placeForSomeoneElse, setPlaceForSomeoneElse] = (0, import_react40.useState)(false);
|
|
11077
|
+
const [customerContacts, setCustomerContacts] = (0, import_react40.useState)([]);
|
|
11078
|
+
const [customerContactsLoading, setCustomerContactsLoading] = (0, import_react40.useState)(false);
|
|
11079
|
+
const [subContactId, setSubContactId] = (0, import_react40.useState)(null);
|
|
11080
|
+
const [subContactName, setSubContactName] = (0, import_react40.useState)("");
|
|
11081
|
+
const [subContactEmail, setSubContactEmail] = (0, import_react40.useState)("");
|
|
11082
|
+
const [subContactPhone, setSubContactPhone] = (0, import_react40.useState)("");
|
|
11098
11083
|
const [status, setStatus] = (0, import_react40.useState)("pending");
|
|
11099
11084
|
const [currency, setCurrency] = (0, import_react40.useState)("INR");
|
|
11100
11085
|
const [loadingOrder, setLoadingOrder] = (0, import_react40.useState)(Boolean(editOrderId));
|
|
11101
11086
|
const [orderLoadError, setOrderLoadError] = (0, import_react40.useState)(null);
|
|
11102
|
-
const [couponError, setCouponError] = (0, import_react40.useState)(null);
|
|
11103
|
-
const [couponLoading, setCouponLoading] = (0, import_react40.useState)(false);
|
|
11104
11087
|
const [coupons, setCoupons] = (0, import_react40.useState)([]);
|
|
11105
|
-
const
|
|
11106
|
-
const emptyAddress = {
|
|
11107
|
-
line1: "",
|
|
11108
|
-
line2: "",
|
|
11109
|
-
city: "",
|
|
11110
|
-
state: "",
|
|
11111
|
-
country: "",
|
|
11112
|
-
postalCode: ""
|
|
11113
|
-
};
|
|
11088
|
+
const emptyAddress = { line1: "", line2: "", city: "", state: "", country: "", postalCode: "" };
|
|
11114
11089
|
const [billingAddress, setBillingAddress] = (0, import_react40.useState)(emptyAddress);
|
|
11115
11090
|
const [shippingAddress, setShippingAddress] = (0, import_react40.useState)(emptyAddress);
|
|
11116
11091
|
const [sameAsBilling, setSameAsBilling] = (0, import_react40.useState)(true);
|
|
@@ -11173,9 +11148,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11173
11148
|
setSameAsBilling(true);
|
|
11174
11149
|
}
|
|
11175
11150
|
const loadedCid = typeof cid === "number" && Number.isFinite(cid) ? cid : null;
|
|
11176
|
-
if (loadedCid && !o.billingAddress && !o.shippingAddress)
|
|
11177
|
-
void loadCustomerAddresses(loadedCid);
|
|
11178
|
-
}
|
|
11151
|
+
if (loadedCid && !o.billingAddress && !o.shippingAddress) void loadContactAddresses(loadedCid);
|
|
11179
11152
|
setStatus(String(o.status ?? "pending"));
|
|
11180
11153
|
const cur = String(o.currency ?? "INR").trim().toUpperCase().slice(0, 8);
|
|
11181
11154
|
if (cur) setCurrency(cur);
|
|
@@ -11185,13 +11158,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11185
11158
|
const coll = p?.collection?.name;
|
|
11186
11159
|
const nm = p?.name;
|
|
11187
11160
|
const label = [coll, nm].filter(Boolean).join(" \u2014 ") || nm || `Product #${pid}`;
|
|
11188
|
-
return {
|
|
11189
|
-
key: `line-${it.id}`,
|
|
11190
|
-
productId: pid,
|
|
11191
|
-
label,
|
|
11192
|
-
sku: p?.sku ?? null,
|
|
11193
|
-
quantity: Math.max(1, Math.floor(Number(it.quantity) || 1))
|
|
11194
|
-
};
|
|
11161
|
+
return { key: `line-${it.id}`, productId: pid, label, sku: p?.sku ?? null, quantity: Math.max(1, Math.floor(Number(it.quantity) || 1)) };
|
|
11195
11162
|
});
|
|
11196
11163
|
setLines(draft);
|
|
11197
11164
|
try {
|
|
@@ -11204,18 +11171,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11204
11171
|
if (dr.ok) {
|
|
11205
11172
|
const d = await dr.json();
|
|
11206
11173
|
if (d?.id) {
|
|
11207
|
-
setCoupons([{
|
|
11208
|
-
couponCode: d.couponCode ?? "",
|
|
11209
|
-
discountId: d.id,
|
|
11210
|
-
name: d.name,
|
|
11211
|
-
value: d.value,
|
|
11212
|
-
type: d.discountType,
|
|
11213
|
-
canCombine: !!d.canCombineWithOtherDiscounts,
|
|
11214
|
-
calculatedDiscount: 0,
|
|
11215
|
-
rewardProductIds: [],
|
|
11216
|
-
rewardLines: [],
|
|
11217
|
-
isAutomatic: d.isAutomatic ?? false
|
|
11218
|
-
}]);
|
|
11174
|
+
setCoupons([{ couponCode: d.couponCode ?? "", discountId: d.id, name: d.name, value: d.value, type: d.discountType, canCombine: !!d.canCombineWithOtherDiscounts, calculatedDiscount: 0, rewardProductIds: [], rewardLines: [], isAutomatic: d.isAutomatic ?? false }]);
|
|
11219
11175
|
}
|
|
11220
11176
|
}
|
|
11221
11177
|
}
|
|
@@ -11226,12 +11182,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11226
11182
|
const pid = Number(it.productId);
|
|
11227
11183
|
const p = it.product;
|
|
11228
11184
|
const unit = Number(it.unitPrice ?? 0);
|
|
11229
|
-
productCache.current.set(pid, {
|
|
11230
|
-
id: pid,
|
|
11231
|
-
name: p?.name ?? null,
|
|
11232
|
-
sku: p?.sku ?? null,
|
|
11233
|
-
price: Number.isFinite(unit) ? unit : 0
|
|
11234
|
-
});
|
|
11185
|
+
productCache.current.set(pid, { id: pid, name: p?.name ?? null, sku: p?.sku ?? null, price: Number.isFinite(unit) ? unit : 0 });
|
|
11235
11186
|
}
|
|
11236
11187
|
} catch {
|
|
11237
11188
|
if (!cancelled) setOrderLoadError("Failed to load order");
|
|
@@ -11248,23 +11199,17 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11248
11199
|
(async () => {
|
|
11249
11200
|
setCustomersLoading(true);
|
|
11250
11201
|
try {
|
|
11251
|
-
const res = await fetch(
|
|
11252
|
-
"/api/vendor_customers?limit=500&sortField=createdAt&sortOrder=asc"
|
|
11253
|
-
);
|
|
11202
|
+
const res = await fetch("/api/vendor_customers?limit=500&sortField=createdAt&sortOrder=asc");
|
|
11254
11203
|
if (!res.ok) {
|
|
11255
11204
|
if (!cancelled) setCustomers([]);
|
|
11256
11205
|
return;
|
|
11257
11206
|
}
|
|
11258
11207
|
const data = await res.json();
|
|
11259
11208
|
const rows = (Array.isArray(data?.data) ? data.data : []).map((row) => {
|
|
11260
|
-
const id = Number(row.
|
|
11209
|
+
const id = Number(row.id);
|
|
11261
11210
|
if (!Number.isFinite(id)) return null;
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
name: row.name ?? row.contact?.name ?? null,
|
|
11265
|
-
email: row.email ?? row.contact?.email ?? null,
|
|
11266
|
-
phone: row.phone ?? row.contact?.phone ?? null
|
|
11267
|
-
};
|
|
11211
|
+
const rawCustomerId = Number(row.customerId);
|
|
11212
|
+
return { id, customerId: Number.isFinite(rawCustomerId) && rawCustomerId > 0 ? rawCustomerId : null, name: row.name ?? null, email: row.email ?? null, phone: row.phone ?? null };
|
|
11268
11213
|
}).filter((r) => r != null);
|
|
11269
11214
|
if (!cancelled) setCustomers(rows);
|
|
11270
11215
|
} catch {
|
|
@@ -11277,6 +11222,140 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11277
11222
|
cancelled = true;
|
|
11278
11223
|
};
|
|
11279
11224
|
}, []);
|
|
11225
|
+
async function loadCustomerContacts(customerId) {
|
|
11226
|
+
setCustomerContactsLoading(true);
|
|
11227
|
+
setCustomerContacts([]);
|
|
11228
|
+
setSubContactId(null);
|
|
11229
|
+
setSubContactName("");
|
|
11230
|
+
setSubContactEmail("");
|
|
11231
|
+
setSubContactPhone("");
|
|
11232
|
+
try {
|
|
11233
|
+
const res = await fetch(`/api/customer_contacts?customerId=${customerId}&limit=200&sortField=id&sortOrder=asc`);
|
|
11234
|
+
if (!res.ok) return;
|
|
11235
|
+
const data = await res.json();
|
|
11236
|
+
const rows = (Array.isArray(data?.data) ? data.data : Array.isArray(data) ? data : []).filter((r) => r.contact && Number.isFinite(Number(r.contactId)));
|
|
11237
|
+
setCustomerContacts(rows);
|
|
11238
|
+
} catch {
|
|
11239
|
+
setCustomerContacts([]);
|
|
11240
|
+
} finally {
|
|
11241
|
+
setCustomerContactsLoading(false);
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
async function loadContactAddressesByEmail(email) {
|
|
11245
|
+
try {
|
|
11246
|
+
const res = await fetch(`/api/contacts?search=${encodeURIComponent(email)}&limit=5`);
|
|
11247
|
+
if (!res.ok) {
|
|
11248
|
+
setBillingAddress({ ...emptyAddress });
|
|
11249
|
+
setShippingAddress({ ...emptyAddress });
|
|
11250
|
+
return;
|
|
11251
|
+
}
|
|
11252
|
+
const data = await res.json();
|
|
11253
|
+
const match = data.data?.find((c) => c.email?.toLowerCase() === email.toLowerCase());
|
|
11254
|
+
if (!match) {
|
|
11255
|
+
setBillingAddress({ ...emptyAddress });
|
|
11256
|
+
setShippingAddress({ ...emptyAddress });
|
|
11257
|
+
return;
|
|
11258
|
+
}
|
|
11259
|
+
await loadContactAddresses(match.id);
|
|
11260
|
+
} catch {
|
|
11261
|
+
setBillingAddress({ ...emptyAddress });
|
|
11262
|
+
setShippingAddress({ ...emptyAddress });
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
async function loadContactAddresses(cid) {
|
|
11266
|
+
try {
|
|
11267
|
+
const res = await fetch(`/api/order_addresses?contactId=${cid}&limit=100&sortField=createdAt&sortOrder=desc`);
|
|
11268
|
+
if (!res.ok) return;
|
|
11269
|
+
const body = await res.json();
|
|
11270
|
+
const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
11271
|
+
const billing = rows.find((a) => a.addressType === "billing_address");
|
|
11272
|
+
const shipping = rows.find((a) => a.addressType === "shipping_address");
|
|
11273
|
+
if (billing) setBillingAddress(mapAddressToForm(billing));
|
|
11274
|
+
else setBillingAddress({ ...emptyAddress });
|
|
11275
|
+
if (sameAsBilling) setShippingAddress(billing ? mapAddressToForm(billing) : { ...emptyAddress });
|
|
11276
|
+
else setShippingAddress(shipping ? mapAddressToForm(shipping) : { ...emptyAddress });
|
|
11277
|
+
} catch {
|
|
11278
|
+
setBillingAddress(emptyAddress);
|
|
11279
|
+
setShippingAddress(emptyAddress);
|
|
11280
|
+
}
|
|
11281
|
+
}
|
|
11282
|
+
function handleCustomerSelect(value) {
|
|
11283
|
+
if (value === MANUAL_CUSTOMER) {
|
|
11284
|
+
setContactId(null);
|
|
11285
|
+
setSelectedCustomerId(null);
|
|
11286
|
+
setCustomerContacts([]);
|
|
11287
|
+
setSubContactId(null);
|
|
11288
|
+
setSubContactName("");
|
|
11289
|
+
setSubContactEmail("");
|
|
11290
|
+
setSubContactPhone("");
|
|
11291
|
+
setBillingAddress({ ...emptyAddress });
|
|
11292
|
+
setShippingAddress({ ...emptyAddress });
|
|
11293
|
+
return;
|
|
11294
|
+
}
|
|
11295
|
+
const id = Number(value);
|
|
11296
|
+
if (!Number.isFinite(id)) return;
|
|
11297
|
+
const customer = customers.find((c) => c.id === id);
|
|
11298
|
+
if (!customer) return;
|
|
11299
|
+
setContactId(id);
|
|
11300
|
+
setSelectedCustomerId(customer.customerId ?? null);
|
|
11301
|
+
setContactName(customer.name ?? "");
|
|
11302
|
+
setContactEmail(customer.email ?? "");
|
|
11303
|
+
setContactPhone(customer.phone ?? "");
|
|
11304
|
+
setSubContactId(null);
|
|
11305
|
+
setSubContactName("");
|
|
11306
|
+
setSubContactEmail("");
|
|
11307
|
+
setSubContactPhone("");
|
|
11308
|
+
setPlaceForSomeoneElse(false);
|
|
11309
|
+
if (customer.customerId != null) loadCustomerContacts(customer.customerId);
|
|
11310
|
+
if (customer.email) {
|
|
11311
|
+
loadContactAddressesByEmail(customer.email);
|
|
11312
|
+
} else {
|
|
11313
|
+
setBillingAddress({ ...emptyAddress });
|
|
11314
|
+
setShippingAddress({ ...emptyAddress });
|
|
11315
|
+
}
|
|
11316
|
+
}
|
|
11317
|
+
function handleSubContactSelect(value) {
|
|
11318
|
+
if (value === MANUAL_CONTACT) {
|
|
11319
|
+
setSubContactId(null);
|
|
11320
|
+
setSubContactName("");
|
|
11321
|
+
setSubContactEmail("");
|
|
11322
|
+
setSubContactPhone("");
|
|
11323
|
+
setBillingAddress({ ...emptyAddress });
|
|
11324
|
+
setShippingAddress({ ...emptyAddress });
|
|
11325
|
+
return;
|
|
11326
|
+
}
|
|
11327
|
+
const cid = Number(value);
|
|
11328
|
+
if (!Number.isFinite(cid)) return;
|
|
11329
|
+
const row = customerContacts.find((r) => r.contactId === cid);
|
|
11330
|
+
if (!row) return;
|
|
11331
|
+
setSubContactId(cid);
|
|
11332
|
+
setSubContactName(row.contact.name ?? "");
|
|
11333
|
+
setSubContactEmail(row.contact.email ?? "");
|
|
11334
|
+
setSubContactPhone(row.contact.phone ?? "");
|
|
11335
|
+
loadContactAddresses(cid);
|
|
11336
|
+
}
|
|
11337
|
+
function handlePlaceForSomeoneElseToggle(checked) {
|
|
11338
|
+
setPlaceForSomeoneElse(checked);
|
|
11339
|
+
if (checked) {
|
|
11340
|
+
setBillingAddress({ ...emptyAddress });
|
|
11341
|
+
setShippingAddress({ ...emptyAddress });
|
|
11342
|
+
setSubContactId(null);
|
|
11343
|
+
setSubContactName("");
|
|
11344
|
+
setSubContactEmail("");
|
|
11345
|
+
setSubContactPhone("");
|
|
11346
|
+
return;
|
|
11347
|
+
}
|
|
11348
|
+
setSubContactId(null);
|
|
11349
|
+
setSubContactName("");
|
|
11350
|
+
setSubContactEmail("");
|
|
11351
|
+
setSubContactPhone("");
|
|
11352
|
+
if (contactEmail) {
|
|
11353
|
+
loadContactAddressesByEmail(contactEmail);
|
|
11354
|
+
} else {
|
|
11355
|
+
setBillingAddress({ ...emptyAddress });
|
|
11356
|
+
setShippingAddress({ ...emptyAddress });
|
|
11357
|
+
}
|
|
11358
|
+
}
|
|
11280
11359
|
const cartProductIds = (0, import_react40.useMemo)(() => new Set(lines.map((l) => l.productId)), [lines]);
|
|
11281
11360
|
(0, import_react40.useEffect)(() => {
|
|
11282
11361
|
const q = productQuery.trim();
|
|
@@ -11285,13 +11364,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11285
11364
|
(async () => {
|
|
11286
11365
|
setHitsLoading(true);
|
|
11287
11366
|
try {
|
|
11288
|
-
const params = new URLSearchParams({
|
|
11289
|
-
limit: "50",
|
|
11290
|
-
page: "1",
|
|
11291
|
-
sortField: "name",
|
|
11292
|
-
sortOrder: "asc",
|
|
11293
|
-
status: "available"
|
|
11294
|
-
});
|
|
11367
|
+
const params = new URLSearchParams({ limit: "50", page: "1", sortField: "name", sortOrder: "asc", status: "available" });
|
|
11295
11368
|
if (q.length >= 2) params.set("search", q);
|
|
11296
11369
|
const res = await fetch(`/api/products?${params}`);
|
|
11297
11370
|
if (!res.ok) {
|
|
@@ -11372,53 +11445,34 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11372
11445
|
const res = await fetch("/api/discounts/validate-coupon", {
|
|
11373
11446
|
method: "POST",
|
|
11374
11447
|
headers: { "Content-Type": "application/json" },
|
|
11375
|
-
body: JSON.stringify({
|
|
11376
|
-
discountId: coupon.discountId,
|
|
11377
|
-
isAutomatic: true,
|
|
11378
|
-
orderLines: nonRewardLines.map((l) => ({ productId: l.productId, quantity: l.quantity })),
|
|
11379
|
-
currency,
|
|
11380
|
-
contactId
|
|
11381
|
-
})
|
|
11448
|
+
body: JSON.stringify({ discountId: coupon.discountId, isAutomatic: true, orderLines: nonRewardLines.map((l) => ({ productId: l.productId, quantity: l.quantity })), currency, contactId })
|
|
11382
11449
|
});
|
|
11383
11450
|
if (cancelled) return;
|
|
11384
11451
|
const data = await res.json();
|
|
11385
11452
|
if (!data.valid) {
|
|
11386
11453
|
const rewardPids = new Set(coupon.rewardLines.map((r) => r.productId));
|
|
11387
11454
|
setCoupons((prev) => prev.filter((c) => c.discountId !== coupon.discountId));
|
|
11388
|
-
setLines(
|
|
11389
|
-
(
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
}).filter((l) => l !== null)
|
|
11396
|
-
);
|
|
11455
|
+
setLines((prev) => prev.map((l) => {
|
|
11456
|
+
if (!rewardPids.has(l.productId)) return l;
|
|
11457
|
+
const rewardLine = coupon.rewardLines.find((r) => r.productId === l.productId);
|
|
11458
|
+
if (!rewardLine) return l;
|
|
11459
|
+
const newQty = l.quantity - rewardLine.quantity;
|
|
11460
|
+
return newQty > 0 ? { ...l, quantity: newQty } : null;
|
|
11461
|
+
}).filter((l) => l !== null));
|
|
11397
11462
|
rewardPids.forEach((pid) => rewardProductIds.current.delete(pid));
|
|
11398
11463
|
rewardInjectedForDiscount.current.delete(coupon.discountId);
|
|
11399
11464
|
} else {
|
|
11400
11465
|
const newRewardLines = data.discount.rewardLines ?? [];
|
|
11401
|
-
setCoupons((prev) => prev.map(
|
|
11402
|
-
(c) => c.discountId !== coupon.discountId ? c : {
|
|
11403
|
-
...c,
|
|
11404
|
-
calculatedDiscount: data.discount.calculatedDiscount,
|
|
11405
|
-
rewardProductIds: data.discount.rewardProductIds ?? [],
|
|
11406
|
-
rewardLines: newRewardLines
|
|
11407
|
-
}
|
|
11408
|
-
));
|
|
11466
|
+
setCoupons((prev) => prev.map((c) => c.discountId !== coupon.discountId ? c : { ...c, calculatedDiscount: data.discount.calculatedDiscount, rewardProductIds: data.discount.rewardProductIds ?? [], rewardLines: newRewardLines }));
|
|
11409
11467
|
setLines((prev) => {
|
|
11410
11468
|
let next = [...prev];
|
|
11411
11469
|
for (const rewardLine of newRewardLines) {
|
|
11412
11470
|
const idx = next.findIndex((l) => l.productId === rewardLine.productId);
|
|
11413
|
-
if (idx >= 0 && rewardProductIds.current.has(rewardLine.productId)) {
|
|
11414
|
-
next[idx] = { ...next[idx], quantity: rewardLine.quantity };
|
|
11415
|
-
}
|
|
11471
|
+
if (idx >= 0 && rewardProductIds.current.has(rewardLine.productId)) next[idx] = { ...next[idx], quantity: rewardLine.quantity };
|
|
11416
11472
|
}
|
|
11417
11473
|
const newRewardPids = new Set(newRewardLines.map((r) => r.productId));
|
|
11418
11474
|
const oldRewardPids = new Set(coupon.rewardLines.map((r) => r.productId));
|
|
11419
|
-
next = next.filter(
|
|
11420
|
-
(l) => !oldRewardPids.has(l.productId) || newRewardPids.has(l.productId)
|
|
11421
|
-
);
|
|
11475
|
+
next = next.filter((l) => !oldRewardPids.has(l.productId) || newRewardPids.has(l.productId));
|
|
11422
11476
|
return next;
|
|
11423
11477
|
});
|
|
11424
11478
|
}
|
|
@@ -11433,126 +11487,28 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11433
11487
|
lines.filter((l) => !rewardProductIds.current.has(l.productId)).map((l) => `${l.productId}:${l.quantity}`).join(","),
|
|
11434
11488
|
coupons.filter((c) => c.isAutomatic).map((c) => c.discountId).join(",")
|
|
11435
11489
|
]);
|
|
11436
|
-
async function handleApplyCoupon() {
|
|
11437
|
-
const code = couponInput.trim().toUpperCase();
|
|
11438
|
-
if (!code) return;
|
|
11439
|
-
if (coupons.some((c) => c.couponCode === code)) {
|
|
11440
|
-
setCouponError("Coupon already applied");
|
|
11441
|
-
return;
|
|
11442
|
-
}
|
|
11443
|
-
if (coupons.length > 0 && !coupons[0].canCombine) {
|
|
11444
|
-
setCouponError("Applied coupon cannot be combined with other discounts");
|
|
11445
|
-
return;
|
|
11446
|
-
}
|
|
11447
|
-
setCouponError(null);
|
|
11448
|
-
setCouponLoading(true);
|
|
11449
|
-
try {
|
|
11450
|
-
const orderLines = lines.map((l) => ({ productId: l.productId, quantity: l.quantity }));
|
|
11451
|
-
const res = await fetch("/api/discounts/validate-coupon", {
|
|
11452
|
-
method: "POST",
|
|
11453
|
-
headers: { "Content-Type": "application/json" },
|
|
11454
|
-
body: JSON.stringify({ couponCode: code, orderLines, currency, contactId })
|
|
11455
|
-
});
|
|
11456
|
-
const data = await res.json();
|
|
11457
|
-
if (!res.ok || !data.valid) {
|
|
11458
|
-
setCouponError(data.error ?? "Invalid coupon");
|
|
11459
|
-
return;
|
|
11460
|
-
}
|
|
11461
|
-
const d = data.discount;
|
|
11462
|
-
if (!d.canCombineWithOtherDiscounts && coupons.length > 0) {
|
|
11463
|
-
setCouponError("This coupon cannot be combined with other discounts");
|
|
11464
|
-
return;
|
|
11465
|
-
}
|
|
11466
|
-
setCoupons((prev) => [...prev, {
|
|
11467
|
-
couponCode: code,
|
|
11468
|
-
discountId: d.id,
|
|
11469
|
-
name: d.name,
|
|
11470
|
-
value: d.value,
|
|
11471
|
-
type: d.discountType,
|
|
11472
|
-
canCombine: d.canCombineWithOtherDiscounts,
|
|
11473
|
-
calculatedDiscount: d.calculatedDiscount,
|
|
11474
|
-
rewardProductIds: d.rewardProductIds ?? [],
|
|
11475
|
-
rewardLines: d.rewardLines ?? [],
|
|
11476
|
-
isAutomatic: false
|
|
11477
|
-
}]);
|
|
11478
|
-
setCouponInput("");
|
|
11479
|
-
import_sonner7.toast.success(`Coupon "${code}" applied`);
|
|
11480
|
-
} catch {
|
|
11481
|
-
setCouponError("Failed to validate coupon");
|
|
11482
|
-
} finally {
|
|
11483
|
-
setCouponLoading(false);
|
|
11484
|
-
}
|
|
11485
|
-
}
|
|
11486
11490
|
function handleRemoveCoupon(couponCode) {
|
|
11487
11491
|
const coupon = coupons.find((c) => c.couponCode === couponCode);
|
|
11488
11492
|
if (coupon) {
|
|
11489
|
-
const
|
|
11490
|
-
const rewardKeyForDiscount = new Set(
|
|
11491
|
-
(coupon.rewardLines ?? []).map((r) => `reward-${r.productId}-${coupon.discountId}`)
|
|
11492
|
-
);
|
|
11493
|
+
const rewardKeyForDiscount = new Set((coupon.rewardLines ?? []).map((r) => `reward-${r.productId}-${coupon.discountId}`));
|
|
11493
11494
|
if (rewardKeyForDiscount.size > 0) {
|
|
11494
|
-
setLines(
|
|
11495
|
-
(prev) => prev.filter((l) => !rewardKeyForDiscount.has(l.key))
|
|
11496
|
-
);
|
|
11495
|
+
setLines((prev) => prev.filter((l) => !rewardKeyForDiscount.has(l.key)));
|
|
11497
11496
|
coupon.rewardLines.forEach((r) => {
|
|
11498
|
-
const stillRewardByOther = coupons.some(
|
|
11499
|
-
(c) => c.couponCode !== couponCode && c.rewardLines.some((rl) => rl.productId === r.productId)
|
|
11500
|
-
);
|
|
11497
|
+
const stillRewardByOther = coupons.some((c) => c.couponCode !== couponCode && c.rewardLines.some((rl) => rl.productId === r.productId));
|
|
11501
11498
|
if (!stillRewardByOther) rewardProductIds.current.delete(r.productId);
|
|
11502
11499
|
});
|
|
11503
11500
|
}
|
|
11504
11501
|
rewardInjectedForDiscount.current.delete(coupon.discountId);
|
|
11505
11502
|
}
|
|
11506
11503
|
setCoupons((prev) => prev.filter((c) => c.couponCode !== couponCode));
|
|
11507
|
-
setCouponError(null);
|
|
11508
11504
|
}
|
|
11509
11505
|
function mapAddressToForm(addr) {
|
|
11510
|
-
return {
|
|
11511
|
-
line1: addr.line1 ?? "",
|
|
11512
|
-
line2: addr.line2 ?? "",
|
|
11513
|
-
city: addr.city ?? "",
|
|
11514
|
-
state: addr.state ?? "",
|
|
11515
|
-
country: addr.country ?? "",
|
|
11516
|
-
postalCode: addr.postalCode ?? ""
|
|
11517
|
-
};
|
|
11506
|
+
return { line1: addr.line1 ?? "", line2: addr.line2 ?? "", city: addr.city ?? "", state: addr.state ?? "", country: addr.country ?? "", postalCode: addr.postalCode ?? "" };
|
|
11518
11507
|
}
|
|
11519
11508
|
function updateAddress(type, field, value) {
|
|
11520
11509
|
if (type === "billing") setBillingAddress((prev) => ({ ...prev, [field]: value }));
|
|
11521
11510
|
else setShippingAddress((prev) => ({ ...prev, [field]: value }));
|
|
11522
11511
|
}
|
|
11523
|
-
function handleCustomerSelect(value) {
|
|
11524
|
-
if (value === MANUAL_CUSTOMER) {
|
|
11525
|
-
setContactId(null);
|
|
11526
|
-
return;
|
|
11527
|
-
}
|
|
11528
|
-
const id = Number(value);
|
|
11529
|
-
if (!Number.isFinite(id)) return;
|
|
11530
|
-
const customer = customers.find((c) => c.id === id);
|
|
11531
|
-
if (!customer) return;
|
|
11532
|
-
setContactId(id);
|
|
11533
|
-
setContactName(customer.name ?? "");
|
|
11534
|
-
setContactEmail(customer.email ?? "");
|
|
11535
|
-
setContactPhone(customer.phone ?? "");
|
|
11536
|
-
loadCustomerAddresses(id);
|
|
11537
|
-
}
|
|
11538
|
-
async function loadCustomerAddresses(customerId) {
|
|
11539
|
-
try {
|
|
11540
|
-
const res = await fetch(`/api/order_addresses?contactId=${customerId}&limit=100&sortField=createdAt&sortOrder=desc`);
|
|
11541
|
-
if (!res.ok) return;
|
|
11542
|
-
const body = await res.json();
|
|
11543
|
-
const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
11544
|
-
const billing = rows.find((a) => a.addressType === "billing_address");
|
|
11545
|
-
const shipping = rows.find((a) => a.addressType === "shipping_address");
|
|
11546
|
-
if (billing) setBillingAddress(mapAddressToForm(billing));
|
|
11547
|
-
else setBillingAddress({ ...emptyAddress });
|
|
11548
|
-
if (sameAsBilling) setShippingAddress(billing ? mapAddressToForm(billing) : { ...emptyAddress });
|
|
11549
|
-
else setShippingAddress(shipping ? mapAddressToForm(shipping) : { ...emptyAddress });
|
|
11550
|
-
} catch {
|
|
11551
|
-
setBillingAddress(emptyAddress);
|
|
11552
|
-
setShippingAddress(emptyAddress);
|
|
11553
|
-
}
|
|
11554
|
-
}
|
|
11555
|
-
const customerSelectValue = contactId != null ? String(contactId) : MANUAL_CUSTOMER;
|
|
11556
11512
|
function toggleStaged(id) {
|
|
11557
11513
|
setStagedIds((prev) => {
|
|
11558
11514
|
const next = new Set(prev);
|
|
@@ -11589,9 +11545,7 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11589
11545
|
if (idx >= 0) {
|
|
11590
11546
|
const row = next[idx];
|
|
11591
11547
|
next[idx] = { ...row, quantity: row.quantity + 1 };
|
|
11592
|
-
} else {
|
|
11593
|
-
next.push({ key: String(id), productId: id, label: hit.name ?? `Product #${id}`, sku: hit.sku, quantity: 1 });
|
|
11594
|
-
}
|
|
11548
|
+
} else next.push({ key: String(id), productId: id, label: hit.name ?? `Product #${id}`, sku: hit.sku, quantity: 1 });
|
|
11595
11549
|
}
|
|
11596
11550
|
return next;
|
|
11597
11551
|
});
|
|
@@ -11620,9 +11574,12 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11620
11574
|
}, [preview]);
|
|
11621
11575
|
async function handleSubmit(e) {
|
|
11622
11576
|
e.preventDefault();
|
|
11623
|
-
const
|
|
11624
|
-
|
|
11625
|
-
|
|
11577
|
+
const effectiveEmail = placeForSomeoneElse ? subContactEmail.trim() : contactEmail.trim();
|
|
11578
|
+
const effectiveName = placeForSomeoneElse ? subContactName.trim() : contactName.trim();
|
|
11579
|
+
const effectivePhone = placeForSomeoneElse ? subContactPhone.trim() : contactPhone.trim();
|
|
11580
|
+
const effectiveContactId = placeForSomeoneElse ? subContactId : contactId;
|
|
11581
|
+
if (effectiveContactId == null && !effectiveEmail) {
|
|
11582
|
+
import_sonner7.toast.error(placeForSomeoneElse ? "Enter the contact's email address" : "Select a customer or enter an email address");
|
|
11626
11583
|
return;
|
|
11627
11584
|
}
|
|
11628
11585
|
if (lines.length === 0) {
|
|
@@ -11636,46 +11593,67 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11636
11593
|
}
|
|
11637
11594
|
setSubmitting(true);
|
|
11638
11595
|
try {
|
|
11596
|
+
let resolvedContactId = placeForSomeoneElse ? subContactId : null;
|
|
11597
|
+
if (!resolvedContactId && effectiveEmail) {
|
|
11598
|
+
try {
|
|
11599
|
+
const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(effectiveEmail)}&limit=1`);
|
|
11600
|
+
if (res2.ok) {
|
|
11601
|
+
const data = await res2.json();
|
|
11602
|
+
const match = data.data?.find((c) => c.email?.toLowerCase() === effectiveEmail.toLowerCase());
|
|
11603
|
+
if (match) resolvedContactId = match.id;
|
|
11604
|
+
}
|
|
11605
|
+
} catch {
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
let orderContactId = resolvedContactId;
|
|
11609
|
+
if (placeForSomeoneElse) {
|
|
11610
|
+
if (contactEmail) {
|
|
11611
|
+
try {
|
|
11612
|
+
const res2 = await fetch(`/api/contacts?search=${encodeURIComponent(contactEmail)}&limit=5`);
|
|
11613
|
+
if (res2.ok) {
|
|
11614
|
+
const data = await res2.json();
|
|
11615
|
+
const match = data.data?.find((c) => c.email?.toLowerCase() === contactEmail.toLowerCase());
|
|
11616
|
+
if (match) orderContactId = match.id;
|
|
11617
|
+
}
|
|
11618
|
+
} catch {
|
|
11619
|
+
}
|
|
11620
|
+
}
|
|
11621
|
+
}
|
|
11639
11622
|
const payload = {
|
|
11640
11623
|
status,
|
|
11641
11624
|
currency,
|
|
11642
|
-
"contact.name":
|
|
11643
|
-
"contact.email":
|
|
11644
|
-
"contact.phone":
|
|
11625
|
+
"contact.name": effectiveName || effectiveEmail.split("@")[0] || "Customer",
|
|
11626
|
+
"contact.email": effectiveEmail || "",
|
|
11627
|
+
"contact.phone": effectivePhone || null,
|
|
11645
11628
|
billingAddress,
|
|
11646
11629
|
shippingAddress: sameAsBilling ? billingAddress : shippingAddress,
|
|
11647
11630
|
orderLines: lines.map((l) => {
|
|
11648
11631
|
const calc = preview?.lines.find((pl) => pl.productId === l.productId);
|
|
11649
|
-
return {
|
|
11650
|
-
productId: l.productId,
|
|
11651
|
-
quantity: l.quantity,
|
|
11652
|
-
unitPrice: calc?.unitPrice ?? productCache.current.get(l.productId)?.price ?? 0
|
|
11653
|
-
};
|
|
11632
|
+
return { productId: l.productId, quantity: l.quantity, unitPrice: calc?.unitPrice ?? productCache.current.get(l.productId)?.price ?? 0 };
|
|
11654
11633
|
})
|
|
11655
11634
|
};
|
|
11656
|
-
if (
|
|
11635
|
+
if (orderContactId != null) payload.contactId = orderContactId;
|
|
11636
|
+
if (!placeForSomeoneElse && contactId != null) payload.customerId = contactId;
|
|
11657
11637
|
if (coupons.length > 0) payload.discountId = coupons[0].discountId;
|
|
11658
11638
|
const url = editOrderId ? `/api/orders/${editOrderId}` : "/api/orders";
|
|
11659
11639
|
const method = editOrderId ? "PUT" : "POST";
|
|
11660
|
-
const res = await fetch(url, {
|
|
11661
|
-
method,
|
|
11662
|
-
headers: { "Content-Type": "application/json" },
|
|
11663
|
-
body: JSON.stringify(payload)
|
|
11664
|
-
});
|
|
11640
|
+
const res = await fetch(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
|
|
11665
11641
|
const body = await res.json().catch(() => ({}));
|
|
11666
11642
|
if (!res.ok) {
|
|
11667
11643
|
import_sonner7.toast.error(body.error ?? (editOrderId ? "Failed to update order" : "Failed to create order"));
|
|
11668
11644
|
return;
|
|
11669
11645
|
}
|
|
11670
|
-
if (
|
|
11671
|
-
const
|
|
11646
|
+
if (resolvedContactId == null) {
|
|
11647
|
+
const serverContactId = body.contactId;
|
|
11648
|
+
if (serverContactId != null && Number.isFinite(serverContactId)) {
|
|
11649
|
+
resolvedContactId = serverContactId;
|
|
11650
|
+
}
|
|
11651
|
+
}
|
|
11652
|
+
if (editOrderId && effectiveContactId != null) {
|
|
11653
|
+
const cr = await fetch(`/api/contacts/${effectiveContactId}`, {
|
|
11672
11654
|
method: "PUT",
|
|
11673
11655
|
headers: { "Content-Type": "application/json" },
|
|
11674
|
-
body: JSON.stringify({
|
|
11675
|
-
name: contactName.trim() || email.split("@")[0] || "Customer",
|
|
11676
|
-
email: email || void 0,
|
|
11677
|
-
phone: contactPhone.trim() || null
|
|
11678
|
-
})
|
|
11656
|
+
body: JSON.stringify({ name: effectiveName || effectiveEmail.split("@")[0] || "Customer", email: effectiveEmail || void 0, phone: effectivePhone || null })
|
|
11679
11657
|
});
|
|
11680
11658
|
if (!cr.ok) {
|
|
11681
11659
|
const cb = await cr.json().catch(() => ({}));
|
|
@@ -11687,6 +11665,25 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11687
11665
|
router.push(withAdminListFromParam(listReturnUrl, `/admin/orders/${editOrderId}/view`));
|
|
11688
11666
|
return;
|
|
11689
11667
|
}
|
|
11668
|
+
if (selectedCustomerId != null && resolvedContactId != null) {
|
|
11669
|
+
try {
|
|
11670
|
+
const linkCheck = await fetch(`/api/customer_contacts?customerId=${selectedCustomerId}&limit=200`);
|
|
11671
|
+
if (linkCheck.ok) {
|
|
11672
|
+
const linkData = await linkCheck.json();
|
|
11673
|
+
const existingRows = Array.isArray(linkData?.data) ? linkData.data : Array.isArray(linkData) ? linkData : [];
|
|
11674
|
+
const alreadyLinked = existingRows.some((r) => Number(r.contactId) === resolvedContactId);
|
|
11675
|
+
if (!alreadyLinked) {
|
|
11676
|
+
await fetch("/api/customer_contacts", {
|
|
11677
|
+
method: "POST",
|
|
11678
|
+
headers: { "Content-Type": "application/json" },
|
|
11679
|
+
body: JSON.stringify({ customerId: selectedCustomerId, contactId: resolvedContactId })
|
|
11680
|
+
});
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
} catch {
|
|
11684
|
+
console.warn("[OrderPlacement] Could not link contact to customer");
|
|
11685
|
+
}
|
|
11686
|
+
}
|
|
11690
11687
|
const id = body.id;
|
|
11691
11688
|
import_sonner7.toast.success("Order created");
|
|
11692
11689
|
if (id != null) router.push(withAdminListFromParam(listReturnUrl, `/admin/orders/${id}/view`));
|
|
@@ -11698,6 +11695,8 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11698
11695
|
}
|
|
11699
11696
|
}
|
|
11700
11697
|
const allVisibleStaged = productHits.length > 0 && productHits.every((p) => stagedIds.has(p.id));
|
|
11698
|
+
const customerSelectValue = contactId != null ? String(contactId) : MANUAL_CUSTOMER;
|
|
11699
|
+
const subContactSelectValue = subContactId != null ? String(subContactId) : MANUAL_CONTACT;
|
|
11701
11700
|
if (editOrderId && loadingOrder) {
|
|
11702
11701
|
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "rounded-lg bg-white shadow-md min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "flex justify-center items-center py-16 text-gray-500 text-sm", children: "Loading order\u2026" }) });
|
|
11703
11702
|
}
|
|
@@ -11742,74 +11741,168 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11742
11741
|
] }),
|
|
11743
11742
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
11744
11743
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "c-email", children: "Email" }),
|
|
11745
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { id: "c-email", type: "email", value: contactEmail, onChange: (e) => setContactEmail(e.target.value), placeholder: "name@example.com", required: contactId == null })
|
|
11744
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { id: "c-email", type: "email", value: contactEmail, onChange: (e) => setContactEmail(e.target.value), placeholder: "name@example.com", required: contactId == null && !placeForSomeoneElse })
|
|
11746
11745
|
] }),
|
|
11747
11746
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
11748
11747
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "c-phone", children: "Phone" }),
|
|
11749
11748
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { id: "c-phone", value: contactPhone, onChange: (e) => setContactPhone(e.target.value), placeholder: "phone number" })
|
|
11749
|
+
] })
|
|
11750
|
+
] }),
|
|
11751
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/60 px-4 py-3 mt-1", children: [
|
|
11752
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11753
|
+
Checkbox,
|
|
11754
|
+
{
|
|
11755
|
+
id: "place-for-someone-else",
|
|
11756
|
+
checked: placeForSomeoneElse,
|
|
11757
|
+
onCheckedChange: (checked) => handlePlaceForSomeoneElseToggle(Boolean(checked)),
|
|
11758
|
+
disabled: contactId == null
|
|
11759
|
+
}
|
|
11760
|
+
),
|
|
11761
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-0.5", children: [
|
|
11762
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11763
|
+
"label",
|
|
11764
|
+
{
|
|
11765
|
+
htmlFor: "place-for-someone-else",
|
|
11766
|
+
className: `text-sm font-medium leading-none cursor-pointer ${contactId == null ? "text-gray-400" : "text-gray-700"}`,
|
|
11767
|
+
children: "Place this order for someone else"
|
|
11768
|
+
}
|
|
11769
|
+
),
|
|
11770
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-xs text-gray-500", children: contactId == null ? "Select a customer above to enable this option." : "Select a linked contact or enter their details below. Addresses will be filled from the selected contact." })
|
|
11771
|
+
] })
|
|
11772
|
+
] }),
|
|
11773
|
+
placeForSomeoneElse && contactId != null && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "rounded-lg border border-blue-200 bg-blue-50/40 p-4 space-y-3", children: [
|
|
11774
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
11775
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react30.Users, { className: "h-4 w-4 text-blue-600" }),
|
|
11776
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("h3", { className: "text-sm font-semibold text-blue-800", children: "Order recipient" })
|
|
11750
11777
|
] }),
|
|
11751
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11752
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11753
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
+
customerContactsLoading ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-xs text-gray-500", children: "Loading contacts\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2", children: [
|
|
11779
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "sc-select", children: "Select contact" }),
|
|
11780
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
11781
|
+
"select",
|
|
11782
|
+
{
|
|
11783
|
+
id: "sc-select",
|
|
11784
|
+
value: subContactSelectValue,
|
|
11785
|
+
onChange: (e) => handleSubContactSelect(e.target.value),
|
|
11786
|
+
className: "w-full h-10 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
11787
|
+
children: [
|
|
11788
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("option", { value: MANUAL_CONTACT, children: "Enter manually (new contact)" }),
|
|
11789
|
+
customerContacts.map((r) => /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("option", { value: String(r.contactId), children: [
|
|
11790
|
+
r.contact.name,
|
|
11791
|
+
r.contact.email ? ` \u2014 ${r.contact.email}` : ""
|
|
11792
|
+
] }, r.contactId))
|
|
11793
|
+
]
|
|
11794
|
+
}
|
|
11795
|
+
),
|
|
11796
|
+
customerContacts.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-3 py-2", children: "No linked contacts found for this customer. Fill in the details below." })
|
|
11797
|
+
] }),
|
|
11798
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [
|
|
11799
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
11800
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "sc-name", children: "Recipient name" }),
|
|
11801
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11802
|
+
Input,
|
|
11803
|
+
{
|
|
11804
|
+
id: "sc-name",
|
|
11805
|
+
value: subContactName,
|
|
11806
|
+
onChange: (e) => setSubContactName(e.target.value),
|
|
11807
|
+
placeholder: "Full name",
|
|
11808
|
+
readOnly: subContactId != null,
|
|
11809
|
+
className: subContactId != null ? "bg-gray-100 cursor-not-allowed" : ""
|
|
11810
|
+
}
|
|
11811
|
+
)
|
|
11812
|
+
] }),
|
|
11813
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
11814
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "sc-email", children: "Recipient email" }),
|
|
11815
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11816
|
+
Input,
|
|
11817
|
+
{
|
|
11818
|
+
id: "sc-email",
|
|
11819
|
+
type: "email",
|
|
11820
|
+
value: subContactEmail,
|
|
11821
|
+
onChange: (e) => setSubContactEmail(e.target.value),
|
|
11822
|
+
placeholder: "email@example.com",
|
|
11823
|
+
required: placeForSomeoneElse && subContactId == null,
|
|
11824
|
+
readOnly: subContactId != null,
|
|
11825
|
+
className: subContactId != null ? "bg-gray-100 cursor-not-allowed" : ""
|
|
11826
|
+
}
|
|
11827
|
+
)
|
|
11828
|
+
] }),
|
|
11829
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
11830
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { htmlFor: "sc-phone", children: "Recipient phone" }),
|
|
11831
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11832
|
+
Input,
|
|
11833
|
+
{
|
|
11834
|
+
id: "sc-phone",
|
|
11835
|
+
value: subContactPhone,
|
|
11836
|
+
onChange: (e) => setSubContactPhone(e.target.value),
|
|
11837
|
+
placeholder: "phone number",
|
|
11838
|
+
readOnly: subContactId != null,
|
|
11839
|
+
className: subContactId != null ? "bg-gray-100 cursor-not-allowed" : ""
|
|
11840
|
+
}
|
|
11841
|
+
)
|
|
11842
|
+
] })
|
|
11843
|
+
] })
|
|
11844
|
+
] }),
|
|
11845
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-4 border rounded-lg p-4", children: [
|
|
11846
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("h3", { className: "text-sm font-semibold", children: "Billing Address" }),
|
|
11847
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [
|
|
11848
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11849
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Address Line 1" }),
|
|
11850
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.line1, onChange: (e) => updateAddress("billing", "line1", e.target.value), placeholder: "Street Address" })
|
|
11851
|
+
] }),
|
|
11852
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11853
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Address Line 2" }),
|
|
11854
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.line2, onChange: (e) => updateAddress("billing", "line2", e.target.value), placeholder: "Apartment, Suite, Floor, etc." })
|
|
11855
|
+
] }),
|
|
11856
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2", children: [
|
|
11857
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "City" }),
|
|
11858
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.city, onChange: (e) => updateAddress("billing", "city", e.target.value), placeholder: "City" })
|
|
11859
|
+
] }),
|
|
11860
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2", children: [
|
|
11861
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "State" }),
|
|
11862
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.state, onChange: (e) => updateAddress("billing", "state", e.target.value), placeholder: "State" })
|
|
11863
|
+
] }),
|
|
11864
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2", children: [
|
|
11865
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Country" }),
|
|
11866
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.country, onChange: (e) => updateAddress("billing", "country", e.target.value), placeholder: "Country" })
|
|
11867
|
+
] }),
|
|
11868
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2", children: [
|
|
11869
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Postal Code" }),
|
|
11870
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: billingAddress.postalCode, onChange: (e) => updateAddress("billing", "postalCode", e.target.value), placeholder: "Postal Code" })
|
|
11871
|
+
] })
|
|
11872
|
+
] })
|
|
11873
|
+
] }),
|
|
11874
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-4 border rounded-lg p-4", children: [
|
|
11875
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
11876
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("h3", { className: "text-sm font-semibold", children: "Shipping Address" }),
|
|
11877
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
11878
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Checkbox, { checked: sameAsBilling, onCheckedChange: (checked) => setSameAsBilling(Boolean(checked)) }),
|
|
11879
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm text-gray-600", children: "Same as Billing" })
|
|
11778
11880
|
] })
|
|
11779
11881
|
] }),
|
|
11780
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "
|
|
11781
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "
|
|
11782
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11783
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11784
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Checkbox, { checked: sameAsBilling, onCheckedChange: (checked) => setSameAsBilling(Boolean(checked)) }),
|
|
11785
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm text-gray-600", children: "Same as Billing" })
|
|
11786
|
-
] })
|
|
11882
|
+
!sameAsBilling && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [
|
|
11883
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11884
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Address Line 1" }),
|
|
11885
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.line1, onChange: (e) => updateAddress("shipping", "line1", e.target.value), placeholder: "Street Address" })
|
|
11787
11886
|
] }),
|
|
11788
|
-
|
|
11789
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
11806
|
-
|
|
11807
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.country, onChange: (e) => updateAddress("shipping", "country", e.target.value), placeholder: "Country" })
|
|
11808
|
-
] }),
|
|
11809
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11810
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Postal Code" }),
|
|
11811
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.postalCode, onChange: (e) => updateAddress("shipping", "postalCode", e.target.value), placeholder: "Postal Code" })
|
|
11812
|
-
] })
|
|
11887
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11888
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Address Line 2" }),
|
|
11889
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.line2, onChange: (e) => updateAddress("shipping", "line2", e.target.value), placeholder: "Apartment, Suite, Floor, etc." })
|
|
11890
|
+
] }),
|
|
11891
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11892
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "City" }),
|
|
11893
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.city, onChange: (e) => updateAddress("shipping", "city", e.target.value), placeholder: "City" })
|
|
11894
|
+
] }),
|
|
11895
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11896
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "State" }),
|
|
11897
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.state, onChange: (e) => updateAddress("shipping", "state", e.target.value), placeholder: "State" })
|
|
11898
|
+
] }),
|
|
11899
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11900
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Country" }),
|
|
11901
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.country, onChange: (e) => updateAddress("shipping", "country", e.target.value), placeholder: "Country" })
|
|
11902
|
+
] }),
|
|
11903
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "space-y-2 md:col-span-2", children: [
|
|
11904
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Label3, { children: "Postal Code" }),
|
|
11905
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { value: shippingAddress.postalCode, onChange: (e) => updateAddress("shipping", "postalCode", e.target.value), placeholder: "Postal Code" })
|
|
11813
11906
|
] })
|
|
11814
11907
|
] })
|
|
11815
11908
|
] })
|
|
@@ -11907,51 +12000,21 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
11907
12000
|
const active = selectedKey === line.key;
|
|
11908
12001
|
const calc = calcByProductId.get(line.productId);
|
|
11909
12002
|
const isReward = line.key.startsWith("reward-");
|
|
11910
|
-
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
min: 1,
|
|
11926
|
-
className: "h-8 w-14 ml-auto text-right",
|
|
11927
|
-
value: line.quantity,
|
|
11928
|
-
onChange: (e) => updateQty(line.key, Number(e.target.value)),
|
|
11929
|
-
onClick: (e) => e.stopPropagation(),
|
|
11930
|
-
readOnly: isReward,
|
|
11931
|
-
disabled: isReward
|
|
11932
|
-
}
|
|
11933
|
-
) }),
|
|
11934
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right text-sm hidden sm:table-cell whitespace-nowrap", children: calc ? formatMoney3(calc.unitPrice, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
11935
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right text-sm hidden md:table-cell whitespace-nowrap", children: calc ? formatMoney3(calc.tax, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
11936
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right font-medium text-sm whitespace-nowrap", children: calc ? formatMoney3(calc.total, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
11937
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top", children: !isReward && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
11938
|
-
Button,
|
|
11939
|
-
{
|
|
11940
|
-
type: "button",
|
|
11941
|
-
variant: "ghost",
|
|
11942
|
-
size: "icon",
|
|
11943
|
-
className: "h-8 w-8 text-red-600",
|
|
11944
|
-
onClick: (e) => {
|
|
11945
|
-
e.stopPropagation();
|
|
11946
|
-
removeLine(line.key);
|
|
11947
|
-
},
|
|
11948
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react30.Trash2, { className: "h-4 w-4" })
|
|
11949
|
-
}
|
|
11950
|
-
) })
|
|
11951
|
-
]
|
|
11952
|
-
},
|
|
11953
|
-
line.key
|
|
11954
|
-
);
|
|
12003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(TableRow, { className: active ? "bg-blue-50/80 cursor-pointer" : "cursor-pointer", onClick: () => setSelectedKey(line.key), children: [
|
|
12004
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(TableCell, { className: "align-top min-w-0", children: [
|
|
12005
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "font-medium text-gray-900 break-words", children: line.label }),
|
|
12006
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-xs text-gray-500 font-mono", children: line.sku ?? "\u2014" }),
|
|
12007
|
+
isReward && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-[10px] text-purple-600 font-medium", children: "\u{1F381} Reward" })
|
|
12008
|
+
] }),
|
|
12009
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Input, { type: "number", min: 1, className: "h-8 w-14 ml-auto text-right", value: line.quantity, onChange: (e) => updateQty(line.key, Number(e.target.value)), onClick: (e) => e.stopPropagation(), readOnly: isReward, disabled: isReward }) }),
|
|
12010
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right text-sm hidden sm:table-cell whitespace-nowrap", children: calc ? formatMoney3(calc.unitPrice, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
12011
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right text-sm hidden md:table-cell whitespace-nowrap", children: calc ? formatMoney3(calc.tax, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
12012
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top text-right font-medium text-sm whitespace-nowrap", children: calc ? formatMoney3(calc.total, currency) : previewLoading ? "\u2026" : "\u2014" }),
|
|
12013
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TableCell, { className: "align-top", children: !isReward && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8 text-red-600", onClick: (e) => {
|
|
12014
|
+
e.stopPropagation();
|
|
12015
|
+
removeLine(line.key);
|
|
12016
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react30.Trash2, { className: "h-4 w-4" }) }) })
|
|
12017
|
+
] }, line.key);
|
|
11955
12018
|
}) })
|
|
11956
12019
|
] }) }),
|
|
11957
12020
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex flex-wrap items-center gap-4 justify-between border border-gray-200 rounded-lg p-4 bg-gray-50/70 text-sm", children: [
|
|
@@ -12018,21 +12081,13 @@ function OrderPlacementPage({ editOrderId }) {
|
|
|
12018
12081
|
if (!hit) return;
|
|
12019
12082
|
const productHit = hit;
|
|
12020
12083
|
setLines((prev) => {
|
|
12021
|
-
const existingRewardIdx = prev.findIndex(
|
|
12022
|
-
(l) => l.productId === pid && rewardProductIds.current.has(pid)
|
|
12023
|
-
);
|
|
12084
|
+
const existingRewardIdx = prev.findIndex((l) => l.productId === pid && rewardProductIds.current.has(pid));
|
|
12024
12085
|
if (existingRewardIdx >= 0) {
|
|
12025
12086
|
const next = [...prev];
|
|
12026
12087
|
next[existingRewardIdx] = { ...next[existingRewardIdx], quantity: qty };
|
|
12027
12088
|
return next;
|
|
12028
12089
|
}
|
|
12029
|
-
return [...prev, {
|
|
12030
|
-
key: `reward-${pid}-${coupon.discountId}`,
|
|
12031
|
-
productId: pid,
|
|
12032
|
-
label: productHit.name ?? `Product #${pid}`,
|
|
12033
|
-
sku: productHit.sku,
|
|
12034
|
-
quantity: qty
|
|
12035
|
-
}];
|
|
12090
|
+
return [...prev, { key: `reward-${pid}-${coupon.discountId}`, productId: pid, label: productHit.name ?? `Product #${pid}`, sku: productHit.sku, quantity: qty }];
|
|
12036
12091
|
});
|
|
12037
12092
|
});
|
|
12038
12093
|
}
|
|
@@ -21050,8 +21105,12 @@ function SchedulesPage() {
|
|
|
21050
21105
|
|
|
21051
21106
|
// src/admin/pages/VendorOnboardPage.tsx
|
|
21052
21107
|
var import_react59 = require("react");
|
|
21108
|
+
var import_navigation27 = require("next/navigation");
|
|
21053
21109
|
var import_react60 = require("next-auth/react");
|
|
21110
|
+
var import_lucide_react46 = require("lucide-react");
|
|
21054
21111
|
init_vendor_scope();
|
|
21112
|
+
init_DetailPageHeader();
|
|
21113
|
+
init_DetailPageLayout();
|
|
21055
21114
|
init_button();
|
|
21056
21115
|
init_input();
|
|
21057
21116
|
|
|
@@ -21090,10 +21149,39 @@ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
|
21090
21149
|
|
|
21091
21150
|
// src/admin/pages/VendorOnboardPage.tsx
|
|
21092
21151
|
var import_jsx_runtime76 = require("react/jsx-runtime");
|
|
21093
|
-
|
|
21094
|
-
|
|
21095
|
-
|
|
21096
|
-
|
|
21152
|
+
var VENDOR_LIST_COLUMNS = [
|
|
21153
|
+
{ field: "name", displayName: "Name" },
|
|
21154
|
+
{ field: "slug", displayName: "Slug" },
|
|
21155
|
+
{ field: "email", displayName: "Email" },
|
|
21156
|
+
{ field: "phone", displayName: "Phone" },
|
|
21157
|
+
{ field: "active", displayName: "Active", type: "boolean" },
|
|
21158
|
+
{
|
|
21159
|
+
field: "inviteStatus",
|
|
21160
|
+
displayName: "Invite",
|
|
21161
|
+
type: "select",
|
|
21162
|
+
options: [
|
|
21163
|
+
{ value: "none", label: "None" },
|
|
21164
|
+
{ value: "pending", label: "Pending" },
|
|
21165
|
+
{ value: "expired", label: "Expired" },
|
|
21166
|
+
{ value: "accepted", label: "Accepted" }
|
|
21167
|
+
]
|
|
21168
|
+
},
|
|
21169
|
+
{ field: "createdAt", displayName: "Created", type: "date" }
|
|
21170
|
+
];
|
|
21171
|
+
function AccessDenied({ groupName }) {
|
|
21172
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "rounded-lg bg-white p-6 shadow-md max-w-lg", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("p", { className: "text-sm text-red-600", children: [
|
|
21173
|
+
"Only users in the ",
|
|
21174
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("strong", { children: "Administrator" }),
|
|
21175
|
+
" group can manage vendors.",
|
|
21176
|
+
groupName ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(import_jsx_runtime76.Fragment, { children: [
|
|
21177
|
+
" ",
|
|
21178
|
+
"Your group is ",
|
|
21179
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("strong", { children: groupName }),
|
|
21180
|
+
". Assign Administrator under System \u2192 Users, then sign out and sign in again."
|
|
21181
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(import_jsx_runtime76.Fragment, { children: " Assign the Administrator group under System \u2192 Users, then sign out and sign in again." })
|
|
21182
|
+
] }) });
|
|
21183
|
+
}
|
|
21184
|
+
function VendorOnboardForm({ onSuccess }) {
|
|
21097
21185
|
const [vendorName, setVendorName] = (0, import_react59.useState)("");
|
|
21098
21186
|
const [vendorSlug, setVendorSlug] = (0, import_react59.useState)("");
|
|
21099
21187
|
const [vendorEmail, setVendorEmail] = (0, import_react59.useState)("");
|
|
@@ -21101,44 +21189,31 @@ function VendorOnboardPage() {
|
|
|
21101
21189
|
const [ownerName, setOwnerName] = (0, import_react59.useState)("");
|
|
21102
21190
|
const [ownerEmail, setOwnerEmail] = (0, import_react59.useState)("");
|
|
21103
21191
|
const [activationMode, setActivationMode] = (0, import_react59.useState)("invite");
|
|
21104
|
-
const [
|
|
21192
|
+
const [sendOwnerEmail, setSendOwnerEmail] = (0, import_react59.useState)(true);
|
|
21105
21193
|
const [ownerPassword, setOwnerPassword] = (0, import_react59.useState)("");
|
|
21106
21194
|
const [ownerPasswordConfirm, setOwnerPasswordConfirm] = (0, import_react59.useState)("");
|
|
21107
21195
|
const [loading, setLoading] = (0, import_react59.useState)(false);
|
|
21108
21196
|
const [message, setMessage] = (0, import_react59.useState)(null);
|
|
21109
21197
|
const [inviteLink, setInviteLink] = (0, import_react59.useState)(null);
|
|
21110
|
-
const [
|
|
21111
|
-
|
|
21112
|
-
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "p-6 max-w-lg", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("p", { className: "text-sm text-red-600", children: [
|
|
21113
|
-
"Only users in the ",
|
|
21114
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("strong", { children: "Administrator" }),
|
|
21115
|
-
" group can onboard vendors.",
|
|
21116
|
-
sessionUser?.groupName ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(import_jsx_runtime76.Fragment, { children: [
|
|
21117
|
-
" Your group is ",
|
|
21118
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("strong", { children: sessionUser.groupName }),
|
|
21119
|
-
". Assign Administrator under System \u2192 Users, then sign out and sign in again."
|
|
21120
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(import_jsx_runtime76.Fragment, { children: " Assign the Administrator group under System \u2192 Users, then sign out and sign in again." })
|
|
21121
|
-
] }) });
|
|
21122
|
-
}
|
|
21123
|
-
const handleSubmit = async (e) => {
|
|
21124
|
-
e.preventDefault();
|
|
21198
|
+
const [errors, setErrors] = (0, import_react59.useState)([]);
|
|
21199
|
+
const handleSubmit = async () => {
|
|
21125
21200
|
setLoading(true);
|
|
21126
21201
|
setMessage(null);
|
|
21127
|
-
|
|
21202
|
+
setErrors([]);
|
|
21128
21203
|
setInviteLink(null);
|
|
21129
21204
|
if (activationMode === "password") {
|
|
21130
21205
|
if (!ownerPassword) {
|
|
21131
|
-
|
|
21206
|
+
setErrors(["Enter a password for the owner account"]);
|
|
21132
21207
|
setLoading(false);
|
|
21133
21208
|
return;
|
|
21134
21209
|
}
|
|
21135
21210
|
if (ownerPassword !== ownerPasswordConfirm) {
|
|
21136
|
-
|
|
21211
|
+
setErrors(["Passwords do not match"]);
|
|
21137
21212
|
setLoading(false);
|
|
21138
21213
|
return;
|
|
21139
21214
|
}
|
|
21140
21215
|
if (ownerPassword.length < 6) {
|
|
21141
|
-
|
|
21216
|
+
setErrors(["Password must be at least 6 characters"]);
|
|
21142
21217
|
setLoading(false);
|
|
21143
21218
|
return;
|
|
21144
21219
|
}
|
|
@@ -21149,7 +21224,7 @@ function VendorOnboardPage() {
|
|
|
21149
21224
|
headers: { "Content-Type": "application/json" },
|
|
21150
21225
|
body: JSON.stringify({
|
|
21151
21226
|
activation: activationMode,
|
|
21152
|
-
|
|
21227
|
+
sendOwnerEmail,
|
|
21153
21228
|
vendor: {
|
|
21154
21229
|
name: vendorName,
|
|
21155
21230
|
slug: vendorSlug || void 0,
|
|
@@ -21166,140 +21241,241 @@ function VendorOnboardPage() {
|
|
|
21166
21241
|
});
|
|
21167
21242
|
const data = await res.json();
|
|
21168
21243
|
if (!res.ok) {
|
|
21169
|
-
|
|
21244
|
+
setErrors([data.error || "Onboarding failed"]);
|
|
21170
21245
|
return;
|
|
21171
21246
|
}
|
|
21172
21247
|
setMessage(data.message || "Vendor created");
|
|
21173
21248
|
setInviteLink(data.inviteLink ?? null);
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
setVendorPhone("");
|
|
21178
|
-
setOwnerName("");
|
|
21179
|
-
setOwnerEmail("");
|
|
21180
|
-
setOwnerPassword("");
|
|
21181
|
-
setOwnerPasswordConfirm("");
|
|
21249
|
+
if (!data.inviteLink) {
|
|
21250
|
+
onSuccess();
|
|
21251
|
+
}
|
|
21182
21252
|
} catch {
|
|
21183
|
-
|
|
21253
|
+
setErrors(["Request failed"]);
|
|
21184
21254
|
} finally {
|
|
21185
21255
|
setLoading(false);
|
|
21186
21256
|
}
|
|
21187
21257
|
};
|
|
21188
|
-
const submitLabel = activationMode === "password" ? "Create vendor & set password" :
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
|
|
21212
|
-
|
|
21213
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h2", { className: "text-sm font-medium text-gray-900 dark:text-white", children: "Owner account" }),
|
|
21214
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21215
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerName", children: "Name" }),
|
|
21216
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Input, { id: "ownerName", required: true, value: ownerName, onChange: (e) => setOwnerName(e.target.value), className: "mt-1" })
|
|
21217
|
-
] }),
|
|
21218
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21219
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerEmail", children: "Email" }),
|
|
21220
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Input, { id: "ownerEmail", type: "email", required: true, value: ownerEmail, onChange: (e) => setOwnerEmail(e.target.value), className: "mt-1" })
|
|
21221
|
-
] })
|
|
21258
|
+
const submitLabel = activationMode === "password" ? sendOwnerEmail ? "Create vendor & send welcome email" : "Create vendor & set password" : sendOwnerEmail ? "Create vendor & send invite" : "Create vendor & copy invite link";
|
|
21259
|
+
const fieldClass = "w-full rounded-md border border-gray-300 px-3 py-2 text-sm";
|
|
21260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "rounded-lg bg-white shadow-md", children: [
|
|
21261
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21262
|
+
DetailPageHeader,
|
|
21263
|
+
{
|
|
21264
|
+
title: "Create vendor",
|
|
21265
|
+
subtitle: "Creates a vendor store and a Vendor Owner account",
|
|
21266
|
+
closeHref: "/admin/vendor-onboard",
|
|
21267
|
+
menuItems: [{ label: loading ? "Creating\u2026" : submitLabel, icon: import_lucide_react46.Save, onClick: handleSubmit }]
|
|
21268
|
+
}
|
|
21269
|
+
),
|
|
21270
|
+
errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex", children: [
|
|
21271
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(import_lucide_react46.AlertCircle, { className: "h-5 w-5 text-red-400 flex-shrink-0" }),
|
|
21272
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "ml-3", children: [
|
|
21273
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h3", { className: "text-sm font-medium text-red-800", children: "Please fix the following errors:" }),
|
|
21274
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("ul", { className: "mt-2 text-sm text-red-700 list-disc pl-5 space-y-1", children: errors.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("li", { children: e }, i)) })
|
|
21275
|
+
] })
|
|
21276
|
+
] }) }),
|
|
21277
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "mx-6 mt-4 rounded-lg border border-green-200 bg-green-50 p-4 text-sm text-green-800", children: [
|
|
21278
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { children: message }),
|
|
21279
|
+
inviteLink && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("p", { className: "mt-2 text-xs break-all", children: [
|
|
21280
|
+
"Invite link:",
|
|
21281
|
+
" ",
|
|
21282
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("a", { href: inviteLink, className: "text-blue-600 underline", children: inviteLink })
|
|
21222
21283
|
] }),
|
|
21223
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21228
|
-
|
|
21229
|
-
|
|
21230
|
-
className: "
|
|
21231
|
-
children: [
|
|
21232
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", {
|
|
21233
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21234
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21284
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Button, { type: "button", variant: "outline", size: "sm", className: "mt-3", onClick: onSuccess, children: "Back to vendors" })
|
|
21285
|
+
] }),
|
|
21286
|
+
!message && /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21287
|
+
DetailPageLayout,
|
|
21288
|
+
{
|
|
21289
|
+
main: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(import_jsx_runtime76.Fragment, { children: [
|
|
21290
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { children: [
|
|
21291
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Store" }),
|
|
21292
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-4", children: [
|
|
21293
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21294
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "vendorName", className: "text-xs font-medium text-gray-600", children: "Store name *" }),
|
|
21295
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21296
|
+
Input,
|
|
21297
|
+
{
|
|
21298
|
+
id: "vendorName",
|
|
21299
|
+
required: true,
|
|
21300
|
+
value: vendorName,
|
|
21301
|
+
onChange: (e) => setVendorName(e.target.value),
|
|
21302
|
+
className: `mt-1 ${fieldClass}`
|
|
21303
|
+
}
|
|
21304
|
+
)
|
|
21238
21305
|
] }),
|
|
21239
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", {
|
|
21240
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21241
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.
|
|
21242
|
-
|
|
21243
|
-
|
|
21244
|
-
|
|
21306
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21307
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "vendorSlug", className: "text-xs font-medium text-gray-600", children: "Slug (optional)" }),
|
|
21308
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21309
|
+
Input,
|
|
21310
|
+
{
|
|
21311
|
+
id: "vendorSlug",
|
|
21312
|
+
value: vendorSlug,
|
|
21313
|
+
onChange: (e) => setVendorSlug(e.target.value),
|
|
21314
|
+
placeholder: "auto from name",
|
|
21315
|
+
className: `mt-1 ${fieldClass}`
|
|
21316
|
+
}
|
|
21317
|
+
)
|
|
21318
|
+
] }),
|
|
21319
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21320
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "vendorEmail", className: "text-xs font-medium text-gray-600", children: "Store email" }),
|
|
21321
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21322
|
+
Input,
|
|
21323
|
+
{
|
|
21324
|
+
id: "vendorEmail",
|
|
21325
|
+
type: "email",
|
|
21326
|
+
value: vendorEmail,
|
|
21327
|
+
onChange: (e) => setVendorEmail(e.target.value),
|
|
21328
|
+
className: `mt-1 ${fieldClass}`
|
|
21329
|
+
}
|
|
21330
|
+
)
|
|
21331
|
+
] }),
|
|
21332
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21333
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "vendorPhone", className: "text-xs font-medium text-gray-600", children: "Phone" }),
|
|
21334
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21335
|
+
Input,
|
|
21336
|
+
{
|
|
21337
|
+
id: "vendorPhone",
|
|
21338
|
+
value: vendorPhone,
|
|
21339
|
+
onChange: (e) => setVendorPhone(e.target.value),
|
|
21340
|
+
className: `mt-1 ${fieldClass}`
|
|
21341
|
+
}
|
|
21342
|
+
)
|
|
21245
21343
|
] })
|
|
21246
|
-
]
|
|
21247
|
-
}
|
|
21248
|
-
),
|
|
21249
|
-
activationMode === "invite" && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex items-center gap-2 pt-1", children: [
|
|
21250
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21251
|
-
Checkbox,
|
|
21252
|
-
{
|
|
21253
|
-
id: "sendInviteEmail",
|
|
21254
|
-
checked: sendInviteEmail,
|
|
21255
|
-
onCheckedChange: (checked) => setSendInviteEmail(checked === true)
|
|
21256
|
-
}
|
|
21257
|
-
),
|
|
21258
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "sendInviteEmail", className: "font-normal cursor-pointer text-sm", children: "Send invite email to owner" })
|
|
21259
|
-
] }),
|
|
21260
|
-
activationMode === "password" && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "space-y-3 pt-1", children: [
|
|
21261
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21262
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerPassword", children: "Password" }),
|
|
21263
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21264
|
-
Input,
|
|
21265
|
-
{
|
|
21266
|
-
id: "ownerPassword",
|
|
21267
|
-
type: "password",
|
|
21268
|
-
required: true,
|
|
21269
|
-
autoComplete: "new-password",
|
|
21270
|
-
value: ownerPassword,
|
|
21271
|
-
onChange: (e) => setOwnerPassword(e.target.value),
|
|
21272
|
-
className: "mt-1"
|
|
21273
|
-
}
|
|
21274
|
-
)
|
|
21344
|
+
] })
|
|
21275
21345
|
] }),
|
|
21276
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("
|
|
21277
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21278
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.
|
|
21279
|
-
|
|
21346
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { children: [
|
|
21347
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Owner account" }),
|
|
21348
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-4", children: [
|
|
21349
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21350
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerName", className: "text-xs font-medium text-gray-600", children: "Name *" }),
|
|
21351
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21352
|
+
Input,
|
|
21353
|
+
{
|
|
21354
|
+
id: "ownerName",
|
|
21355
|
+
required: true,
|
|
21356
|
+
value: ownerName,
|
|
21357
|
+
onChange: (e) => setOwnerName(e.target.value),
|
|
21358
|
+
className: `mt-1 ${fieldClass}`
|
|
21359
|
+
}
|
|
21360
|
+
)
|
|
21361
|
+
] }),
|
|
21362
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21363
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerEmail", className: "text-xs font-medium text-gray-600", children: "Email *" }),
|
|
21364
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21365
|
+
Input,
|
|
21366
|
+
{
|
|
21367
|
+
id: "ownerEmail",
|
|
21368
|
+
type: "email",
|
|
21369
|
+
required: true,
|
|
21370
|
+
value: ownerEmail,
|
|
21371
|
+
onChange: (e) => setOwnerEmail(e.target.value),
|
|
21372
|
+
className: `mt-1 ${fieldClass}`
|
|
21373
|
+
}
|
|
21374
|
+
)
|
|
21375
|
+
] })
|
|
21376
|
+
] })
|
|
21377
|
+
] })
|
|
21378
|
+
] }),
|
|
21379
|
+
sidebar: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { children: [
|
|
21380
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Owner access" }),
|
|
21381
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4 bg-gray-50/50 space-y-4", children: [
|
|
21382
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(
|
|
21383
|
+
RadioGroup2,
|
|
21280
21384
|
{
|
|
21281
|
-
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21385
|
+
value: activationMode,
|
|
21386
|
+
onValueChange: (v) => setActivationMode(v),
|
|
21387
|
+
className: "gap-3",
|
|
21388
|
+
children: [
|
|
21389
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex items-start gap-2", children: [
|
|
21390
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(RadioGroupItem, { value: "invite", id: "activation-invite", className: "mt-0.5" }),
|
|
21391
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21392
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "activation-invite", className: "font-normal cursor-pointer text-sm", children: "Send invite link" }),
|
|
21393
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "text-xs text-gray-500 mt-0.5", children: "Owner sets their own password via email or the link below." })
|
|
21394
|
+
] })
|
|
21395
|
+
] }),
|
|
21396
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex items-start gap-2", children: [
|
|
21397
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(RadioGroupItem, { value: "password", id: "activation-password", className: "mt-0.5" }),
|
|
21398
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21399
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "activation-password", className: "font-normal cursor-pointer text-sm", children: "Set password now" }),
|
|
21400
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "text-xs text-gray-500 mt-0.5", children: "Account is active immediately; share the password with the owner securely." })
|
|
21401
|
+
] })
|
|
21402
|
+
] })
|
|
21403
|
+
]
|
|
21288
21404
|
}
|
|
21289
|
-
)
|
|
21405
|
+
),
|
|
21406
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex items-center gap-2 pt-1", children: [
|
|
21407
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21408
|
+
Checkbox,
|
|
21409
|
+
{
|
|
21410
|
+
id: "sendOwnerEmail",
|
|
21411
|
+
checked: sendOwnerEmail,
|
|
21412
|
+
onCheckedChange: (checked) => setSendOwnerEmail(checked === true)
|
|
21413
|
+
}
|
|
21414
|
+
),
|
|
21415
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "sendOwnerEmail", className: "font-normal cursor-pointer text-sm", children: activationMode === "invite" ? "Send invite email to owner" : "Send welcome email to owner" })
|
|
21416
|
+
] }),
|
|
21417
|
+
activationMode === "password" && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "space-y-3 pt-1", children: [
|
|
21418
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21419
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerPassword", className: "text-xs font-medium text-gray-600", children: "Password *" }),
|
|
21420
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21421
|
+
Input,
|
|
21422
|
+
{
|
|
21423
|
+
id: "ownerPassword",
|
|
21424
|
+
type: "password",
|
|
21425
|
+
required: true,
|
|
21426
|
+
autoComplete: "new-password",
|
|
21427
|
+
value: ownerPassword,
|
|
21428
|
+
onChange: (e) => setOwnerPassword(e.target.value),
|
|
21429
|
+
className: `mt-1 ${fieldClass}`
|
|
21430
|
+
}
|
|
21431
|
+
)
|
|
21432
|
+
] }),
|
|
21433
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
|
|
21434
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Label3, { htmlFor: "ownerPasswordConfirm", className: "text-xs font-medium text-gray-600", children: "Confirm password *" }),
|
|
21435
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21436
|
+
Input,
|
|
21437
|
+
{
|
|
21438
|
+
id: "ownerPasswordConfirm",
|
|
21439
|
+
type: "password",
|
|
21440
|
+
required: true,
|
|
21441
|
+
autoComplete: "new-password",
|
|
21442
|
+
value: ownerPasswordConfirm,
|
|
21443
|
+
onChange: (e) => setOwnerPasswordConfirm(e.target.value),
|
|
21444
|
+
className: `mt-1 ${fieldClass}`
|
|
21445
|
+
}
|
|
21446
|
+
)
|
|
21447
|
+
] })
|
|
21448
|
+
] }),
|
|
21449
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Button, { type: "button", disabled: loading, onClick: handleSubmit, className: "w-full", children: loading ? "Creating\u2026" : submitLabel })
|
|
21290
21450
|
] })
|
|
21291
21451
|
] })
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
message && /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "text-sm text-green-700", children: message }),
|
|
21295
|
-
inviteLink && /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("p", { className: "text-xs text-gray-600 break-all", children: [
|
|
21296
|
-
"Invite link: ",
|
|
21297
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("a", { href: inviteLink, className: "text-blue-600 underline", children: inviteLink })
|
|
21298
|
-
] }),
|
|
21299
|
-
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Button, { type: "submit", disabled: loading, children: loading ? "Creating\u2026" : submitLabel })
|
|
21300
|
-
] })
|
|
21452
|
+
}
|
|
21453
|
+
)
|
|
21301
21454
|
] });
|
|
21302
21455
|
}
|
|
21456
|
+
function VendorOnboardPage({ showCreateForm }) {
|
|
21457
|
+
const router = (0, import_navigation27.useRouter)();
|
|
21458
|
+
const { data: session } = (0, import_react60.useSession)();
|
|
21459
|
+
const sessionUser = session?.user;
|
|
21460
|
+
const isAdmin = canOnboardVendors(session?.user);
|
|
21461
|
+
if (!isAdmin) {
|
|
21462
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(AccessDenied, { groupName: sessionUser?.groupName });
|
|
21463
|
+
}
|
|
21464
|
+
if (showCreateForm) {
|
|
21465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(VendorOnboardForm, { onSuccess: () => router.push("/admin/vendor-onboard") });
|
|
21466
|
+
}
|
|
21467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
|
|
21468
|
+
AdminCRUD,
|
|
21469
|
+
{
|
|
21470
|
+
title: "Vendors",
|
|
21471
|
+
apiEndpoint: "/api/vendors",
|
|
21472
|
+
columns: [...VENDOR_LIST_COLUMNS],
|
|
21473
|
+
addEditPageUrl: "/admin/vendor-onboard",
|
|
21474
|
+
defaultSortField: "createdAt",
|
|
21475
|
+
defaultSortOrder: "desc"
|
|
21476
|
+
}
|
|
21477
|
+
);
|
|
21478
|
+
}
|
|
21303
21479
|
|
|
21304
21480
|
// src/admin/pages/VendorTeamPage.tsx
|
|
21305
21481
|
var import_react61 = require("react");
|
|
@@ -21812,9 +21988,9 @@ var STORE_CRUD_CONFIGS = {
|
|
|
21812
21988
|
// src/admin/pages/DiscountDetailsPage.tsx
|
|
21813
21989
|
var import_react63 = require("react");
|
|
21814
21990
|
var import_link14 = __toESM(require("next/link"), 1);
|
|
21815
|
-
var
|
|
21991
|
+
var import_navigation28 = require("next/navigation");
|
|
21816
21992
|
init_admin_list_return_url();
|
|
21817
|
-
var
|
|
21993
|
+
var import_lucide_react47 = require("lucide-react");
|
|
21818
21994
|
init_button();
|
|
21819
21995
|
init_utils();
|
|
21820
21996
|
init_DetailPageLayout();
|
|
@@ -21903,7 +22079,7 @@ function RuleTreeNode({ node, depth = 0 }) {
|
|
|
21903
22079
|
type: "button",
|
|
21904
22080
|
onClick: () => setOpen(!open),
|
|
21905
22081
|
className: "text-gray-400 hover:text-gray-600",
|
|
21906
|
-
children: open ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
22082
|
+
children: open ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.ChevronRight, { className: "h-4 w-4" })
|
|
21907
22083
|
}
|
|
21908
22084
|
),
|
|
21909
22085
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "font-semibold text-xs uppercase text-gray-500", children: "Group" }),
|
|
@@ -21918,8 +22094,8 @@ function RuleTreeNode({ node, depth = 0 }) {
|
|
|
21918
22094
|
] });
|
|
21919
22095
|
}
|
|
21920
22096
|
function DiscountDetailPage({ discountId }) {
|
|
21921
|
-
const router = (0,
|
|
21922
|
-
const searchParams = (0,
|
|
22097
|
+
const router = (0, import_navigation28.useRouter)();
|
|
22098
|
+
const searchParams = (0, import_navigation28.useSearchParams)();
|
|
21923
22099
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/discounts";
|
|
21924
22100
|
const [discount, setDiscount] = (0, import_react63.useState)(null);
|
|
21925
22101
|
const [loading, setLoading] = (0, import_react63.useState)(true);
|
|
@@ -21967,7 +22143,7 @@ function DiscountDetailPage({ discountId }) {
|
|
|
21967
22143
|
return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "rounded-lg bg-white p-6 shadow-md", children: [
|
|
21968
22144
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("p", { className: "text-red-600", children: error ?? "Not found" }),
|
|
21969
22145
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_link14.default, { href: listReturnUrl, children: /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(Button, { variant: "outline", className: "mt-4", children: [
|
|
21970
|
-
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
22146
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.X, { className: "h-4 w-4 mr-2" }),
|
|
21971
22147
|
" Close"
|
|
21972
22148
|
] }) })
|
|
21973
22149
|
] });
|
|
@@ -21989,14 +22165,14 @@ function DiscountDetailPage({ discountId }) {
|
|
|
21989
22165
|
title: /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
21990
22166
|
discount.name,
|
|
21991
22167
|
discount.isAutomatic && /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-700", children: [
|
|
21992
|
-
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
22168
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.Zap, { className: "h-3 w-3" }),
|
|
21993
22169
|
" Automatic"
|
|
21994
22170
|
] })
|
|
21995
22171
|
] }),
|
|
21996
22172
|
subtitle: discount.isAutomatic ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "text-sm text-gray-400 italic", children: "No coupon code \u2014 applied automatically" }) : /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "font-mono text-sm text-gray-400", children: discount.couponCode }),
|
|
21997
22173
|
closeHref: listReturnUrl,
|
|
21998
22174
|
menuItems: [
|
|
21999
|
-
{ label: "Edit discount", icon:
|
|
22175
|
+
{ label: "Edit discount", icon: import_lucide_react47.Pencil, onClick: () => router.push(editHref) }
|
|
22000
22176
|
]
|
|
22001
22177
|
}
|
|
22002
22178
|
),
|
|
@@ -22036,7 +22212,7 @@ function DiscountDetailPage({ discountId }) {
|
|
|
22036
22212
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
|
|
22037
22213
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("dt", { className: "text-gray-500", children: "Application" }),
|
|
22038
22214
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("dd", { className: "font-medium text-gray-900", children: discount.isAutomatic ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "inline-flex items-center gap-1 text-amber-700", children: [
|
|
22039
|
-
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
22215
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.Zap, { className: "h-3.5 w-3.5" }),
|
|
22040
22216
|
" Automatic"
|
|
22041
22217
|
] }) : "Coupon code" })
|
|
22042
22218
|
] }),
|
|
@@ -22131,7 +22307,7 @@ function DiscountDetailPage({ discountId }) {
|
|
|
22131
22307
|
discount.isAutomatic && /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("section", { children: [
|
|
22132
22308
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Application" }),
|
|
22133
22309
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "border border-amber-200 rounded-lg p-4 bg-amber-50/50 flex items-start gap-2 text-sm text-amber-800", children: [
|
|
22134
|
-
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
22310
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react47.Zap, { className: "h-4 w-4 mt-0.5 flex-shrink-0 text-amber-500" }),
|
|
22135
22311
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { children: [
|
|
22136
22312
|
"This discount is applied ",
|
|
22137
22313
|
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("strong", { children: "automatically" }),
|
|
@@ -22147,15 +22323,15 @@ function DiscountDetailPage({ discountId }) {
|
|
|
22147
22323
|
|
|
22148
22324
|
// src/admin/pages/DiscountEditPage.tsx
|
|
22149
22325
|
var import_react65 = require("react");
|
|
22150
|
-
var
|
|
22326
|
+
var import_navigation29 = require("next/navigation");
|
|
22151
22327
|
init_admin_list_return_url();
|
|
22152
|
-
var
|
|
22328
|
+
var import_lucide_react49 = require("lucide-react");
|
|
22153
22329
|
init_DetailPageLayout();
|
|
22154
22330
|
init_DetailPageHeader();
|
|
22155
22331
|
|
|
22156
22332
|
// src/admin/pages/DiscountsConditionsBuilder.tsx
|
|
22157
22333
|
var import_react64 = require("react");
|
|
22158
|
-
var
|
|
22334
|
+
var import_lucide_react48 = require("lucide-react");
|
|
22159
22335
|
init_button();
|
|
22160
22336
|
var import_jsx_runtime79 = require("react/jsx-runtime");
|
|
22161
22337
|
function uid() {
|
|
@@ -22358,14 +22534,14 @@ function ProductPicker({
|
|
|
22358
22534
|
onClick: () => setOpen((o) => !o),
|
|
22359
22535
|
className: "flex items-center gap-2 w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm text-left hover:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-400 bg-white",
|
|
22360
22536
|
children: [
|
|
22361
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22537
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Package, { className: "h-3.5 w-3.5 text-gray-400 shrink-0" }),
|
|
22362
22538
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: label ? "text-gray-900" : "text-gray-400", children: label ?? "Select product\u2026" })
|
|
22363
22539
|
]
|
|
22364
22540
|
}
|
|
22365
22541
|
),
|
|
22366
22542
|
open && /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "absolute z-50 top-full left-0 right-0 mt-1 rounded-lg border border-gray-200 bg-white shadow-lg", children: [
|
|
22367
22543
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "p-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex items-center gap-2 px-2 py-1 rounded border border-gray-200", children: [
|
|
22368
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22544
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Search, { className: "h-3.5 w-3.5 text-gray-400" }),
|
|
22369
22545
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("input", { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search products\u2026", className: "flex-1 text-sm outline-none bg-transparent" })
|
|
22370
22546
|
] }) }),
|
|
22371
22547
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "max-h-48 overflow-y-auto", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "Loading\u2026" }) : hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "No products found" }) : hits.map((p) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
@@ -22426,14 +22602,14 @@ function CategoryPicker({
|
|
|
22426
22602
|
onClick: () => setOpen((o) => !o),
|
|
22427
22603
|
className: "flex items-center gap-2 w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm text-left hover:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-400 bg-white",
|
|
22428
22604
|
children: [
|
|
22429
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22605
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Package, { className: "h-3.5 w-3.5 text-gray-400 shrink-0" }),
|
|
22430
22606
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: label ? "text-gray-900" : "text-gray-400", children: label ?? "Select category\u2026" })
|
|
22431
22607
|
]
|
|
22432
22608
|
}
|
|
22433
22609
|
),
|
|
22434
22610
|
open && /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "absolute z-50 top-full left-0 right-0 mt-1 rounded-lg border border-gray-200 bg-white shadow-lg", children: [
|
|
22435
22611
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "p-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex items-center gap-2 px-2 py-1 rounded border border-gray-200", children: [
|
|
22436
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22612
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Search, { className: "h-3.5 w-3.5 text-gray-400" }),
|
|
22437
22613
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("input", { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search categories\u2026", className: "flex-1 text-sm outline-none bg-transparent" })
|
|
22438
22614
|
] }) }),
|
|
22439
22615
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "max-h-48 overflow-y-auto", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "Loading\u2026" }) : hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "No categories found" }) : hits.map((c) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
@@ -22533,14 +22709,14 @@ function CustomerPicker({
|
|
|
22533
22709
|
onClick: () => setOpen((o) => !o),
|
|
22534
22710
|
className: "flex items-center gap-2 w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm text-left hover:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-400 bg-white",
|
|
22535
22711
|
children: [
|
|
22536
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22712
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.User, { className: "h-3.5 w-3.5 text-gray-400 shrink-0" }),
|
|
22537
22713
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: displayLabel ? "text-gray-900" : "text-gray-400", children: displayLabel || "Select customer\u2026" })
|
|
22538
22714
|
]
|
|
22539
22715
|
}
|
|
22540
22716
|
),
|
|
22541
22717
|
open && /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "absolute z-50 top-full left-0 right-0 mt-1 rounded-lg border border-gray-200 bg-white shadow-lg", children: [
|
|
22542
22718
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "p-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex items-center gap-2 px-2 py-1 rounded border border-gray-200", children: [
|
|
22543
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22719
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Search, { className: "h-3.5 w-3.5 text-gray-400" }),
|
|
22544
22720
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("input", { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search customers\u2026", className: "flex-1 text-sm outline-none bg-transparent" })
|
|
22545
22721
|
] }) }),
|
|
22546
22722
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "max-h-48 overflow-y-auto", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "Loading\u2026" }) : hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 p-3", children: "No customers found" }) : hits.map((c) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
@@ -22572,16 +22748,16 @@ function ConditionCard({
|
|
|
22572
22748
|
onRemove
|
|
22573
22749
|
}) {
|
|
22574
22750
|
const iconMap = {
|
|
22575
|
-
minAmount: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22576
|
-
minQuantity: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22577
|
-
productMinQuantity: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22578
|
-
nthOrder: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22579
|
-
userType: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22580
|
-
paymentMethod: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22581
|
-
category: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22582
|
-
customers: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22583
|
-
shipping: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22584
|
-
referral: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22751
|
+
minAmount: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.DollarSign, { className: "h-3.5 w-3.5 text-blue-500" }),
|
|
22752
|
+
minQuantity: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Hash, { className: "h-3.5 w-3.5 text-purple-500" }),
|
|
22753
|
+
productMinQuantity: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Hash, { className: "h-3.5 w-3.5 text-green-600" }),
|
|
22754
|
+
nthOrder: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.ShoppingCart, { className: "h-3.5 w-3.5 text-orange-500" }),
|
|
22755
|
+
userType: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.User, { className: "h-3.5 w-3.5 text-pink-500" }),
|
|
22756
|
+
paymentMethod: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.DollarSign, { className: "h-3.5 w-3.5 text-indigo-500" }),
|
|
22757
|
+
category: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Package, { className: "h-3.5 w-3.5 text-teal-500" }),
|
|
22758
|
+
customers: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.User, { className: "h-3.5 w-3.5 text-blue-400" }),
|
|
22759
|
+
shipping: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.DollarSign, { className: "h-3.5 w-3.5 text-gray-500" }),
|
|
22760
|
+
referral: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.User, { className: "h-3.5 w-3.5 text-rose-500" })
|
|
22585
22761
|
};
|
|
22586
22762
|
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-gray-200 bg-white p-3 shadow-sm", children: [
|
|
22587
22763
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "mt-0.5 shrink-0", children: iconMap[condition.kind] }),
|
|
@@ -22670,7 +22846,7 @@ function ConditionCard({
|
|
|
22670
22846
|
condition.kind === "shipping" && /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 italic", children: "Applied when any shipping method is selected." }),
|
|
22671
22847
|
condition.kind === "referral" && /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-xs text-gray-400 italic", children: "Applied when order comes from a referral." })
|
|
22672
22848
|
] }),
|
|
22673
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22849
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Trash2, { className: "h-4 w-4" }) })
|
|
22674
22850
|
] });
|
|
22675
22851
|
}
|
|
22676
22852
|
function RewardCard({
|
|
@@ -22681,7 +22857,7 @@ function RewardCard({
|
|
|
22681
22857
|
discountValue
|
|
22682
22858
|
}) {
|
|
22683
22859
|
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-purple-200 bg-purple-50/40 p-3 shadow-sm", children: [
|
|
22684
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22860
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Gift, { className: "h-3.5 w-3.5 text-purple-500 mt-0.5 shrink-0" }),
|
|
22685
22861
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "flex-1 min-w-0 space-y-2", children: [
|
|
22686
22862
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("p", { className: "text-xs font-medium text-purple-700", children: [
|
|
22687
22863
|
"Reward product \u2014 ",
|
|
@@ -22695,7 +22871,7 @@ function RewardCard({
|
|
|
22695
22871
|
] }),
|
|
22696
22872
|
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("p", { className: "text-[11px] text-purple-600", children: "This product will be automatically added to cart when the discount is applied." })
|
|
22697
22873
|
] }),
|
|
22698
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
22874
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", onClick: onRemove, className: "shrink-0 text-gray-300 hover:text-red-500 transition-colors mt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Trash2, { className: "h-4 w-4" }) })
|
|
22699
22875
|
] });
|
|
22700
22876
|
}
|
|
22701
22877
|
function DiscountConditionsBuilder({
|
|
@@ -22852,7 +23028,7 @@ function DiscountConditionsBuilder({
|
|
|
22852
23028
|
type: "button",
|
|
22853
23029
|
onClick: () => removeGroup(group.id),
|
|
22854
23030
|
className: "text-gray-300 hover:text-red-500 transition-colors",
|
|
22855
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
23031
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Trash2, { className: "h-3.5 w-3.5" })
|
|
22856
23032
|
}
|
|
22857
23033
|
)
|
|
22858
23034
|
] }),
|
|
@@ -22880,7 +23056,7 @@ function DiscountConditionsBuilder({
|
|
|
22880
23056
|
onClick: () => addCondition(group.id),
|
|
22881
23057
|
className: "flex items-center gap-1.5 text-xs mt-1",
|
|
22882
23058
|
children: [
|
|
22883
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
23059
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Plus, { className: "h-3.5 w-3.5" }),
|
|
22884
23060
|
"Add condition"
|
|
22885
23061
|
]
|
|
22886
23062
|
}
|
|
@@ -22896,7 +23072,7 @@ function DiscountConditionsBuilder({
|
|
|
22896
23072
|
onClick: addGroup,
|
|
22897
23073
|
className: "flex items-center gap-1.5 text-xs border-dashed border-gray-300 text-gray-500",
|
|
22898
23074
|
children: [
|
|
22899
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
23075
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Plus, { className: "h-3.5 w-3.5" }),
|
|
22900
23076
|
"Add condition group"
|
|
22901
23077
|
]
|
|
22902
23078
|
}
|
|
@@ -22931,7 +23107,7 @@ function DiscountConditionsBuilder({
|
|
|
22931
23107
|
onClick: addReward,
|
|
22932
23108
|
className: "flex items-center gap-1.5 text-xs border-purple-200 text-purple-700 hover:bg-purple-50",
|
|
22933
23109
|
children: [
|
|
22934
|
-
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
23110
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_lucide_react48.Gift, { className: "h-3.5 w-3.5" }),
|
|
22935
23111
|
"Add reward product (BXGY / BOGO)"
|
|
22936
23112
|
]
|
|
22937
23113
|
}
|
|
@@ -23009,8 +23185,8 @@ function useConvertedValue2(value, fromCurrency, toCurrency, discountType) {
|
|
|
23009
23185
|
var inputCls4 = "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-gray-400";
|
|
23010
23186
|
var isCreate4 = (id) => id === "create";
|
|
23011
23187
|
function DiscountEditPage({ discountId }) {
|
|
23012
|
-
const router = (0,
|
|
23013
|
-
const searchParams = (0,
|
|
23188
|
+
const router = (0, import_navigation29.useRouter)();
|
|
23189
|
+
const searchParams = (0, import_navigation29.useSearchParams)();
|
|
23014
23190
|
const listReturnUrl = safeAdminListReturnUrl(searchParams.get("from")) ?? "/admin/discounts";
|
|
23015
23191
|
const create = isCreate4(discountId);
|
|
23016
23192
|
const [loading, setLoading] = (0, import_react65.useState)(!create);
|
|
@@ -23171,12 +23347,12 @@ function DiscountEditPage({ discountId }) {
|
|
|
23171
23347
|
subtitle: create ? "Create a new discount coupon" : `Editing: ${couponCode || discountId}`,
|
|
23172
23348
|
closeHref: listReturnUrl,
|
|
23173
23349
|
menuItems: [
|
|
23174
|
-
{ label: saving ? "Saving..." : "Save", icon:
|
|
23350
|
+
{ label: saving ? "Saving..." : "Save", icon: import_lucide_react49.Save, onClick: handleSave }
|
|
23175
23351
|
]
|
|
23176
23352
|
}
|
|
23177
23353
|
),
|
|
23178
23354
|
errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "flex", children: [
|
|
23179
|
-
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
23355
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_lucide_react49.AlertCircle, { className: "h-5 w-5 text-red-400 flex-shrink-0" }),
|
|
23180
23356
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "ml-3", children: [
|
|
23181
23357
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("h3", { className: "text-sm font-medium text-red-800", children: "Please fix the following errors:" }),
|
|
23182
23358
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("ul", { className: "mt-2 text-sm text-red-700 list-disc pl-5 space-y-1", children: errors.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("li", { children: e }, i)) })
|
|
@@ -23204,7 +23380,7 @@ function DiscountEditPage({ discountId }) {
|
|
|
23204
23380
|
) }),
|
|
23205
23381
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { children: [
|
|
23206
23382
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "flex items-center gap-1.5 text-sm font-medium text-gray-700", children: [
|
|
23207
|
-
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
23383
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_lucide_react49.Zap, { className: "h-3.5 w-3.5 text-amber-500" }),
|
|
23208
23384
|
"Automatic discount"
|
|
23209
23385
|
] }),
|
|
23210
23386
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("p", { className: "text-xs text-gray-400 mt-0.5", children: "Applied automatically at checkout \u2014 no coupon code needed. When enabled, the coupon code field is hidden." })
|
|
@@ -23455,7 +23631,7 @@ function DiscountEditPage({ discountId }) {
|
|
|
23455
23631
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("p", { className: "font-mono font-semibold text-gray-900", children: couponCode || "\u2014" })
|
|
23456
23632
|
] }),
|
|
23457
23633
|
isAutomatic && /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("p", { className: "flex items-center gap-1 text-amber-600 text-xs font-medium mb-1", children: [
|
|
23458
|
-
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
23634
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_lucide_react49.Zap, { className: "h-3 w-3" }),
|
|
23459
23635
|
" Automatic \u2014 no code needed"
|
|
23460
23636
|
] }),
|
|
23461
23637
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("p", { className: "text-gray-500 mt-2", children: "Discount" }),
|
|
@@ -23491,7 +23667,6 @@ var PAGE_MAP = {
|
|
|
23491
23667
|
roles: RolesPage,
|
|
23492
23668
|
"blog-generator": BlogGeneratorPage,
|
|
23493
23669
|
schedules: SchedulesPage,
|
|
23494
|
-
"vendor-onboard": VendorOnboardPage,
|
|
23495
23670
|
"vendor-team": VendorTeamPage
|
|
23496
23671
|
};
|
|
23497
23672
|
var CRUD_CONFIGS = {
|
|
@@ -23647,8 +23822,8 @@ function BlogEditorWrapper({
|
|
|
23647
23822
|
);
|
|
23648
23823
|
}
|
|
23649
23824
|
function AdminPageResolver({ slug }) {
|
|
23650
|
-
const router = (0,
|
|
23651
|
-
const searchParams = (0,
|
|
23825
|
+
const router = (0, import_navigation31.useRouter)();
|
|
23826
|
+
const searchParams = (0, import_navigation31.useSearchParams)();
|
|
23652
23827
|
const { customCrudConfigs, storeEnabled } = (0, import_react68.useContext)(AdminConfigContext);
|
|
23653
23828
|
const key = slug?.[0] || "dashboard";
|
|
23654
23829
|
const crud = (0, import_react68.useMemo)(() => {
|
|
@@ -23691,6 +23866,10 @@ function AdminPageResolver({ slug }) {
|
|
|
23691
23866
|
/* @__PURE__ */ (0, import_jsx_runtime83.jsx)("span", { className: "ml-2", children: "Opening Plugins\u2026" })
|
|
23692
23867
|
] });
|
|
23693
23868
|
}
|
|
23869
|
+
if (key === "vendor-onboard") {
|
|
23870
|
+
const sub = slug?.[1];
|
|
23871
|
+
return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(VendorOnboardPage, { showCreateForm: sub === "create" });
|
|
23872
|
+
}
|
|
23694
23873
|
const Page = PAGE_MAP[key];
|
|
23695
23874
|
if (Page) {
|
|
23696
23875
|
return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(Page, {});
|
|
@@ -23781,7 +23960,7 @@ function AdminPageResolver({ slug }) {
|
|
|
23781
23960
|
// src/admin/pages/LayoutSettingsPage.tsx
|
|
23782
23961
|
var import_react69 = require("react");
|
|
23783
23962
|
init_button();
|
|
23784
|
-
var
|
|
23963
|
+
var import_lucide_react51 = require("lucide-react");
|
|
23785
23964
|
var import_jsx_runtime84 = require("react/jsx-runtime");
|
|
23786
23965
|
function LayoutSettingsPage() {
|
|
23787
23966
|
const { theme } = (0, import_react69.useContext)(AdminConfigContext);
|
|
@@ -23853,7 +24032,7 @@ function LayoutSettingsPage() {
|
|
|
23853
24032
|
disabled: saving,
|
|
23854
24033
|
className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
23855
24034
|
children: [
|
|
23856
|
-
/* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
|
|
24035
|
+
/* @__PURE__ */ (0, import_jsx_runtime84.jsx)(import_lucide_react51.Save, { className: "h-4 w-4 mr-2" }),
|
|
23857
24036
|
saving ? "Saving..." : "Save"
|
|
23858
24037
|
]
|
|
23859
24038
|
}
|