Skip to content

Commit

Permalink
Merge pull request #2239 from graphcommerce-org/feature/update-price-…
Browse files Browse the repository at this point in the history
…with-customizable-options-GCOM-1351

Feature/update price with customizable options gcom 1351
  • Loading branch information
paales authored Apr 17, 2024
2 parents 796fe22 + 1fd1c8c commit 461cbde
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 36 deletions.
6 changes: 6 additions & 0 deletions .changeset/rude-llamas-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@graphcommerce/magento-product-configurable": patch
"@graphcommerce/magento-product": patch
---

Prices of products are now updated when customizable options are selected
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ const ConfigurableProductPagePrice: PluginType = (props) => {
const { Prev, product, index, ...rest } = props
const variant = useConfigurableSelectedVariant({ url_key: product.url_key, index })

return <Prev product={variant ?? product} index={index} {...rest} />
if (product.__typename !== 'ConfigurableProduct') return <Prev {...props} />

return (
<Prev
product={variant ? { ...variant, options: product.options } : product}
index={index}
{...rest}
/>
)
}

export const Plugin = ConfigurableProductPagePrice
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { TextFieldElement } from '@graphcommerce/ecommerce-ui'
import { CurrencyEnum } from '@graphcommerce/graphql-mesh'
import { TypeRenderer } from '@graphcommerce/next-ui'
import { Money } from '@graphcommerce/magento-store'
import { SectionHeader, TypeRenderer } from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
import { Box } from '@mui/material'
import React from 'react'
import { useFormAddProductsToCart } from '../AddProductsToCart'
import { ProductCustomizableFragment } from './ProductCustomizable.gql'
Expand All @@ -20,24 +22,56 @@ type CustomizableAreaOptionProps = React.ComponentProps<
>

export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
const { uid, areaValue, required, optionIndex, index, title } = props
const { uid, areaValue, required, optionIndex, index, title, currency, productPrice } = props
const maxLength = areaValue?.max_characters ?? undefined
const { control, register } = useFormAddProductsToCart()
const { control, register, getValues } = useFormAddProductsToCart()

if (!areaValue) return null

return (
<>
<Box>
<input
type='hidden'
{...register(`cartItems.${index}.entered_options.${optionIndex}.uid`)}
value={uid}
/>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
<TextFieldElement
sx={{ width: '100%' }}
color='primary'
multiline
minRows={3}
control={control}
name={`cartItems.${index}.entered_options.${optionIndex}.value`}
label={title}
InputProps={{
endAdornment:
areaValue.price === 0
? null
: areaValue.price && (
<Box
sx={{
display: 'flex',
typography: 'body1',
'&.sizeMedium': { typographty: 'subtitle1' },
'&.sizeLarge': { typography: 'h6' },
color: getValues(`cartItems.${index}.entered_options.${optionIndex}.value`)
? 'text.primary'
: 'text.secondary',
}}
>
{/* Change fontFamily so the + is properly outlined */}
<span style={{ fontFamily: 'arial', paddingTop: '1px' }}>+{'\u00A0'}</span>
<Money
value={
areaValue.price_type === 'PERCENT'
? productPrice * (areaValue.price / 100)
: areaValue.price
}
currency={currency}
/>
</Box>
),
}}
required={Boolean(required)}
rules={{ maxLength }}
helperText={
Expand All @@ -47,6 +81,6 @@ export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
})
}
/>
</>
</Box>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Trans } from '@lingui/react'
import { Box } from '@mui/material'
import { useFormAddProductsToCart } from '../AddProductsToCart'
import { OptionTypeRenderer } from './CustomizableAreaOption'
import { Money } from '@graphcommerce/magento-store'

