@graphcommerce/magento-product 8.0.6-canary.4 → 8.0.6

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 CHANGED
@@ -1,32 +1,6 @@
1
1
  # Change Log
2
2
 
3
- ## 8.0.6-canary.4
4
-
5
- ### Patch Changes
6
-
7
- - [#2227](https://github.com/graphcommerce-org/graphcommerce/pull/2227) [`d597719`](https://github.com/graphcommerce-org/graphcommerce/commit/d597719baaabbe079660ac063fd021d871831511) - Added option to change sort order (ASC / DESC) for sort options (Name, price, position etc) on catalog and search pages.
8
- ([@FrankHarland](https://github.com/FrankHarland))
9
-
10
- ## 8.0.6-canary.3
11
-
12
- ## 8.0.6-canary.2
13
-
14
- ### Patch Changes
15
-
16
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`0767bc4`](https://github.com/graphcommerce-org/graphcommerce/commit/0767bc40f7b596209f24ca4e745ff0441f3275c9) - Upgrade input components to no longer use muiRegister, which improves INP scores
17
- ([@FrankHarland](https://github.com/FrankHarland))
18
-
19
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Prevent BillingPage query from rerunning on each mutation
20
- ([@FrankHarland](https://github.com/FrankHarland))
21
-
22
- ## 8.0.6-canary.1
23
-
24
- ## 8.0.6-canary.0
25
-
26
- ### Patch Changes
27
-
28
- - [#2196](https://github.com/graphcommerce-org/graphcommerce/pull/2196) [`84c50e4`](https://github.com/graphcommerce-org/graphcommerce/commit/84c50e49a1a7f154d4a8f4045c37e773e20283ad) - Allow Lingui to use linguiLocale with country identifiers like `en-us`, it would always load `en` in this case. Introced a new `useLocale` hook to use the correct locale string to use in Intl methods.
29
- ([@paales](https://github.com/paales))
3
+ ## 8.0.6
30
4
 
31
5
  ## 8.0.5
32
6
 
@@ -9,9 +9,9 @@ import {
9
9
  MessageSnackbar,
10
10
  MessageSnackbarProps,
11
11
  nonNullable,
12
- useLocale,
13
12
  } from '@graphcommerce/next-ui'
14
13
  import { Trans } from '@lingui/react'
14
+ import { useRouter } from 'next/router'
15
15
  import { useMemo } from 'react'
16
16
  import { findAddedItems } from './findAddedItems'
17
17
  import { toUserErrors } from './toUserErrors'
@@ -26,8 +26,9 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
26
26
  const { errorSnackbar, successSnackbar } = props
27
27
  const { error, data, redirect, control, submittedVariables } = useFormAddProductsToCart()
28
28
  const formState = useFormState({ control })
29
+ const { locale } = useRouter()
29
30
 
30
- const formatter = new Intl.ListFormat(useLocale(), { style: 'long', type: 'conjunction' })
31
+ const formatter = new Intl.ListFormat(locale, { style: 'long', type: 'conjunction' })
31
32
 
32
33
  const userErrors = toUserErrors(data)
33
34
 
@@ -1,6 +1,6 @@
1
1
  import { useFormState } from '@graphcommerce/ecommerce-ui'
2
2
  import { useEventCallback } from '@mui/material'
3
- import { startTransition, useEffect, useState } from 'react'
3
+ import { useEffect, useState } from 'react'
4
4
  import { UseAddProductsToCartActionFragment } from './UseAddProductsToCartAction.gql'
5
5
  import { toUserErrors } from './toUserErrors'
6
6
  import { AddToCartItemSelector, useFormAddProductsToCart } from './useFormAddProductsToCart'
@@ -69,9 +69,7 @@ export function useAddProductsToCartAction(
69
69
  if (!sku) console.warn(`You must provide a 'sku' to useAddProductsToCartAction`)
70
70
  }
71
71
  setValue(`cartItems.${index}.sku`, sku ?? '')
72
- startTransition(() => {
73
- onClickIncoming?.(e)
74
- })
72
+ onClickIncoming?.(e)
75
73
  }),
76
74
  onMouseDown: useEventCallback((e) => e.stopPropagation()),
77
75
  showSuccess,
@@ -2,10 +2,11 @@ fragment JsonLdProductOffer on ProductInterface {
2
2
  price_range {
3
3
  minimum_price {
4
4
  regular_price {
5
- ...Money
5
+ value
6
+ currency
6
7
  }
7
8
  final_price {
8
- ...Money
9
+ value
9
10
  }
10
11
  }
11
12
  }
@@ -1,10 +1,12 @@
1
1
  import { JsonLd } from '@graphcommerce/next-ui'
2
2
  import { JsonLdProductFragment } from './JsonLdProduct.gql'
3
3
 
4
- export function ProductPageJsonLd<
5
- T extends { '@type': string },
6
- P extends JsonLdProductFragment,
7
- >(props: { product: P; render: (product: P) => T & { '@context': 'https://schema.org' } }) {
4
+ type ProductPageJsonLdProps<T extends { '@type': string }> = {
5
+ product: JsonLdProductFragment
6
+ render: (product: JsonLdProductFragment) => T & { '@context': 'https://schema.org' }
7
+ }
8
+
9
+ export function ProductPageJsonLd<T extends { '@type': string }>(props: ProductPageJsonLdProps<T>) {
8
10
  const { product, render } = props
9
11
  return <JsonLd<T> item={render(product)} />
10
12
  }
@@ -1,10 +1,10 @@
1
- import { NumberFieldElement } from '@graphcommerce/ecommerce-ui'
2
1
  import type { ProductInterface } from '@graphcommerce/graphql-mesh'
3
2
  import { ApolloCartErrorAlert, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
4
3
  import { Money, MoneyProps } from '@graphcommerce/magento-store'
5
4
  import {
6
5
  Button,
7
6
  MessageSnackbar,
7
+ TextInputNumber,
8
8
  iconChevronRight,
9
9
  IconSvg,
10
10
  extendableComponent,
@@ -40,7 +40,7 @@ export function ProductAddToCart(
40
40
  defaultValues: { ...variables },
41
41
  })
42
42
 
43
- const { handleSubmit, formState, error, control, required, data } = form
43
+ const { handleSubmit, formState, error, muiRegister, required, data } = form
44
44
  const submitHandler = handleSubmit(() => {})
45
45
 
46
46
  return (
@@ -58,17 +58,15 @@ export function ProductAddToCart(
58
58
  <Money {...price} />
59
59
  </Typography>
60
60
 
61
- <NumberFieldElement
61
+ <TextInputNumber
62
62
  variant='outlined'
63
63
  error={formState.isSubmitted && !!formState.errors.quantity}
64
64
  required={required.quantity}
65
65
  inputProps={{ min: 1 }}
66
- name='quantity'
67
- rules={{ required: required.quantity }}
66
+ {...muiRegister('quantity', { required: required.quantity })}
68
67
  helperText={formState.isSubmitted && formState.errors.quantity?.message}
69
68
  disabled={formState.isSubmitting}
70
69
  size='small'
71
- control={control}
72
70
  />
73
71
  {children}
74
72
  <Box
@@ -1,14 +1,14 @@
1
1
  import { useForm, UseFormProps, UseFormReturn } from '@graphcommerce/ecommerce-ui'
2
2
  import { useMemoObject } from '@graphcommerce/next-ui'
3
3
  import { useEventCallback } from '@mui/material'
4
- import { useRouter } from 'next/router'
5
4
  import React, { BaseSyntheticEvent, createContext, useContext, useMemo } from 'react'
6
- import { productListLinkFromFilter } from '../../hooks/useProductListLink'
5
+ import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
7
6
  import { ProductListFiltersFragment } from '../ProductListFilters/ProductListFilters.gql'
8
7
  import {
9
8
  ProductFilterParams,
10
9
  ProductListParams,
11
10
  toFilterParams,
11
+ toProductListParams,
12
12
  } from '../ProductListItems/filterTypes'
13
13
 
14
14
  type DataProps = {
@@ -50,18 +50,11 @@ export function ProductFiltersPro(props: FilterFormProviderProps) {
50
50
  const defaultValues = useMemoObject(toFilterParams(params))
51
51
  const form = useForm<ProductFilterParams>({ defaultValues, ...formProps })
52
52
 
53
- const router = useRouter()
54
-
53
+ const push = useProductListLinkReplace({ scroll: false })
55
54
  const submit = useEventCallback(
56
- form.handleSubmit(async (formValues) => {
57
- const queryUrl = router.query.url ?? []
58
- const comingFromURLWithoutFilters = !queryUrl.includes('q')
59
- const path = productListLinkFromFilter({ ...formValues, currentPage: 1 })
60
-
61
- if (router.asPath === path) return false
62
- if (comingFromURLWithoutFilters) return router.push(path, path)
63
- return router.replace(path, path)
64
- }),
55
+ form.handleSubmit(async (formValues) =>
56
+ push({ ...toProductListParams(formValues), currentPage: 1 }),
57
+ ),
65
58
  )
66
59
 
67
60
  const filterFormContext: FilterFormContextProps = useMemo(
@@ -29,7 +29,7 @@ const defaultRenderer = {
29
29
  }
30
30
 
31
31
  export function ProductFiltersProAllFiltersChip(props: ProductFiltersProAllFiltersChipProps) {
32
- const { sort_fields, total_count, renderer, category, ...rest } = props
32
+ const { sort_fields, total_count, renderer, ...rest } = props
33
33
 
34
34
  const { submit, params, aggregations, appliedAggregations } = useProductFiltersPro()
35
35
  const { sort } = params
@@ -59,11 +59,7 @@ export function ProductFiltersProAllFiltersChip(props: ProductFiltersProAllFilte
59
59
  >
60
60
  {() => (
61
61
  <>
62
- <ProductFiltersProSortSection
63
- sort_fields={sort_fields}
64
- total_count={total_count}
65
- category={category}
66
- />
62
+ <ProductFiltersProSortSection sort_fields={sort_fields} total_count={total_count} />
67
63
  <ProductFiltersProLimitSection />
68
64
  <ProductFiltersProAggregations renderer={{ ...defaultRenderer, ...renderer }} />
69
65
  </>
@@ -20,15 +20,11 @@ const defaultRenderer = {
20
20
  }
21
21
 
22
22
  export function ProductFiltersProAllFiltersSidebar(props: ProductFiltersProAllFiltersSidebarProps) {
23
- const { sort_fields, total_count, renderer, sx = [], category } = props
23
+ const { sort_fields, total_count, renderer, sx = [] } = props
24
24
 
25
25
  return (
26
26
  <Box sx={[{ display: { xs: 'none', md: 'grid' } }, ...(Array.isArray(sx) ? sx : [sx])]}>
27
- <ProductFiltersProSortSection
28
- sort_fields={sort_fields}
29
- total_count={total_count}
30
- category={category}
31
- />
27
+ <ProductFiltersProSortSection sort_fields={sort_fields} total_count={total_count} />
32
28
  <ProductFiltersProLimitSection />
33
29
  <ProductFiltersProAggregations renderer={{ ...defaultRenderer, ...renderer }} />
34
30
  </Box>
@@ -1,34 +1,53 @@
1
+ import { useWatch } from '@graphcommerce/ecommerce-ui'
2
+ import { useQuery } from '@graphcommerce/graphql'
3
+ import { StoreConfigDocument } from '@graphcommerce/magento-store'
1
4
  import {
2
5
  ActionCard,
3
6
  ActionCardListForm,
4
7
  ChipOverlayOrPopper,
5
8
  ChipOverlayOrPopperProps,
9
+ filterNonNullableKeys,
6
10
  } from '@graphcommerce/next-ui'
7
11
  import { Trans } from '@lingui/react'
12
+ import { useMemo } from 'react'
13
+ import { ProductListSortFragment } from '../ProductListSort/ProductListSort.gql'
8
14
  import { useProductFiltersPro } from './ProductFiltersPro'
9
- import { UseProductFiltersProSortProps, useProductFiltersProSort } from './useProductFiltersProSort'
10
15
 
11
- export type ProductListActionSortProps = UseProductFiltersProSortProps &
16
+ export type ProductListActionSortProps = ProductListSortFragment &
12
17
  Omit<
13
18
  ChipOverlayOrPopperProps,
14
19
  'label' | 'selected' | 'selectedLabel' | 'onApply' | 'onReset' | 'onClose' | 'children'
15
20
  >
16
21
 
17
22
  export function ProductFiltersProSortChip(props: ProductListActionSortProps) {
18
- const { sort_fields, chipProps, category, ...rest } = props
19
- const { submit, form } = useProductFiltersPro()
20
- const { options, showReset, selected, selectedLabel } = useProductFiltersProSort(props)
23
+ const { sort_fields, chipProps, ...rest } = props
24
+ const { params, form, submit } = useProductFiltersPro()
25
+ const { control } = form
26
+ const activeSort = useWatch({ control, name: 'sort' })
27
+
28
+ const { data: storeConfigQuery } = useQuery(StoreConfigDocument)
29
+ const defaultSort = storeConfigQuery?.storeConfig?.catalog_default_sort_by
30
+
31
+ const options = useMemo(
32
+ () =>
33
+ filterNonNullableKeys(sort_fields?.options, ['value', 'label']).map((option) => ({
34
+ ...option,
35
+ value: option.value === defaultSort ? null : option.value,
36
+ title: option.label,
37
+ })),
38
+ [defaultSort, sort_fields?.options],
39
+ )
21
40
 
22
41
  return (
23
42
  <ChipOverlayOrPopper
24
43
  {...rest}
25
44
  overlayProps={{ sizeSm: 'minimal', sizeMd: 'minimal', ...rest.overlayProps }}
26
45
  label={<Trans id='Sort By' />}
27
- selected={selected}
28
- selectedLabel={selectedLabel}
46
+ selected={Boolean(params.sort)}
47
+ selectedLabel={options.find((option) => option.value === params.sort)?.label}
29
48
  onApply={submit}
30
49
  onReset={
31
- showReset
50
+ activeSort
32
51
  ? () => {
33
52
  form.setValue('sort', null)
34
53
  form.setValue('dir', null)
@@ -41,7 +60,7 @@ export function ProductFiltersProSortChip(props: ProductListActionSortProps) {
41
60
  >
42
61
  {() => (
43
62
  <ActionCardListForm
44
- control={form.control}
63
+ control={control}
45
64
  name='sort'
46
65
  layout='list'
47
66
  variant='default'
@@ -1,21 +1,46 @@
1
- import { ActionCard, ActionCardAccordion, ActionCardListForm, Button } from '@graphcommerce/next-ui'
1
+ import { useWatch } from '@graphcommerce/ecommerce-ui'
2
+ import { useQuery } from '@graphcommerce/graphql'
3
+ import { StoreConfigDocument } from '@graphcommerce/magento-store'
4
+ import {
5
+ ActionCard,
6
+ ActionCardAccordion,
7
+ ActionCardListForm,
8
+ Button,
9
+ filterNonNullableKeys,
10
+ } from '@graphcommerce/next-ui'
2
11
  import { Trans } from '@lingui/react'
12
+ import { useMemo } from 'react'
13
+ import { ProductListSortFragment } from '../ProductListSort/ProductListSort.gql'
3
14
  import { useProductFiltersPro } from './ProductFiltersPro'
4
- import { UseProductFiltersProSortProps, useProductFiltersProSort } from './useProductFiltersProSort'
5
15
 
6
- export type ProductFiltersProSortSectionProps = UseProductFiltersProSortProps
16
+ export type ProductFiltersProSortSectionProps = ProductListSortFragment
7
17
 
8
18
  export function ProductFiltersProSortSection(props: ProductFiltersProSortSectionProps) {
19
+ const { sort_fields } = props
9
20
  const { form } = useProductFiltersPro()
10
- const { options, showReset, selected } = useProductFiltersProSort(props)
21
+ const { control } = form
22
+ const activeSort = useWatch({ control, name: 'sort' })
23
+
24
+ const { data: storeConfigQuery } = useQuery(StoreConfigDocument)
25
+ const defaultSort = storeConfigQuery?.storeConfig?.catalog_default_sort_by
26
+
27
+ const options = useMemo(
28
+ () =>
29
+ filterNonNullableKeys(sort_fields?.options, ['value', 'label']).map((option) => ({
30
+ ...option,
31
+ value: option.value === defaultSort ? null : option.value,
32
+ title: option.label,
33
+ })),
34
+ [defaultSort, sort_fields?.options],
35
+ )
11
36
 
12
37
  return (
13
38
  <ActionCardAccordion
14
- defaultExpanded={selected}
39
+ defaultExpanded={!!activeSort}
15
40
  summary={<Trans id='Sort By' />}
16
41
  details={
17
42
  <ActionCardListForm
18
- control={form.control}
43
+ control={control}
19
44
  name='sort'
20
45
  layout='list'
21
46
  variant='default'
@@ -25,7 +50,7 @@ export function ProductFiltersProSortSection(props: ProductFiltersProSortSection
25
50
  />
26
51
  }
27
52
  right={
28
- showReset ? (
53
+ activeSort ? (
29
54
  <Button
30
55
  color='primary'
31
56
  onClick={(e) => {
@@ -31,7 +31,7 @@ export type ProductFilterParams = {
31
31
 
32
32
  export function toFilterParams(params: ProductListParams): ProductFilterParams {
33
33
  const [sortKey] = Object.keys(params.sort) as [keyof ProductAttributeSortInput]
34
- const dir = params.sort[sortKey]?.toUpperCase() as SortEnum | undefined
34
+ const dir = params.sort[sortKey] as SortEnum | undefined
35
35
 
36
36
  return {
37
37
  ...params,
@@ -35,7 +35,7 @@ export function parseParams(
35
35
  }
36
36
  if (param === 'dir') {
37
37
  const [sortBy] = Object.keys(categoryVariables.sort)
38
- if (sortBy) categoryVariables.sort[sortBy] = value?.toUpperCase() as SortEnum
38
+ if (sortBy) categoryVariables.sort[sortBy] = value as SortEnum
39
39
  return undefined
40
40
  }
41
41
 
@@ -13,8 +13,6 @@ export * from './ProductListItems/filterTypes'
13
13
  export * from './ProductListItems/getFilterTypes'
14
14
  export * from './ProductListItems/ProductListItems.gql'
15
15
  export * from './ProductListItems/ProductListItemsBase'
16
- export * from './ProductListItems/productListApplyCategoryDefaults'
17
- export * from './ProductListItems/CategoryDefault.gql'
18
16
  export * from './ProductListItems/ProductListParamsProvider'
19
17
  export * from './ProductListItems/renderer'
20
18
  export * from './ProductListLink/ProductListLink'
@@ -2,13 +2,11 @@ import {
2
2
  isFilterTypeEqual,
3
3
  isFilterTypeMatch,
4
4
  isFilterTypeRange,
5
- ProductFilterParams,
6
5
  ProductListParams,
7
- toFilterParams,
8
6
  } from '../components/ProductListItems/filterTypes'
9
7
 
10
- export function productListLinkFromFilter(props: ProductFilterParams): string {
11
- const { url, sort, dir, currentPage, pageSize, filters: incoming } = props
8
+ export function productListLink(props: ProductListParams): string {
9
+ const { url, sort, currentPage, pageSize, filters: incoming } = props
12
10
  const isSearch = url.startsWith('search')
13
11
  const filters = isSearch ? incoming : { ...incoming, category_uid: undefined }
14
12
  const uid = incoming?.category_uid?.eq || incoming?.category_uid?.in?.[0]
@@ -21,8 +19,9 @@ export function productListLinkFromFilter(props: ProductFilterParams): string {
21
19
 
22
20
  // todo(paales): How should the URL look like with multiple sorts?
23
21
  // Something like: /sort/position,price/dir/asc,asc
24
- if (sort) query += `/sort/${sort}`
25
- if (dir) query += `/dir/desc`
22
+ const [sortBy] = Object.keys(sort)
23
+ if (sort && sortBy) query += `/sort/${sortBy}`
24
+ if (sort && sortBy && sort[sortBy] && sort[sortBy] === 'DESC') query += `/dir/desc`
26
25
  if (pageSize) query += `/page-size/${pageSize}`
27
26
 
28
27
  // Apply filters
@@ -43,10 +42,6 @@ export function productListLinkFromFilter(props: ProductFilterParams): string {
43
42
  return query ? `/${url}${paginateSort}/q${query}` : `/${url}${paginateSort}`
44
43
  }
45
44
 
46
- export function productListLink(props: ProductListParams): string {
47
- return productListLinkFromFilter(toFilterParams(props))
48
- }
49
-
50
45
  export function useProductListLink(props: ProductListParams): string {
51
46
  return productListLink({ ...props, url: `${props.url}` })
52
47
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/magento-product",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "8.0.6-canary.4",
5
+ "version": "8.0.6",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -18,18 +18,18 @@
18
18
  "typescript": "5.3.3"
19
19
  },
20
20
  "peerDependencies": {
21
- "@graphcommerce/ecommerce-ui": "^8.0.6-canary.4",
22
- "@graphcommerce/eslint-config-pwa": "^8.0.6-canary.4",
23
- "@graphcommerce/framer-next-pages": "^8.0.6-canary.4",
24
- "@graphcommerce/framer-scroller": "^8.0.6-canary.4",
25
- "@graphcommerce/graphql": "^8.0.6-canary.4",
26
- "@graphcommerce/graphql-mesh": "^8.0.6-canary.4",
27
- "@graphcommerce/image": "^8.0.6-canary.4",
28
- "@graphcommerce/magento-cart": "^8.0.6-canary.4",
29
- "@graphcommerce/magento-store": "^8.0.6-canary.4",
30
- "@graphcommerce/next-ui": "^8.0.6-canary.4",
31
- "@graphcommerce/prettier-config-pwa": "^8.0.6-canary.4",
32
- "@graphcommerce/typescript-config-pwa": "^8.0.6-canary.4",
21
+ "@graphcommerce/ecommerce-ui": "^8.0.6",
22
+ "@graphcommerce/eslint-config-pwa": "^8.0.6",
23
+ "@graphcommerce/framer-next-pages": "^8.0.6",
24
+ "@graphcommerce/framer-scroller": "^8.0.6",
25
+ "@graphcommerce/graphql": "^8.0.6",
26
+ "@graphcommerce/graphql-mesh": "^8.0.6",
27
+ "@graphcommerce/image": "^8.0.6",
28
+ "@graphcommerce/magento-cart": "^8.0.6",
29
+ "@graphcommerce/magento-store": "^8.0.6",
30
+ "@graphcommerce/next-ui": "^8.0.6",
31
+ "@graphcommerce/prettier-config-pwa": "^8.0.6",
32
+ "@graphcommerce/typescript-config-pwa": "^8.0.6",
33
33
  "@lingui/core": "^4.2.1",
34
34
  "@lingui/macro": "^4.2.1",
35
35
  "@lingui/react": "^4.2.1",
@@ -1,17 +0,0 @@
1
- import { SortEnum } from '@graphcommerce/graphql-mesh'
2
- import { IconSvg } from '@graphcommerce/next-ui'
3
- import * as IconArrowDown from '@graphcommerce/next-ui/icons/arrow-down.svg'
4
- import * as IconArrowUp from '@graphcommerce/next-ui/icons/arrow-up.svg'
5
-
6
- type Props = {
7
- sortDirection: SortEnum | null
8
- }
9
-
10
- export function ProductFiltersProSortDirectionArrow({ sortDirection }: Props) {
11
- return (
12
- <IconSvg
13
- src={sortDirection === 'ASC' || sortDirection === null ? IconArrowUp : IconArrowDown}
14
- sx={{ display: 'flex' }}
15
- />
16
- )
17
- }
@@ -1,74 +0,0 @@
1
- import { useWatch } from '@graphcommerce/ecommerce-ui'
2
- import { useQuery } from '@graphcommerce/graphql'
3
- import { StoreConfigDocument } from '@graphcommerce/magento-store'
4
- import { filterNonNullableKeys } from '@graphcommerce/next-ui'
5
- import { i18n } from '@lingui/core'
6
- import { useMemo } from 'react'
7
- import { CategoryDefaultFragment } from '../ProductListItems/CategoryDefault.gql'
8
- import { ProductFilterParams } from '../ProductListItems/filterTypes'
9
- import { ProductListSortFragment } from '../ProductListSort'
10
- import { useProductFiltersPro } from './ProductFiltersPro'
11
- import type { ProductListActionSortProps } from './ProductFiltersProSortChip'
12
- import { ProductFiltersProSortDirectionArrow } from './ProductFiltersProSortDirectionArrow'
13
-
14
- const exclude = ['relevance', 'position']
15
-
16
- export type UseProductFiltersProSortProps = ProductListSortFragment & {
17
- category?: CategoryDefaultFragment
18
- }
19
-
20
- export function useProductFiltersProSort(props: ProductListActionSortProps) {
21
- const { sort_fields, category } = props
22
-
23
- const { params, form } = useProductFiltersPro()
24
- const { control, setValue } = form
25
-
26
- const sortFields = useMemo(
27
- () =>
28
- filterNonNullableKeys(sort_fields?.options).map((o) =>
29
- !category?.uid && o.value === 'position'
30
- ? { value: 'relevance', label: i18n._('Relevance') }
31
- : o,
32
- ),
33
- [category?.uid, sort_fields?.options],
34
- )
35
- const availableSortBy = category?.available_sort_by ?? sortFields.map((o) => o.value)
36
-
37
- const conf = useQuery(StoreConfigDocument).data?.storeConfig
38
- const defaultSortBy = (
39
- category ? category.default_sort_by ?? conf?.catalog_default_sort_by ?? 'position' : 'relevance'
40
- ) as ProductFilterParams['sort']
41
-
42
- const formSort = useWatch({ control, name: 'sort' })
43
- const formDirection = useWatch({ control, name: 'dir' })
44
- const showReset = Boolean(formSort !== defaultSortBy || formDirection === 'DESC')
45
- const selected = Boolean(params.sort && (params.sort !== defaultSortBy || params.dir === 'DESC'))
46
-
47
- const options = useMemo(
48
- () =>
49
- sortFields
50
- .filter((o) => availableSortBy.includes(o.value))
51
- .map((option) => {
52
- const value = option.value === defaultSortBy ? null : option.value
53
- const showSort = formSort === value && !exclude.includes(option.value)
54
-
55
- return {
56
- ...option,
57
- value,
58
- title: option.label,
59
- ...(showSort && {
60
- onClick: () => setValue('dir', formDirection === 'DESC' ? null : 'DESC'),
61
- price: <ProductFiltersProSortDirectionArrow sortDirection={formDirection} />,
62
- }),
63
- }
64
- }),
65
- [sortFields, availableSortBy, defaultSortBy, formSort, formDirection, setValue],
66
- )
67
-
68
- return {
69
- options,
70
- selected,
71
- showReset,
72
- selectedLabel: options.find((option) => option.value === params.sort)?.label,
73
- }
74
- }
@@ -1,5 +0,0 @@
1
- fragment CategoryDefault on CategoryInterface {
2
- uid
3
- default_sort_by
4
- available_sort_by
5
- }
@@ -1,28 +0,0 @@
1
- import { StoreConfigQuery } from '@graphcommerce/magento-store'
2
- import { CategoryDefaultFragment } from './CategoryDefault.gql'
3
- import { ProductListParams } from './filterTypes'
4
- import { cloneDeep } from '@graphcommerce/graphql'
5
-
6
- export async function productListApplyCategoryDefaults(
7
- params: ProductListParams | undefined,
8
- conf: StoreConfigQuery,
9
- category: Promise<CategoryDefaultFragment | null | undefined>,
10
- ) {
11
- if (!params) return params
12
-
13
- const newParams = cloneDeep(params)
14
- if (!newParams.pageSize) newParams.pageSize = conf.storeConfig?.grid_per_page ?? 12
15
-
16
- if (Object.keys(params.sort).length === 0) {
17
- const categorySort = (await category)?.default_sort_by as keyof ProductListParams['sort']
18
- const defaultSort = conf.storeConfig?.catalog_default_sort_by as keyof ProductListParams['sort']
19
- if (categorySort) newParams.sort = { [categorySort]: 'ASC' }
20
- else if (defaultSort) newParams.sort = { [defaultSort]: 'ASC' }
21
- }
22
-
23
- if (!newParams.filters.category_uid?.in?.[0]) {
24
- newParams.filters.category_uid = { eq: (await category)?.uid }
25
- }
26
-
27
- return newParams
28
- }