@graphcommerce/magento-product-bundle 9.1.0-canary.18 → 9.1.0-canary.20
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 +16 -0
- package/components/BundleProductOptions/BundleOption.tsx +22 -16
- package/components/BundleProductOptions/BundleOptionValue.tsx +25 -36
- package/components/BundleProductOptions/BundleProductOptions.tsx +8 -8
- package/components/BundleProductOptions/calculateBundleOptionValuePrice.ts +45 -0
- package/components/BundleProductOptions/types.ts +24 -10
- package/{ProductListItemBundle.tsx → components/ProductListItemBundle/ProductListItemBundle.tsx} +1 -1
- package/components/index.ts +6 -0
- package/{components/BundleCartItem → graphql/fragments}/BundleCartItem.graphql +1 -9
- package/graphql/fragments/ItemSelectedBundleOption.graphql +13 -0
- package/graphql/fragments/SelectedBundleOption.graphql +11 -0
- package/graphql/index.ts +9 -0
- package/graphql/inject/CreditMemoItem_Bundle.graphql +5 -0
- package/graphql/inject/InvoiceItem_Bundle.graphql +5 -0
- package/graphql/inject/OrderItem_Bundle.graphql +5 -0
- package/{components/BundleProductOptions/BundleProductOptions.graphql → graphql/inject/ProductPageItem_Bundle.graphql} +20 -1
- package/graphql/inject/ShipmentItem_Bundle.graphql +5 -0
- package/index.ts +2 -3
- package/package.json +14 -14
- package/plugins/BundleCartItemActionCard.tsx +25 -10
- package/plugins/BundleCreditMemoItem.tsx +33 -0
- package/plugins/BundleInvoiceItem.tsx +33 -0
- package/plugins/BundleOrderItem.tsx +33 -0
- package/plugins/BundleProductPagePrice.tsx +117 -0
- package/plugins/BundleShipmentItem.tsx +33 -0
- package/plugins/Bundle_cartItemToCartItemInput.ts +48 -0
- package/BundleProductPage.graphql +0 -3
- package/ProductPageBundleQueryFragment.graphql +0 -9
- package/components/BundleCartItem/BundleCartItem.tsx +0 -36
- package/components/BundleProductCartItemOptions/BundleProductCartItemOptions.tsx +0 -42
- /package/{ProductListItemBundle.graphql → graphql/inject/ProductListItemBundle.graphql} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 9.1.0-canary.20
|
|
4
|
+
|
|
5
|
+
## 9.1.0-canary.19
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`224945f`](https://github.com/graphcommerce-org/graphcommerce/commit/224945faf04dff48692b7fcd99e1835d8a683143) - Add the ability to edit bundle products from the cart page ([@paales](https://github.com/paales))
|
|
10
|
+
|
|
11
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`224945f`](https://github.com/graphcommerce-org/graphcommerce/commit/224945faf04dff48692b7fcd99e1835d8a683143) - Magento 2.4.7: Render discounts for bundle products ([@paales](https://github.com/paales))
|
|
12
|
+
|
|
13
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`224945f`](https://github.com/graphcommerce-org/graphcommerce/commit/224945faf04dff48692b7fcd99e1835d8a683143) - Calculate the product page price dynamically based on the options and quantities selected. ([@paales](https://github.com/paales))
|
|
14
|
+
|
|
15
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`6b2b44c`](https://github.com/graphcommerce-org/graphcommerce/commit/6b2b44ca853279144d7768067f3462d4d4bf0af1) - Implement the Cart options as priceModifiers so the logic can be somewhat re-used for multiple locations ([@paales](https://github.com/paales))
|
|
16
|
+
|
|
17
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`2409514`](https://github.com/graphcommerce-org/graphcommerce/commit/240951428ac0bdc11649f4190b6d51c004680b34) - Order/Invoice/CreditMemo and Shipment views ([@paales](https://github.com/paales))
|
|
18
|
+
|
|
3
19
|
## 9.1.0-canary.18
|
|
4
20
|
|
|
5
21
|
## 9.1.0-canary.17
|
|
@@ -6,20 +6,27 @@ import { filterNonNullableKeys, SectionHeader } from '@graphcommerce/next-ui'
|
|
|
6
6
|
import { i18n } from '@lingui/core'
|
|
7
7
|
import React, { useMemo } from 'react'
|
|
8
8
|
import { BundleOptionValue } from './BundleOptionValue'
|
|
9
|
-
import type
|
|
9
|
+
import { toBundleOptionType, type BundleOptionProps, type BundleOptionValueProps } from './types'
|
|
10
10
|
|
|
11
11
|
export const BundleOption = React.memo<BundleOptionProps>((props) => {
|
|
12
|
-
const {
|
|
12
|
+
const { index, item, color, layout, size, variant, product, renderer } = props
|
|
13
|
+
const { options, title, required, type: incomingType, uid, price_range } = item
|
|
13
14
|
const { control } = useFormAddProductsToCart()
|
|
14
|
-
|
|
15
|
-
const required = _required ?? false
|
|
15
|
+
const type = toBundleOptionType(incomingType)
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<div>
|
|
19
|
-
<SectionHeader
|
|
19
|
+
<SectionHeader
|
|
20
|
+
labelLeft={
|
|
21
|
+
<>
|
|
22
|
+
{title} {required && ' *'}
|
|
23
|
+
</>
|
|
24
|
+
}
|
|
25
|
+
/>
|
|
20
26
|
<ActionCardListForm<BundleOptionValueProps & ActionCardItemBase, AddProductsToCartFields>
|
|
21
27
|
control={control}
|
|
22
|
-
required={required}
|
|
28
|
+
required={Boolean(required)}
|
|
29
|
+
multiple={type === 'checkbox' || type === 'multi'}
|
|
23
30
|
color={color}
|
|
24
31
|
layout={layout}
|
|
25
32
|
size={size}
|
|
@@ -30,22 +37,21 @@ export const BundleOption = React.memo<BundleOptionProps>((props) => {
|
|
|
30
37
|
? i18n._(/* i18n*/ 'Please select a value for ‘{label}’', { label: title })
|
|
31
38
|
: false,
|
|
32
39
|
}}
|
|
33
|
-
name={
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
: `cartItems.${index}.selected_options.${idx}`
|
|
37
|
-
}
|
|
38
|
-
render={BundleOptionValue}
|
|
40
|
+
name={`cartItems.${index}.selected_options_record.${uid}`}
|
|
41
|
+
render={renderer ?? BundleOptionValue}
|
|
42
|
+
requireOptionSelection={Boolean(required)}
|
|
39
43
|
items={useMemo(
|
|
40
44
|
() =>
|
|
41
45
|
filterNonNullableKeys(options).map((option) => ({
|
|
42
|
-
|
|
46
|
+
product,
|
|
47
|
+
item,
|
|
48
|
+
option,
|
|
43
49
|
value: option.uid,
|
|
44
|
-
idx,
|
|
45
50
|
index,
|
|
46
|
-
|
|
51
|
+
dynamicPrice: product.dynamic_price ?? false,
|
|
52
|
+
discountPercent: price_range.minimum_price.discount?.percent_off ?? 0,
|
|
47
53
|
})),
|
|
48
|
-
[
|
|
54
|
+
[index, options, required],
|
|
49
55
|
)}
|
|
50
56
|
/>
|
|
51
57
|
</div>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ActionCardItemRenderProps } from '@graphcommerce/ecommerce-ui'
|
|
2
2
|
import { NumberFieldElement } from '@graphcommerce/ecommerce-ui'
|
|
3
3
|
import { Image } from '@graphcommerce/image'
|
|
4
|
-
import { useFormAddProductsToCart } from '@graphcommerce/magento-product'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
4
|
+
import { ProductListPrice, useFormAddProductsToCart } from '@graphcommerce/magento-product'
|
|
5
|
+
import { ActionCard, responsiveVal } from '@graphcommerce/next-ui'
|
|
6
|
+
import {
|
|
7
|
+
calculateBundleOptionValuePrice,
|
|
8
|
+
toProductListPriceFragment,
|
|
9
|
+
} from './calculateBundleOptionValuePrice'
|
|
8
10
|
import type { BundleOptionValueProps } from './types'
|
|
9
11
|
|
|
10
12
|
const swatchSizes = {
|
|
@@ -16,27 +18,30 @@ const swatchSizes = {
|
|
|
16
18
|
export function BundleOptionValue(props: ActionCardItemRenderProps<BundleOptionValueProps>) {
|
|
17
19
|
const {
|
|
18
20
|
selected,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
price,
|
|
21
|
+
item,
|
|
22
|
+
option,
|
|
22
23
|
product,
|
|
23
|
-
|
|
24
|
+
index,
|
|
24
25
|
size = 'large',
|
|
25
26
|
color,
|
|
26
|
-
|
|
27
|
-
quantity = 1,
|
|
28
|
-
required,
|
|
27
|
+
price,
|
|
29
28
|
onReset,
|
|
29
|
+
...rest
|
|
30
30
|
} = props
|
|
31
31
|
const { control } = useFormAddProductsToCart()
|
|
32
32
|
|
|
33
|
-
const thumbnail = product?.thumbnail?.url
|
|
33
|
+
const thumbnail = option.product?.thumbnail?.url
|
|
34
|
+
|
|
35
|
+
const pricing = toProductListPriceFragment(
|
|
36
|
+
calculateBundleOptionValuePrice(product, item, option),
|
|
37
|
+
item.price_range.minimum_price.final_price.currency,
|
|
38
|
+
)
|
|
34
39
|
|
|
35
40
|
return (
|
|
36
41
|
<ActionCard
|
|
37
42
|
{...props}
|
|
38
|
-
title={label}
|
|
39
|
-
price={
|
|
43
|
+
title={option.label}
|
|
44
|
+
price={<ProductListPrice {...pricing} />}
|
|
40
45
|
image={
|
|
41
46
|
thumbnail &&
|
|
42
47
|
!thumbnail.includes('/placeholder/') && (
|
|
@@ -44,7 +49,7 @@ export function BundleOptionValue(props: ActionCardItemRenderProps<BundleOptionV
|
|
|
44
49
|
src={thumbnail}
|
|
45
50
|
width={40}
|
|
46
51
|
height={40}
|
|
47
|
-
alt={label ?? ''}
|
|
52
|
+
alt={option.label ?? option.product?.name ?? ''}
|
|
48
53
|
sizes={swatchSizes[size]}
|
|
49
54
|
sx={{
|
|
50
55
|
display: 'block',
|
|
@@ -55,40 +60,24 @@ export function BundleOptionValue(props: ActionCardItemRenderProps<BundleOptionV
|
|
|
55
60
|
/>
|
|
56
61
|
)
|
|
57
62
|
}
|
|
58
|
-
|
|
59
|
-
(can_change_quantity || !required) && (
|
|
60
|
-
<Button disableRipple variant='inline' color='inherit' size='small' tabIndex={-1}>
|
|
61
|
-
<Trans id='Select' />
|
|
62
|
-
</Button>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
reset={
|
|
66
|
-
(can_change_quantity || !required) && (
|
|
67
|
-
<Button disableRipple variant='inline' color='inherit' size='small' onClick={onReset}>
|
|
68
|
-
{can_change_quantity ? <Trans id='Change' /> : <Trans id='Remove' />}
|
|
69
|
-
</Button>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
63
|
+
reset={<></>}
|
|
72
64
|
secondaryAction={
|
|
73
65
|
selected &&
|
|
74
|
-
can_change_quantity && (
|
|
66
|
+
option.can_change_quantity && (
|
|
75
67
|
<NumberFieldElement
|
|
76
68
|
size='small'
|
|
77
69
|
label='Quantity'
|
|
78
70
|
color={color}
|
|
79
71
|
inputProps={{ min: 1 }}
|
|
80
72
|
required
|
|
81
|
-
defaultValue={`${quantity}`}
|
|
73
|
+
defaultValue={`${option.quantity}`}
|
|
82
74
|
control={control}
|
|
83
75
|
sx={{
|
|
84
76
|
width: responsiveVal(80, 120),
|
|
85
77
|
mt: 2,
|
|
86
|
-
'& .MuiFormHelperText-root': {
|
|
87
|
-
margin: 1,
|
|
88
|
-
width: '100%',
|
|
89
|
-
},
|
|
78
|
+
'& .MuiFormHelperText-root': { margin: 1, width: '100%' },
|
|
90
79
|
}}
|
|
91
|
-
name={`cartItems.${index}.
|
|
80
|
+
name={`cartItems.${index}.entered_options_record.${option.uid}`}
|
|
92
81
|
onMouseDown={(e) => e.stopPropagation()}
|
|
93
82
|
/>
|
|
94
83
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AddToCartItemSelector } from '@graphcommerce/magento-product'
|
|
2
2
|
import type { ActionCardListProps } from '@graphcommerce/next-ui'
|
|
3
|
-
import {
|
|
3
|
+
import { nonNullable } from '@graphcommerce/next-ui'
|
|
4
|
+
import type { ProductPageItem_BundleFragment } from '../../graphql'
|
|
4
5
|
import { BundleOption } from './BundleOption'
|
|
5
6
|
import { BundleOptionValue } from './BundleOptionValue'
|
|
6
|
-
import type { BundleProductOptionsFragment } from './BundleProductOptions.gql'
|
|
7
7
|
import type { BundleOptionValueProps } from './types'
|
|
8
8
|
|
|
9
9
|
export type BundelProductOptionsProps = Pick<
|
|
@@ -11,22 +11,22 @@ export type BundelProductOptionsProps = Pick<
|
|
|
11
11
|
'size' | 'layout' | 'color' | 'variant'
|
|
12
12
|
> & {
|
|
13
13
|
renderer?: React.FC<BundleOptionValueProps>
|
|
14
|
-
product:
|
|
14
|
+
product: ProductPageItem_BundleFragment
|
|
15
15
|
} & AddToCartItemSelector
|
|
16
16
|
|
|
17
17
|
export function BundleProductOptions(props: BundelProductOptionsProps) {
|
|
18
|
-
const { product, index = 0 } = props
|
|
18
|
+
const { product, index = 0, ...rest } = props
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
21
|
<>
|
|
22
|
-
{
|
|
22
|
+
{(product?.items ?? []).filter(nonNullable).map((item) => (
|
|
23
23
|
<BundleOption
|
|
24
24
|
index={index}
|
|
25
25
|
key={item.uid}
|
|
26
26
|
color='primary'
|
|
27
|
-
{
|
|
28
|
-
{
|
|
29
|
-
|
|
27
|
+
item={item}
|
|
28
|
+
product={product}
|
|
29
|
+
{...rest}
|
|
30
30
|
renderer={BundleOptionValue}
|
|
31
31
|
/>
|
|
32
32
|
))}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CurrencyEnum } from '@graphcommerce/graphql-mesh'
|
|
2
|
+
import type { ProductListPriceFragment } from '@graphcommerce/magento-product/components'
|
|
3
|
+
import type { BundleProductItemOptionType, BundleProductItemType, BundleProductType } from './types'
|
|
4
|
+
|
|
5
|
+
export type CalculatedBundleOptionValuePrice = [number, number] // [regularPrice, finalPrice]
|
|
6
|
+
|
|
7
|
+
export function calculateBundleOptionValuePrice(
|
|
8
|
+
product: BundleProductType,
|
|
9
|
+
item: BundleProductItemType,
|
|
10
|
+
option: BundleProductItemOptionType,
|
|
11
|
+
quantity = 1,
|
|
12
|
+
): CalculatedBundleOptionValuePrice {
|
|
13
|
+
const { dynamic_price = false } = product
|
|
14
|
+
const precentOff = item?.price_range.minimum_price.discount?.percent_off ?? 0
|
|
15
|
+
|
|
16
|
+
const regularPrice =
|
|
17
|
+
(dynamic_price ? option.product?.price_range.minimum_price.final_price.value : option.price) ??
|
|
18
|
+
0
|
|
19
|
+
|
|
20
|
+
const finalPrice = regularPrice * (1 - precentOff / 100)
|
|
21
|
+
return [regularPrice * quantity, finalPrice * quantity]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function sumCalculatedBundleOptionValuePrices(
|
|
25
|
+
prices: CalculatedBundleOptionValuePrice[],
|
|
26
|
+
): CalculatedBundleOptionValuePrice {
|
|
27
|
+
return prices.reduce((acc, [regular, final]) => [acc[0] + regular, acc[1] + final], [0, 0])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function substractCalculatedBundleOptionValuePrices(
|
|
31
|
+
basePrice: CalculatedBundleOptionValuePrice,
|
|
32
|
+
substractPrice: CalculatedBundleOptionValuePrice,
|
|
33
|
+
): CalculatedBundleOptionValuePrice {
|
|
34
|
+
return [basePrice[0] - substractPrice[0], basePrice[1] - substractPrice[1]]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function toProductListPriceFragment(
|
|
38
|
+
price: CalculatedBundleOptionValuePrice,
|
|
39
|
+
currency: CurrencyEnum | null | undefined,
|
|
40
|
+
): ProductListPriceFragment {
|
|
41
|
+
return {
|
|
42
|
+
regular_price: { currency: currency, value: price[0] },
|
|
43
|
+
final_price: { currency, value: price[1] },
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import type { ActionCardItemRenderProps } from '@graphcommerce/ecommerce-ui'
|
|
2
2
|
import type { ActionCardListProps } from '@graphcommerce/next-ui'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProductPageItem_BundleFragment } from '../../graphql'
|
|
4
|
+
|
|
5
|
+
export type BundleProductType = ProductPageItem_BundleFragment
|
|
6
|
+
export type BundleProductItemType = NonNullable<NonNullable<BundleProductType['items']>[number]>
|
|
7
|
+
export type BundleProductItemOptionType = NonNullable<
|
|
8
|
+
NonNullable<BundleProductItemType['options']>[number]
|
|
9
|
+
>
|
|
4
10
|
|
|
5
11
|
export type BundleOptionProps = {
|
|
6
|
-
idx: number
|
|
7
|
-
index: number
|
|
8
12
|
renderer?: React.FC<ActionCardItemRenderProps<BundleOptionValueProps>>
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
index: number
|
|
14
|
+
product: BundleProductType
|
|
15
|
+
item: BundleProductItemType
|
|
16
|
+
} & Pick<ActionCardListProps, 'size' | 'layout' | 'color' | 'variant'>
|
|
11
17
|
|
|
12
|
-
export type BundleOptionValueProps =
|
|
13
|
-
NonNullable<BundleOptionProps['options']>[number]
|
|
14
|
-
> & {
|
|
15
|
-
idx: number
|
|
18
|
+
export type BundleOptionValueProps = {
|
|
16
19
|
index: number
|
|
17
|
-
|
|
20
|
+
product: BundleProductType
|
|
21
|
+
item: BundleProductItemType
|
|
22
|
+
option: BundleProductItemOptionType
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const possibleTypes = ['radio', 'checkbox', 'multi', 'select'] as const
|
|
26
|
+
export type BundleOptionType = (typeof possibleTypes)[number]
|
|
27
|
+
|
|
28
|
+
export function toBundleOptionType(type: string | null | undefined): BundleOptionType {
|
|
29
|
+
if (!type) return 'radio'
|
|
30
|
+
if (possibleTypes.includes(type as BundleOptionType)) return type as BundleOptionType
|
|
31
|
+
return 'radio'
|
|
18
32
|
}
|
package/{ProductListItemBundle.tsx → components/ProductListItemBundle/ProductListItemBundle.tsx}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProductListItemProps } from '@graphcommerce/magento-product'
|
|
2
2
|
import { ProductListItem } from '@graphcommerce/magento-product'
|
|
3
|
-
import type { ProductListItemBundleFragment } from '
|
|
3
|
+
import type { ProductListItemBundleFragment } from '../../graphql'
|
|
4
4
|
|
|
5
5
|
export type ProdustListItemBundleProps = ProductListItemBundleFragment & ProductListItemProps
|
|
6
6
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './ProductListItemBundle/ProductListItemBundle'
|
|
2
|
+
export * from './BundleProductOptions/BundleOption'
|
|
3
|
+
export * from './BundleProductOptions/BundleOptionValue'
|
|
4
|
+
export * from './BundleProductOptions/BundleProductOptions'
|
|
5
|
+
export * from './BundleProductOptions/calculateBundleOptionValuePrice'
|
|
6
|
+
export * from './BundleProductOptions/types'
|
|
@@ -5,15 +5,7 @@ fragment BundleCartItem on BundleCartItem @inject(into: ["CartItem"]) {
|
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
bundle_options {
|
|
8
|
-
|
|
9
|
-
label
|
|
10
|
-
type
|
|
11
|
-
values {
|
|
12
|
-
uid
|
|
13
|
-
label
|
|
14
|
-
quantity
|
|
15
|
-
price
|
|
16
|
-
}
|
|
8
|
+
...SelectedBundleOption
|
|
17
9
|
}
|
|
18
10
|
customizable_options {
|
|
19
11
|
...SelectedCustomizableOption
|
package/graphql/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './fragments/BundleCartItem.gql'
|
|
2
|
+
export * from './fragments/ItemSelectedBundleOption.gql'
|
|
3
|
+
export * from './fragments/SelectedBundleOption.gql'
|
|
4
|
+
export * from './inject/CreditMemoItem_Bundle.gql'
|
|
5
|
+
export * from './inject/InvoiceItem_Bundle.gql'
|
|
6
|
+
export * from './inject/OrderItem_Bundle.gql'
|
|
7
|
+
export * from './inject/ProductListItemBundle.gql'
|
|
8
|
+
export * from './inject/ProductPageItem_Bundle.gql'
|
|
9
|
+
export * from './inject/ShipmentItem_Bundle.gql'
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
fragment
|
|
1
|
+
fragment ProductPageItem_Bundle on BundleProduct @inject(into: ["ProductPageItem"]) {
|
|
2
2
|
ship_bundle_items
|
|
3
3
|
dynamic_sku
|
|
4
4
|
dynamic_price
|
|
5
5
|
dynamic_weight
|
|
6
|
+
price_view
|
|
6
7
|
items {
|
|
8
|
+
price_range {
|
|
9
|
+
minimum_price {
|
|
10
|
+
final_price {
|
|
11
|
+
currency
|
|
12
|
+
}
|
|
13
|
+
discount {
|
|
14
|
+
percent_off
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
uid
|
|
8
19
|
position
|
|
9
20
|
required
|
|
@@ -11,6 +22,7 @@ fragment BundleProductOptions on BundleProduct {
|
|
|
11
22
|
title
|
|
12
23
|
type
|
|
13
24
|
options {
|
|
25
|
+
__typename
|
|
14
26
|
can_change_quantity
|
|
15
27
|
uid
|
|
16
28
|
is_default
|
|
@@ -29,6 +41,13 @@ fragment BundleProductOptions on BundleProduct {
|
|
|
29
41
|
thumbnail {
|
|
30
42
|
...ProductImage
|
|
31
43
|
}
|
|
44
|
+
price_range {
|
|
45
|
+
minimum_price {
|
|
46
|
+
final_price {
|
|
47
|
+
...Money
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
}
|
|
34
53
|
}
|
package/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from './ProductListItemBundle'
|
|
2
|
-
export * from './components/BundleCartItem/BundleCartItem'
|
|
3
|
-
export * from './BundleProductPage.gql'
|
|
1
|
+
export * from './components/ProductListItemBundle/ProductListItemBundle'
|
|
4
2
|
export * from './components/BundleProductOptions'
|
|
3
|
+
export * from './graphql'
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-product-bundle",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "9.1.0-canary.
|
|
5
|
+
"version": "9.1.0-canary.20",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@graphcommerce/ecommerce-ui": "^9.1.0-canary.
|
|
16
|
-
"@graphcommerce/eslint-config-pwa": "^9.1.0-canary.
|
|
17
|
-
"@graphcommerce/graphql": "^9.1.0-canary.
|
|
18
|
-
"@graphcommerce/image": "^9.1.0-canary.
|
|
19
|
-
"@graphcommerce/magento-cart": "^9.1.0-canary.
|
|
20
|
-
"@graphcommerce/magento-cart-items": "^9.1.0-canary.
|
|
21
|
-
"@graphcommerce/magento-product": "^9.1.0-canary.
|
|
22
|
-
"@graphcommerce/magento-product-simple": "^9.1.0-canary.
|
|
23
|
-
"@graphcommerce/magento-product-virtual": "^9.1.0-canary.
|
|
24
|
-
"@graphcommerce/magento-store": "^9.1.0-canary.
|
|
25
|
-
"@graphcommerce/next-ui": "^9.1.0-canary.
|
|
26
|
-
"@graphcommerce/prettier-config-pwa": "^9.1.0-canary.
|
|
27
|
-
"@graphcommerce/typescript-config-pwa": "^9.1.0-canary.
|
|
15
|
+
"@graphcommerce/ecommerce-ui": "^9.1.0-canary.20",
|
|
16
|
+
"@graphcommerce/eslint-config-pwa": "^9.1.0-canary.20",
|
|
17
|
+
"@graphcommerce/graphql": "^9.1.0-canary.20",
|
|
18
|
+
"@graphcommerce/image": "^9.1.0-canary.20",
|
|
19
|
+
"@graphcommerce/magento-cart": "^9.1.0-canary.20",
|
|
20
|
+
"@graphcommerce/magento-cart-items": "^9.1.0-canary.20",
|
|
21
|
+
"@graphcommerce/magento-product": "^9.1.0-canary.20",
|
|
22
|
+
"@graphcommerce/magento-product-simple": "^9.1.0-canary.20",
|
|
23
|
+
"@graphcommerce/magento-product-virtual": "^9.1.0-canary.20",
|
|
24
|
+
"@graphcommerce/magento-store": "^9.1.0-canary.20",
|
|
25
|
+
"@graphcommerce/next-ui": "^9.1.0-canary.20",
|
|
26
|
+
"@graphcommerce/prettier-config-pwa": "^9.1.0-canary.20",
|
|
27
|
+
"@graphcommerce/typescript-config-pwa": "^9.1.0-canary.20",
|
|
28
28
|
"@lingui/core": "^4.2.1",
|
|
29
29
|
"@lingui/macro": "^4.2.1",
|
|
30
30
|
"@lingui/react": "^4.2.1",
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
selectedCustomizableOptionsModifiers,
|
|
3
|
+
type CartItemActionCardProps,
|
|
4
|
+
} from '@graphcommerce/magento-cart-items'
|
|
5
|
+
import type { PriceModifier } from '@graphcommerce/magento-store'
|
|
2
6
|
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
-
import {
|
|
4
|
-
import { BundleProductCartItemOptions } from '../components/BundleProductCartItemOptions/BundleProductCartItemOptions'
|
|
7
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
5
8
|
|
|
6
9
|
export const config: PluginConfig = {
|
|
7
10
|
type: 'component',
|
|
@@ -11,17 +14,29 @@ export const config: PluginConfig = {
|
|
|
11
14
|
export function CartItemActionCard(props: PluginProps<CartItemActionCardProps>) {
|
|
12
15
|
const { Prev, ...rest } = props
|
|
13
16
|
|
|
14
|
-
if (
|
|
17
|
+
if (rest.cartItem.__typename !== 'BundleCartItem') return <Prev {...rest} />
|
|
18
|
+
|
|
19
|
+
const bundleModifiers = filterNonNullableKeys(rest.cartItem.bundle_options).map<PriceModifier>(
|
|
20
|
+
(option) => ({
|
|
21
|
+
key: option.uid,
|
|
22
|
+
label: option.label,
|
|
23
|
+
items: filterNonNullableKeys(option.values).map((value) => ({
|
|
24
|
+
key: value.uid,
|
|
25
|
+
label: value.label,
|
|
26
|
+
amount: value.price,
|
|
27
|
+
quantity: value.quantity,
|
|
28
|
+
})),
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
15
31
|
|
|
16
32
|
return (
|
|
17
33
|
<Prev
|
|
18
34
|
{...rest}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
35
|
+
priceModifiers={[
|
|
36
|
+
...(rest.priceModifiers ?? []),
|
|
37
|
+
...bundleModifiers,
|
|
38
|
+
...selectedCustomizableOptionsModifiers(rest.cartItem),
|
|
39
|
+
]}
|
|
25
40
|
/>
|
|
26
41
|
)
|
|
27
42
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CreditMemoItemProps } from '@graphcommerce/magento-customer'
|
|
2
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
4
|
+
|
|
5
|
+
export const config: PluginConfig = {
|
|
6
|
+
type: 'component',
|
|
7
|
+
module: '@graphcommerce/magento-customer',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function CreditMemoItem(props: PluginProps<CreditMemoItemProps>) {
|
|
11
|
+
const { Prev, ...rest } = props
|
|
12
|
+
|
|
13
|
+
if (rest.item.__typename !== 'BundleCreditMemoItem') return <Prev {...rest} />
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Prev
|
|
17
|
+
{...rest}
|
|
18
|
+
priceModifiers={[
|
|
19
|
+
...(rest.priceModifiers ?? []),
|
|
20
|
+
...filterNonNullableKeys(rest.item.bundle_options).map((option) => ({
|
|
21
|
+
key: option.uid,
|
|
22
|
+
label: option.label,
|
|
23
|
+
items: filterNonNullableKeys(option.values).map((value, index) => ({
|
|
24
|
+
key: `${index}`,
|
|
25
|
+
label: value.product_name,
|
|
26
|
+
amount: value.price.value ?? 0,
|
|
27
|
+
quantity: value.quantity,
|
|
28
|
+
})),
|
|
29
|
+
})),
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { InvoiceItemProps } from '@graphcommerce/magento-customer'
|
|
2
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
4
|
+
|
|
5
|
+
export const config: PluginConfig = {
|
|
6
|
+
type: 'component',
|
|
7
|
+
module: '@graphcommerce/magento-customer',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function InvoiceItem(props: PluginProps<InvoiceItemProps>) {
|
|
11
|
+
const { Prev, ...rest } = props
|
|
12
|
+
|
|
13
|
+
if (rest.item.__typename !== 'BundleInvoiceItem') return <Prev {...rest} />
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Prev
|
|
17
|
+
{...rest}
|
|
18
|
+
priceModifiers={[
|
|
19
|
+
...(rest.priceModifiers ?? []),
|
|
20
|
+
...filterNonNullableKeys(rest.item.bundle_options).map((option) => ({
|
|
21
|
+
key: option.uid,
|
|
22
|
+
label: option.label,
|
|
23
|
+
items: filterNonNullableKeys(option.values).map((value, index) => ({
|
|
24
|
+
key: `${index}`,
|
|
25
|
+
label: value.product_name,
|
|
26
|
+
amount: value.price.value ?? 0,
|
|
27
|
+
quantity: value.quantity,
|
|
28
|
+
})),
|
|
29
|
+
})),
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OrderItemProps } from '@graphcommerce/magento-customer'
|
|
2
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
4
|
+
|
|
5
|
+
export const config: PluginConfig = {
|
|
6
|
+
type: 'component',
|
|
7
|
+
module: '@graphcommerce/magento-customer',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function OrderItem(props: PluginProps<OrderItemProps>) {
|
|
11
|
+
const { Prev, ...rest } = props
|
|
12
|
+
|
|
13
|
+
if (rest.item.__typename !== 'BundleOrderItem') return <Prev {...rest} />
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Prev
|
|
17
|
+
{...rest}
|
|
18
|
+
priceModifiers={[
|
|
19
|
+
...(rest.priceModifiers ?? []),
|
|
20
|
+
...filterNonNullableKeys(rest.item.bundle_options).map((option) => ({
|
|
21
|
+
key: option.uid,
|
|
22
|
+
label: option.label,
|
|
23
|
+
items: filterNonNullableKeys(option.values).map((value, index) => ({
|
|
24
|
+
key: `${index}`,
|
|
25
|
+
label: value.product_name,
|
|
26
|
+
amount: value.price.value ?? 0,
|
|
27
|
+
quantity: value.quantity,
|
|
28
|
+
})),
|
|
29
|
+
})),
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useWatch } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import {
|
|
3
|
+
useFormAddProductsToCart,
|
|
4
|
+
type AddToCartItemSelector,
|
|
5
|
+
type ProductPagePriceProps,
|
|
6
|
+
} from '@graphcommerce/magento-product'
|
|
7
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
8
|
+
import { filterNonNullableKeys, nonNullable } from '@graphcommerce/next-ui'
|
|
9
|
+
import {
|
|
10
|
+
calculateBundleOptionValuePrice,
|
|
11
|
+
substractCalculatedBundleOptionValuePrices,
|
|
12
|
+
sumCalculatedBundleOptionValuePrices,
|
|
13
|
+
toProductListPriceFragment,
|
|
14
|
+
type CalculatedBundleOptionValuePrice,
|
|
15
|
+
} from '../components/BundleProductOptions/calculateBundleOptionValuePrice'
|
|
16
|
+
import type { BundleProductItemOptionType } from '../components/BundleProductOptions/types'
|
|
17
|
+
import type { ProductPageItem_BundleFragment } from '../graphql'
|
|
18
|
+
|
|
19
|
+
export const config: PluginConfig = {
|
|
20
|
+
type: 'component',
|
|
21
|
+
module: '@graphcommerce/magento-product',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isBundleProduct(
|
|
25
|
+
product:
|
|
26
|
+
| ProductPagePriceProps['product']
|
|
27
|
+
| (ProductPagePriceProps['product'] & ProductPageItem_BundleFragment),
|
|
28
|
+
): product is ProductPagePriceProps['product'] & ProductPageItem_BundleFragment {
|
|
29
|
+
return (
|
|
30
|
+
product.__typename === 'BundleProduct' &&
|
|
31
|
+
Array.isArray((product as ProductPageItem_BundleFragment).items)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ProductPagePrice(
|
|
36
|
+
props: PluginProps<ProductPagePriceProps> & AddToCartItemSelector,
|
|
37
|
+
) {
|
|
38
|
+
const { Prev, product, index = 0, ...rest } = props
|
|
39
|
+
|
|
40
|
+
const form = useFormAddProductsToCart()
|
|
41
|
+
const allSelectedOptions =
|
|
42
|
+
useWatch({
|
|
43
|
+
control: form.control,
|
|
44
|
+
name: `cartItems.${index}.selected_options_record`,
|
|
45
|
+
}) ?? {}
|
|
46
|
+
|
|
47
|
+
const allEnteredOptions =
|
|
48
|
+
useWatch({
|
|
49
|
+
control: form.control,
|
|
50
|
+
name: `cartItems.${index}.entered_options_record`,
|
|
51
|
+
}) ?? {}
|
|
52
|
+
|
|
53
|
+
if (!isBundleProduct(product)) {
|
|
54
|
+
return <Prev product={product} index={index} {...rest} />
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cheapestPricesAlreadyIncludedInBasePrice = filterNonNullableKeys(product.items)
|
|
58
|
+
.filter((item) => item.required)
|
|
59
|
+
.map((item) =>
|
|
60
|
+
item.options
|
|
61
|
+
.filter(nonNullable)
|
|
62
|
+
.map((option) => calculateBundleOptionValuePrice(product, item, option))
|
|
63
|
+
.reduce((acc, price) => (price[1] < acc[1] ? price : acc)),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const reduceBase = sumCalculatedBundleOptionValuePrices(cheapestPricesAlreadyIncludedInBasePrice)
|
|
67
|
+
|
|
68
|
+
const basePrice: CalculatedBundleOptionValuePrice = [
|
|
69
|
+
product.price_range.minimum_price.regular_price.value ?? 0,
|
|
70
|
+
product.price_range.minimum_price.final_price.value ?? 0,
|
|
71
|
+
]
|
|
72
|
+
const base = substractCalculatedBundleOptionValuePrices(basePrice, reduceBase)
|
|
73
|
+
|
|
74
|
+
// This only works with Magento 2.4.7, but that is fine.
|
|
75
|
+
const itemPrices = filterNonNullableKeys(product.items)
|
|
76
|
+
.map((item) => {
|
|
77
|
+
const selectedOption = allSelectedOptions[item.uid]
|
|
78
|
+
const allOptions = item.options.filter(nonNullable)
|
|
79
|
+
|
|
80
|
+
const options: BundleProductItemOptionType[] = selectedOption
|
|
81
|
+
? allOptions.filter((o) => {
|
|
82
|
+
if (Array.isArray(selectedOption)) return selectedOption.includes(o?.uid ?? '')
|
|
83
|
+
return selectedOption === o?.uid
|
|
84
|
+
})
|
|
85
|
+
: allOptions.filter((o) => o?.is_default)
|
|
86
|
+
|
|
87
|
+
return options.map((option) => {
|
|
88
|
+
const quantity = allEnteredOptions[option.uid]
|
|
89
|
+
? Number(allEnteredOptions[option.uid])
|
|
90
|
+
: (option.quantity ?? 1)
|
|
91
|
+
return calculateBundleOptionValuePrice(product, item, option, quantity)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
.flat(1)
|
|
95
|
+
|
|
96
|
+
const totalPrice = toProductListPriceFragment(
|
|
97
|
+
sumCalculatedBundleOptionValuePrices([base, ...itemPrices]),
|
|
98
|
+
product.price_range.minimum_price.final_price.currency,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Prev
|
|
103
|
+
product={{
|
|
104
|
+
...product,
|
|
105
|
+
price_range: {
|
|
106
|
+
...product.price_range,
|
|
107
|
+
minimum_price: {
|
|
108
|
+
...product.price_range.minimum_price,
|
|
109
|
+
...totalPrice,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}}
|
|
113
|
+
index={index}
|
|
114
|
+
{...rest}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ShipmentItemProps } from '@graphcommerce/magento-customer'
|
|
2
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
4
|
+
|
|
5
|
+
export const config: PluginConfig = {
|
|
6
|
+
type: 'component',
|
|
7
|
+
module: '@graphcommerce/magento-customer',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ShipmentItem(props: PluginProps<ShipmentItemProps>) {
|
|
11
|
+
const { Prev, ...rest } = props
|
|
12
|
+
|
|
13
|
+
if (rest.item.__typename !== 'BundleShipmentItem') return <Prev {...rest} />
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Prev
|
|
17
|
+
{...rest}
|
|
18
|
+
priceModifiers={[
|
|
19
|
+
...(rest.priceModifiers ?? []),
|
|
20
|
+
...filterNonNullableKeys(rest.item.bundle_options).map((option) => ({
|
|
21
|
+
key: option.uid,
|
|
22
|
+
label: option.label,
|
|
23
|
+
items: filterNonNullableKeys(option.values).map((value, index) => ({
|
|
24
|
+
key: `${index}`,
|
|
25
|
+
label: value.product_name,
|
|
26
|
+
amount: value.price.value ?? 0,
|
|
27
|
+
quantity: value.quantity,
|
|
28
|
+
})),
|
|
29
|
+
})),
|
|
30
|
+
]}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type cartItemToCartItemInput as cartItemToCartItemInputType } from '@graphcommerce/magento-cart-items'
|
|
2
|
+
import type { AddProductsToCartFields } from '@graphcommerce/magento-product/components'
|
|
3
|
+
import type { FunctionPlugin, PluginConfig } from '@graphcommerce/next-config'
|
|
4
|
+
import { filterNonNullableKeys } from '@graphcommerce/next-ui'
|
|
5
|
+
import { toBundleOptionType } from '../components/BundleProductOptions/types'
|
|
6
|
+
|
|
7
|
+
export const config: PluginConfig = {
|
|
8
|
+
type: 'function',
|
|
9
|
+
module: '@graphcommerce/magento-cart-items',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const cartItemToCartItemInput: FunctionPlugin<typeof cartItemToCartItemInputType> = (
|
|
13
|
+
prev,
|
|
14
|
+
props,
|
|
15
|
+
) => {
|
|
16
|
+
const result = prev(props)
|
|
17
|
+
const { product, cartItem } = props
|
|
18
|
+
|
|
19
|
+
if (!result) return result
|
|
20
|
+
if (product.__typename !== 'BundleProduct') return result
|
|
21
|
+
if (cartItem.__typename !== 'BundleCartItem') return result
|
|
22
|
+
|
|
23
|
+
const selected: AddProductsToCartFields['cartItems'][number]['selected_options_record'] = {}
|
|
24
|
+
const entered: AddProductsToCartFields['cartItems'][number]['entered_options_record'] = {}
|
|
25
|
+
|
|
26
|
+
const items = filterNonNullableKeys(product.items)
|
|
27
|
+
|
|
28
|
+
filterNonNullableKeys(cartItem.bundle_options).forEach((option) => {
|
|
29
|
+
const values = filterNonNullableKeys(option?.values)
|
|
30
|
+
const productItem = items.find((item) => item.uid === option.uid)
|
|
31
|
+
const type = toBundleOptionType(productItem?.type)
|
|
32
|
+
|
|
33
|
+
const vals = values.map((v) => v.uid)
|
|
34
|
+
selected[option.uid] = type === 'multi' || type === 'checkbox' ? vals : vals[0]
|
|
35
|
+
|
|
36
|
+
values.forEach((v) => {
|
|
37
|
+
const productOptions = filterNonNullableKeys(productItem?.options)
|
|
38
|
+
const productOption = productOptions.find((o) => o.uid === v.uid)
|
|
39
|
+
if (productOption?.can_change_quantity) entered[v.uid] = v.quantity
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
...result,
|
|
45
|
+
selected_options_record: { ...result.selected_options_record, ...selected },
|
|
46
|
+
entered_options_record: { ...result.entered_options_record, ...entered },
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { SelectedCustomizableOptions } from '@graphcommerce/magento-cart-items'
|
|
2
|
-
import { Money } from '@graphcommerce/magento-store'
|
|
3
|
-
import { Typography } from '@mui/material'
|
|
4
|
-
import type { BundleProductCartItemOptionsProps } from '../BundleProductCartItemOptions/BundleProductCartItemOptions'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @deprecated
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
export function BundleCartItem(props: BundleProductCartItemOptionsProps) {
|
|
11
|
-
const { bundle_options } = props
|
|
12
|
-
return (
|
|
13
|
-
<>
|
|
14
|
-
{bundle_options.map((option) => {
|
|
15
|
-
if (!option?.uid) return null
|
|
16
|
-
return (
|
|
17
|
-
<div key={option.uid}>
|
|
18
|
-
{option.values.map((value) => {
|
|
19
|
-
if (!value?.uid) return null
|
|
20
|
-
return (
|
|
21
|
-
<Typography variant='body2' component='div' key={value.uid}>
|
|
22
|
-
{value.quantity > 1 && <>{value.quantity} × </>}
|
|
23
|
-
<Typography variant='subtitle2' component='span'>
|
|
24
|
-
{value.label}
|
|
25
|
-
</Typography>{' '}
|
|
26
|
-
{value.price > 0 && <Money value={value.price} />}
|
|
27
|
-
</Typography>
|
|
28
|
-
)
|
|
29
|
-
})}
|
|
30
|
-
</div>
|
|
31
|
-
)
|
|
32
|
-
})}
|
|
33
|
-
<SelectedCustomizableOptions {...props} />
|
|
34
|
-
</>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { CartItemFragment } from '@graphcommerce/magento-cart-items'
|
|
2
|
-
import { SelectedCustomizableOptions } from '@graphcommerce/magento-cart-items'
|
|
3
|
-
import { Money } from '@graphcommerce/magento-store'
|
|
4
|
-
import { nonNullable } from '@graphcommerce/next-ui'
|
|
5
|
-
import { Box } from '@mui/material'
|
|
6
|
-
import type { BundleCartItemFragment } from '../BundleCartItem/BundleCartItem.gql'
|
|
7
|
-
|
|
8
|
-
export type BundleProductCartItemOptionsProps = BundleCartItemFragment & CartItemFragment
|
|
9
|
-
|
|
10
|
-
export function BundleProductCartItemOptions(props: BundleProductCartItemOptionsProps) {
|
|
11
|
-
const { bundle_options, prices } = props
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<>
|
|
15
|
-
{bundle_options?.map((option) =>
|
|
16
|
-
option?.values.filter(nonNullable).map((option_value) => (
|
|
17
|
-
<Box
|
|
18
|
-
key={option_value.uid}
|
|
19
|
-
sx={(theme) => ({
|
|
20
|
-
display: 'flex',
|
|
21
|
-
gap: theme.spacings.xxs,
|
|
22
|
-
[theme.breakpoints.down('sm')]: {
|
|
23
|
-
fontSize: theme.typography.caption.fontSize,
|
|
24
|
-
},
|
|
25
|
-
})}
|
|
26
|
-
>
|
|
27
|
-
<Box sx={{ color: 'text.primary' }}>
|
|
28
|
-
{option_value.label} {option_value.quantity > 1 && `x${option_value.quantity}`}
|
|
29
|
-
</Box>
|
|
30
|
-
{option_value.price > 0 && (
|
|
31
|
-
<Box sx={(theme) => ({ position: 'absolute', right: theme.spacings.xs })}>
|
|
32
|
-
<Money currency={prices?.price.currency} value={option_value.price} />
|
|
33
|
-
</Box>
|
|
34
|
-
)}
|
|
35
|
-
</Box>
|
|
36
|
-
)),
|
|
37
|
-
)}
|
|
38
|
-
|
|
39
|
-
<SelectedCustomizableOptions {...props} />
|
|
40
|
-
</>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
File without changes
|