medusa-product-helper 0.0.16 → 0.0.18
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/.medusa/server/src/admin/index.js +59 -117
- package/.medusa/server/src/admin/index.mjs +59 -117
- package/.medusa/server/src/api/store/product-helper/products/route.js +41 -5
- package/.medusa/server/src/api/store/product-helper/products/validators.js +2 -1
- package/.medusa/server/src/providers/filter-providers/availability-provider.js +53 -67
- package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +1 -19
- package/.medusa/server/src/providers/filter-providers/base-product-provider.js +37 -100
- package/.medusa/server/src/providers/filter-providers/category-provider.js +15 -34
- package/.medusa/server/src/providers/filter-providers/collection-provider.js +15 -32
- package/.medusa/server/src/providers/filter-providers/index.js +13 -49
- package/.medusa/server/src/providers/filter-providers/metadata-provider.js +38 -57
- package/.medusa/server/src/providers/filter-providers/price-range-provider.js +66 -79
- package/.medusa/server/src/providers/filter-providers/promotion-provider.js +106 -169
- package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +53 -93
- package/.medusa/server/src/providers/filter-providers/rating-provider.js +47 -70
- package/.medusa/server/src/services/dynamic-filter-service.js +455 -744
- package/.medusa/server/src/services/filter-provider-loader.js +91 -139
- package/.medusa/server/src/services/filter-provider-registry.js +8 -107
- package/.medusa/server/src/services/product-filter-service.js +127 -174
- package/.medusa/server/src/shared/product-metadata/utils.js +66 -116
- package/.medusa/server/src/utils/query-builders/product-filters.js +89 -111
- package/.medusa/server/src/utils/query-parser.js +24 -76
- package/.medusa/server/src/workflows/add-to-wishlist.js +12 -26
- package/.medusa/server/src/workflows/get-wishlist.js +53 -51
- package/.medusa/server/src/workflows/remove-from-wishlist.js +3 -8
- package/package.json +1 -1
|
@@ -6,45 +6,35 @@ const react = require("react");
|
|
|
6
6
|
const reactQuery = require("@tanstack/react-query");
|
|
7
7
|
const METADATA_FIELD_TYPES = ["number", "text", "file", "bool"];
|
|
8
8
|
const VALID_FIELD_TYPES = new Set(METADATA_FIELD_TYPES);
|
|
9
|
+
const BOOLEAN_TRUES = /* @__PURE__ */ new Set(["true", "1", "yes", "y", "on"]);
|
|
10
|
+
const BOOLEAN_FALSES = /* @__PURE__ */ new Set(["false", "0", "no", "n", "off"]);
|
|
9
11
|
function normalizeMetadataDescriptors(input) {
|
|
10
|
-
if (!Array.isArray(input))
|
|
11
|
-
return [];
|
|
12
|
-
}
|
|
12
|
+
if (!Array.isArray(input)) return [];
|
|
13
13
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const label = getNormalizedLabel(item.label);
|
|
28
|
-
const filterable = typeof item.filterable === "boolean" ? item.filterable : Boolean(item.filterable);
|
|
29
|
-
normalized.push({
|
|
14
|
+
return input.filter(
|
|
15
|
+
(item) => item && typeof item === "object"
|
|
16
|
+
).map((item) => {
|
|
17
|
+
const key = normalizeKey(item.key);
|
|
18
|
+
const type = normalizeType(item.type);
|
|
19
|
+
const label = normalizeLabel(item.label);
|
|
20
|
+
const filterable = !!item.filterable;
|
|
21
|
+
return { key, type, label, filterable };
|
|
22
|
+
}).filter(({ key, type }) => key && type && !seenKeys.has(key)).map(({ key, type, label, filterable }) => {
|
|
23
|
+
seenKeys.add(key);
|
|
24
|
+
return {
|
|
30
25
|
key,
|
|
31
26
|
type,
|
|
32
|
-
...label
|
|
33
|
-
...filterable
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
return normalized;
|
|
27
|
+
...label && { label },
|
|
28
|
+
...filterable && { filterable: true }
|
|
29
|
+
};
|
|
30
|
+
});
|
|
38
31
|
}
|
|
39
32
|
function buildInitialFormState(descriptors, metadata) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
{}
|
|
47
|
-
);
|
|
33
|
+
const base = metadata && typeof metadata === "object" ? metadata : {};
|
|
34
|
+
return descriptors.reduce((acc, descriptor) => {
|
|
35
|
+
acc[descriptor.key] = normalizeFormValue(descriptor, base[descriptor.key]);
|
|
36
|
+
return acc;
|
|
37
|
+
}, {});
|
|
48
38
|
}
|
|
49
39
|
function buildMetadataPayload({
|
|
50
40
|
descriptors,
|
|
@@ -53,13 +43,12 @@ function buildMetadataPayload({
|
|
|
53
43
|
}) {
|
|
54
44
|
const base = originalMetadata && typeof originalMetadata === "object" ? { ...originalMetadata } : {};
|
|
55
45
|
descriptors.forEach((descriptor) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (typeof coerced === "undefined") {
|
|
46
|
+
const coerced = coerceMetadataValue(descriptor, values[descriptor.key]);
|
|
47
|
+
if (coerced === void 0) {
|
|
59
48
|
delete base[descriptor.key];
|
|
60
|
-
|
|
49
|
+
} else {
|
|
50
|
+
base[descriptor.key] = coerced;
|
|
61
51
|
}
|
|
62
|
-
base[descriptor.key] = coerced;
|
|
63
52
|
});
|
|
64
53
|
return base;
|
|
65
54
|
}
|
|
@@ -70,38 +59,27 @@ function hasMetadataChanges({
|
|
|
70
59
|
}) {
|
|
71
60
|
const next = buildMetadataPayload({ descriptors, values, originalMetadata });
|
|
72
61
|
const prev = originalMetadata && typeof originalMetadata === "object" ? originalMetadata : {};
|
|
73
|
-
return descriptors.some((
|
|
74
|
-
const prevValue = prev[descriptor.key];
|
|
75
|
-
const nextValue = next[descriptor.key];
|
|
76
|
-
return !isDeepEqual(prevValue, nextValue);
|
|
77
|
-
});
|
|
62
|
+
return descriptors.some(({ key }) => !isDeepEqual(prev[key], next[key]));
|
|
78
63
|
}
|
|
79
64
|
function validateValueForDescriptor(descriptor, value) {
|
|
80
65
|
if (descriptor.type === "number") {
|
|
81
|
-
if (value
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const numericValue = typeof value === "number" ? value : Number(String(value).trim());
|
|
85
|
-
if (Number.isNaN(numericValue)) {
|
|
86
|
-
return "Enter a valid number";
|
|
87
|
-
}
|
|
66
|
+
if (value == null || value === "") return void 0;
|
|
67
|
+
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
68
|
+
return isNaN(num) ? "Enter a valid number" : void 0;
|
|
88
69
|
}
|
|
89
70
|
if (descriptor.type === "file") {
|
|
90
|
-
if (!value)
|
|
91
|
-
return void 0;
|
|
92
|
-
}
|
|
71
|
+
if (!value) return void 0;
|
|
93
72
|
try {
|
|
94
73
|
new URL(String(value).trim());
|
|
95
|
-
|
|
74
|
+
return void 0;
|
|
75
|
+
} catch {
|
|
96
76
|
return "Enter a valid URL";
|
|
97
77
|
}
|
|
98
78
|
}
|
|
99
79
|
return void 0;
|
|
100
80
|
}
|
|
101
81
|
function normalizeFormValue(descriptor, currentValue) {
|
|
102
|
-
if (descriptor.type === "bool")
|
|
103
|
-
return Boolean(currentValue);
|
|
104
|
-
}
|
|
82
|
+
if (descriptor.type === "bool") return Boolean(currentValue);
|
|
105
83
|
if ((descriptor.type === "number" || descriptor.type === "text") && typeof currentValue === "number") {
|
|
106
84
|
return currentValue.toString();
|
|
107
85
|
}
|
|
@@ -111,78 +89,42 @@ function normalizeFormValue(descriptor, currentValue) {
|
|
|
111
89
|
return "";
|
|
112
90
|
}
|
|
113
91
|
function coerceMetadataValue(descriptor, value) {
|
|
114
|
-
if (value
|
|
115
|
-
return void 0;
|
|
116
|
-
}
|
|
92
|
+
if (value == null || value === "") return void 0;
|
|
117
93
|
if (descriptor.type === "bool") {
|
|
118
|
-
if (typeof value === "boolean")
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (typeof value === "string") {
|
|
125
|
-
const normalized = value.trim().toLowerCase();
|
|
126
|
-
if (!normalized) {
|
|
127
|
-
return void 0;
|
|
128
|
-
}
|
|
129
|
-
if (["true", "1", "yes", "y", "on"].includes(normalized)) {
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
if (["false", "0", "no", "n", "off"].includes(normalized)) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
94
|
+
if (typeof value === "boolean") return value;
|
|
95
|
+
if (typeof value === "number") return value !== 0;
|
|
96
|
+
const normalized = String(value).trim().toLowerCase();
|
|
97
|
+
if (!normalized) return void 0;
|
|
98
|
+
if (BOOLEAN_TRUES.has(normalized)) return true;
|
|
99
|
+
if (BOOLEAN_FALSES.has(normalized)) return false;
|
|
136
100
|
return Boolean(value);
|
|
137
101
|
}
|
|
138
102
|
if (descriptor.type === "number") {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
const parsed = Number(String(value).trim());
|
|
143
|
-
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
103
|
+
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
104
|
+
return isNaN(num) ? void 0 : num;
|
|
144
105
|
}
|
|
145
106
|
return String(value).trim();
|
|
146
107
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
const trimmed = value.trim();
|
|
152
|
-
return trimmed.length ? trimmed : null;
|
|
108
|
+
function normalizeKey(value) {
|
|
109
|
+
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
153
110
|
}
|
|
154
|
-
function
|
|
155
|
-
if (typeof value !== "string")
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
111
|
+
function normalizeType(value) {
|
|
112
|
+
if (typeof value !== "string") return void 0;
|
|
158
113
|
const type = value.trim().toLowerCase();
|
|
159
|
-
return VALID_FIELD_TYPES.has(type) ? type :
|
|
114
|
+
return VALID_FIELD_TYPES.has(type) ? type : void 0;
|
|
160
115
|
}
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
return void 0;
|
|
164
|
-
}
|
|
165
|
-
const trimmed = value.trim();
|
|
166
|
-
return trimmed.length ? trimmed : void 0;
|
|
116
|
+
function normalizeLabel(value) {
|
|
117
|
+
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
167
118
|
}
|
|
168
119
|
function isDeepEqual(a, b) {
|
|
169
|
-
if (a === b)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
return aKeys.every(
|
|
179
|
-
(key) => isDeepEqual(
|
|
180
|
-
a[key],
|
|
181
|
-
b[key]
|
|
182
|
-
)
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
return false;
|
|
120
|
+
if (a === b) return true;
|
|
121
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
122
|
+
const aKeys = Object.keys(a);
|
|
123
|
+
const bKeys = Object.keys(b);
|
|
124
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
125
|
+
return aKeys.every(
|
|
126
|
+
(key) => isDeepEqual(a[key], b[key])
|
|
127
|
+
);
|
|
186
128
|
}
|
|
187
129
|
const CONFIG_ENDPOINT = "/admin/product-metadata-config";
|
|
188
130
|
const QUERY_KEY = ["medusa-product-helper", "metadata-config"];
|
|
@@ -5,45 +5,35 @@ import { useState, useEffect, useMemo, useRef } from "react";
|
|
|
5
5
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
const METADATA_FIELD_TYPES = ["number", "text", "file", "bool"];
|
|
7
7
|
const VALID_FIELD_TYPES = new Set(METADATA_FIELD_TYPES);
|
|
8
|
+
const BOOLEAN_TRUES = /* @__PURE__ */ new Set(["true", "1", "yes", "y", "on"]);
|
|
9
|
+
const BOOLEAN_FALSES = /* @__PURE__ */ new Set(["false", "0", "no", "n", "off"]);
|
|
8
10
|
function normalizeMetadataDescriptors(input) {
|
|
9
|
-
if (!Array.isArray(input))
|
|
10
|
-
return [];
|
|
11
|
-
}
|
|
11
|
+
if (!Array.isArray(input)) return [];
|
|
12
12
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
const label = getNormalizedLabel(item.label);
|
|
27
|
-
const filterable = typeof item.filterable === "boolean" ? item.filterable : Boolean(item.filterable);
|
|
28
|
-
normalized.push({
|
|
13
|
+
return input.filter(
|
|
14
|
+
(item) => item && typeof item === "object"
|
|
15
|
+
).map((item) => {
|
|
16
|
+
const key = normalizeKey(item.key);
|
|
17
|
+
const type = normalizeType(item.type);
|
|
18
|
+
const label = normalizeLabel(item.label);
|
|
19
|
+
const filterable = !!item.filterable;
|
|
20
|
+
return { key, type, label, filterable };
|
|
21
|
+
}).filter(({ key, type }) => key && type && !seenKeys.has(key)).map(({ key, type, label, filterable }) => {
|
|
22
|
+
seenKeys.add(key);
|
|
23
|
+
return {
|
|
29
24
|
key,
|
|
30
25
|
type,
|
|
31
|
-
...label
|
|
32
|
-
...filterable
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
return normalized;
|
|
26
|
+
...label && { label },
|
|
27
|
+
...filterable && { filterable: true }
|
|
28
|
+
};
|
|
29
|
+
});
|
|
37
30
|
}
|
|
38
31
|
function buildInitialFormState(descriptors, metadata) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
{}
|
|
46
|
-
);
|
|
32
|
+
const base = metadata && typeof metadata === "object" ? metadata : {};
|
|
33
|
+
return descriptors.reduce((acc, descriptor) => {
|
|
34
|
+
acc[descriptor.key] = normalizeFormValue(descriptor, base[descriptor.key]);
|
|
35
|
+
return acc;
|
|
36
|
+
}, {});
|
|
47
37
|
}
|
|
48
38
|
function buildMetadataPayload({
|
|
49
39
|
descriptors,
|
|
@@ -52,13 +42,12 @@ function buildMetadataPayload({
|
|
|
52
42
|
}) {
|
|
53
43
|
const base = originalMetadata && typeof originalMetadata === "object" ? { ...originalMetadata } : {};
|
|
54
44
|
descriptors.forEach((descriptor) => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
if (typeof coerced === "undefined") {
|
|
45
|
+
const coerced = coerceMetadataValue(descriptor, values[descriptor.key]);
|
|
46
|
+
if (coerced === void 0) {
|
|
58
47
|
delete base[descriptor.key];
|
|
59
|
-
|
|
48
|
+
} else {
|
|
49
|
+
base[descriptor.key] = coerced;
|
|
60
50
|
}
|
|
61
|
-
base[descriptor.key] = coerced;
|
|
62
51
|
});
|
|
63
52
|
return base;
|
|
64
53
|
}
|
|
@@ -69,38 +58,27 @@ function hasMetadataChanges({
|
|
|
69
58
|
}) {
|
|
70
59
|
const next = buildMetadataPayload({ descriptors, values, originalMetadata });
|
|
71
60
|
const prev = originalMetadata && typeof originalMetadata === "object" ? originalMetadata : {};
|
|
72
|
-
return descriptors.some((
|
|
73
|
-
const prevValue = prev[descriptor.key];
|
|
74
|
-
const nextValue = next[descriptor.key];
|
|
75
|
-
return !isDeepEqual(prevValue, nextValue);
|
|
76
|
-
});
|
|
61
|
+
return descriptors.some(({ key }) => !isDeepEqual(prev[key], next[key]));
|
|
77
62
|
}
|
|
78
63
|
function validateValueForDescriptor(descriptor, value) {
|
|
79
64
|
if (descriptor.type === "number") {
|
|
80
|
-
if (value
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const numericValue = typeof value === "number" ? value : Number(String(value).trim());
|
|
84
|
-
if (Number.isNaN(numericValue)) {
|
|
85
|
-
return "Enter a valid number";
|
|
86
|
-
}
|
|
65
|
+
if (value == null || value === "") return void 0;
|
|
66
|
+
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
67
|
+
return isNaN(num) ? "Enter a valid number" : void 0;
|
|
87
68
|
}
|
|
88
69
|
if (descriptor.type === "file") {
|
|
89
|
-
if (!value)
|
|
90
|
-
return void 0;
|
|
91
|
-
}
|
|
70
|
+
if (!value) return void 0;
|
|
92
71
|
try {
|
|
93
72
|
new URL(String(value).trim());
|
|
94
|
-
|
|
73
|
+
return void 0;
|
|
74
|
+
} catch {
|
|
95
75
|
return "Enter a valid URL";
|
|
96
76
|
}
|
|
97
77
|
}
|
|
98
78
|
return void 0;
|
|
99
79
|
}
|
|
100
80
|
function normalizeFormValue(descriptor, currentValue) {
|
|
101
|
-
if (descriptor.type === "bool")
|
|
102
|
-
return Boolean(currentValue);
|
|
103
|
-
}
|
|
81
|
+
if (descriptor.type === "bool") return Boolean(currentValue);
|
|
104
82
|
if ((descriptor.type === "number" || descriptor.type === "text") && typeof currentValue === "number") {
|
|
105
83
|
return currentValue.toString();
|
|
106
84
|
}
|
|
@@ -110,78 +88,42 @@ function normalizeFormValue(descriptor, currentValue) {
|
|
|
110
88
|
return "";
|
|
111
89
|
}
|
|
112
90
|
function coerceMetadataValue(descriptor, value) {
|
|
113
|
-
if (value
|
|
114
|
-
return void 0;
|
|
115
|
-
}
|
|
91
|
+
if (value == null || value === "") return void 0;
|
|
116
92
|
if (descriptor.type === "bool") {
|
|
117
|
-
if (typeof value === "boolean")
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (typeof value === "string") {
|
|
124
|
-
const normalized = value.trim().toLowerCase();
|
|
125
|
-
if (!normalized) {
|
|
126
|
-
return void 0;
|
|
127
|
-
}
|
|
128
|
-
if (["true", "1", "yes", "y", "on"].includes(normalized)) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
if (["false", "0", "no", "n", "off"].includes(normalized)) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
93
|
+
if (typeof value === "boolean") return value;
|
|
94
|
+
if (typeof value === "number") return value !== 0;
|
|
95
|
+
const normalized = String(value).trim().toLowerCase();
|
|
96
|
+
if (!normalized) return void 0;
|
|
97
|
+
if (BOOLEAN_TRUES.has(normalized)) return true;
|
|
98
|
+
if (BOOLEAN_FALSES.has(normalized)) return false;
|
|
135
99
|
return Boolean(value);
|
|
136
100
|
}
|
|
137
101
|
if (descriptor.type === "number") {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
const parsed = Number(String(value).trim());
|
|
142
|
-
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
102
|
+
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
103
|
+
return isNaN(num) ? void 0 : num;
|
|
143
104
|
}
|
|
144
105
|
return String(value).trim();
|
|
145
106
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
const trimmed = value.trim();
|
|
151
|
-
return trimmed.length ? trimmed : null;
|
|
107
|
+
function normalizeKey(value) {
|
|
108
|
+
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
152
109
|
}
|
|
153
|
-
function
|
|
154
|
-
if (typeof value !== "string")
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
110
|
+
function normalizeType(value) {
|
|
111
|
+
if (typeof value !== "string") return void 0;
|
|
157
112
|
const type = value.trim().toLowerCase();
|
|
158
|
-
return VALID_FIELD_TYPES.has(type) ? type :
|
|
113
|
+
return VALID_FIELD_TYPES.has(type) ? type : void 0;
|
|
159
114
|
}
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
return void 0;
|
|
163
|
-
}
|
|
164
|
-
const trimmed = value.trim();
|
|
165
|
-
return trimmed.length ? trimmed : void 0;
|
|
115
|
+
function normalizeLabel(value) {
|
|
116
|
+
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
166
117
|
}
|
|
167
118
|
function isDeepEqual(a, b) {
|
|
168
|
-
if (a === b)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
return aKeys.every(
|
|
178
|
-
(key) => isDeepEqual(
|
|
179
|
-
a[key],
|
|
180
|
-
b[key]
|
|
181
|
-
)
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
return false;
|
|
119
|
+
if (a === b) return true;
|
|
120
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
121
|
+
const aKeys = Object.keys(a);
|
|
122
|
+
const bKeys = Object.keys(b);
|
|
123
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
124
|
+
return aKeys.every(
|
|
125
|
+
(key) => isDeepEqual(a[key], b[key])
|
|
126
|
+
);
|
|
185
127
|
}
|
|
186
128
|
const CONFIG_ENDPOINT = "/admin/product-metadata-config";
|
|
187
129
|
const QUERY_KEY = ["medusa-product-helper", "metadata-config"];
|
|
@@ -47,13 +47,49 @@ const GET = async (req, res) => {
|
|
|
47
47
|
pricingContext: req.pricingContext,
|
|
48
48
|
query: validatedQuery,
|
|
49
49
|
}, options);
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// Calculate min_price and max_price if price_range=true
|
|
51
|
+
let minPrice = null;
|
|
52
|
+
let maxPrice = null;
|
|
53
|
+
if (validatedQuery.price_range === true) {
|
|
54
|
+
for (const product of products) {
|
|
55
|
+
if (!product.variants || !Array.isArray(product.variants)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
for (const variant of product.variants) {
|
|
59
|
+
if (!variant.prices || !Array.isArray(variant.prices) || variant.prices.length === 0) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const price = variant.prices[0]?.amount;
|
|
63
|
+
if (price !== undefined && typeof price === "number") {
|
|
64
|
+
if (minPrice === null || price < minPrice) {
|
|
65
|
+
minPrice = price;
|
|
66
|
+
}
|
|
67
|
+
if (maxPrice === null || price > maxPrice) {
|
|
68
|
+
maxPrice = price;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Build response object
|
|
75
|
+
const response = {
|
|
52
76
|
products: products,
|
|
53
77
|
count: metadata?.count || count || products.length,
|
|
54
78
|
offset: metadata?.skip || 0,
|
|
55
|
-
limit
|
|
56
|
-
|
|
79
|
+
// Ensure limit is never 0 - use metadata take, validated query limit, or default to 20
|
|
80
|
+
limit: (metadata?.take && metadata.take > 0)
|
|
81
|
+
? metadata.take
|
|
82
|
+
: (validatedQuery.limit && validatedQuery.limit > 0)
|
|
83
|
+
? validatedQuery.limit
|
|
84
|
+
: (products.length > 0 ? products.length : 20),
|
|
85
|
+
};
|
|
86
|
+
// Add price range fields if price_range=true
|
|
87
|
+
if (validatedQuery.price_range === true) {
|
|
88
|
+
response.min_price = minPrice;
|
|
89
|
+
response.max_price = maxPrice;
|
|
90
|
+
}
|
|
91
|
+
// Return response matching Medusa's format
|
|
92
|
+
res.json(response);
|
|
57
93
|
}
|
|
58
94
|
catch (error) {
|
|
59
95
|
// Handle validation errors
|
|
@@ -73,4 +109,4 @@ const GET = async (req, res) => {
|
|
|
73
109
|
}
|
|
74
110
|
};
|
|
75
111
|
exports.GET = GET;
|
|
76
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
112
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3Byb2R1Y3QtaGVscGVyL3Byb2R1Y3RzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHFEQUFxRTtBQUNyRSxzRkFBdUY7QUFDdkYsd0ZBQWtGO0FBQ2xGLGlFQUFxRTtBQUNyRSw2Q0FFcUI7QUFnQnJCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBQ0ksTUFBTSxHQUFHLEdBQUcsS0FBSyxFQUN0QixHQUF1QixFQUN2QixHQUF1RCxFQUN2RCxFQUFFO0lBQ0YsSUFBSSxDQUFDO1FBQ0gseUVBQXlFO1FBQ3pFLDhEQUE4RDtRQUM5RCxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQTtRQUNoQyxNQUFNLGVBQWUsR0FBRyxJQUFBLG1DQUFvQixFQUMxQyxRQUFtQyxDQUNwQyxDQUFBO1FBRUQsNkNBQTZDO1FBQzdDLDREQUE0RDtRQUM1RCxNQUFNLGNBQWMsR0FBRyxnREFBbUMsQ0FBQyxLQUFLLENBQzlELGVBQWUsQ0FDaEIsQ0FBQTtRQUVELHlCQUF5QjtRQUN6QixNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxhQUFhLENBQUMsQ0FBQTtRQUMvRSxNQUFNLE9BQU8sR0FBRyxJQUFBLG9EQUEyQixFQUFDLFlBQVksQ0FBQyxDQUFBO1FBRXpELDJDQUEyQztRQUMzQyxtRUFBbUU7UUFDbkUsTUFBTSxhQUFhLEdBQUcsSUFBSSw2Q0FBb0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDekQsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLEdBQUcsTUFBTSxhQUFhLENBQUMsZ0NBQWdDLENBQ3hGO1lBQ0UsV0FBVyxFQUFFLEdBQUcsQ0FBQyxXQUFXO1lBQzVCLGdCQUFnQixFQUFF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
|
|
@@ -145,6 +145,7 @@ exports.StoreProductHelperFilterQuerySchema = zod_1.z
|
|
|
145
145
|
rating_min: optionalNumberish,
|
|
146
146
|
rating_max: optionalNumberish,
|
|
147
147
|
rating_required: booleanish.default(false),
|
|
148
|
+
price_range: booleanish.default(false),
|
|
148
149
|
})
|
|
149
150
|
.passthrough() // Allow unknown properties for custom filter providers
|
|
150
151
|
.refine((value) => typeof value.price_min === "undefined" ||
|
|
@@ -169,4 +170,4 @@ exports.StoreBestSellingQuerySchema = zod_1.z.object({
|
|
|
169
170
|
variant_id: stringArray,
|
|
170
171
|
product_id: stringArray,
|
|
171
172
|
});
|
|
172
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
173
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvc3RvcmUvcHJvZHVjdC1oZWxwZXIvcHJvZHVjdHMvdmFsaWRhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBdUI7QUFFdkIsTUFBTSxjQUFjLEdBQUcsT0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFBO0FBQ3hELE1BQU0saUJBQWlCLEdBQUcsT0FBQztLQUN4QixLQUFLLENBQUMsQ0FBQyxPQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsT0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0tBQ2hELFNBQVMsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFO0lBQ25CLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUMxRCxPQUFPLFNBQVMsQ0FBQTtJQUNsQixDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQ1gsT0FBTyxLQUFLLEtBQUssUUFBUTtRQUN2QixDQUFDLENBQUMsS0FBSztRQUNQLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUVwRCxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUMxQixNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixDQUFDLENBQUE7SUFDMUMsQ0FBQztJQUVELE9BQU8sT0FBTyxDQUFBO0FBQ2hCLENBQUMsQ0FBQyxDQUFBO0FBRUosTUFBTSxXQUFXLEdBQUcsT0FBQztLQUNsQixLQUFLLENBQUMsQ0FBQyxP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
|