@scayle/storefront-product-listing 0.5.0 → 0.6.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/dist/index.d.mts +3 -48
- package/dist/index.d.ts +3 -48
- package/dist/index.mjs +4 -3
- package/dist/runtime/composables/useFiltersForListing.d.ts +33 -0
- package/dist/runtime/composables/{useProductListFilter.js → useFiltersForListing.js} +13 -22
- package/dist/runtime/composables/useProductListingSeoData.d.ts +5 -8
- package/dist/runtime/composables/useProductListingSeoData.js +4 -10
- package/dist/runtime/composables/{useProductsByCategory.d.ts → useProductsForListing.d.ts} +9 -20
- package/dist/runtime/composables/useProductsForListing.js +52 -0
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/utils/filters.d.ts +1 -3
- package/dist/runtime/utils/filters.js +17 -21
- package/package.json +3 -3
- package/dist/runtime/composables/useProductListFilter.d.ts +0 -32
- package/dist/runtime/composables/useProductsByCategory.js +0 -76
package/dist/index.d.mts
CHANGED
|
@@ -1,56 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BreadcrumbItem } from '@scayle/storefront-nuxt';
|
|
2
2
|
import { Factory } from 'fishery';
|
|
3
|
+
import { FilterItemWithValues } from '@scayle/storefront-api';
|
|
3
4
|
|
|
4
|
-
interface AttributesFilterValue {
|
|
5
|
-
name: string;
|
|
6
|
-
id: number;
|
|
7
|
-
productCount: number;
|
|
8
|
-
value: string;
|
|
9
|
-
}
|
|
10
|
-
type BooleanFilterValue = [
|
|
11
|
-
{
|
|
12
|
-
name: true | false;
|
|
13
|
-
productCount: number;
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
name: true | false;
|
|
17
|
-
productCount: number;
|
|
18
|
-
}
|
|
19
|
-
];
|
|
20
|
-
type RangeFilterValue = [
|
|
21
|
-
{
|
|
22
|
-
min: CentAmount;
|
|
23
|
-
max: CentAmount;
|
|
24
|
-
productCount: number;
|
|
25
|
-
}
|
|
26
|
-
];
|
|
27
|
-
type FilterItemWithValues = BooleanFilterItemWithValues | AttributesFilterItemWithValues | RangeFilterItemWithValues;
|
|
28
|
-
interface BooleanFilterItemWithValues {
|
|
29
|
-
id: null;
|
|
30
|
-
slug: string;
|
|
31
|
-
name: string;
|
|
32
|
-
values: BooleanFilterValue;
|
|
33
|
-
type: 'boolean';
|
|
34
|
-
}
|
|
35
|
-
interface AttributesFilterItemWithValues {
|
|
36
|
-
id: number | null;
|
|
37
|
-
slug: string;
|
|
38
|
-
name: string;
|
|
39
|
-
values: AttributesFilterValue[];
|
|
40
|
-
type: 'attributes';
|
|
41
|
-
attributeGroupType: string;
|
|
42
|
-
}
|
|
43
|
-
interface RangeFilterItemWithValues {
|
|
44
|
-
id: null;
|
|
45
|
-
slug: string;
|
|
46
|
-
name: string;
|
|
47
|
-
values: RangeFilterValue;
|
|
48
|
-
type: 'range';
|
|
49
|
-
}
|
|
50
5
|
type RangeTuple = [start: number, end: number];
|
|
51
6
|
|
|
52
7
|
declare const breadcrumbsFactory: Factory<BreadcrumbItem[], any, BreadcrumbItem[]>;
|
|
53
8
|
|
|
54
9
|
declare const filtersFactory: Factory<FilterItemWithValues[], any, FilterItemWithValues[]>;
|
|
55
10
|
|
|
56
|
-
export { type
|
|
11
|
+
export { type RangeTuple, breadcrumbsFactory, filtersFactory };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,56 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BreadcrumbItem } from '@scayle/storefront-nuxt';
|
|
2
2
|
import { Factory } from 'fishery';
|
|
3
|
+
import { FilterItemWithValues } from '@scayle/storefront-api';
|
|
3
4
|
|
|
4
|
-
interface AttributesFilterValue {
|
|
5
|
-
name: string;
|
|
6
|
-
id: number;
|
|
7
|
-
productCount: number;
|
|
8
|
-
value: string;
|
|
9
|
-
}
|
|
10
|
-
type BooleanFilterValue = [
|
|
11
|
-
{
|
|
12
|
-
name: true | false;
|
|
13
|
-
productCount: number;
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
name: true | false;
|
|
17
|
-
productCount: number;
|
|
18
|
-
}
|
|
19
|
-
];
|
|
20
|
-
type RangeFilterValue = [
|
|
21
|
-
{
|
|
22
|
-
min: CentAmount;
|
|
23
|
-
max: CentAmount;
|
|
24
|
-
productCount: number;
|
|
25
|
-
}
|
|
26
|
-
];
|
|
27
|
-
type FilterItemWithValues = BooleanFilterItemWithValues | AttributesFilterItemWithValues | RangeFilterItemWithValues;
|
|
28
|
-
interface BooleanFilterItemWithValues {
|
|
29
|
-
id: null;
|
|
30
|
-
slug: string;
|
|
31
|
-
name: string;
|
|
32
|
-
values: BooleanFilterValue;
|
|
33
|
-
type: 'boolean';
|
|
34
|
-
}
|
|
35
|
-
interface AttributesFilterItemWithValues {
|
|
36
|
-
id: number | null;
|
|
37
|
-
slug: string;
|
|
38
|
-
name: string;
|
|
39
|
-
values: AttributesFilterValue[];
|
|
40
|
-
type: 'attributes';
|
|
41
|
-
attributeGroupType: string;
|
|
42
|
-
}
|
|
43
|
-
interface RangeFilterItemWithValues {
|
|
44
|
-
id: null;
|
|
45
|
-
slug: string;
|
|
46
|
-
name: string;
|
|
47
|
-
values: RangeFilterValue;
|
|
48
|
-
type: 'range';
|
|
49
|
-
}
|
|
50
5
|
type RangeTuple = [start: number, end: number];
|
|
51
6
|
|
|
52
7
|
declare const breadcrumbsFactory: Factory<BreadcrumbItem[], any, BreadcrumbItem[]>;
|
|
53
8
|
|
|
54
9
|
declare const filtersFactory: Factory<FilterItemWithValues[], any, FilterItemWithValues[]>;
|
|
55
10
|
|
|
56
|
-
export { type
|
|
11
|
+
export { type RangeTuple, breadcrumbsFactory, filtersFactory };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Factory } from 'fishery';
|
|
2
|
+
import { FilterTypes } from '@scayle/storefront-api';
|
|
2
3
|
|
|
3
4
|
const breadcrumbsFactory = Factory.define(() => [
|
|
4
5
|
{ to: "/de/c/women/clothing/shirts-2045", value: "shirts" },
|
|
@@ -18,7 +19,7 @@ const filtersFactory = Factory.define(
|
|
|
18
19
|
value: "123"
|
|
19
20
|
}
|
|
20
21
|
],
|
|
21
|
-
type:
|
|
22
|
+
type: FilterTypes.ATTRIBUTES,
|
|
22
23
|
attributeGroupType: "group-type"
|
|
23
24
|
}, {
|
|
24
25
|
id: null,
|
|
@@ -34,7 +35,7 @@ const filtersFactory = Factory.define(
|
|
|
34
35
|
productCount: 2
|
|
35
36
|
}
|
|
36
37
|
],
|
|
37
|
-
type:
|
|
38
|
+
type: FilterTypes.BOOLEAN
|
|
38
39
|
}, {
|
|
39
40
|
id: null,
|
|
40
41
|
slug: "prices",
|
|
@@ -46,7 +47,7 @@ const filtersFactory = Factory.define(
|
|
|
46
47
|
productCount: 30
|
|
47
48
|
}
|
|
48
49
|
],
|
|
49
|
-
type:
|
|
50
|
+
type: FilterTypes.RANGE
|
|
50
51
|
}]
|
|
51
52
|
);
|
|
52
53
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type FetchFiltersParams } from '@scayle/storefront-nuxt';
|
|
2
|
+
import { useFilters } from '@scayle/storefront-nuxt/composables';
|
|
3
|
+
import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
|
|
4
|
+
import type { FilterItemWithValues } from '@scayle/storefront-api';
|
|
5
|
+
export type FiltersForListingOptions = Partial<{
|
|
6
|
+
params: MaybeRefOrGetter<Omit<FetchFiltersParams, 'category'>>;
|
|
7
|
+
fetchingOptions: Partial<{
|
|
8
|
+
lazy: boolean;
|
|
9
|
+
immediate: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
fetchingKey: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type UseFiltersForListingReturn = Pick<Awaited<ReturnType<typeof useFilters>>, 'error' | 'status' | 'refresh'> & {
|
|
14
|
+
availableFilters: ComputedRef<FilterItemWithValues[]>;
|
|
15
|
+
filteredProductCount: ComputedRef<number>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Composable to manage filters based on current category ID and route query parameters.
|
|
19
|
+
*
|
|
20
|
+
* @param {FiltersForListingOptions} options - Configuration options for fetching products.
|
|
21
|
+
* @param {Partial<FetchFiltersParams>} options.params - Parameters to control product fetch behavior, excluding `category`.
|
|
22
|
+
* @param {Partial<{ lazy: boolean; immediate: boolean }>} options.fetchingOptions - Options for fetch timing, such as lazy loading or immediate fetching.
|
|
23
|
+
* @param {string} [options.fetchingKey] - Optional unique key to control the fetching cache.
|
|
24
|
+
*
|
|
25
|
+
* @returns {UseFiltersForListingReturn} - An object containing reactive data, computed properties and async RPC call for managing filters:
|
|
26
|
+
* - `fetch`: call for fetching the filters.
|
|
27
|
+
* - `error`: returns "useFilters" error.
|
|
28
|
+
* - `status`: returns "useFilters" status.
|
|
29
|
+
* - `fetching`: returns "useFilters" fetching flag.
|
|
30
|
+
* - `availableFilters` {ComputedRef<FilterItemWithValues[]>}: returns computed available filters.
|
|
31
|
+
* - `filteredProductCount` {ComputedRef<number>}: returns the count of the products which the applied filter returns.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useFiltersForListing({ params, fetchingKey, fetchingOptions, }?: FiltersForListingOptions): UseFiltersForListingReturn & Promise<UseFiltersForListingReturn>;
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import { extendPromise } from "@scayle/storefront-nuxt";
|
|
2
2
|
import { useFilters } from "@scayle/storefront-nuxt/composables";
|
|
3
3
|
import { computed, toValue } from "vue";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export function useFiltersForListing({
|
|
5
|
+
params = {
|
|
6
|
+
includeSellableForFree: false,
|
|
7
|
+
includeSoldOut: false
|
|
8
|
+
},
|
|
9
|
+
fetchingKey,
|
|
10
|
+
fetchingOptions
|
|
11
|
+
} = {}) {
|
|
9
12
|
const filterData = useFilters({
|
|
10
|
-
params
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
...appliedFilter.value,
|
|
14
|
-
...route.query.term && { term: String(route.query.term) }
|
|
15
|
-
},
|
|
16
|
-
includeSellableForFree: true,
|
|
17
|
-
includeSoldOut: false
|
|
18
|
-
})),
|
|
19
|
-
options: { immediate }
|
|
20
|
-
}, `${toValue(currentCategoryId) ?? keyPrefix}-filters`);
|
|
13
|
+
params,
|
|
14
|
+
options: fetchingOptions
|
|
15
|
+
}, fetchingKey ?? `${toValue(params).categoryId}-filters`);
|
|
21
16
|
const {
|
|
22
17
|
data,
|
|
23
18
|
refresh,
|
|
@@ -38,18 +33,14 @@ export function useProductListFilter(route, currentCategoryId, options = {}) {
|
|
|
38
33
|
return trueValue.productCount !== 0;
|
|
39
34
|
});
|
|
40
35
|
});
|
|
41
|
-
const
|
|
36
|
+
const filteredProductCount = computed(() => {
|
|
42
37
|
return data.value?.unfilteredCount ?? 0;
|
|
43
38
|
});
|
|
44
|
-
const clearedPriceQuery = computed(() => {
|
|
45
|
-
return getClearedPriceQuery(route);
|
|
46
|
-
});
|
|
47
39
|
return extendPromise(filterData.then(() => ({})), {
|
|
48
40
|
refresh,
|
|
49
41
|
status,
|
|
50
42
|
error,
|
|
51
43
|
availableFilters,
|
|
52
|
-
|
|
53
|
-
clearedPriceQuery
|
|
44
|
+
filteredProductCount
|
|
54
45
|
});
|
|
55
46
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { type ComputedRef, type
|
|
2
|
-
import type { BreadcrumbItem
|
|
1
|
+
import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import type { BreadcrumbItem } from '@scayle/storefront-nuxt';
|
|
3
3
|
import type { BreadcrumbList, WithContext } from 'schema-dts';
|
|
4
4
|
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
|
5
5
|
type CanonicalLink = Record<'rel' | 'key' | 'href', string>;
|
|
6
6
|
type UrlParams = Record<'baseUrl' | 'fullPath', string>;
|
|
7
7
|
export interface UseProductListingSeoDataReturn {
|
|
8
8
|
title: ComputedRef<string>;
|
|
9
|
-
activeCategoryName: ComputedRef<string | undefined>;
|
|
10
9
|
robots: ComputedRef<string>;
|
|
11
10
|
canonicalLink: ComputedRef<CanonicalLink[]>;
|
|
12
11
|
categoryBreadcrumbSchema: ComputedRef<WithContext<BreadcrumbList> | []>;
|
|
@@ -14,18 +13,16 @@ export interface UseProductListingSeoDataReturn {
|
|
|
14
13
|
/**
|
|
15
14
|
* Composable for handling product listing SEO data.
|
|
16
15
|
*
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {BreadcrumbItem[]} breadcrumbs - Breadcrumb items.
|
|
16
|
+
* @param {MaybeRefOrGetter<BreadcrumbItem[]>} breadcrumbs - Breadcrumb items.
|
|
19
17
|
* @param {RouteLocationNormalizedLoadedGeneric} [route] - route object to handle queries.
|
|
20
18
|
* @param {UrlParams} URL params - URL data for constructing the canonical link.
|
|
21
19
|
*
|
|
22
|
-
* @returns {
|
|
20
|
+
* @returns {UseProductListingSeoDataReturn} An object containing reactive data and computed properties for managing SEO data.
|
|
23
21
|
*
|
|
24
22
|
* @property {ComputedRef<string>} title - A computed string that is constructed from breadcrumb values.
|
|
25
|
-
* @property {ComputedRef<string | undefined>} activeCategoryName - A computed active category name.
|
|
26
23
|
* @property {ComputedRef<string>} robots - A computed robots. Value can be "index,follow" or "noindex,follow".
|
|
27
24
|
* @property {ComputedRef<CanonicalLink[]>} canonicalLink - A computed canonical link.
|
|
28
25
|
* @property {ComputedRef<WithContext<BreadcrumbList> | []>} categoryBreadcrumbSchema - A computed breadcrumb schema.
|
|
29
26
|
*/
|
|
30
|
-
export declare function useProductListingSeoData(
|
|
27
|
+
export declare function useProductListingSeoData(breadcrumbs: MaybeRefOrGetter<BreadcrumbItem[]>, route: RouteLocationNormalizedLoadedGeneric, { baseUrl, fullPath }: UrlParams): UseProductListingSeoDataReturn;
|
|
31
28
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { computed } from "vue";
|
|
1
|
+
import { computed, toValue } from "vue";
|
|
2
2
|
import {
|
|
3
3
|
generateCategoryBreadcrumbSchema,
|
|
4
4
|
sanitizeCanonicalURL
|
|
5
5
|
} from "@scayle/storefront-nuxt";
|
|
6
6
|
import { useAppliedFilters } from "./useAppliedFilters.js";
|
|
7
7
|
import { useProductListSort } from "./useProductListSort.js";
|
|
8
|
-
export function useProductListingSeoData(
|
|
8
|
+
export function useProductListingSeoData(breadcrumbs, route, { baseUrl, fullPath }) {
|
|
9
9
|
const { areFiltersApplied } = useAppliedFilters(route);
|
|
10
10
|
const { isDefaultSortSelected } = useProductListSort(route);
|
|
11
11
|
const robots = computed(() => {
|
|
@@ -18,20 +18,14 @@ export function useProductListingSeoData(category, breadcrumbs, route, { baseUrl
|
|
|
18
18
|
href: sanitizeCanonicalURL(`${baseUrl}${fullPath}`)
|
|
19
19
|
}]
|
|
20
20
|
);
|
|
21
|
-
const activeCategoryName = computed(
|
|
22
|
-
() => category.value?.name?.toLowerCase()
|
|
23
|
-
);
|
|
24
21
|
const title = computed(() => {
|
|
25
|
-
return
|
|
22
|
+
return toValue(breadcrumbs).map(({ value }) => value).join(" - ");
|
|
26
23
|
});
|
|
27
24
|
const categoryBreadcrumbSchema = computed(
|
|
28
|
-
() =>
|
|
29
|
-
return category.value ? generateCategoryBreadcrumbSchema(breadcrumbs) : [];
|
|
30
|
-
}
|
|
25
|
+
() => generateCategoryBreadcrumbSchema(toValue(breadcrumbs))
|
|
31
26
|
);
|
|
32
27
|
return {
|
|
33
28
|
title,
|
|
34
|
-
activeCategoryName,
|
|
35
29
|
robots,
|
|
36
30
|
canonicalLink,
|
|
37
31
|
categoryBreadcrumbSchema
|
|
@@ -1,39 +1,28 @@
|
|
|
1
|
-
import { type FetchProductsByCategoryParams, type Product
|
|
1
|
+
import { type FetchProductsByCategoryParams, type Product } from '@scayle/storefront-nuxt';
|
|
2
2
|
import { type MaybeRefOrGetter, type ComputedRef } from 'vue';
|
|
3
3
|
import { useProducts } from '@scayle/storefront-nuxt/composables';
|
|
4
4
|
import type { Pagination } from '@scayle/storefront-api';
|
|
5
|
-
|
|
6
|
-
export type UseProductsByCategoryReturn = Pick<Awaited<ReturnType<typeof useProducts>>, 'error' | 'status'> & {
|
|
5
|
+
export type UseProductsForListingReturn = Pick<Awaited<ReturnType<typeof useProducts>>, 'error' | 'status'> & {
|
|
7
6
|
products: ComputedRef<Product[]>;
|
|
8
7
|
pagination: ComputedRef<Pagination | undefined>;
|
|
9
8
|
totalProductsCount: ComputedRef<number>;
|
|
10
9
|
paginationOffset: ComputedRef<number>;
|
|
11
10
|
};
|
|
12
|
-
export interface
|
|
13
|
-
params
|
|
14
|
-
fetchingOptions
|
|
11
|
+
export interface ProductsForListingOptions {
|
|
12
|
+
params: MaybeRefOrGetter<Omit<FetchProductsByCategoryParams, 'category'>>;
|
|
13
|
+
fetchingOptions: Partial<{
|
|
15
14
|
lazy: boolean;
|
|
16
15
|
immediate: boolean;
|
|
17
16
|
}>;
|
|
18
|
-
|
|
19
|
-
fetchProductsCacheTtl?: number;
|
|
20
|
-
cacheKeyPrefix?: string;
|
|
21
|
-
fetchingKey?: string;
|
|
22
|
-
withParams?: ProductWith;
|
|
17
|
+
fetchingKey: string;
|
|
23
18
|
}
|
|
24
19
|
/**
|
|
25
20
|
* Composable to fetch and manage products by category with configurable options.
|
|
26
21
|
*
|
|
27
|
-
* @param {
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {ProductsByCategoryOptions} options - Configuration options for fetching products.
|
|
30
|
-
* @param {Partial<FetchProductsByCategoryParams>} options.params - Parameters to control product fetch behavior, excluding `category` and `categoryId`.
|
|
22
|
+
* @param {ProductsForListingOptions} options - Configuration options for fetching products.
|
|
23
|
+
* @param {Partial<FetchProductsByCategoryParams>} options.params - Parameters to control product fetch behavior, excluding `category`.
|
|
31
24
|
* @param {Partial<{ lazy: boolean; immediate: boolean }>} options.fetchingOptions - Options for fetch timing, such as lazy loading or immediate fetching.
|
|
32
|
-
* @param {number} options.productsPerPage - Number of products to fetch per page.
|
|
33
|
-
* @param {number} options.fetchProductsCacheTtl - Cache time-to-live for product data in milliseconds.
|
|
34
|
-
* @param {string} options.cacheKeyPrefix - Prefix for cache keys to uniquely identify cached data for this fetch.
|
|
35
25
|
* @param {string} [options.fetchingKey] - Optional unique key to control the fetching cache.
|
|
36
|
-
* @param {ProductWith} [options.withParams] - Additional fields to include with each product.
|
|
37
26
|
*
|
|
38
27
|
* @returns {UseProductsByCategoryReturn} - An object containing reactive data and computed properties for managing products by category.
|
|
39
28
|
* @property {ComputedRef<Product[]>} products - A computed array of products in the specified category.
|
|
@@ -43,4 +32,4 @@ export interface ProductsByCategoryOptions {
|
|
|
43
32
|
* @property {Ref<boolean>} fetching - A reactive boolean indicating if products are currently being fetched.
|
|
44
33
|
* @property {Ref<Error | null>} error - A reactive error object, null if no error occurred.
|
|
45
34
|
*/
|
|
46
|
-
export declare function
|
|
35
|
+
export declare function useProductsForListing({ params, fetchingKey, fetchingOptions, }?: Partial<ProductsForListingOptions>): UseProductsForListingReturn & Promise<UseProductsForListingReturn>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extendPromise
|
|
3
|
+
} from "@scayle/storefront-nuxt";
|
|
4
|
+
import { computed, toValue } from "vue";
|
|
5
|
+
import { useProducts } from "@scayle/storefront-nuxt/composables";
|
|
6
|
+
export function useProductsForListing({
|
|
7
|
+
params = {
|
|
8
|
+
includeSoldOut: false,
|
|
9
|
+
includeSellableForFree: false,
|
|
10
|
+
pricePromotionKey: "",
|
|
11
|
+
sort: {}
|
|
12
|
+
},
|
|
13
|
+
fetchingKey,
|
|
14
|
+
fetchingOptions
|
|
15
|
+
} = {}) {
|
|
16
|
+
const productsData = useProducts({
|
|
17
|
+
params: () => ({
|
|
18
|
+
// NOTE: We don't want to include trackSearchAnalyticsEvent parameter when paginating,
|
|
19
|
+
// as this logs the search term on every page request.
|
|
20
|
+
// Instead, we log the term only once, when the search is first performed.
|
|
21
|
+
// See: https://scayle.dev/en/developer-guide/products/search#scayle-search-analytics
|
|
22
|
+
trackSearchAnalyticsEvent: !!(toValue(params).page === 1 && toValue(params)?.where?.term),
|
|
23
|
+
...toValue(params)
|
|
24
|
+
}),
|
|
25
|
+
options: fetchingOptions
|
|
26
|
+
}, fetchingKey || `${toValue(params).categoryId ?? 0}-products`);
|
|
27
|
+
const products = computed(() => {
|
|
28
|
+
return productsData.data.value?.products ?? [];
|
|
29
|
+
});
|
|
30
|
+
const pagination = computed(() => {
|
|
31
|
+
return productsData.data.value?.pagination;
|
|
32
|
+
});
|
|
33
|
+
const totalProductsCount = computed(() => {
|
|
34
|
+
return productsData.data.value?.pagination?.total ?? 0;
|
|
35
|
+
});
|
|
36
|
+
const paginationOffset = computed(() => {
|
|
37
|
+
const page = pagination.value?.page ?? 1;
|
|
38
|
+
const perPage = pagination.value?.perPage ?? toValue(params).perPage ?? 0;
|
|
39
|
+
return (page - 1) * perPage;
|
|
40
|
+
});
|
|
41
|
+
return extendPromise(
|
|
42
|
+
productsData.then(() => ({})),
|
|
43
|
+
{
|
|
44
|
+
error: productsData.error,
|
|
45
|
+
status: productsData.status,
|
|
46
|
+
products,
|
|
47
|
+
pagination,
|
|
48
|
+
totalProductsCount,
|
|
49
|
+
paginationOffset
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './composables/useProductListSort.js';
|
|
2
2
|
export * from './composables/useAppliedFilters.js';
|
|
3
|
-
export * from './composables/
|
|
3
|
+
export * from './composables/useFiltersForListing.js';
|
|
4
4
|
export * from './composables/useProductListingSeoData.js';
|
|
5
|
-
export * from './composables/
|
|
5
|
+
export * from './composables/useProductsForListing.js';
|
|
6
6
|
export * from './utils/filters.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./composables/useProductListSort.js";
|
|
2
2
|
export * from "./composables/useAppliedFilters.js";
|
|
3
|
-
export * from "./composables/
|
|
3
|
+
export * from "./composables/useFiltersForListing.js";
|
|
4
4
|
export * from "./composables/useProductListingSeoData.js";
|
|
5
|
-
export * from "./composables/
|
|
5
|
+
export * from "./composables/useProductsForListing.js";
|
|
6
6
|
export * from "./utils/filters.js";
|
|
@@ -2,8 +2,6 @@ import type { LocationQuery, RouteLocationNormalizedLoadedGeneric } from 'vue-ro
|
|
|
2
2
|
import type { ProductSearchQuery } from '@scayle/storefront-api';
|
|
3
3
|
import type { RangeTuple } from '~/src';
|
|
4
4
|
export declare const parseFilterDataFromRoute: (route: RouteLocationNormalizedLoadedGeneric, filtersPrefix: string) => ProductSearchQuery;
|
|
5
|
-
export declare const getClearedFilterQueryByKey: (route: RouteLocationNormalizedLoadedGeneric, key: string) => LocationQuery | undefined;
|
|
6
|
-
export declare const getNewQueryFilters: (route: RouteLocationNormalizedLoadedGeneric, filter: LocationQuery) => LocationQuery;
|
|
7
5
|
export declare const createNewAttributeQuery: (route: RouteLocationNormalizedLoadedGeneric, appliedFilter: ProductSearchQuery, { slug, id }: {
|
|
8
6
|
slug: string;
|
|
9
7
|
id: number;
|
|
@@ -13,4 +11,4 @@ export declare const createNewBoolAttributeQuery: (route: RouteLocationNormalize
|
|
|
13
11
|
slug: string;
|
|
14
12
|
value: boolean;
|
|
15
13
|
}) => LocationQuery;
|
|
16
|
-
export declare const
|
|
14
|
+
export declare const getClearedFilterQueryByKey: (route: RouteLocationNormalizedLoadedGeneric, key: string) => LocationQuery;
|
|
@@ -45,22 +45,6 @@ export const parseFilterDataFromRoute = (route, filtersPrefix) => {
|
|
|
45
45
|
});
|
|
46
46
|
return productSearchQuery;
|
|
47
47
|
};
|
|
48
|
-
export const getClearedFilterQueryByKey = (route, key) => {
|
|
49
|
-
const query = { ...route.query };
|
|
50
|
-
const filterKey = `filters[${key}]`;
|
|
51
|
-
if (!query[filterKey]) {
|
|
52
|
-
return query;
|
|
53
|
-
}
|
|
54
|
-
delete query[filterKey];
|
|
55
|
-
return query;
|
|
56
|
-
};
|
|
57
|
-
export const getNewQueryFilters = (route, filter) => {
|
|
58
|
-
return {
|
|
59
|
-
sort: route.query.sort,
|
|
60
|
-
term: route.query.term,
|
|
61
|
-
...filter
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
48
|
export const createNewAttributeQuery = (route, appliedFilter, { slug, id }) => {
|
|
65
49
|
const attributeFilterQuery = {};
|
|
66
50
|
const query = { ...route.query };
|
|
@@ -117,12 +101,24 @@ export const createNewBoolAttributeQuery = (route, appliedFilter, { slug, value
|
|
|
117
101
|
booleanFilterQuery[key] = String(value);
|
|
118
102
|
return { ...query, ...booleanFilterQuery };
|
|
119
103
|
};
|
|
120
|
-
|
|
104
|
+
const getClearedPriceQuery = (route) => {
|
|
121
105
|
const query = { ...route.query };
|
|
122
|
-
if (
|
|
123
|
-
|
|
106
|
+
if (query["filters[maxPrice]"]) {
|
|
107
|
+
delete query["filters[maxPrice]"];
|
|
108
|
+
}
|
|
109
|
+
if (query["filters[minPrice]"]) {
|
|
110
|
+
delete query["filters[minPrice]"];
|
|
111
|
+
}
|
|
112
|
+
return query;
|
|
113
|
+
};
|
|
114
|
+
export const getClearedFilterQueryByKey = (route, key) => {
|
|
115
|
+
if (key === "prices") {
|
|
116
|
+
return getClearedPriceQuery(route);
|
|
117
|
+
}
|
|
118
|
+
const query = { ...route.query };
|
|
119
|
+
const filterKey = `filters[${key}]`;
|
|
120
|
+
if (query[filterKey]) {
|
|
121
|
+
delete query[filterKey];
|
|
124
122
|
}
|
|
125
|
-
delete query["filters[minPrice]"];
|
|
126
|
-
delete query["filters[maxPrice]"];
|
|
127
123
|
return query;
|
|
128
124
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scayle/storefront-product-listing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Collection of essential composables and utilities to work with product listing",
|
|
5
5
|
"author": "SCAYLE Commerce Engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"@nuxt/kit": "3.13.2",
|
|
56
56
|
"@nuxt/module-builder": "0.8.4",
|
|
57
57
|
"@nuxt/schema": "3.13.2",
|
|
58
|
-
"@nuxt/test-utils": "3.
|
|
58
|
+
"@nuxt/test-utils": "3.15.1",
|
|
59
59
|
"@scayle/eslint-config-storefront": "4.3.2",
|
|
60
|
-
"@scayle/storefront-nuxt": "8.
|
|
60
|
+
"@scayle/storefront-nuxt": "8.1.1",
|
|
61
61
|
"@types/node": "22.10.1",
|
|
62
62
|
"@vue/test-utils": "2.4.6",
|
|
63
63
|
"@vueuse/core": "12.0.0",
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { useFilters } from '@scayle/storefront-nuxt/composables';
|
|
2
|
-
import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
|
|
3
|
-
import type { LocationQuery, RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
|
4
|
-
import type { FilterItemWithValues } from '../../types/filter.js';
|
|
5
|
-
export type ProductListFilterOptions = Partial<{
|
|
6
|
-
immediate: boolean;
|
|
7
|
-
keyPrefix: string;
|
|
8
|
-
}>;
|
|
9
|
-
export type UseProductListFilterReturn = Pick<Awaited<ReturnType<typeof useFilters>>, 'error' | 'status' | 'refresh'> & {
|
|
10
|
-
availableFilters: ComputedRef<FilterItemWithValues[]>;
|
|
11
|
-
clearedPriceQuery: ComputedRef<LocationQuery | undefined>;
|
|
12
|
-
unfilteredCount: ComputedRef<number>;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Composable to manage filters based on current category ID and route query parameters.
|
|
16
|
-
*
|
|
17
|
-
* @param {RouteLocationNormalizedLoadedGeneric} [route] - Route object to handle the queries.
|
|
18
|
-
* @param {number} [currentCategoryId] - current/active category ID
|
|
19
|
-
* @param {ProductListFilterOptions} options - Configuration options for filters.
|
|
20
|
-
* @param {string} options.immediate - Will enable "immediate" flag via "useFilters" composable in it.
|
|
21
|
-
* @param {string} options.keyPrefix - The prefix used to construct "key" via "useFilters" payload. Default is "base".
|
|
22
|
-
*
|
|
23
|
-
* @returns {UseProductListFilterReturn} - An object containing reactive data, computed properties and async RPC call for managing filters:
|
|
24
|
-
* - `fetch`: call for fetching the filters.
|
|
25
|
-
* - `error`: returns "useFilters" error.
|
|
26
|
-
* - `status`: returns "useFilters" status.
|
|
27
|
-
* - `fetching`: returns "useFilters" fetching flag.
|
|
28
|
-
* - `availableFilters` {ComputedRef<FilterItemWithValues[]>}: returns computed available filters.
|
|
29
|
-
* - `unfilteredCount` {ComputedRef<number>}: returns unfiltered filters count.
|
|
30
|
-
* - `clearedPriceQuery` {ComputedRef<LocationQuery | undefined>}: constructs an empty query object.
|
|
31
|
-
*/
|
|
32
|
-
export declare function useProductListFilter(route: RouteLocationNormalizedLoadedGeneric, currentCategoryId?: MaybeRefOrGetter<number | undefined>, options?: ProductListFilterOptions): UseProductListFilterReturn & Promise<UseProductListFilterReturn>;
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
extendPromise
|
|
3
|
-
} from "@scayle/storefront-nuxt";
|
|
4
|
-
import { computed, toValue } from "vue";
|
|
5
|
-
import { useProducts } from "@scayle/storefront-nuxt/composables";
|
|
6
|
-
import { useProductListSort } from "./useProductListSort.js";
|
|
7
|
-
import { useAppliedFilters } from "./useAppliedFilters.js";
|
|
8
|
-
const PRODUCTS_PER_PAGE = 24;
|
|
9
|
-
const DEFAULT_CACHE_TTL = 1e3;
|
|
10
|
-
const DEFAULT_CACHE_KEY_PREFIX = "PLP";
|
|
11
|
-
export function useProductsByCategory(categoryId, route, {
|
|
12
|
-
params = {
|
|
13
|
-
includeSoldOut: false,
|
|
14
|
-
includeSellableForFree: false,
|
|
15
|
-
pricePromotionKey: ""
|
|
16
|
-
},
|
|
17
|
-
withParams,
|
|
18
|
-
cacheKeyPrefix = DEFAULT_CACHE_KEY_PREFIX,
|
|
19
|
-
fetchingKey,
|
|
20
|
-
fetchingOptions = {},
|
|
21
|
-
fetchProductsCacheTtl = DEFAULT_CACHE_TTL,
|
|
22
|
-
productsPerPage = PRODUCTS_PER_PAGE
|
|
23
|
-
} = {}) {
|
|
24
|
-
const { selectedSort } = useProductListSort(route);
|
|
25
|
-
const { appliedFilter } = useAppliedFilters(route);
|
|
26
|
-
const productsData = useProducts({
|
|
27
|
-
params: () => ({
|
|
28
|
-
...route?.query.page && { page: +route?.query.page },
|
|
29
|
-
sort: selectedSort.value,
|
|
30
|
-
perPage: route?.query.products_per_page ? parseInt(route?.query.products_per_page, 10) : productsPerPage,
|
|
31
|
-
with: withParams,
|
|
32
|
-
categoryId: toValue(categoryId),
|
|
33
|
-
includeSoldOut: params?.includeSoldOut,
|
|
34
|
-
includeSellableForFree: params?.includeSellableForFree,
|
|
35
|
-
pricePromotionKey: params?.pricePromotionKey,
|
|
36
|
-
orFiltersOperator: params?.orFiltersOperator,
|
|
37
|
-
cache: {
|
|
38
|
-
ttl: fetchProductsCacheTtl,
|
|
39
|
-
cacheKeyPrefix: cacheKeyPrefix || `PLP:${toValue(categoryId)}`
|
|
40
|
-
},
|
|
41
|
-
where: appliedFilter.value,
|
|
42
|
-
// NOTE: We don't want to include trackSearchAnalyticsEvent parameter when paginating,
|
|
43
|
-
// as this logs the search term on every page request.
|
|
44
|
-
// Instead, we log the term only once, when the search is first performed.
|
|
45
|
-
// See: https://scayle.dev/en/developer-guide/products/search#scayle-search-analytics
|
|
46
|
-
trackSearchAnalyticsEvent: !!(params?.page === 1 && appliedFilter.value.term),
|
|
47
|
-
...params
|
|
48
|
-
}),
|
|
49
|
-
options: fetchingOptions
|
|
50
|
-
}, fetchingKey || `${toValue(categoryId)}-products`);
|
|
51
|
-
const products = computed(() => {
|
|
52
|
-
return productsData.data.value?.products ?? [];
|
|
53
|
-
});
|
|
54
|
-
const pagination = computed(() => {
|
|
55
|
-
return productsData.data.value?.pagination;
|
|
56
|
-
});
|
|
57
|
-
const totalProductsCount = computed(() => {
|
|
58
|
-
return productsData.data.value?.pagination?.total ?? 0;
|
|
59
|
-
});
|
|
60
|
-
const paginationOffset = computed(() => {
|
|
61
|
-
const page = pagination.value?.page ?? 1;
|
|
62
|
-
const perPage = pagination.value?.perPage ?? productsPerPage ?? PRODUCTS_PER_PAGE;
|
|
63
|
-
return (page - 1) * perPage;
|
|
64
|
-
});
|
|
65
|
-
return extendPromise(
|
|
66
|
-
productsData.then(() => ({})),
|
|
67
|
-
{
|
|
68
|
-
error: productsData.error,
|
|
69
|
-
status: productsData.status,
|
|
70
|
-
products,
|
|
71
|
-
pagination,
|
|
72
|
-
totalProductsCount,
|
|
73
|
-
paginationOffset
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
}
|