@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 CHANGED
@@ -1,56 +1,11 @@
1
- import { CentAmount, BreadcrumbItem } from '@scayle/storefront-nuxt';
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 AttributesFilterItemWithValues, type AttributesFilterValue, type BooleanFilterItemWithValues, type BooleanFilterValue, type FilterItemWithValues, type RangeFilterItemWithValues, type RangeFilterValue, type RangeTuple, breadcrumbsFactory, filtersFactory };
11
+ export { type RangeTuple, breadcrumbsFactory, filtersFactory };
package/dist/index.d.ts CHANGED
@@ -1,56 +1,11 @@
1
- import { CentAmount, BreadcrumbItem } from '@scayle/storefront-nuxt';
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 AttributesFilterItemWithValues, type AttributesFilterValue, type BooleanFilterItemWithValues, type BooleanFilterValue, type FilterItemWithValues, type RangeFilterItemWithValues, type RangeFilterValue, type RangeTuple, breadcrumbsFactory, filtersFactory };
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: "attributes",
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: "boolean"
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: "range"
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
- import { getClearedPriceQuery } from "../utils/filters.js";
5
- import { useAppliedFilters } from "./useAppliedFilters.js";
6
- export function useProductListFilter(route, currentCategoryId, options = {}) {
7
- const { immediate = true, keyPrefix = "base" } = options;
8
- const { appliedFilter } = useAppliedFilters(route);
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: computed(() => ({
11
- categoryId: toValue(currentCategoryId),
12
- where: {
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 unfilteredCount = computed(() => {
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
- unfilteredCount,
53
- clearedPriceQuery
44
+ filteredProductCount
54
45
  });
55
46
  }
@@ -1,12 +1,11 @@
1
- import { type ComputedRef, type Ref } from 'vue';
2
- import type { BreadcrumbItem, Category } from '@scayle/storefront-nuxt';
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 {Ref<Category>} category - Reactive category
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 {UseProductListingSeoData} An object containing reactive data and computed properties for managing SEO data.
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(category: Ref<Category | undefined | null>, breadcrumbs: BreadcrumbItem[], route: RouteLocationNormalizedLoadedGeneric, { baseUrl, fullPath }: UrlParams): UseProductListingSeoDataReturn;
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(category, breadcrumbs, route, { baseUrl, fullPath }) {
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 category.value ? breadcrumbs.map(({ value }) => value).join(" - ") : "";
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, type ProductWith } from '@scayle/storefront-nuxt';
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
- import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
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 ProductsByCategoryOptions {
13
- params?: Omit<Partial<FetchProductsByCategoryParams>, 'category' | 'categoryId'>;
14
- fetchingOptions?: Partial<{
11
+ export interface ProductsForListingOptions {
12
+ params: MaybeRefOrGetter<Omit<FetchProductsByCategoryParams, 'category'>>;
13
+ fetchingOptions: Partial<{
15
14
  lazy: boolean;
16
15
  immediate: boolean;
17
16
  }>;
18
- productsPerPage?: number;
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 {MaybeRefOrGetter<number>} categoryId - The ID of the category to fetch products for. Accepts a ref, computed, or static number.
28
- * @param {RouteLocationNormalizedLoadedGeneric} [route] - Route object to handle the queries.
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 useProductsByCategory(categoryId: MaybeRefOrGetter<number>, route: RouteLocationNormalizedLoadedGeneric, { params, withParams, cacheKeyPrefix, fetchingKey, fetchingOptions, fetchProductsCacheTtl, productsPerPage, }?: Partial<ProductsByCategoryOptions>): UseProductsByCategoryReturn & Promise<UseProductsByCategoryReturn>;
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
+ }
@@ -1,6 +1,6 @@
1
1
  export * from './composables/useProductListSort.js';
2
2
  export * from './composables/useAppliedFilters.js';
3
- export * from './composables/useProductListFilter.js';
3
+ export * from './composables/useFiltersForListing.js';
4
4
  export * from './composables/useProductListingSeoData.js';
5
- export * from './composables/useProductsByCategory.js';
5
+ export * from './composables/useProductsForListing.js';
6
6
  export * from './utils/filters.js';
@@ -1,6 +1,6 @@
1
1
  export * from "./composables/useProductListSort.js";
2
2
  export * from "./composables/useAppliedFilters.js";
3
- export * from "./composables/useProductListFilter.js";
3
+ export * from "./composables/useFiltersForListing.js";
4
4
  export * from "./composables/useProductListingSeoData.js";
5
- export * from "./composables/useProductsByCategory.js";
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 getClearedPriceQuery: (route: RouteLocationNormalizedLoadedGeneric) => LocationQuery | undefined;
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
- export const getClearedPriceQuery = (route) => {
104
+ const getClearedPriceQuery = (route) => {
121
105
  const query = { ...route.query };
122
- if (!query["filters[minPrice]"] && !query["filters[maxPrice]"]) {
123
- return;
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.5.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.14.4",
58
+ "@nuxt/test-utils": "3.15.1",
59
59
  "@scayle/eslint-config-storefront": "4.3.2",
60
- "@scayle/storefront-nuxt": "8.0.0",
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
- }