@graphcommerce/magento-product 9.1.0-canary.40 → 9.1.0-canary.41

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,5 +1,17 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.1.0-canary.41
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2530](https://github.com/graphcommerce-org/graphcommerce/pull/2530) [`2075f33`](https://github.com/graphcommerce-org/graphcommerce/commit/2075f331eec38e894722d8ba4539d865f2db5507) - Add support for `variant=unit` and `variant=total` rendering of `ProductPagePrice` to include the quantity or not. ([@paales](https://github.com/paales))
8
+
9
+ - [#2530](https://github.com/graphcommerce-org/graphcommerce/pull/2530) [`4c60c55`](https://github.com/graphcommerce-org/graphcommerce/commit/4c60c55a0e83a8635fb2e97622cecd981d894970) - Created a ProductPagePriceLowest component that switches when the configurable option changes. ([@paales](https://github.com/paales))
10
+
11
+ - [#2530](https://github.com/graphcommerce-org/graphcommerce/pull/2530) [`5900c8d`](https://github.com/graphcommerce-org/graphcommerce/commit/5900c8d56bc9a3e0e4c2c8e61d5ff219877fd9ec) - Solve issue where the tier price doesn't get divided by the quantity, thus showing the wrong price. ([@paales](https://github.com/paales))
12
+
13
+ - [#2530](https://github.com/graphcommerce-org/graphcommerce/pull/2530) [`f4a20a7`](https://github.com/graphcommerce-org/graphcommerce/commit/f4a20a7bb37701b779dfe7bd3073574eb6c1cab2) - Make sure the product price is updated when the quantity of a product is changed. ([@paales](https://github.com/paales))
14
+
3
15
  ## 9.1.0-canary.40
4
16
 
5
17
  ## 9.1.0-canary.39
@@ -42,7 +42,7 @@ export function findAddedItems(
42
42
  return foundItem
43
43
  })
44
44
  ) {
45
- // console.log("SKU matche, this isn't the configurable")
45
+ // console.log("SKU matched, this isn't the configurable")
46
46
  return false
47
47
  }
48
48
  }
@@ -18,23 +18,19 @@ export type ProductListPriceProps = ProductListPriceFragment &
18
18
  Pick<TypographyProps, 'sx'> & {
19
19
  prefix?: React.ReactNode
20
20
  suffix?: React.ReactNode
21
+ asNumber?: boolean
21
22
  }
22
23
 
23
24
  export function ProductListPrice(props: ProductListPriceProps) {
24
- const { regular_price, final_price, sx, prefix, suffix } = props
25
+ const { regular_price, final_price, sx, prefix, suffix, asNumber } = props
25
26
 
26
27
  return (
27
- <Box
28
- className={classes.root}
29
- sx={sxx({ typography: 'body1', display: 'inline-flex', columnGap: '0.3em' }, sx)}
30
- >
28
+ <Box className={classes.root} sx={sxx({ display: 'inline-flex', columnGap: '0.3em' }, sx)}>
31
29
  {prefix && (
32
30
  <PrivateQueryMask
33
31
  component='span'
34
- skeleton={{
35
- variant: 'text',
36
- sx: { width: '3.5em', transform: 'none' },
37
- }}
32
+ sx={{ '&:empty': { display: 'none' } }}
33
+ skeleton={{ variant: 'text', sx: { width: '2em', transform: 'none' } }}
38
34
  className={classes.prefix}
39
35
  >
40
36
  {prefix}
@@ -44,38 +40,27 @@ export function ProductListPrice(props: ProductListPriceProps) {
44
40
  {regular_price.value !== final_price.value && (
45
41
  <PrivateQueryMask
46
42
  component='span'
47
- sx={{
48
- textDecoration: 'line-through',
49
- color: 'text.disabled',
50
- }}
51
- skeleton={{
52
- variant: 'text',
53
- sx: { width: '3.5em', transform: 'none' },
54
- }}
43
+ sx={{ textDecoration: 'line-through', color: 'text.disabled' }}
44
+ skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
55
45
  className={classes.discountPrice}
56
46
  >
57
- <Money {...regular_price} />
47
+ <Money {...regular_price} asNumber={asNumber} />
58
48
  </PrivateQueryMask>
59
49
  )}
60
50
 
61
51
  <PrivateQueryMask
62
52
  className={classes.finalPrice}
63
53
  component='span'
64
- skeleton={{
65
- variant: 'text',
66
- sx: { width: '3.5em', transform: 'none' },
67
- }}
54
+ skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
68
55
  >
69
- <Money {...final_price} />
56
+ <Money {...final_price} asNumber={asNumber} />
70
57
  </PrivateQueryMask>
71
58
 
72
59
  {suffix && (
73
60
  <PrivateQueryMask
74
61
  component='span'
75
- skeleton={{
76
- variant: 'text',
77
- sx: { width: '3.5em', transform: 'none' },
78
- }}
62
+ sx={{ '&:empty': { display: 'none' } }}
63
+ skeleton={{ variant: 'text', sx: { width: '2em', transform: 'none' } }}
79
64
  className={classes.suffix}
80
65
  >
81
66
  {suffix}
@@ -15,6 +15,8 @@ export type ProductPagePriceProps = {
15
15
  prefix?: React.ReactNode
16
16
  suffix?: React.ReactNode
17
17
  sx?: SxProps<Theme>
18
+ variant?: 'item' | 'total'
19
+ asNumber?: boolean
18
20
  } & AddToCartItemSelector &
19
21
  UseCustomizableOptionPriceProps
20
22
 
@@ -26,15 +28,18 @@ const { classes } = extendableComponent('ProductPagePrice', [
26
28
  ] as const)
27
29
 
28
30
  export function ProductPagePrice(props: ProductPagePriceProps) {
29
- const { product, index = 0, prefix, suffix, sx } = props
31
+ const { product, index = 0, prefix, suffix, sx, variant = 'item', asNumber } = props
30
32
 
31
33
  const { control } = useFormAddProductsToCart()
32
34
  const quantity = useWatch({ control, name: `cartItems.${index}.quantity` })
33
35
  const price =
34
36
  getProductTierPrice(product, quantity) ?? product.price_range.minimum_price.final_price
35
37
 
36
- const priceValue = useCustomizableOptionPrice(props)
38
+ const priceValue = useCustomizableOptionPrice(props) ?? 0
39
+ const finalPriceValue = variant === 'total' ? priceValue * quantity : priceValue
37
40
  const regularPrice = product.price_range.minimum_price.regular_price
41
+ const regularPriceValue =
42
+ variant === 'total' ? (regularPrice.value ?? 0) * quantity : regularPrice.value
38
43
 
39
44
  return (
40
45
  <Box component='span' sx={sxx({ display: 'inline-flex', columnGap: '0.3em' }, sx)}>
@@ -42,10 +47,8 @@ export function ProductPagePrice(props: ProductPagePriceProps) {
42
47
  <PrivateQueryMask
43
48
  component='span'
44
49
  className={classes.prefix}
45
- skeleton={{
46
- variant: 'text',
47
- sx: { width: '3.5em', transform: 'none' },
48
- }}
50
+ sx={{ '&:empty': { display: 'none' } }}
51
+ skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
49
52
  >
50
53
  {prefix}
51
54
  </PrivateQueryMask>
@@ -57,7 +60,7 @@ export function ProductPagePrice(props: ProductPagePriceProps) {
57
60
  skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
58
61
  sx={[{ textDecoration: 'line-through', color: 'text.disabled' }]}
59
62
  >
60
- <Money {...regularPrice} />
63
+ <Money {...regularPrice} value={regularPriceValue} asNumber={asNumber} />
61
64
  </PrivateQueryMask>
62
65
  )}
63
66
  <PrivateQueryMask
@@ -65,16 +68,15 @@ export function ProductPagePrice(props: ProductPagePriceProps) {
65
68
  skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
66
69
  className={classes.finalPrice}
67
70
  >
68
- <Money {...price} value={priceValue} />
71
+ <Money {...price} value={finalPriceValue} asNumber={asNumber} />
69
72
  </PrivateQueryMask>
73
+
70
74
  {suffix && (
71
75
  <PrivateQueryMask
72
76
  component='span'
73
77
  className={classes.suffix}
74
- skeleton={{
75
- variant: 'text',
76
- sx: { width: '3.5em', transform: 'none' },
77
- }}
78
+ sx={{ '&:empty': { display: 'none' } }}
79
+ skeleton={{ variant: 'text', sx: { width: '3.5em', transform: 'none' } }}
78
80
  >
79
81
  {suffix}
80
82
  </PrivateQueryMask>
@@ -0,0 +1,35 @@
1
+ import { filterNonNullableKeys, sxx } from '@graphcommerce/next-ui'
2
+ import { Trans } from '@lingui/macro'
3
+ import { Box, type SxProps, type Theme } from '@mui/material'
4
+ import { type AddToCartItemSelector } from '../AddProductsToCart'
5
+ import { ProductListPrice } from '../ProductListPrice'
6
+ import { type ProductPagePriceFragment } from './ProductPagePrice.gql'
7
+ import type { UseCustomizableOptionPriceProps } from './useCustomizableOptionPrice'
8
+
9
+ export type ProductPagePriceLowestProps = {
10
+ sx?: SxProps<Theme>
11
+ product: ProductPagePriceFragment
12
+ } & AddToCartItemSelector &
13
+ UseCustomizableOptionPriceProps
14
+
15
+ export function ProductPagePriceLowest(props: ProductPagePriceLowestProps) {
16
+ const { product, sx, index = 0 } = props
17
+ const priceTiers = filterNonNullableKeys(product.price_tiers, ['quantity', 'final_price'])
18
+ const lastTier = priceTiers[priceTiers.length - 1]
19
+
20
+ const lowestTier = (lastTier?.final_price.value ?? 0) / (lastTier?.quantity ?? 1)
21
+ const finalPrice = lowestTier
22
+ ? {
23
+ value: lowestTier,
24
+ currency: product.price_range.minimum_price.final_price.currency,
25
+ }
26
+ : product.price_range.minimum_price.final_price
27
+
28
+ return (
29
+ <Box component='div' sx={sxx({}, sx)}>
30
+ <Trans>
31
+ As low as <ProductListPrice final_price={finalPrice} regular_price={finalPrice} />
32
+ </Trans>
33
+ </Box>
34
+ )
35
+ }
@@ -1,4 +1,5 @@
1
1
  import type { MoneyFragment } from '@graphcommerce/magento-store'
2
+ import { filterNonNullableKeys } from '@graphcommerce/next-ui'
2
3
  import type { ProductPagePriceFragment } from './ProductPagePrice.gql'
3
4
 
4
5
  export function getProductTierPrice(
@@ -8,8 +9,12 @@ export function getProductTierPrice(
8
9
  const { price_tiers } = price
9
10
  let result: MoneyFragment | undefined | null
10
11
 
11
- price_tiers?.forEach((priceTier) => {
12
- if (priceTier?.quantity && quantity >= priceTier?.quantity) result = priceTier?.final_price
12
+ filterNonNullableKeys(price_tiers, ['quantity', 'final_price'])?.forEach((priceTier) => {
13
+ if (quantity >= priceTier.quantity)
14
+ result = {
15
+ value: (priceTier.final_price.value ?? 0) / priceTier.quantity,
16
+ currency: priceTier.final_price.currency,
17
+ }
13
18
  })
14
19
 
15
20
  return result
@@ -1,4 +1,5 @@
1
1
  export * from './ProductPagePrice.gql'
2
2
  export * from './ProductPagePriceTiers'
3
+ export * from './ProductPagePriceLowest'
3
4
  export * from './ProductPagePrice'
4
5
  export * from './getProductTierPrice'
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": "9.1.0-canary.40",
5
+ "version": "9.1.0-canary.41",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -18,18 +18,18 @@
18
18
  "typescript": "5.7.2"
19
19
  },
20
20
  "peerDependencies": {
21
- "@graphcommerce/ecommerce-ui": "^9.1.0-canary.40",
22
- "@graphcommerce/eslint-config-pwa": "^9.1.0-canary.40",
23
- "@graphcommerce/framer-next-pages": "^9.1.0-canary.40",
24
- "@graphcommerce/framer-scroller": "^9.1.0-canary.40",
25
- "@graphcommerce/graphql": "^9.1.0-canary.40",
26
- "@graphcommerce/graphql-mesh": "^9.1.0-canary.40",
27
- "@graphcommerce/image": "^9.1.0-canary.40",
28
- "@graphcommerce/magento-cart": "^9.1.0-canary.40",
29
- "@graphcommerce/magento-store": "^9.1.0-canary.40",
30
- "@graphcommerce/next-ui": "^9.1.0-canary.40",
31
- "@graphcommerce/prettier-config-pwa": "^9.1.0-canary.40",
32
- "@graphcommerce/typescript-config-pwa": "^9.1.0-canary.40",
21
+ "@graphcommerce/ecommerce-ui": "^9.1.0-canary.41",
22
+ "@graphcommerce/eslint-config-pwa": "^9.1.0-canary.41",
23
+ "@graphcommerce/framer-next-pages": "^9.1.0-canary.41",
24
+ "@graphcommerce/framer-scroller": "^9.1.0-canary.41",
25
+ "@graphcommerce/graphql": "^9.1.0-canary.41",
26
+ "@graphcommerce/graphql-mesh": "^9.1.0-canary.41",
27
+ "@graphcommerce/image": "^9.1.0-canary.41",
28
+ "@graphcommerce/magento-cart": "^9.1.0-canary.41",
29
+ "@graphcommerce/magento-store": "^9.1.0-canary.41",
30
+ "@graphcommerce/next-ui": "^9.1.0-canary.41",
31
+ "@graphcommerce/prettier-config-pwa": "^9.1.0-canary.41",
32
+ "@graphcommerce/typescript-config-pwa": "^9.1.0-canary.41",
33
33
  "@lingui/core": "^4.2.1",
34
34
  "@lingui/macro": "^4.2.1",
35
35
  "@lingui/react": "^4.2.1",