@zenky/storefront-vue 0.9.47 → 0.10.3
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/CHANGELOG.md +46 -0
- package/LICENSE.md +2 -2
- package/README.md +213 -6
- package/dist/cart/index.d.ts +588 -0
- package/dist/cart/index.js +12 -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-57IOMDAM.js +38 -0
- package/dist/chunk-57IOMDAM.js.map +1 -0
- package/dist/chunk-5QCUH3FF.js +15 -0
- package/dist/chunk-5QCUH3FF.js.map +1 -0
- package/dist/chunk-7FRMMCR5.js +93 -0
- package/dist/chunk-7FRMMCR5.js.map +1 -0
- package/dist/chunk-BWUKMWHL.js +45 -0
- package/dist/chunk-BWUKMWHL.js.map +1 -0
- package/dist/chunk-E3G7NGCH.js +45 -0
- package/dist/chunk-E3G7NGCH.js.map +1 -0
- package/dist/chunk-EMGQXBU3.js +3 -0
- package/dist/chunk-EMGQXBU3.js.map +1 -0
- package/dist/chunk-FFBZKQRV.js +393 -0
- package/dist/chunk-FFBZKQRV.js.map +1 -0
- package/dist/chunk-G2X4SBCO.js +2011 -0
- package/dist/chunk-G2X4SBCO.js.map +1 -0
- package/dist/chunk-GLTOJTZO.js +41 -0
- package/dist/chunk-GLTOJTZO.js.map +1 -0
- package/dist/chunk-GW2QNTAD.js +327 -0
- package/dist/chunk-GW2QNTAD.js.map +1 -0
- package/dist/chunk-JLFBAAFT.js +566 -0
- package/dist/chunk-JLFBAAFT.js.map +1 -0
- package/dist/chunk-MZ66ARDL.js +244 -0
- package/dist/chunk-MZ66ARDL.js.map +1 -0
- package/dist/chunk-N3NOTJUC.js +111 -0
- package/dist/chunk-N3NOTJUC.js.map +1 -0
- package/dist/chunk-RO25KNM5.js +36 -0
- package/dist/chunk-RO25KNM5.js.map +1 -0
- package/dist/chunk-RYYY7MVP.js +102 -0
- package/dist/chunk-RYYY7MVP.js.map +1 -0
- package/dist/chunk-TEG7EL4N.js +106 -0
- package/dist/chunk-TEG7EL4N.js.map +1 -0
- package/dist/chunk-V3VVDQIR.js +27 -0
- package/dist/chunk-V3VVDQIR.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 +106 -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/docs/consumer-setup.md +275 -0
- package/docs/ports.md +144 -0
- package/docs/releasing.md +46 -0
- package/package.json +143 -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,73 @@
|
|
|
1
|
+
import { NestedCategory, Product, Store, City, Stock, RemoteCatalog, ZenkyStorefront, CollectionsListRequest, ProductsCollection } from '@zenky/storefront-api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Рекурсивный поиск категории в дереве по `short_id`.
|
|
5
|
+
*
|
|
6
|
+
* `NestedCategory[]` — это дерево: корневые категории на верхнем уровне,
|
|
7
|
+
* вложенные — внутри `children[]`. Простой `tree.find()` найдёт только
|
|
8
|
+
* корневые. Для вложенных (например «Куртки» внутри «Мужская» внутри «Одежда»)
|
|
9
|
+
* нужно рекурсивно обойти все ветки.
|
|
10
|
+
*
|
|
11
|
+
* Возвращает первое совпадение (DFS pre-order).
|
|
12
|
+
*/
|
|
13
|
+
declare function findCategoryByShortId(tree: NestedCategory[], shortId: string): NestedCategory | null;
|
|
14
|
+
/**
|
|
15
|
+
* Находит корневого предка категории по `parent_id`.
|
|
16
|
+
*
|
|
17
|
+
* Для вложенной «Куртки» (parent: «Мужская») вернёт «Одежда» (корень ветки).
|
|
18
|
+
* Для корневой «Одежда» вернёт саму себя.
|
|
19
|
+
*
|
|
20
|
+
* Используется в `CategoryTabs`, чтобы подсветить корневой таб когда юзер
|
|
21
|
+
* находится на вложенной категории.
|
|
22
|
+
*/
|
|
23
|
+
declare function findRootAncestor(tree: NestedCategory[], category: NestedCategory): NestedCategory | null;
|
|
24
|
+
/** Рекурсивный поиск по `id` (не `short_id`). Используется в findRootAncestor. */
|
|
25
|
+
declare function findCategoryById(tree: NestedCategory[], id: string): NestedCategory | null;
|
|
26
|
+
/**
|
|
27
|
+
* Возвращает путь от корневой категории до указанной (включительно).
|
|
28
|
+
*
|
|
29
|
+
* Пример для «Куртки» в ветке «Одежда → Мужская → Куртки»:
|
|
30
|
+
* → [Одежда, Мужская, Куртки]
|
|
31
|
+
*
|
|
32
|
+
* Используется для построения хлебных крошек.
|
|
33
|
+
*/
|
|
34
|
+
declare function getCategoryPath(tree: NestedCategory[], target: NestedCategory): NestedCategory[];
|
|
35
|
+
|
|
36
|
+
interface SubcategoryGroup {
|
|
37
|
+
category: NestedCategory;
|
|
38
|
+
products: Product[];
|
|
39
|
+
}
|
|
40
|
+
interface GroupedProducts {
|
|
41
|
+
rootDirect: Product[];
|
|
42
|
+
groups: SubcategoryGroup[];
|
|
43
|
+
}
|
|
44
|
+
declare function collectDescendantIds(category: NestedCategory): string[];
|
|
45
|
+
declare function groupProductsBySubcategory(rootCategory: NestedCategory, products: Product[]): GroupedProducts;
|
|
46
|
+
|
|
47
|
+
declare function getCity(store: Store, cityId?: string | null): City | null;
|
|
48
|
+
declare function getStock(city: City, stockId?: string | null): Stock | null;
|
|
49
|
+
declare function getRemoteCatalog(store: Store, city: City, stock: Stock | null): RemoteCatalog | null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Загрузка списка коллекций товаров магазина.
|
|
53
|
+
* Pure composable — SDK передаётся параметром (DI), Nuxt не нужен.
|
|
54
|
+
*
|
|
55
|
+
* NOTE: одиночный `getCollection(id)` в storefront API не предусмотрен,
|
|
56
|
+
* для получения товаров коллекции используется `GET /products?collection_id=...`.
|
|
57
|
+
* Чтобы получить метаданные коллекции — ищем в списке через `loadCollections`.
|
|
58
|
+
*/
|
|
59
|
+
declare function useCollections(sdk: ZenkyStorefront): {
|
|
60
|
+
loadCollections: (storeId: string, request?: CollectionsListRequest) => Promise<ProductsCollection[]>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** URL-хелперы каталога — единая точка правды для путей категорий/коллекций. */
|
|
64
|
+
declare const catalogRoutes: {
|
|
65
|
+
/** Корневая страница каталога: список категорий + коллекций */
|
|
66
|
+
readonly index: () => string;
|
|
67
|
+
/** Страница конкретной категории */
|
|
68
|
+
readonly category: (shortId: string, slug: string | null) => string;
|
|
69
|
+
/** Страница конкретной коллекции */
|
|
70
|
+
readonly collection: (id: string) => string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export { type GroupedProducts, type SubcategoryGroup, catalogRoutes, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, getCity, getRemoteCatalog, getStock, groupProductsBySubcategory, useCollections };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { catalogRoutes, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, groupProductsBySubcategory, useCollections } from '../chunk-RYYY7MVP.js';
|
|
2
|
+
export { getCity, getRemoteCatalog, getStock } from '../chunk-E3G7NGCH.js';
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useStoreProfileStore } from './chunk-TEG7EL4N.js';
|
|
2
|
+
import { defineStore, storeToRefs } from 'pinia';
|
|
3
|
+
import { ref, computed } from 'vue';
|
|
4
|
+
|
|
5
|
+
var useProductsStore = defineStore("zenky-products", () => {
|
|
6
|
+
const products = ref([]);
|
|
7
|
+
function setProducts(list) {
|
|
8
|
+
products.value = list;
|
|
9
|
+
}
|
|
10
|
+
const hasProducts = computed(() => products.value.length > 0);
|
|
11
|
+
return { products, setProducts, hasProducts };
|
|
12
|
+
});
|
|
13
|
+
function useStore() {
|
|
14
|
+
const profileStore = useStoreProfileStore();
|
|
15
|
+
return {
|
|
16
|
+
store: computed(() => profileStore.store),
|
|
17
|
+
city: computed(() => profileStore.city),
|
|
18
|
+
stock: computed(() => profileStore.stock)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function useStorefrontProfile() {
|
|
22
|
+
const profileStore = useStoreProfileStore();
|
|
23
|
+
const { store, city, stock, categories } = storeToRefs(profileStore);
|
|
24
|
+
const isReady = computed(() => profileStore.isReady);
|
|
25
|
+
const hasStock = computed(() => profileStore.hasStock);
|
|
26
|
+
return {
|
|
27
|
+
store,
|
|
28
|
+
city,
|
|
29
|
+
stock,
|
|
30
|
+
categories,
|
|
31
|
+
isReady,
|
|
32
|
+
hasStock
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { useProductsStore, useStore, useStorefrontProfile };
|
|
37
|
+
//# sourceMappingURL=chunk-57IOMDAM.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-57IOMDAM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/products.ts","../src/stores/useStore.ts","../src/stores/useStorefrontProfile.ts"],"names":["computed"],"mappings":";;;;AAaO,IAAM,gBAAA,GAAmB,WAAA,CAAY,gBAAA,EAAkB,MAAqB;AAC/E,EAAA,MAAM,QAAA,GAAW,GAAA,CAAe,EAAE,CAAA;AAElC,EAAA,SAAS,YAAY,IAAA,EAAuB;AACxC,IAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AAAA,EACrB;AAEA,EAAA,MAAM,cAAc,QAAA,CAAS,MAAM,QAAA,CAAS,KAAA,CAAM,SAAS,CAAC,CAAA;AAE5D,EAAA,OAAO,EAAE,QAAA,EAAU,WAAA,EAAa,WAAA,EAAY;AAChD,CAAC;AChBM,SAAS,QAAA,GAId;AACE,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAE1C,EAAA,OAAO;AAAA,IACH,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAM,CAAA;AAAA,IACzC,IAAA,EAAMA,QAAAA,CAAS,MAAM,YAAA,CAAa,IAAK,CAAA;AAAA,IACvC,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAK;AAAA,GAC5C;AACJ;ACbO,SAAS,oBAAA,GAAuB;AACnC,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAC1C,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,OAAO,UAAA,EAAW,GAAI,YAAY,YAAY,CAAA;AAEnE,EAAA,MAAM,OAAA,GAAUA,QAAAA,CAAS,MAAM,YAAA,CAAa,OAAO,CAAA;AACnD,EAAA,MAAM,QAAA,GAAWA,QAAAA,CAAS,MAAM,YAAA,CAAa,QAAQ,CAAA;AAErD,EAAA,OAAO;AAAA,IACH,KAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACJ;AACJ","file":"chunk-57IOMDAM.js","sourcesContent":["import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { Product } from '@zenky/storefront-api'\n\n// Явный тип возврата — сохраняет `Product` именованной ссылкой в .d.ts вместо разворачивания.\nexport interface ProductsStore {\n products: Ref<Product[]>\n setProducts: (list: Product[]) => void\n hasProducts: ComputedRef<boolean>\n}\n\n// Товары держим в сторе, но наполняем из каталога (не из отдельного API-вызова).\nexport const useProductsStore = defineStore('zenky-products', (): ProductsStore => {\n const products = ref<Product[]>([])\n\n function setProducts(list: Product[]): void {\n products.value = list\n }\n\n const hasProducts = computed(() => products.value.length > 0)\n\n return { products, setProducts, hasProducts }\n})\n","import { computed, type ComputedRef } from 'vue'\n\nimport type { City, Stock, Store } from '@zenky/storefront-api'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Аксессор загруженного стора/города/склада (store/city — гарантированно загружены). */\nexport function useStore(): {\n store: ComputedRef<Store>\n city: ComputedRef<City>\n stock: ComputedRef<Stock | null>\n} {\n const profileStore = useStoreProfileStore()\n\n return {\n store: computed(() => profileStore.store!),\n city: computed(() => profileStore.city!),\n stock: computed(() => profileStore.stock),\n }\n}\n","import { storeToRefs } from 'pinia'\nimport { computed } from 'vue'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Реактивный доступ к профилю (store/city/stock/categories + флаги готовности). */\nexport function useStorefrontProfile() {\n const profileStore = useStoreProfileStore()\n const { store, city, stock, categories } = storeToRefs(profileStore)\n\n const isReady = computed(() => profileStore.isReady)\n const hasStock = computed(() => profileStore.hasStock)\n\n return {\n store,\n city,\n stock,\n categories,\n isReady,\n hasStock,\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/ports/runtimeState.ts
|
|
2
|
+
function createRuntimeState(create) {
|
|
3
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
return (runtime) => {
|
|
5
|
+
const existing = states.get(runtime);
|
|
6
|
+
if (existing !== void 0) return existing;
|
|
7
|
+
const state = create();
|
|
8
|
+
states.set(runtime, state);
|
|
9
|
+
return state;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { createRuntimeState };
|
|
14
|
+
//# sourceMappingURL=chunk-5QCUH3FF.js.map
|
|
15
|
+
//# sourceMappingURL=chunk-5QCUH3FF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ports/runtimeState.ts"],"names":[],"mappings":";AAEO,SAAS,mBAAsB,MAAA,EAAoD;AACtF,EAAA,MAAM,MAAA,uBAAa,OAAA,EAA8B;AAEjD,EAAA,OAAO,CAAC,OAAA,KAAkC;AACtC,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,OAAO,CAAA;AACnC,IAAA,IAAI,QAAA,KAAa,QAAW,OAAO,QAAA;AAEnC,IAAA,MAAM,QAAQ,MAAA,EAAO;AACrB,IAAA,MAAA,CAAO,GAAA,CAAI,SAAS,KAAK,CAAA;AACzB,IAAA,OAAO,KAAA;AAAA,EACX,CAAA;AACJ","file":"chunk-5QCUH3FF.js","sourcesContent":["import type { StorefrontRuntime } from './index'\n\nexport function createRuntimeState<T>(create: () => T): (runtime: StorefrontRuntime) => T {\n const states = new WeakMap<StorefrontRuntime, T>()\n\n return (runtime: StorefrontRuntime): T => {\n const existing = states.get(runtime)\n if (existing !== undefined) return existing\n\n const state = create()\n states.set(runtime, state)\n return state\n }\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/snippets/snippets.ts
|
|
2
|
+
function normalizeSnippets(groups) {
|
|
3
|
+
if (!Array.isArray(groups)) return [];
|
|
4
|
+
return groups.flatMap(
|
|
5
|
+
(group, groupIndex) => (group.snippets ?? []).filter((item) => !!item.content?.trim()).map((item, itemIndex) => {
|
|
6
|
+
const snippet = {
|
|
7
|
+
id: `${group.region}-${groupIndex}-${itemIndex}`,
|
|
8
|
+
position: group.region,
|
|
9
|
+
language: item.language,
|
|
10
|
+
code: item.content,
|
|
11
|
+
enabled: item.enabled ?? true
|
|
12
|
+
};
|
|
13
|
+
if (item.name !== void 0) snippet.name = item.name;
|
|
14
|
+
return snippet;
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/snippets/buildSnippetHead.ts
|
|
20
|
+
function wrapSnippetJs(snippet) {
|
|
21
|
+
const id = JSON.stringify(snippet.id);
|
|
22
|
+
return `;(() => {
|
|
23
|
+
try {
|
|
24
|
+
${snippet.code}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('[snippet:error]', { id: ${id}, error });
|
|
27
|
+
window.dispatchEvent(new CustomEvent('zenky:snippet-error', {
|
|
28
|
+
detail: { id: ${id}, message: error instanceof Error ? error.message : String(error) },
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
})();`;
|
|
32
|
+
}
|
|
33
|
+
function parseTagAttributes(raw) {
|
|
34
|
+
const attrs = {};
|
|
35
|
+
const re = /([a-zA-Z][\w:-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
36
|
+
for (const match of raw.matchAll(re)) {
|
|
37
|
+
const name = match[1];
|
|
38
|
+
if (name) attrs[name] = match[2] ?? match[3] ?? match[4] ?? true;
|
|
39
|
+
}
|
|
40
|
+
return attrs;
|
|
41
|
+
}
|
|
42
|
+
function parseHeadHtml(html) {
|
|
43
|
+
const result = { meta: [], link: [], script: [] };
|
|
44
|
+
const metaRe = /<meta\b([^>]*)>/gi;
|
|
45
|
+
for (const match of html.matchAll(metaRe)) {
|
|
46
|
+
result.meta.push(parseTagAttributes(match[1] ?? ""));
|
|
47
|
+
}
|
|
48
|
+
const linkRe = /<link\b([^>]*)>/gi;
|
|
49
|
+
for (const match of html.matchAll(linkRe)) {
|
|
50
|
+
result.link.push(parseTagAttributes(match[1] ?? ""));
|
|
51
|
+
}
|
|
52
|
+
const scriptRe = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
53
|
+
for (const match of html.matchAll(scriptRe)) {
|
|
54
|
+
const attrs = parseTagAttributes(match[1] ?? "");
|
|
55
|
+
const inner = (match[2] ?? "").trim();
|
|
56
|
+
result.script.push(inner ? { ...attrs, innerHTML: inner } : attrs);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
function buildSnippetHead(snippets) {
|
|
61
|
+
const enabled = snippets.filter((s) => s.enabled);
|
|
62
|
+
const jsSnippets = enabled.filter((s) => s.language === "javascript");
|
|
63
|
+
const cssSnippets = enabled.filter((s) => s.language === "css");
|
|
64
|
+
const headHtmlSnippets = enabled.filter(
|
|
65
|
+
(s) => s.language === "html" && s.position.startsWith("head")
|
|
66
|
+
);
|
|
67
|
+
const parsedHead = headHtmlSnippets.map((s) => parseHeadHtml(s.code));
|
|
68
|
+
const headMeta = parsedHead.flatMap((p) => p.meta);
|
|
69
|
+
const headLink = parsedHead.flatMap((p) => p.link);
|
|
70
|
+
const headScripts = parsedHead.flatMap((p) => p.script);
|
|
71
|
+
return {
|
|
72
|
+
meta: headMeta,
|
|
73
|
+
link: headLink,
|
|
74
|
+
script: [
|
|
75
|
+
...jsSnippets.map((snippet) => ({
|
|
76
|
+
key: `snippet-js-${snippet.id}`,
|
|
77
|
+
innerHTML: wrapSnippetJs(snippet),
|
|
78
|
+
body: snippet.position.startsWith("body"),
|
|
79
|
+
"data-snippet-id": snippet.id
|
|
80
|
+
})),
|
|
81
|
+
...headScripts
|
|
82
|
+
],
|
|
83
|
+
style: cssSnippets.map((snippet) => ({
|
|
84
|
+
key: `snippet-css-${snippet.id}`,
|
|
85
|
+
innerHTML: snippet.code,
|
|
86
|
+
"data-snippet-id": snippet.id
|
|
87
|
+
}))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { buildSnippetHead, normalizeSnippets };
|
|
92
|
+
//# sourceMappingURL=chunk-7FRMMCR5.js.map
|
|
93
|
+
//# sourceMappingURL=chunk-7FRMMCR5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/snippets/snippets.ts","../src/snippets/buildSnippetHead.ts"],"names":[],"mappings":"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","file":"chunk-7FRMMCR5.js","sourcesContent":["export type SnippetPosition = 'head_start' | 'head_end' | 'body_start' | 'body_end'\nexport type SnippetLanguage = 'html' | 'css' | 'javascript'\n\nexport interface Snippet {\n id: string\n name?: string\n position: SnippetPosition\n language: SnippetLanguage\n code: string\n enabled: boolean\n}\n\nexport interface RawSnippetItem {\n language: SnippetLanguage\n content: string\n enabled?: boolean\n name?: string\n}\n\nexport interface SnippetGroup {\n region: SnippetPosition\n snippets: RawSnippetItem[]\n}\n\nexport function normalizeSnippets(groups: SnippetGroup[] | null | undefined): Snippet[] {\n if (!Array.isArray(groups)) return []\n\n return groups.flatMap((group, groupIndex) =>\n (group.snippets ?? [])\n .filter((item) => !!item.content?.trim())\n .map((item, itemIndex): Snippet => {\n const snippet: Snippet = {\n id: `${group.region}-${groupIndex}-${itemIndex}`,\n position: group.region,\n language: item.language,\n code: item.content,\n enabled: item.enabled ?? true,\n }\n // exactOptionalPropertyTypes: name задаём только если оно есть\n if (item.name !== undefined) snippet.name = item.name\n return snippet\n })\n )\n}\n","import type { Snippet } from './snippets'\n\n/** Head-нагрузка для инъекции сниппетов (потребитель отдаёт её в useHead). */\nexport interface SnippetHead {\n meta: Record<string, string | boolean>[]\n link: Record<string, string | boolean>[]\n script: Record<string, unknown>[]\n style: Record<string, string | boolean>[]\n}\n\n/** JS-сниппет в try/catch с dispatch `zenky:snippet-error` при падении. */\nfunction wrapSnippetJs(snippet: Snippet): string {\n const id = JSON.stringify(snippet.id)\n return `;(() => {\n try {\n ${snippet.code}\n } catch (error) {\n console.error('[snippet:error]', { id: ${id}, error });\n window.dispatchEvent(new CustomEvent('zenky:snippet-error', {\n detail: { id: ${id}, message: error instanceof Error ? error.message : String(error) },\n }));\n }\n})();`\n}\n\ntype HeadAttrs = Record<string, string | boolean>\n\ninterface ParsedHeadHtml {\n meta: HeadAttrs[]\n link: HeadAttrs[]\n script: Array<HeadAttrs & { innerHTML?: string }>\n}\n\nfunction parseTagAttributes(raw: string): HeadAttrs {\n const attrs: HeadAttrs = {}\n const re = /([a-zA-Z][\\w:-]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n for (const match of raw.matchAll(re)) {\n const name = match[1]\n if (name) attrs[name] = match[2] ?? match[3] ?? match[4] ?? true\n }\n return attrs\n}\n\nfunction parseHeadHtml(html: string): ParsedHeadHtml {\n const result: ParsedHeadHtml = { meta: [], link: [], script: [] }\n\n const metaRe = /<meta\\b([^>]*)>/gi\n for (const match of html.matchAll(metaRe)) {\n result.meta.push(parseTagAttributes(match[1] ?? ''))\n }\n\n const linkRe = /<link\\b([^>]*)>/gi\n for (const match of html.matchAll(linkRe)) {\n result.link.push(parseTagAttributes(match[1] ?? ''))\n }\n\n const scriptRe = /<script\\b([^>]*)>([\\s\\S]*?)<\\/script>/gi\n for (const match of html.matchAll(scriptRe)) {\n const attrs = parseTagAttributes(match[1] ?? '')\n const inner = (match[2] ?? '').trim()\n result.script.push(inner ? { ...attrs, innerHTML: inner } : attrs)\n }\n\n return result\n}\n\n/**\n * Раскладывает сниппеты магазина в head-нагрузку: JS (обёрнут в try/catch),\n * CSS (style), head-HTML (распарсенные meta/link/script). Инъекция — у потребителя.\n */\nexport function buildSnippetHead(snippets: Snippet[]): SnippetHead {\n const enabled = snippets.filter((s) => s.enabled)\n\n const jsSnippets = enabled.filter((s) => s.language === 'javascript')\n const cssSnippets = enabled.filter((s) => s.language === 'css')\n const headHtmlSnippets = enabled.filter(\n (s) => s.language === 'html' && s.position.startsWith('head')\n )\n\n const parsedHead = headHtmlSnippets.map((s) => parseHeadHtml(s.code))\n const headMeta = parsedHead.flatMap((p) => p.meta)\n const headLink = parsedHead.flatMap((p) => p.link)\n const headScripts = parsedHead.flatMap((p) => p.script)\n\n return {\n meta: headMeta,\n link: headLink,\n script: [\n ...jsSnippets.map((snippet) => ({\n key: `snippet-js-${snippet.id}`,\n innerHTML: wrapSnippetJs(snippet),\n body: snippet.position.startsWith('body'),\n 'data-snippet-id': snippet.id,\n })),\n ...headScripts,\n ],\n style: cssSnippets.map((snippet) => ({\n key: `snippet-css-${snippet.id}`,\n innerHTML: snippet.code,\n 'data-snippet-id': snippet.id,\n })),\n }\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
import { ref, computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
// src/stores/promotions.ts
|
|
5
|
+
var usePromotionsStore = defineStore("zenky-promotions", () => {
|
|
6
|
+
const rewards = ref([]);
|
|
7
|
+
const checking = ref(false);
|
|
8
|
+
function addOrUpdateReward(selection) {
|
|
9
|
+
const index = rewards.value.findIndex((r) => r.promotionId === selection.promotionId);
|
|
10
|
+
if (index >= 0) {
|
|
11
|
+
rewards.value[index] = selection;
|
|
12
|
+
} else {
|
|
13
|
+
rewards.value.push(selection);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function removeRewardByPromotion(promotionId) {
|
|
17
|
+
rewards.value = rewards.value.filter((r) => r.promotionId !== promotionId);
|
|
18
|
+
}
|
|
19
|
+
function clearRewards() {
|
|
20
|
+
rewards.value = [];
|
|
21
|
+
}
|
|
22
|
+
function setChecking(value) {
|
|
23
|
+
checking.value = value;
|
|
24
|
+
}
|
|
25
|
+
const hasRewards = computed(() => rewards.value.length > 0);
|
|
26
|
+
const totalEmptySlots = computed(() => rewards.value.reduce((sum, r) => sum + r.limit, 0));
|
|
27
|
+
const rewardSelection = computed(() => rewards.value[0] ?? null);
|
|
28
|
+
const needsSelection = computed(() => rewards.value.some((r) => r.context !== null));
|
|
29
|
+
return {
|
|
30
|
+
rewards,
|
|
31
|
+
checking,
|
|
32
|
+
addOrUpdateReward,
|
|
33
|
+
removeRewardByPromotion,
|
|
34
|
+
clearRewards,
|
|
35
|
+
setChecking,
|
|
36
|
+
hasRewards,
|
|
37
|
+
totalEmptySlots,
|
|
38
|
+
rewardSelection,
|
|
39
|
+
needsSelection
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export { usePromotionsStore };
|
|
44
|
+
//# sourceMappingURL=chunk-BWUKMWHL.js.map
|
|
45
|
+
//# sourceMappingURL=chunk-BWUKMWHL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/promotions.ts"],"names":[],"mappings":";;;;AA6BO,IAAM,kBAAA,GAAqB,WAAA,CAAY,kBAAA,EAAoB,MAAuB;AAErF,EAAA,MAAM,OAAA,GAAU,GAAA,CAAuB,EAAE,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,IAAI,KAAK,CAAA;AAE1B,EAAA,SAAS,kBAAkB,SAAA,EAAkC;AACzD,IAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,CAAM,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,KAAgB,SAAA,CAAU,WAAW,CAAA;AACpF,IAAA,IAAI,SAAS,CAAA,EAAG;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GAAI,SAAA;AAAA,IAC3B,CAAA,MAAO;AACH,MAAA,OAAA,CAAQ,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,IAChC;AAAA,EACJ;AAEA,EAAA,SAAS,wBAAwB,WAAA,EAA2B;AACxD,IAAA,OAAA,CAAQ,KAAA,GAAQ,QAAQ,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,gBAAgB,WAAW,CAAA;AAAA,EAC7E;AAEA,EAAA,SAAS,YAAA,GAAqB;AAC1B,IAAA,OAAA,CAAQ,QAAQ,EAAC;AAAA,EACrB;AAEA,EAAA,SAAS,YAAY,KAAA,EAAsB;AACvC,IAAA,QAAA,CAAS,KAAA,GAAQ,KAAA;AAAA,EACrB;AAEA,EAAA,MAAM,aAAa,QAAA,CAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAC,CAAA;AAE1D,EAAA,MAAM,eAAA,GAAkB,QAAA,CAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,GAAA,GAAM,CAAA,CAAE,KAAA,EAAO,CAAC,CAAC,CAAA;AAEzF,EAAA,MAAM,kBAAkB,QAAA,CAAiC,MAAM,QAAQ,KAAA,CAAM,CAAC,KAAK,IAAI,CAAA;AACvF,EAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAI,CAAC,CAAA;AAEnF,EAAA,OAAO;AAAA,IACH,OAAA;AAAA,IACA,QAAA;AAAA,IACA,iBAAA;AAAA,IACA,uBAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACJ;AACJ,CAAC","file":"chunk-BWUKMWHL.js","sourcesContent":["import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nexport interface RewardSelectionContext {\n type: string\n id: string\n}\n\nexport interface RewardSelection {\n offerId: string\n promotionId: string\n rewardId: string\n context: RewardSelectionContext | null\n limit: number\n}\n\nexport interface PromotionsStore {\n rewards: Ref<RewardSelection[]>\n checking: Ref<boolean>\n addOrUpdateReward: (selection: RewardSelection) => void\n removeRewardByPromotion: (promotionId: string) => void\n clearRewards: () => void\n setChecking: (value: boolean) => void\n hasRewards: ComputedRef<boolean>\n totalEmptySlots: ComputedRef<number>\n rewardSelection: ComputedRef<RewardSelection | null>\n needsSelection: ComputedRef<boolean>\n}\n\nexport const usePromotionsStore = defineStore('zenky-promotions', (): PromotionsStore => {\n /** Доступные награды (может быть несколько акций одновременно) */\n const rewards = ref<RewardSelection[]>([])\n /** Идёт проверка промо-акций */\n const checking = ref(false)\n\n function addOrUpdateReward(selection: RewardSelection): void {\n const index = rewards.value.findIndex((r) => r.promotionId === selection.promotionId)\n if (index >= 0) {\n rewards.value[index] = selection\n } else {\n rewards.value.push(selection)\n }\n }\n\n function removeRewardByPromotion(promotionId: string): void {\n rewards.value = rewards.value.filter((r) => r.promotionId !== promotionId)\n }\n\n function clearRewards(): void {\n rewards.value = []\n }\n\n function setChecking(value: boolean): void {\n checking.value = value\n }\n\n const hasRewards = computed(() => rewards.value.length > 0)\n /** Общее количество пустых слотов по всем акциям */\n const totalEmptySlots = computed(() => rewards.value.reduce((sum, r) => sum + r.limit, 0))\n /** Для обратной совместимости — первая награда */\n const rewardSelection = computed<RewardSelection | null>(() => rewards.value[0] ?? null)\n const needsSelection = computed(() => rewards.value.some((r) => r.context !== null))\n\n return {\n rewards,\n checking,\n addOrUpdateReward,\n removeRewardByPromotion,\n clearRewards,\n setChecking,\n hasRewards,\n totalEmptySlots,\n rewardSelection,\n needsSelection,\n }\n})\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/catalog/catalogResolver.ts
|
|
2
|
+
function getCity(store, cityId) {
|
|
3
|
+
if (!store.cities.length) return null;
|
|
4
|
+
const defaultCity = store.cities.find((c) => c.is_default) ?? store.cities[0] ?? null;
|
|
5
|
+
if (!cityId) return defaultCity;
|
|
6
|
+
return store.cities.find((c) => c.id === cityId) ?? defaultCity;
|
|
7
|
+
}
|
|
8
|
+
function getStock(city, stockId) {
|
|
9
|
+
if (!city.stocks?.length) return null;
|
|
10
|
+
const defaultStock = city.stocks[0] ?? null;
|
|
11
|
+
if (!stockId) return defaultStock;
|
|
12
|
+
return city.stocks.find((s) => s.id === stockId) ?? defaultStock;
|
|
13
|
+
}
|
|
14
|
+
function getRemoteCatalog(store, city, stock) {
|
|
15
|
+
if (!store.catalogs.length) {
|
|
16
|
+
console.warn("[catalog-resolver] store.catalogs is empty", {
|
|
17
|
+
storeId: store.id,
|
|
18
|
+
cityId: city.id,
|
|
19
|
+
stockId: stock?.id ?? null
|
|
20
|
+
});
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (stock) {
|
|
24
|
+
const exact = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === stock.id);
|
|
25
|
+
if (exact) return exact;
|
|
26
|
+
}
|
|
27
|
+
const cityOnly = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === null);
|
|
28
|
+
if (cityOnly) return cityOnly;
|
|
29
|
+
const global = store.catalogs.find((c) => c.city_id === null && c.stock_id === null);
|
|
30
|
+
if (global) return global;
|
|
31
|
+
console.warn("[catalog-resolver] no catalog matched (exact/city-only/global)", {
|
|
32
|
+
storeId: store.id,
|
|
33
|
+
cityId: city.id,
|
|
34
|
+
stockId: stock?.id ?? null,
|
|
35
|
+
availableCatalogs: store.catalogs.map((c) => ({
|
|
36
|
+
city_id: c.city_id,
|
|
37
|
+
stock_id: c.stock_id
|
|
38
|
+
}))
|
|
39
|
+
});
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { getCity, getRemoteCatalog, getStock };
|
|
44
|
+
//# sourceMappingURL=chunk-E3G7NGCH.js.map
|
|
45
|
+
//# sourceMappingURL=chunk-E3G7NGCH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/catalog/catalogResolver.ts"],"names":[],"mappings":";AAEO,SAAS,OAAA,CAAQ,OAAc,MAAA,EAAqC;AACvE,EAAA,IAAI,CAAC,KAAA,CAAM,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AAEjC,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,CAAA,IAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,IAAK,IAAA;AAEjF,EAAA,IAAI,CAAC,QAAQ,OAAO,WAAA;AAEpB,EAAA,OAAO,KAAA,CAAM,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,MAAM,CAAA,IAAK,WAAA;AACxD;AAEO,SAAS,QAAA,CAAS,MAAY,OAAA,EAAuC;AACxE,EAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,MAAA,EAAQ,OAAO,IAAA;AAEjC,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,IAAK,IAAA;AAEvC,EAAA,IAAI,CAAC,SAAS,OAAO,YAAA;AAErB,EAAA,OAAO,IAAA,CAAK,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,OAAO,CAAA,IAAK,YAAA;AACxD;AAEO,SAAS,gBAAA,CACZ,KAAA,EACA,IAAA,EACA,KAAA,EACoB;AACpB,EAAA,IAAI,CAAC,KAAA,CAAM,QAAA,CAAS,MAAA,EAAQ;AACxB,IAAA,OAAA,CAAQ,KAAK,4CAAA,EAA8C;AAAA,MACvD,SAAS,KAAA,CAAM,EAAA;AAAA,MACf,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,OAAA,EAAS,OAAO,EAAA,IAAM;AAAA,KACzB,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACX;AAGA,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,CAAK,EAAA,IAAM,CAAA,CAAE,QAAA,KAAa,MAAM,EAAE,CAAA;AACzF,IAAA,IAAI,OAAO,OAAO,KAAA;AAAA,EACtB;AAEA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,CAAK,EAAA,IAAM,CAAA,CAAE,QAAA,KAAa,IAAI,CAAA;AACxF,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,IAAQ,CAAA,CAAE,QAAA,KAAa,IAAI,CAAA;AACnF,EAAA,IAAI,QAAQ,OAAO,MAAA;AAEnB,EAAA,OAAA,CAAQ,KAAK,gEAAA,EAAkE;AAAA,IAC3E,SAAS,KAAA,CAAM,EAAA;AAAA,IACf,QAAQ,IAAA,CAAK,EAAA;AAAA,IACb,OAAA,EAAS,OAAO,EAAA,IAAM,IAAA;AAAA,IACtB,iBAAA,EAAmB,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC1C,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,UAAU,CAAA,CAAE;AAAA,KAChB,CAAE;AAAA,GACL,CAAA;AAED,EAAA,OAAO,IAAA;AACX","file":"chunk-E3G7NGCH.js","sourcesContent":["import type { City, RemoteCatalog, Stock, Store } from '@zenky/storefront-api'\n\nexport function getCity(store: Store, cityId?: string | null): City | null {\n if (!store.cities.length) return null\n\n const defaultCity = store.cities.find((c) => c.is_default) ?? store.cities[0] ?? null\n\n if (!cityId) return defaultCity\n\n return store.cities.find((c) => c.id === cityId) ?? defaultCity\n}\n\nexport function getStock(city: City, stockId?: string | null): Stock | null {\n if (!city.stocks?.length) return null\n\n const defaultStock = city.stocks[0] ?? null\n\n if (!stockId) return defaultStock\n\n return city.stocks.find((s) => s.id === stockId) ?? defaultStock\n}\n\nexport function getRemoteCatalog(\n store: Store,\n city: City,\n stock: Stock | null\n): RemoteCatalog | null {\n if (!store.catalogs.length) {\n console.warn('[catalog-resolver] store.catalogs is empty', {\n storeId: store.id,\n cityId: city.id,\n stockId: stock?.id ?? null,\n })\n return null\n }\n\n // Приоритет: точное совпадение city+stock → city-only → глобальный.\n if (stock) {\n const exact = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === stock.id)\n if (exact) return exact\n }\n\n const cityOnly = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === null)\n if (cityOnly) return cityOnly\n\n const global = store.catalogs.find((c) => c.city_id === null && c.stock_id === null)\n if (global) return global\n\n console.warn('[catalog-resolver] no catalog matched (exact/city-only/global)', {\n storeId: store.id,\n cityId: city.id,\n stockId: stock?.id ?? null,\n availableCatalogs: store.catalogs.map((c) => ({\n city_id: c.city_id,\n stock_id: c.stock_id,\n })),\n })\n\n return null\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-EMGQXBU3.js"}
|