Skip to content
12 changes: 9 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"js/ts.tsdk.path": "node_modules/typescript/lib",
"[typescriptreact][typescript][javascriptreact][javascript][json][jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.formatOnSave": true,
"javascript.format.enable": true,
"[js, ts].format.enable": true,
"eslint.workingDirectories": [
"packages/react-components",
"packages/docs",
Expand All @@ -17,5 +20,8 @@
"scss.validate": false,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
}
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
}
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useNx": false,
"npmClient": "pnpm",
"version": "4.29.4",
"version": "4.29.5-beta.1",
"command": {
"version": {
"preid": "beta"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"clean": "pnpm dlx rimraf --glob **/node_modules **/pnpm-lock.yaml"
},
"devDependencies": {
"@biomejs/biome": "^2.3.11",
"@biomejs/biome": "^2.4.6",
"husky": "^9.1.7",
"lerna": "^9.0.3",
"typescript": "^5.9.3"
Expand Down
2 changes: 1 addition & 1 deletion packages/react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@commercelayer/react-components",
"version": "4.29.4",
"version": "4.29.5-beta.1",
"description": "The Official Commerce Layer React Components",
"main": "lib/cjs/index.js",
"module": "lib/esm/index.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: Avoid infinite loop */
import type { Order } from "@commercelayer/sdk"
import isFunction from "lodash/isFunction"
import {
Expand Down Expand Up @@ -87,7 +88,6 @@ export function PlaceOrderButton(props: Props): JSX.Element {
} = useContext(PaymentMethodContext)
const { order, setOrderErrors, errors } = useContext(OrderContext)
const isFree = order?.total_amount_with_taxes_cents === 0
// biome-ignore lint/correctness/useExhaustiveDependencies: Need to test
useEffect(() => {
if (isFree && !isPermitted) {
setNotPermitted(false)
Expand All @@ -97,7 +97,7 @@ export function PlaceOrderButton(props: Props): JSX.Element {
if (paymentType === currentPaymentMethodType && paymentType) {
const paymentSourceStatus =
// @ts-expect-error no type
order?.payment_source?.payment_response?.status?.toLowerCase()
order?.payment_source?.payment_response?.status?.toLowerCase?.()
const card = getCardDetails({
customerPayment: {
payment_source: paymentSource,
Expand Down Expand Up @@ -157,7 +157,6 @@ export function PlaceOrderButton(props: Props): JSX.Element {
setNotPermitted(false)
}
}, [errors?.length, paymentMethodErrors?.length])
// biome-ignore lint/correctness/useExhaustiveDependencies: Need to test
useEffect(() => {
// PayPal redirect flow
if (
Expand All @@ -170,7 +169,6 @@ export function PlaceOrderButton(props: Props): JSX.Element {
handleClick()
}
}, [options?.paypalPayerId, paymentType != null])
// biome-ignore lint/correctness/useExhaustiveDependencies: Need to test
useEffect(() => {
// Stripe redirect flow
if (
Expand Down Expand Up @@ -221,7 +219,6 @@ export function PlaceOrderButton(props: Props): JSX.Element {
paymentType != null,
order?.payment_source != null,
])
// biome-ignore lint/correctness/useExhaustiveDependencies: Need to test
useEffect(() => {
if (order?.status != null && ["draft", "pending"].includes(order?.status)) {
// Adyen redirect flow
Expand Down Expand Up @@ -489,7 +486,7 @@ export function PlaceOrderButton(props: Props): JSX.Element {
: paymentSource
const checkPaymentSourceStatus =
// @ts-expect-error no type
checkPaymentSource?.payment_response?.status?.toLowerCase()
checkPaymentSource?.payment_response?.status?.toLowerCase?.()
const card =
paymentType &&
getCardDetails({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import OrderContext from "#context/OrderContext"
import CommerceLayerContext from "#context/CommerceLayerContext"
import { setPlaceOrder } from "../../reducers/PlaceOrderReducer"
import useCustomContext from "#utils/hooks/useCustomContext"
import { useOrganizationConfig } from "#utils/organization"

interface Props {
children: ReactNode
Expand All @@ -43,6 +44,10 @@ export function PlaceOrderContainer(props: Props): JSX.Element {
key: "order",
})
const config = useContext(CommerceLayerContext)
const organizationConfig = useOrganizationConfig({
accessToken: config.accessToken,
endpoint: config.endpoint,
})
// biome-ignore lint/correctness/useExhaustiveDependencies: Infinite loop
useEffect(() => {
if (!include?.includes("shipments.available_shipping_methods")) {
Expand Down Expand Up @@ -93,9 +98,11 @@ export function PlaceOrderContainer(props: Props): JSX.Element {
options: {
...options,
},
privacyUrl: organizationConfig?.urls?.privacy,
termsUrl: organizationConfig?.urls?.terms,
})
}
}, [order, include, includeLoaded])
}, [order, include, includeLoaded, organizationConfig])
const contextValue = {
...state,
setPlaceOrder: async ({
Expand Down Expand Up @@ -130,6 +137,8 @@ export function PlaceOrderContainer(props: Props): JSX.Element {
options: {
...options,
},
privacyUrl: organizationConfig?.urls?.privacy,
termsUrl: organizationConfig?.urls?.terms,
})
},
setButtonRef: (ref: RefObject<HTMLButtonElement | null>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
import OrderContext from '#context/OrderContext'
import PlaceOrderContext from '#context/PlaceOrderContext'
import { useContext, useEffect, useState, type JSX } from 'react';
import BaseInput, { type BaseInputProps } from '../utils/BaseInput'
import { type JSX, useContext, useEffect, useState } from "react"
import CommerceLayerContext from "#context/CommerceLayerContext"
import OrderContext from "#context/OrderContext"
import PlaceOrderContext from "#context/PlaceOrderContext"
import { useOrganizationConfig } from "#utils/organization"
import BaseInput, { type BaseInputProps } from "../utils/BaseInput"

export function PrivacyAndTermsCheckbox(
props: Partial<BaseInputProps>
props: Partial<BaseInputProps>,
): JSX.Element {
const { accessToken, endpoint } = useContext(CommerceLayerContext)
const { order } = useContext(OrderContext)
const { placeOrderPermitted } = useContext(PlaceOrderContext)
const [forceDisabled, setForceDisabled] = useState(true)
const [checked, setChecked] = useState(false)
const fieldName = 'privacy-terms'
const handleChange: any = (e: React.ChangeEvent<HTMLInputElement>): void => {
const v = e.target?.checked
const fieldName = "privacy-terms"
const organizationConfig = useOrganizationConfig({ accessToken, endpoint })

const privacyUrl = order?.privacy_url ?? organizationConfig?.urls?.privacy
const termsUrl = order?.terms_url ?? organizationConfig?.urls?.terms

const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
): void => {
const v = (e.target as HTMLInputElement)?.checked
setChecked(v)
localStorage.setItem(fieldName, v.toString())
if (placeOrderPermitted) placeOrderPermitted()
}

// biome-ignore lint/correctness/useExhaustiveDependencies: If we add checked to the dependencies, it creates an wrong behavior to disable the place order button.
useEffect(() => {
if (order?.privacy_url && order?.terms_url) setForceDisabled(false)
if (privacyUrl && termsUrl) setForceDisabled(false)
if (!checked) localStorage.setItem(fieldName, checked.toString())
return () => {
setForceDisabled(true)
localStorage.removeItem(fieldName)
}
}, [order?.privacy_url, order?.terms_url])
}, [privacyUrl, termsUrl])
return (
<BaseInput
type='checkbox'
type="checkbox"
name={fieldName}
disabled={forceDisabled}
onChange={handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export function PaymentMethod({
const isSingle = paymentMethods.length === 1
const paymentSourceStatus = paymentSource
? // @ts-expect-error no type
paymentSource.payment_response?.status?.toLowerCase()
paymentSource.payment_response?.status?.toLowerCase?.()
: null
if (isSingle) {
const [paymentMethod] = paymentMethods ?? []
Expand Down Expand Up @@ -232,7 +232,7 @@ export function PaymentMethod({
const isSingle = paymentMethods.length === 1
const paymentSourceStatus = paymentSource
? // @ts-expect-error no type
paymentSource.payment_response?.status?.toLowerCase()
paymentSource.payment_response?.status?.toLowerCase?.()
: null
if (isSingle && autoSelectSinglePaymentMethod) {
if (paymentSource) {
Expand Down Expand Up @@ -269,7 +269,7 @@ export function PaymentMethod({
useEffect(() => {
const status =
// @ts-expect-error no type
order?.payment_source?.payment_response?.status?.toLowerCase()
order?.payment_source?.payment_response?.status
// If showLoader is undefined, we don't change the loading
if (showLoader && status) {
if (status.toLowerCase() === "declined") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ export function AdyenPayment({
ref.current as unknown as FormEvent<HTMLFormElement>,
)
}
setPaymentMethodErrors([])
setPaymentRef({ ref })
if (placeOrderButtonRef?.current != null) {
placeOrderButtonRef.current.disabled = false
}
}
}
}
Expand Down Expand Up @@ -638,6 +642,7 @@ export function AdyenPayment({
},
onSelect: (component) => {
const id: string = component._id
const isValid = component.isValid
if (id.search("scheme") === -1) {
if (ref.current) {
/**
Expand All @@ -659,6 +664,20 @@ export function AdyenPayment({
setPaymentRef({ ref })
}
}
if (isValid) {
if (ref.current) {
ref.current.onsubmit = async () => {
return await handleSubmit(
ref.current as unknown as FormEvent<HTMLFormElement>,
)
}
setPaymentMethodErrors([])
setPaymentRef({ ref })
if (placeOrderButtonRef?.current != null) {
placeOrderButtonRef.current.disabled = false
}
}
}
if (onSelect) {
onSelect(component)
}
Expand All @@ -682,7 +701,7 @@ export function AdyenPayment({
setPaymentRef({ ref: { current: null } })
setLoadAdyen(false)
}
}, [clientKey, ref != null, status])
}, [clientKey, ref != null, status, setPaymentMethodErrors != null])
return !clientKey && !loadAdyen && !checkout ? null : (
<form
ref={ref}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: Avoid infinite loop */
import { type JSX, useContext, useEffect, useState } from "react"
import CustomerContext from "#context/CustomerContext"
import OrderContext from "#context/OrderContext"
Expand Down Expand Up @@ -51,7 +52,7 @@ export function PaymentSource(props: PaymentSourceProps): JSX.Element {
currentCustomerPaymentSourceId === paymentSource?.id
const checkPaymentSourceStatus =
// @ts-expect-error no type
paymentSource?.payment_response?.status?.toLowerCase()
paymentSource?.payment_response?.status?.toLowerCase?.()
if (readonly) {
setShow(true)
setShowCard(true)
Expand Down
9 changes: 7 additions & 2 deletions packages/react-components/src/reducers/PlaceOrderReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { setCustomerOrderParam } from "#utils/localStorage"
import { isDoNotShip, shipmentsFilled } from "#utils/shipments"
import { updateOrderSubscriptionCustomerPaymentSource } from "#utils/updateOrderSubscriptionCustomerPaymentSource"
import type { PaymentResource, PaymentSourceType } from "./PaymentMethodReducer"

export type PlaceOrderActionType =
| "setErrors"
| "setPlaceOrderPermitted"
Expand Down Expand Up @@ -105,17 +104,23 @@ interface TPlaceOrderPermittedParams {
order?: Order
// TODO: Remove it soon
options?: PlaceOrderOptions
privacyUrl?: string | null
termsUrl?: string | null
}

export function placeOrderPermitted({
config,
order,
dispatch,
options,
privacyUrl,
termsUrl,
}: TPlaceOrderPermittedParams): void {
if (order && config) {
let isPermitted = true
if (order.privacy_url && order.terms_url) {
const resolvedPrivacyUrl = privacyUrl ?? order.privacy_url
const resolvedTermsUrl = termsUrl ?? order.terms_url
if (resolvedPrivacyUrl && resolvedTermsUrl) {
isPermitted = localStorage.getItem("privacy-terms") === "true"
}
const billingAddress = order.billing_address
Expand Down
Loading
Loading