@zenky/storefront-vue 0.9.47 → 0.10.0
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/LICENSE.md +2 -2
- package/README.md +163 -6
- package/dist/cart/index.d.ts +559 -0
- package/dist/cart/index.js +13 -0
- package/dist/cart/index.js.map +1 -0
- package/dist/catalog/index.d.ts +73 -0
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/chunk-2OD7CLKD.js +102 -0
- package/dist/chunk-2OD7CLKD.js.map +1 -0
- package/dist/chunk-4MJKN7PL.js +111 -0
- package/dist/chunk-4MJKN7PL.js.map +1 -0
- package/dist/chunk-5QCUH3FF.js +15 -0
- package/dist/chunk-5QCUH3FF.js.map +1 -0
- package/dist/chunk-AL5W5JSD.js +41 -0
- package/dist/chunk-AL5W5JSD.js.map +1 -0
- package/dist/chunk-BIGTKVC6.js +38 -0
- package/dist/chunk-BIGTKVC6.js.map +1 -0
- package/dist/chunk-BWUKMWHL.js +45 -0
- package/dist/chunk-BWUKMWHL.js.map +1 -0
- package/dist/chunk-DKWDKVXQ.js +91 -0
- package/dist/chunk-DKWDKVXQ.js.map +1 -0
- package/dist/chunk-EMGQXBU3.js +3 -0
- package/dist/chunk-EMGQXBU3.js.map +1 -0
- package/dist/chunk-EYRCAUUP.js +45 -0
- package/dist/chunk-EYRCAUUP.js.map +1 -0
- package/dist/chunk-GW2QNTAD.js +327 -0
- package/dist/chunk-GW2QNTAD.js.map +1 -0
- package/dist/chunk-IGDDWGS4.js +557 -0
- package/dist/chunk-IGDDWGS4.js.map +1 -0
- package/dist/chunk-OQLU7FSR.js +1841 -0
- package/dist/chunk-OQLU7FSR.js.map +1 -0
- package/dist/chunk-PJ2LEKW6.js +229 -0
- package/dist/chunk-PJ2LEKW6.js.map +1 -0
- package/dist/chunk-PWHIDGKW.js +97 -0
- package/dist/chunk-PWHIDGKW.js.map +1 -0
- package/dist/chunk-RO25KNM5.js +36 -0
- package/dist/chunk-RO25KNM5.js.map +1 -0
- package/dist/chunk-V3VVDQIR.js +27 -0
- package/dist/chunk-V3VVDQIR.js.map +1 -0
- package/dist/chunk-VL3T36VM.js +386 -0
- package/dist/chunk-VL3T36VM.js.map +1 -0
- package/dist/chunk-XUGXLJZB.js +14 -0
- package/dist/chunk-XUGXLJZB.js.map +1 -0
- package/dist/chunk-Z3IW4ODZ.js +36 -0
- package/dist/chunk-Z3IW4ODZ.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.d.ts +54 -0
- package/dist/layout/index.js +102 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/money/index.d.ts +26 -0
- package/dist/money/index.js +3 -0
- package/dist/money/index.js.map +1 -0
- package/dist/order/index.d.ts +34 -0
- package/dist/order/index.js +209 -0
- package/dist/order/index.js.map +1 -0
- package/dist/pagination/index.d.ts +87 -0
- package/dist/pagination/index.js +97 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/ports/index.d.ts +124 -0
- package/dist/ports/index.js +3 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/products/index.d.ts +105 -0
- package/dist/products/index.js +7 -0
- package/dist/products/index.js.map +1 -0
- package/dist/promotions/index.d.ts +10 -0
- package/dist/promotions/index.js +7 -0
- package/dist/promotions/index.js.map +1 -0
- package/dist/schedule-Dd3zwHEb.d.ts +27 -0
- package/dist/seo/index.d.ts +91 -0
- package/dist/seo/index.js +289 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/snippets/index.d.ts +17 -0
- package/dist/snippets/index.js +3 -0
- package/dist/snippets/index.js.map +1 -0
- package/dist/snippets-BC5bkSjm.d.ts +23 -0
- package/dist/stores/index.d.ts +196 -0
- package/dist/stores/index.js +8 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/theme/index.d.ts +60 -0
- package/dist/theme/index.js +3 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/utils/index.d.ts +66 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/website-settings/index.d.ts +38 -0
- package/dist/website-settings/index.js +3 -0
- package/dist/website-settings/index.js.map +1 -0
- package/package.json +121 -47
- package/dist/cjs/index.cjs +0 -3742
- package/dist/esm/index.mjs +0 -3642
- package/dist/zenky.d.ts +0 -10377
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import { createRuntimeState } from './chunk-5QCUH3FF.js';
|
|
2
|
+
import { useStoreProfileStore, useAuthStore } from './chunk-PWHIDGKW.js';
|
|
3
|
+
import { createDebounce } from './chunk-XUGXLJZB.js';
|
|
4
|
+
import { useRuntime } from './chunk-V3VVDQIR.js';
|
|
5
|
+
import { UnitType } from '@zenky/storefront-api';
|
|
6
|
+
import { computed, ref, watch } from 'vue';
|
|
7
|
+
|
|
8
|
+
var UNIT_LABELS = {
|
|
9
|
+
[UnitType.Gram]: "\u0433",
|
|
10
|
+
[UnitType.Kilogram]: "\u043A\u0433",
|
|
11
|
+
[UnitType.Meter]: "\u043C",
|
|
12
|
+
[UnitType.Piece]: "\u0433"
|
|
13
|
+
};
|
|
14
|
+
function getWeightFromDimensions(variants) {
|
|
15
|
+
if (!variants?.length) return null;
|
|
16
|
+
const dimension = variants[0]?.dimensions?.find((d) => d.dimension === "weight");
|
|
17
|
+
return dimension?.value ?? null;
|
|
18
|
+
}
|
|
19
|
+
function getVariantWeight(variant) {
|
|
20
|
+
if (!variant) return null;
|
|
21
|
+
return variant.dimensions?.find((d) => d.dimension === "weight")?.value ?? null;
|
|
22
|
+
}
|
|
23
|
+
function getProductWeight(product) {
|
|
24
|
+
return product.weight ?? getWeightFromDimensions(product.variants) ?? null;
|
|
25
|
+
}
|
|
26
|
+
function toGrams(weight, unitType) {
|
|
27
|
+
if (!weight) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (unitType === UnitType.Kilogram) {
|
|
31
|
+
return weight * 1e3;
|
|
32
|
+
}
|
|
33
|
+
if (unitType === UnitType.Meter) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return weight;
|
|
37
|
+
}
|
|
38
|
+
function formatWeight(weight, unitType) {
|
|
39
|
+
if (!weight) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const label = UNIT_LABELS[unitType];
|
|
43
|
+
return `${weight} ${label}`;
|
|
44
|
+
}
|
|
45
|
+
function useProductsByCategory(products, categories) {
|
|
46
|
+
const grouped = computed(() => {
|
|
47
|
+
if (!categories.value.length || !products.value.length) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const productsByCategory = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const product of products.value) {
|
|
52
|
+
for (const category of product.categories) {
|
|
53
|
+
const existing = productsByCategory.get(category.id) ?? [];
|
|
54
|
+
existing.push(product);
|
|
55
|
+
productsByCategory.set(category.id, existing);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return flattenCategories(categories.value).filter((category) => productsByCategory.has(category.id)).map((category) => ({
|
|
59
|
+
id: category.id,
|
|
60
|
+
shortId: category.short_id,
|
|
61
|
+
slug: category.slug,
|
|
62
|
+
name: category.name,
|
|
63
|
+
products: productsByCategory.get(category.id) ?? []
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
return { grouped };
|
|
67
|
+
}
|
|
68
|
+
function flattenCategories(categories) {
|
|
69
|
+
const result = [];
|
|
70
|
+
for (const category of categories) {
|
|
71
|
+
result.push(category);
|
|
72
|
+
if (category.children?.length) {
|
|
73
|
+
result.push(...flattenCategories(category.children));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function getInitialModifiersModel(variant) {
|
|
79
|
+
if (!variant?.default_modifiers?.modifiers.length) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return variant.default_modifiers.modifiers.map((modifier) => ({
|
|
83
|
+
modifier_id: modifier.modifier_id,
|
|
84
|
+
modifiers_group_id: modifier.modifiers_group_id,
|
|
85
|
+
quantity: modifier.quantity
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
function useProductVariantModifiers(variant) {
|
|
89
|
+
const modifiersModel = ref([
|
|
90
|
+
...getInitialModifiersModel(variant.value)
|
|
91
|
+
]);
|
|
92
|
+
const selectedModifiersHash = ref(null);
|
|
93
|
+
const hasAnyModifier = computed(() => modifiersModel.value.length > 0);
|
|
94
|
+
watch(variant, () => {
|
|
95
|
+
const newDefaults = getInitialModifiersModel(variant.value);
|
|
96
|
+
const userStandalone = modifiersModel.value.filter((m) => !m.modifiers_group_id);
|
|
97
|
+
const defaultStandaloneIds = new Set(
|
|
98
|
+
newDefaults.filter((d) => !d.modifiers_group_id).map((d) => d.modifier_id)
|
|
99
|
+
);
|
|
100
|
+
const preservedStandalone = userStandalone.filter(
|
|
101
|
+
(m) => !defaultStandaloneIds.has(m.modifier_id)
|
|
102
|
+
);
|
|
103
|
+
modifiersModel.value = [...preservedStandalone, ...newDefaults];
|
|
104
|
+
});
|
|
105
|
+
return { modifiersModel, selectedModifiersHash, hasAnyModifier };
|
|
106
|
+
}
|
|
107
|
+
function resolveVariantPrice(variant, stockId) {
|
|
108
|
+
if (variant.prices?.length) {
|
|
109
|
+
if (stockId) {
|
|
110
|
+
const stockEntry = variant.prices.find((p) => p.stock_id === stockId);
|
|
111
|
+
if (stockEntry) {
|
|
112
|
+
return {
|
|
113
|
+
price: stockEntry.price,
|
|
114
|
+
originalPrice: stockEntry.original_price,
|
|
115
|
+
discount: stockEntry.discount
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const globalEntry = variant.prices.find((p) => p.stock_id === null);
|
|
120
|
+
if (globalEntry) {
|
|
121
|
+
return {
|
|
122
|
+
price: globalEntry.price,
|
|
123
|
+
originalPrice: globalEntry.original_price,
|
|
124
|
+
discount: globalEntry.discount
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
price: variant.price,
|
|
130
|
+
originalPrice: variant.original_price ?? null,
|
|
131
|
+
discount: variant.discount ?? null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function useProductPricing(product, stockId) {
|
|
135
|
+
const variants = computed(() => product.value.variants ?? []);
|
|
136
|
+
const currentStockId = computed(() => stockId?.value ?? null);
|
|
137
|
+
const resolvedPrices = computed(
|
|
138
|
+
() => variants.value.map((v) => resolveVariantPrice(v, currentStockId.value))
|
|
139
|
+
);
|
|
140
|
+
const minRequiredModifiersCost = computed(() => {
|
|
141
|
+
let total = 0;
|
|
142
|
+
for (const groupEntry of product.value.modifiers_groups ?? []) {
|
|
143
|
+
const group = groupEntry.group;
|
|
144
|
+
for (const modEntry of group.modifiers) {
|
|
145
|
+
if (modEntry.is_required && modEntry.modifier.price) {
|
|
146
|
+
total += modEntry.modifier.price * Math.max(modEntry.min_quantity ?? 0, 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (groupEntry.is_required && group.modifiers.length && !group.modifiers.some((m) => m.is_required)) {
|
|
150
|
+
const cheapest = group.modifiers.reduce(
|
|
151
|
+
(min, m) => (m.modifier.price ?? 0) < (min.modifier.price ?? 0) ? m : min
|
|
152
|
+
);
|
|
153
|
+
total += (cheapest.modifier.price ?? 0) * Math.max(cheapest.min_quantity ?? 0, 1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return total;
|
|
157
|
+
});
|
|
158
|
+
const displayPrice = computed(() => {
|
|
159
|
+
if (!resolvedPrices.value.length) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
return Math.min(...resolvedPrices.value.map((r) => r.price)) + minRequiredModifiersCost.value;
|
|
163
|
+
});
|
|
164
|
+
const originalDisplayPrice = computed(() => {
|
|
165
|
+
if (!resolvedPrices.value.length) return null;
|
|
166
|
+
const cheapest = resolvedPrices.value.reduce((min, r) => r.price < min.price ? r : min);
|
|
167
|
+
let originalVariantPrice = null;
|
|
168
|
+
if (cheapest.originalPrice && cheapest.originalPrice > cheapest.price) {
|
|
169
|
+
originalVariantPrice = cheapest.originalPrice;
|
|
170
|
+
} else if (cheapest.discount && cheapest.discount.difference > 0) {
|
|
171
|
+
originalVariantPrice = cheapest.price + cheapest.discount.difference;
|
|
172
|
+
}
|
|
173
|
+
if (!originalVariantPrice) return null;
|
|
174
|
+
return originalVariantPrice + minRequiredModifiersCost.value;
|
|
175
|
+
});
|
|
176
|
+
const hasDifferentPrices = computed(() => {
|
|
177
|
+
if (resolvedPrices.value.length <= 1) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const prices = new Set(resolvedPrices.value.map((r) => r.price));
|
|
181
|
+
return prices.size > 1;
|
|
182
|
+
});
|
|
183
|
+
const needsSelection = computed(() => {
|
|
184
|
+
if (variants.value.length > 1) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
const mods = product.value.modifiers;
|
|
188
|
+
const groups = product.value.modifiers_groups;
|
|
189
|
+
return Array.isArray(mods) && mods.length > 0 || Array.isArray(groups) && groups.length > 0;
|
|
190
|
+
});
|
|
191
|
+
return { displayPrice, originalDisplayPrice, hasDifferentPrices, needsSelection };
|
|
192
|
+
}
|
|
193
|
+
function getDefaultOptionsModel(product) {
|
|
194
|
+
const model = {};
|
|
195
|
+
if (!product.variants?.length) {
|
|
196
|
+
return model;
|
|
197
|
+
}
|
|
198
|
+
const firstVariant = product.variants.find(
|
|
199
|
+
(variant) => variant.options && variant.options.length
|
|
200
|
+
);
|
|
201
|
+
if (!firstVariant) {
|
|
202
|
+
return model;
|
|
203
|
+
}
|
|
204
|
+
firstVariant.options.forEach((option) => {
|
|
205
|
+
model[option.id] = option.value.id;
|
|
206
|
+
});
|
|
207
|
+
return model;
|
|
208
|
+
}
|
|
209
|
+
function getProductVariantOptionsList(product) {
|
|
210
|
+
const list = [];
|
|
211
|
+
if (!product.variants?.length) {
|
|
212
|
+
return list;
|
|
213
|
+
}
|
|
214
|
+
product.variants.forEach((variant) => {
|
|
215
|
+
if (!variant.options?.length) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
variant.options.forEach((option) => {
|
|
219
|
+
let index = list.findIndex((item) => item.id === option.id);
|
|
220
|
+
if (index === -1) {
|
|
221
|
+
index = list.push({
|
|
222
|
+
id: option.id,
|
|
223
|
+
name: option.name,
|
|
224
|
+
values: []
|
|
225
|
+
});
|
|
226
|
+
index--;
|
|
227
|
+
}
|
|
228
|
+
const valueIndex = list[index].values.findIndex(
|
|
229
|
+
(value) => value.id === option.value.id
|
|
230
|
+
);
|
|
231
|
+
if (valueIndex === -1) {
|
|
232
|
+
list[index].values.push(option.value);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
return list;
|
|
237
|
+
}
|
|
238
|
+
function computeDisabledValues(variants, optionsList, optionsModel) {
|
|
239
|
+
if (!variants.length) return {};
|
|
240
|
+
const result = {};
|
|
241
|
+
for (const option of optionsList) {
|
|
242
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
243
|
+
for (const value of option.values) {
|
|
244
|
+
const hasMatch = variants.some((variant) => {
|
|
245
|
+
if (!variant.options?.length) return false;
|
|
246
|
+
const variantMap = {};
|
|
247
|
+
for (const opt of variant.options) {
|
|
248
|
+
variantMap[opt.id] = opt.value.id;
|
|
249
|
+
}
|
|
250
|
+
if (variantMap[option.id] !== value.id) return false;
|
|
251
|
+
for (const [otherId, selectedId] of Object.entries(optionsModel)) {
|
|
252
|
+
if (otherId === option.id) continue;
|
|
253
|
+
if (variantMap[otherId] !== selectedId) return false;
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
});
|
|
257
|
+
if (!hasMatch) {
|
|
258
|
+
disabled.add(value.id);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
result[option.id] = disabled;
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
function useProductVariant(product) {
|
|
266
|
+
const optionsModel = ref({ ...getDefaultOptionsModel(product.value) });
|
|
267
|
+
const optionsList = ref([...getProductVariantOptionsList(product.value)]);
|
|
268
|
+
const selectedVariant = computed(() => {
|
|
269
|
+
if (!product.value.variants?.length) {
|
|
270
|
+
return null;
|
|
271
|
+
} else if (product.value.variants.length === 1 || !Object.keys(optionsModel.value).length) {
|
|
272
|
+
return product.value.variants[0] ?? null;
|
|
273
|
+
}
|
|
274
|
+
return product.value.variants.find((variant) => {
|
|
275
|
+
if (!variant.options?.length) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return variant.options.every(
|
|
279
|
+
(option) => optionsModel.value[option.id] === option.value.id
|
|
280
|
+
);
|
|
281
|
+
}) ?? null;
|
|
282
|
+
});
|
|
283
|
+
const disabledValues = computed(
|
|
284
|
+
() => computeDisabledValues(product.value.variants ?? [], optionsList.value, optionsModel.value)
|
|
285
|
+
);
|
|
286
|
+
watch(product, () => {
|
|
287
|
+
optionsModel.value = { ...getDefaultOptionsModel(product.value) };
|
|
288
|
+
optionsList.value = [...getProductVariantOptionsList(product.value)];
|
|
289
|
+
});
|
|
290
|
+
return { optionsModel, optionsList, selectedVariant, disabledValues };
|
|
291
|
+
}
|
|
292
|
+
function usePriceRecalculation({
|
|
293
|
+
product,
|
|
294
|
+
selectedVariant,
|
|
295
|
+
modifiersModel,
|
|
296
|
+
requiredModifiersGroupIsMissing,
|
|
297
|
+
selectedModifiersHash
|
|
298
|
+
}) {
|
|
299
|
+
const sdk = useRuntime().sdk;
|
|
300
|
+
const profileStore = useStoreProfileStore();
|
|
301
|
+
const currentStockId = computed(() => profileStore.stock?.id ?? null);
|
|
302
|
+
const resolvedVariant = computed(() => {
|
|
303
|
+
if (!selectedVariant.value) return null;
|
|
304
|
+
return resolveVariantPrice(selectedVariant.value, currentStockId.value);
|
|
305
|
+
});
|
|
306
|
+
const modifierPriceMap = computed(() => {
|
|
307
|
+
const map = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const item of product.value.modifiers ?? []) {
|
|
309
|
+
if (item.modifier.price) {
|
|
310
|
+
map.set(item.modifier.id, item.modifier.price);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
for (const group of product.value.modifiers_groups ?? []) {
|
|
314
|
+
for (const item of group.group.modifiers ?? []) {
|
|
315
|
+
if (item.modifier.price) {
|
|
316
|
+
map.set(item.modifier.id, item.modifier.price);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return map;
|
|
321
|
+
});
|
|
322
|
+
const selectedModifierTotal = computed(
|
|
323
|
+
() => modifiersModel.value.reduce(
|
|
324
|
+
(sum, req) => sum + (modifierPriceMap.value.get(req.modifier_id) ?? 0) * req.quantity,
|
|
325
|
+
0
|
|
326
|
+
)
|
|
327
|
+
);
|
|
328
|
+
const optimisticPrice = computed(() => {
|
|
329
|
+
if (!resolvedVariant.value) return 0;
|
|
330
|
+
return resolvedVariant.value.price + selectedModifierTotal.value;
|
|
331
|
+
});
|
|
332
|
+
const optimisticOriginalPrice = computed(() => {
|
|
333
|
+
if (!resolvedVariant.value) return null;
|
|
334
|
+
const r = resolvedVariant.value;
|
|
335
|
+
if (r.originalPrice && r.originalPrice > r.price) return r.originalPrice;
|
|
336
|
+
if (r.discount && r.discount.difference > 0) return r.price + r.discount.difference;
|
|
337
|
+
return null;
|
|
338
|
+
});
|
|
339
|
+
const apiPrice = ref(null);
|
|
340
|
+
const apiOriginalPrice = ref(null);
|
|
341
|
+
const priceIsRecalculating = ref(false);
|
|
342
|
+
let requestId = 0;
|
|
343
|
+
const price = computed(() => apiPrice.value ?? optimisticPrice.value);
|
|
344
|
+
const originalPrice = computed(
|
|
345
|
+
() => apiOriginalPrice.value ?? optimisticOriginalPrice.value
|
|
346
|
+
);
|
|
347
|
+
const priceWasModified = computed(() => apiPrice.value !== null);
|
|
348
|
+
async function recalculatePrice() {
|
|
349
|
+
if (requiredModifiersGroupIsMissing.value) return;
|
|
350
|
+
if (!modifiersModel.value.length || !selectedVariant.value) return;
|
|
351
|
+
const currentRequestId = ++requestId;
|
|
352
|
+
priceIsRecalculating.value = true;
|
|
353
|
+
try {
|
|
354
|
+
const response = await sdk.products.getProductVariantPrice(
|
|
355
|
+
profileStore.store.id,
|
|
356
|
+
product.value.id,
|
|
357
|
+
selectedVariant.value.id,
|
|
358
|
+
{
|
|
359
|
+
modifiers: modifiersModel.value,
|
|
360
|
+
// exactOptionalPropertyTypes: stock_id включаем только если есть
|
|
361
|
+
...currentStockId.value ? { stock_id: currentStockId.value } : {}
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
if (currentRequestId !== requestId) return;
|
|
365
|
+
apiPrice.value = response.price;
|
|
366
|
+
apiOriginalPrice.value = response.original_price;
|
|
367
|
+
selectedModifiersHash.value = response.modifiers_hash;
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.error("[usePriceRecalculation] price recalculation failed", e);
|
|
370
|
+
} finally {
|
|
371
|
+
if (currentRequestId === requestId) {
|
|
372
|
+
priceIsRecalculating.value = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const debouncedRecalculate = createDebounce(() => recalculatePrice(), 300);
|
|
377
|
+
watch(selectedVariant, () => {
|
|
378
|
+
apiPrice.value = null;
|
|
379
|
+
apiOriginalPrice.value = null;
|
|
380
|
+
requestId++;
|
|
381
|
+
selectedModifiersHash.value = selectedVariant.value?.default_modifiers?.hash ?? null;
|
|
382
|
+
});
|
|
383
|
+
watch(
|
|
384
|
+
modifiersModel,
|
|
385
|
+
() => {
|
|
386
|
+
apiPrice.value = null;
|
|
387
|
+
apiOriginalPrice.value = null;
|
|
388
|
+
requestId++;
|
|
389
|
+
if (modifiersModel.value.length && selectedVariant.value) {
|
|
390
|
+
debouncedRecalculate();
|
|
391
|
+
} else {
|
|
392
|
+
selectedModifiersHash.value = selectedVariant.value?.default_modifiers?.hash ?? null;
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
{ deep: true }
|
|
396
|
+
);
|
|
397
|
+
return { price, originalPrice, priceWasModified, priceIsRecalculating };
|
|
398
|
+
}
|
|
399
|
+
function resolveQuantity(minQuantity) {
|
|
400
|
+
return minQuantity && minQuantity > 0 ? minQuantity : 1;
|
|
401
|
+
}
|
|
402
|
+
function useProductVariantSelector(product) {
|
|
403
|
+
const { optionsModel, optionsList, selectedVariant, disabledValues } = useProductVariant(product);
|
|
404
|
+
const { modifiersModel, selectedModifiersHash } = useProductVariantModifiers(selectedVariant);
|
|
405
|
+
const requiredModifierEntries = computed(() => {
|
|
406
|
+
const result = [];
|
|
407
|
+
for (const groupEntry of product.value.modifiers_groups ?? []) {
|
|
408
|
+
const group = groupEntry.group;
|
|
409
|
+
const hasIndividualRequired = group.modifiers.some((m) => m.is_required);
|
|
410
|
+
for (const modEntry of group.modifiers) {
|
|
411
|
+
if (modEntry.is_required) {
|
|
412
|
+
result.push({
|
|
413
|
+
modifier_id: modEntry.modifier.id,
|
|
414
|
+
modifiers_group_id: group.id,
|
|
415
|
+
quantity: resolveQuantity(modEntry.min_quantity)
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (groupEntry.is_required && !hasIndividualRequired) {
|
|
420
|
+
const sorted = [...group.modifiers].sort(
|
|
421
|
+
(a, b) => (a.modifier.price ?? 0) - (b.modifier.price ?? 0)
|
|
422
|
+
);
|
|
423
|
+
const cheapest = sorted[0];
|
|
424
|
+
if (cheapest) {
|
|
425
|
+
result.push({
|
|
426
|
+
modifier_id: cheapest.modifier.id,
|
|
427
|
+
modifiers_group_id: group.id,
|
|
428
|
+
quantity: resolveQuantity(cheapest.min_quantity)
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
});
|
|
435
|
+
function seedRequiredModifiers() {
|
|
436
|
+
const missing = requiredModifierEntries.value.filter(
|
|
437
|
+
(req) => !modifiersModel.value.some((m) => m.modifier_id === req.modifier_id)
|
|
438
|
+
);
|
|
439
|
+
if (missing.length) {
|
|
440
|
+
modifiersModel.value = [...modifiersModel.value, ...missing];
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
seedRequiredModifiers();
|
|
444
|
+
watch(selectedVariant, seedRequiredModifiers);
|
|
445
|
+
const requiredModifiersGroupIsMissing = computed(() => {
|
|
446
|
+
const requiredGroups = (product.value.modifiers_groups ?? []).filter((group) => group.is_required).map((group) => group.group.id);
|
|
447
|
+
if (!requiredGroups.length) return false;
|
|
448
|
+
return requiredGroups.some(
|
|
449
|
+
(groupId) => !modifiersModel.value.find((m) => m.modifiers_group_id === groupId && m.quantity > 0)
|
|
450
|
+
);
|
|
451
|
+
});
|
|
452
|
+
const { price, originalPrice, priceWasModified, priceIsRecalculating } = usePriceRecalculation({
|
|
453
|
+
product,
|
|
454
|
+
selectedVariant,
|
|
455
|
+
modifiersModel,
|
|
456
|
+
requiredModifiersGroupIsMissing,
|
|
457
|
+
selectedModifiersHash
|
|
458
|
+
});
|
|
459
|
+
return {
|
|
460
|
+
optionsModel,
|
|
461
|
+
optionsList,
|
|
462
|
+
disabledValues,
|
|
463
|
+
modifiersModel,
|
|
464
|
+
selectedVariant,
|
|
465
|
+
price,
|
|
466
|
+
originalPrice,
|
|
467
|
+
priceWasModified,
|
|
468
|
+
priceIsRecalculating,
|
|
469
|
+
selectedModifiersHash,
|
|
470
|
+
requiredModifiersGroupIsMissing
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/products/useRelatedProducts.ts
|
|
475
|
+
function canAddDirectly(product) {
|
|
476
|
+
const variantsCount = product.variants?.length ?? 0;
|
|
477
|
+
const hasModifiers = (product.modifiers?.length ?? 0) > 0;
|
|
478
|
+
const hasModifierGroups = (product.modifiers_groups?.length ?? 0) > 0;
|
|
479
|
+
return variantsCount === 1 && !hasModifiers && !hasModifierGroups;
|
|
480
|
+
}
|
|
481
|
+
var RELATED_PRODUCT_INCLUDES = "variants.prices,modifiers,modifiers_groups";
|
|
482
|
+
var getRelatedProductsState = createRuntimeState(() => ({
|
|
483
|
+
productInFlight: /* @__PURE__ */ new Map()
|
|
484
|
+
}));
|
|
485
|
+
function useRelatedProducts() {
|
|
486
|
+
const runtime = useRuntime();
|
|
487
|
+
const { sdk } = runtime;
|
|
488
|
+
const { productInFlight } = getRelatedProductsState(runtime);
|
|
489
|
+
const profileStore = useStoreProfileStore();
|
|
490
|
+
const authStore = useAuthStore();
|
|
491
|
+
function getStoreId() {
|
|
492
|
+
const id = profileStore.store?.id;
|
|
493
|
+
if (!id) throw new Error("Store is not initialized");
|
|
494
|
+
return id;
|
|
495
|
+
}
|
|
496
|
+
async function fetchForProduct(productId, orderCredentials, signal) {
|
|
497
|
+
if (signal?.aborted) return [];
|
|
498
|
+
const key = orderCredentials ? `${productId}:${orderCredentials.id}` : productId;
|
|
499
|
+
let promise = productInFlight.get(key);
|
|
500
|
+
if (!promise) {
|
|
501
|
+
promise = (async () => {
|
|
502
|
+
try {
|
|
503
|
+
const storeId = getStoreId();
|
|
504
|
+
const query = { with: RELATED_PRODUCT_INCLUDES };
|
|
505
|
+
if (orderCredentials) {
|
|
506
|
+
query.order_id = orderCredentials.id;
|
|
507
|
+
query.order_token = orderCredentials.token;
|
|
508
|
+
}
|
|
509
|
+
const url = sdk.client.getStoreUrl(
|
|
510
|
+
storeId,
|
|
511
|
+
`/products/${productId}/related/products`,
|
|
512
|
+
query
|
|
513
|
+
);
|
|
514
|
+
const response = await sdk.client.request("GET", url, void 0, authStore.token);
|
|
515
|
+
return response?.data ?? [];
|
|
516
|
+
} catch (e) {
|
|
517
|
+
console.warn("[recommendations:product] fetch failed", e);
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
})().finally(() => productInFlight.delete(key));
|
|
521
|
+
productInFlight.set(key, promise);
|
|
522
|
+
}
|
|
523
|
+
const result = await promise;
|
|
524
|
+
return signal?.aborted ? [] : result;
|
|
525
|
+
}
|
|
526
|
+
async function fetchForOrder(orderId, orderToken, signal) {
|
|
527
|
+
if (signal?.aborted) return [];
|
|
528
|
+
try {
|
|
529
|
+
const storeId = getStoreId();
|
|
530
|
+
const url = sdk.client.getStoreUrl(storeId, `/orders/${orderId}/related/products`, {
|
|
531
|
+
token: orderToken,
|
|
532
|
+
with: RELATED_PRODUCT_INCLUDES
|
|
533
|
+
});
|
|
534
|
+
const response = await sdk.client.request("GET", url, void 0, authStore.token);
|
|
535
|
+
if (signal?.aborted) return [];
|
|
536
|
+
return response?.data ?? [];
|
|
537
|
+
} catch (e) {
|
|
538
|
+
console.warn("[recommendations:order] fetch failed", e);
|
|
539
|
+
return [];
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return { fetchForProduct, fetchForOrder };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/products/findProductInCatalog.ts
|
|
546
|
+
function findProductInCatalog(catalog, shortId) {
|
|
547
|
+
for (const item of catalog) {
|
|
548
|
+
if (!item.products.length) continue;
|
|
549
|
+
const found = item.products.find((p) => p.short_id === shortId);
|
|
550
|
+
if (found) return { product: found, catalogItem: item };
|
|
551
|
+
}
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts };
|
|
556
|
+
//# sourceMappingURL=chunk-IGDDWGS4.js.map
|
|
557
|
+
//# sourceMappingURL=chunk-IGDDWGS4.js.map
|