Skip to content

Commit

Permalink
Removed deprecated fields from AddProductsToCartForm
Browse files Browse the repository at this point in the history
Added support for TIME and DATE for the customizable options. Added required stars.
Renamed customizable_options_entered to entered_options_record and customizable_options to selected_options_record
Refactored the price calculation of customizable options on the product page so required options are correctly handled.
  • Loading branch information
paales committed Feb 19, 2025
1 parent 164d607 commit 02dcc99
Show file tree
Hide file tree
Showing 17 changed files with 145 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .changeset/empty-chicken-push.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
'@graphcommerce/magento-product': patch
---

Moved entered_options to customizable_options_entered for the AddToCartForm. Added support for TIME and DATE for the customizable options.
Added support for TIME and DATE for the customizable options. Added required stars.
5 changes: 5 additions & 0 deletions .changeset/plenty-moons-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-product': patch
---

Refactored the price calculation of customizable options on the product page so required options are correctly handled.
5 changes: 5 additions & 0 deletions .changeset/soft-cycles-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-cart': patch
---

Removed deprecated fields from AddProductsToCartForm
5 changes: 5 additions & 0 deletions .changeset/wild-goats-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-product': patch
---

Renamed customizable_options_entered to entered_options_record and customizable_options to selected_options_record
15 changes: 7 additions & 8 deletions packages/magento-cart-items/utils/cartItemToCartItemInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function cartItemToCartItemInput(
const cartItemInput: CartItemInput = {
sku: product.sku,
quantity: cartItem.quantity,
customizable_options: {},
selected_options_record: {},
selected_options: [],
entered_options: [],
}
Expand Down Expand Up @@ -58,30 +58,29 @@ export function cartItemToCartItemInput(

if (Array.isArray(possibleProductValues)) {
const value = cartItemCustomizableOptionValue.map((v) => v.customizable_option_value_uid)
if (!cartItemInput.customizable_options) cartItemInput.customizable_options = {}
cartItemInput.customizable_options[productOption.uid] = isTypename(productOption, [
if (!cartItemInput.selected_options_record) cartItemInput.selected_options_record = {}
cartItemInput.selected_options_record[productOption.uid] = isTypename(productOption, [
'CustomizableRadioOption',
'CustomizableDropDownOption',
])
? value[0]
: value
} else {
if (!cartItemInput.customizable_options_entered)
cartItemInput.customizable_options_entered = {}
if (!cartItemInput.entered_options_record) cartItemInput.entered_options_record = {}

if (productOption.__typename === 'CustomizableDateOption') {
// Dates are not available in an iso format, so we can't really parse it.
// if (productOption.dateValue?.type === 'TIME') {
// cartItemInput.customizable_options_entered[productOption.uid] =
// cartItemInput.entered_options_record[productOption.uid] =
// `01-01-1970 ${cartItemCustomizableOptionValue[0].value}.000Z`
// }
// if (productOption.dateValue?.type === 'DATE_TIME') {
// console.log(`${cartItemCustomizableOptionValue[0].value}.000Z`)
// cartItemInput.customizable_options_entered[productOption.uid] =
// cartItemInput.entered_options_record[productOption.uid] =
// `${cartItemCustomizableOptionValue[0].value}`
// }
} else {
cartItemInput.customizable_options_entered[productOption.uid] =
cartItemInput.entered_options_record[productOption.uid] =
cartItemCustomizableOptionValue[0].value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ export type AddProductsToCartFormProps = {
sx?: SxProps<Theme>
redirect?: RedirectType
snackbarProps?: AddProductsToCartSnackbarProps

/** @deprecated Use snackbarProps.errorSnackbar instead */
errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
/** @deprecated Use snackbarProps.successSnackbar instead */
successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
/** @deprecated Use snackbarProps.disableSuccessSnackbar instead */
disableSuccessSnackbar?: boolean
} & UseFormGraphQlOptions<AddProductsToCartMutation, AddProductsToCartFields>

const name = 'AddProductsToCartForm'
Expand All @@ -45,17 +38,7 @@ const name = 'AddProductsToCartForm'
* - Redirects the user to the cart/checkout/added page after successful submission.
*/
export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
let {
children,
redirect,
onComplete,
sx,
disableSuccessSnackbar,
errorSnackbar,
successSnackbar,
snackbarProps,
...formProps
} = props
let { children, redirect, onComplete, sx, snackbarProps, ...formProps } = props
const router = useRouter()
const client = useApolloClient()
const crosssellsQuery = useRef<Promise<ApolloQueryResult<CrosssellsQuery>>>()
Expand All @@ -78,18 +61,16 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
cartId,
cartItems: cartItems
.filter((cartItem) => cartItem.sku && cartItem.quantity !== 0)
.map(({ customizable_options, customizable_options_entered, ...cartItem }) => ({
.map(({ selected_options_record = {}, entered_options_record = {}, ...cartItem }) => ({
...cartItem,
quantity: cartItem.quantity || 1,
selected_options: [
...(cartItem.selected_options ?? []).filter(nonNullable),
...Object.values(customizable_options ?? {})
.flat(1)
.filter(nonNullable),
...Object.values(selected_options_record).flat(1).filter(nonNullable),
],
entered_options: [
...(cartItem.entered_options ?? []).filter(nonNullable),
...Object.entries(customizable_options_entered ?? {}).map(([uid, value]) => {
...Object.entries(entered_options_record).map(([uid, value]) => {
if (value instanceof Date) {
const dateValue = value.toISOString().replace(/.000Z/, '').replace('T', ' ')
return { uid, value: dateValue }
Expand Down Expand Up @@ -147,12 +128,7 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
<Box component='form' onSubmit={submit} noValidate sx={sx} className={name}>
{children}
</Box>
<AddProductsToCartSnackbar
errorSnackbar={errorSnackbar}
successSnackbar={successSnackbar}
disableSuccessSnackbar={disableSuccessSnackbar}
{...snackbarProps}
/>
<AddProductsToCartSnackbar {...snackbarProps} />
</AddProductsToCartContext.Provider>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,21 @@ export type RedirectType = LiteralUnion<'added' | undefined | false, `/${string}

type Item = Simplify<
AddProductsToCartMutationVariables['cartItems'][number] & {
customizable_options?: Record<string, string | string[]>
customizable_options_entered?: Record<string, string | number | Date>
/**
* The value of the selected_options_record values will be added to the selected_options array.
*
* This format exists to prevent name collisions and without having to select by index in the
* selected_options array.
*/
selected_options_record?: Record<string, string | string[]>
/**
* The value of the entered_options_record entries will be coverted to entries for the
* entered_options array.
*
* This format exists to prevent name collisions and without having to select by index in the
* entered_options array.
*/
entered_options_record?: Record<string, string | number | Date>
}
>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
const maxLength = areaValue?.max_characters ?? undefined
const { control } = useFormAddProductsToCart()

const name = `cartItems.${index}.customizable_options_entered.${uid}` as const
const name = `cartItems.${index}.entered_options_record.${uid}` as const
if (!areaValue) return null

return (
<Box>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
<SectionHeader
labelLeft={
<>
{title} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<TextFieldElement
sx={{ width: '100%' }}
color='primary'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ export function CustomizableCheckboxOption(props: CustomizableCheckboxOptionProp

return (
<Box>
<SectionHeader labelLeft={label} sx={{ mt: 0 }} />
<SectionHeader
labelLeft={
<>
{label} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<ActionCardListForm
sx={(theme) => ({
mt: theme.spacings.xxs,
Expand All @@ -71,7 +78,7 @@ export function CustomizableCheckboxOption(props: CustomizableCheckboxOptionProp
: false,
}}
render={CustomizableCheckboxActionCard}
name={`cartItems.${index}.customizable_options.${uid}`}
name={`cartItems.${index}.selected_options_record.${uid}`}
items={(checkboxValue ?? []).filter(nonNullable).map((checkboxVal) => ({
productPrice,
currency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
const { uid, required, index, title, minDate, maxDate, dateValue, currency, productPrice } = props
const { control } = useFormAddProductsToCart()

const name = `cartItems.${index}.customizable_options_entered.${uid}` as const
const name = `cartItems.${index}.entered_options_record.${uid}` as const
if (!dateValue) return null

minDate?.setSeconds(0, 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useController } from '@graphcommerce/ecommerce-ui'
import { Money } from '@graphcommerce/magento-store'
import { filterNonNullableKeys, SectionHeader } from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
import { Box, MenuItem, TextField } from '@mui/material'
import { useFormAddProductsToCart } from '../AddProductsToCart'
import type { OptionTypeRenderer } from './CustomizableAreaOption'
Expand All @@ -17,18 +18,26 @@ export function CustomizableDropDownOption(props: CustomizableDropDownOptionProp
field: { onChange, value, ref, ...field },
fieldState: { invalid, error },
} = useController({
name: `cartItems.${index}.customizable_options.${uid}`,
name: `cartItems.${index}.selected_options_record.${uid}`,
rules: {
required: Boolean(required),
required: required
? i18n._(/* i18n*/ 'Please select a value for ‘{label}’', { label: title })
: false,
},
control,
defaultValue: '',
})

return (
<Box>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />

<SectionHeader
labelLeft={
<>
{title} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<TextField
sx={{
width: '100%',
Expand All @@ -44,7 +53,6 @@ export function CustomizableDropDownOption(props: CustomizableDropDownOptionProp
inputRef={ref}
onChange={(event) => onChange(event.target.value)}
select
required={Boolean(required)}
error={invalid}
helperText={error?.message}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,23 @@ export type CustomizableFieldOptionProps = React.ComponentProps<
>

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

const name = `cartItems.${index}.customizable_options_entered.${uid}` as const
const name = `cartItems.${index}.entered_options_record.${uid}` as const
if (!fieldValue) return null

const maxLength = fieldValue.max_characters ?? 0
return (
<Box>
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
<SectionHeader
labelLeft={
<>
{label} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<TextFieldElement
sx={{ width: '100%' }}
color='primary'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ export function CustomizableMultipleOption(props: CustomizableMultipleOptionProp

return (
<Box>
<SectionHeader labelLeft={label} sx={{ mt: 0 }} />
<SectionHeader
labelLeft={
<>
{label} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<ActionCardListForm
sx={(theme) => ({
mt: theme.spacings.xxs,
Expand All @@ -66,7 +73,7 @@ export function CustomizableMultipleOption(props: CustomizableMultipleOptionProp
}}
control={control}
render={CustomizableMultipleActionCard}
name={`cartItems.${index}.customizable_options.${uid}`}
name={`cartItems.${index}.selected_options_record.${uid}`}
items={filterNonNullableKeys(multipleValue, ['title']).map((multipleVal) => ({
productPrice,
currency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,22 @@ export function CustomizableRadioOption(props: CustomizableRadioOptionProps) {

return (
<Box>
<SectionHeader labelLeft={label} sx={{ mt: 0 }} />
<SectionHeader
labelLeft={
<>
{label} {required && ' *'}
</>
}
sx={{ mt: 0 }}
/>
<ActionCardListForm
sx={(theme) => ({
mt: theme.spacings.xxs,
})}
layout='stack'
control={control}
render={CustomizableRadioActionCard}
name={`cartItems.${index}.customizable_options.${uid}`}
name={`cartItems.${index}.selected_options_record.${uid}`}
rules={{
required: required
? i18n._(/* i18n*/ 'Please select a value for ‘{label}’', { label })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export function ProductCustomizable(props: ProductCustomizableProps) {
key={option.uid}
renderer={{ ...defaultRenderer, ...renderer }}
{...option}
optionIndex={option.sort_order + 100}
index={index}
currency={product.price_range.minimum_price.final_price.currency}
productPrice={product.price_range.minimum_price.final_price.value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ export type AnyOption = NonNullable<
>

export type OptionValueSelector = {
[T in AnyOption as T['__typename']]: (
option: T,
) => CustomizableProductOptionBase | CustomizableProductOptionBase[]
[T in AnyOption as T['__typename']]: (option: AnyOption) => CustomizableProductOptionBase[]
}

type MissingOptionValueSelectors = Omit<
Expand All @@ -44,12 +42,12 @@ type Selectors = Simplify<
>

export const productCustomizableSelectors = {
CustomizableAreaOption: (o: CustomizableAreaOptionFragment) => o.areaValue,
CustomizableAreaOption: (o: CustomizableAreaOptionFragment) => [o.areaValue],
CustomizableCheckboxOption: (o: CustomizableCheckboxOptionFragment) => o.checkboxValue,
CustomizableFileOption: (o: CustomizableFileOptionFragment) => o.fileValue,
CustomizableDateOption: (o: CustomizableDateOptionFragment) => o.dateValue,
CustomizableFileOption: (o: CustomizableFileOptionFragment) => [o.fileValue],
CustomizableDateOption: (o: CustomizableDateOptionFragment) => [o.dateValue],
CustomizableDropDownOption: (o: CustomizableDropDownOptionFragment) => o.dropdownValue,
CustomizableFieldOption: (o: CustomizableFieldOptionFragment) => o.fieldValue,
CustomizableFieldOption: (o: CustomizableFieldOptionFragment) => [o.fieldValue],
CustomizableMultipleOption: (o: CustomizableMultipleOptionFragment) => o.multipleValue,
CustomizableRadioOption: (o: CustomizableRadioOptionFragment) => o.radioValue,
}
Expand Down
Loading

0 comments on commit 02dcc99

Please sign in to comment.