type CustomizableDateOptionProps = React.ComponentProps<
OptionTypeRenderer['CustomizableDateOption']
Expand All @@ -21,14 +22,56 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
title,
minDate = new Date('1950-11-12T00:00'),
maxDate = new Date('9999-11-12T00:00'),
dateValue,
currency,
productPrice,
} = props
const { register, setValue, setError, getFieldState, clearErrors, control } =
useFormAddProductsToCart()
const {
register,
setValue,
setError,
getFieldState,
clearErrors,
control,
resetField,
getValues,
} = useFormAddProductsToCart()

const { invalid } = getFieldState(`cartItems.${index}.entered_options.${optionIndex}.value`)

minDate.setSeconds(0, 0)
maxDate.setSeconds(0, 0)
if (!dateValue) return null

const price =
dateValue.price === 0
? null
: dateValue.price && (
<Box
sx={{
display: 'flex',
typography: 'body1',
'&.sizeMedium': { typographty: 'subtitle1' },
'&.sizeLarge': { typography: 'h6' },
color:
dateValue.uid ===
getValues(`cartItems.${index}.entered_options.${optionIndex}.value`)
? 'text.primary'
: 'text.secondary',
}}
>
{/* Change fontFamily so the + is properly outlined */}
<span style={{ fontFamily: 'arial' }}>+{'\u00A0'}</span>
<Money
value={
dateValue.price_type === 'PERCENT'
? productPrice * (dateValue.price / 100)
: dateValue.price
}
currency={currency}
/>
</Box>
)
return (
<Box>
<input
Expand All @@ -41,12 +84,20 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
<TextFieldElement
control={control}
name={`cartItems.${index}.entered_options.${optionIndex}.value`}
sx={{ width: '100%' }}
sx={{
width: '100%',
'& input[type="datetime-local"]::-webkit-calendar-picker-indicator': {
filter: (theme) => (theme.palette.mode === 'dark' ? 'invert(100%)' : 'none'),
mr: '10px',
},
}}
defaultValue=''
required={!!required}
error={invalid}
helperText={invalid ? <Trans id='Invalid date' /> : ''}
type='datetime-local'
InputProps={{
endAdornment: price,
inputProps: {
min: minDate.toISOString().replace(/:00.000Z/, ''),
max: maxDate.toISOString().replace(/:00.000Z/, ''),
Expand All @@ -61,10 +112,12 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
} else {
clearErrors(`cartItems.${index}.entered_options.${optionIndex}.value`)
}
setValue(
`cartItems.${index}.entered_options.${optionIndex}.value`,
`${data.currentTarget.value.replace('T', ' ')}:00`,
)
if (data.currentTarget.value)
setValue(
`cartItems.${index}.entered_options.${optionIndex}.value`,
`${data.currentTarget.value.replace('T', ' ')}:00`,
)
else resetField(`cartItems.${index}.entered_options.${optionIndex}.value`)
}}
/>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,81 @@
import { SelectElement } from '@graphcommerce/ecommerce-ui'
import { SelectElement, useController } from '@graphcommerce/ecommerce-ui'
import { SectionHeader, filterNonNullableKeys } from '@graphcommerce/next-ui'
import { Box } from '@mui/material'
import { Box, ListItemText, MenuItem, TextField, Typography } from '@mui/material'
import { useFormAddProductsToCart } from '../AddProductsToCart'
import { OptionTypeRenderer } from './CustomizableAreaOption'
import { Money } from '@graphcommerce/magento-store'

type CustomizableDropDownOptionProps = React.ComponentProps<
OptionTypeRenderer['CustomizableDropDownOption']
>

export function CustomizableDropDownOption(props: CustomizableDropDownOptionProps) {
const { uid, required, index, title, dropdownValue } = props
const { control } = useFormAddProductsToCart()
const { uid, required, index, title, dropdownValue, productPrice, currency } = props
const { control, getValues } = useFormAddProductsToCart()

const {
field: { onChange, value, ref, ...field },
fieldState: { invalid, error },
} = useController({
name: `cartItems.${index}.customizable_options.${uid}`,
rules: {
required: Boolean(required),
},
control,
defaultValue: '',
})

return (
<Box>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
<SelectElement
sx={{ width: '100%' }}

<TextField
sx={{
width: '100%',
'& .MuiSelect-select': {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
}}
color='primary'
control={control}
name={`cartItems.${index}.customizable_options.${uid}`}
label={title}
value={value ?? ''}
{...field}
inputRef={ref}
onChange={(event) => onChange(event.target.value)}
select
required={Boolean(required)}
defaultValue=''
options={filterNonNullableKeys(dropdownValue, ['title']).map((option) => ({
id: option.uid,
label: option.title,
}))}
/>
error={invalid}
helperText={error?.message}
>
{filterNonNullableKeys(dropdownValue, ['title']).map((option) => (
<MenuItem key={option.uid} value={option.uid}>
<Box>{option.title}</Box>

{option.price ? (
<Box
sx={{
// display: 'flex',
typography: 'body1',
'&.sizeMedium': { typographty: 'subtitle1' },
'&.sizeLarge': { typography: 'h6' },
color: option.uid === value ? 'text.primary' : 'text.secondary',
}}
>
<span style={{ fontFamily: 'arial', paddingTop: '1px' }}>+&nbsp;</span>
<Money
value={
option.price_type === 'PERCENT'
? productPrice * (option.price / 100)
: option.price
}
currency={currency}
/>
</Box>
) : null}
</MenuItem>
))}
</TextField>
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Money } from '@graphcommerce/magento-store'
import { TextFieldElement } from '@graphcommerce/ecommerce-ui'
import { SectionHeader } from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
Expand All @@ -10,10 +11,12 @@ type CustomizableFieldOptionProps = React.ComponentProps<
>

export function CustomizableFieldOption(props: CustomizableFieldOptionProps) {
const { uid, required, optionIndex, index, title, fieldValue } = props
const { control, register } = useFormAddProductsToCart()
const { uid, required, optionIndex, index, title, fieldValue, productPrice, currency } = props
const { control, register, resetField, getValues } = useFormAddProductsToCart()

const maxLength = fieldValue?.max_characters ?? 0
if (!fieldValue) return null

const maxLength = fieldValue.max_characters ?? 0
return (
<Box>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
Expand All @@ -29,6 +32,35 @@ export function CustomizableFieldOption(props: CustomizableFieldOptionProps) {
control={control}
name={`cartItems.${index}.entered_options.${optionIndex}.value`}
required={Boolean(required)}
InputProps={{
endAdornment:
fieldValue.price === 0
? null
: fieldValue.price && (
<Box
sx={{
display: 'flex',
typography: 'body1',
'&.sizeMedium': { typographty: 'subtitle1' },
'&.sizeLarge': { typography: 'h6' },
color: getValues(`cartItems.${index}.entered_options.${optionIndex}.value`)
? 'text.primary'
: 'text.secondary',
}}
>
{/* Change fontFamily so the + is properly outlined */}
<span style={{ fontFamily: 'arial', paddingTop: '1px' }}>+{'\u00A0'}</span>
<Money
value={
fieldValue.price_type === 'PERCENT'
? productPrice * (fieldValue.price / 100)
: fieldValue.price
}
currency={currency}
/>
</Box>
),
}}
rules={{
maxLength: {
value: maxLength,
Expand All @@ -43,6 +75,10 @@ export function CustomizableFieldOption(props: CustomizableFieldOptionProps) {
maxLength,
})
}
onChange={(data) => {
if (!data.currentTarget.value)
resetField(`cartItems.${index}.entered_options.${optionIndex}.value`)
}}
/>
</Box>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fragment ProductPagePrice on ProductInterface {
__typename
url_key
price_range {
minimum_price {
Expand Down Expand Up @@ -30,4 +31,6 @@ fragment ProductPagePrice on ProductInterface {
}
quantity
}

...ProductCustomizable
}
Loading

0 comments on commit 461cbde

Please sign in to comment.