All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.components.add_to_cart.AddToCart.js Maven / Gradle / Ivy

The newest version!
/**
 * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

import classnames from 'classnames';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';

import ServiceProvider from '../../ServiceProvider/index';
import {
	CART_PRODUCT_QUANTITY_CHANGED,
	CP_INSTANCE_CHANGED,
	CP_QUANTITY_SELECTOR_CHANGED,
	CP_UNIT_OF_MEASURE_SELECTOR_CHANGED,
} from '../../utilities/eventsDefinitions';
import {useCommerceAccount, useCommerceCart} from '../../utilities/hooks';
import {getMinQuantity, getMultipleQuantity} from '../../utilities/quantities';
import QuantitySelector from '../quantity_selector/QuantitySelector';
import UnitOfMeasureSelector from '../unit_of_measure_selector/UnitOfMeasureSelector';
import AddToCartButton from './AddToCartButton';
import {ALL} from './constants';

const CartResource = ServiceProvider.DeliveryCartAPI('v1');

function getQuantity(settings, skuUnitOfMeasure) {
	if (settings?.productConfiguration?.allowedOrderQuantities?.length) {
		return Math.min(
			...settings.productConfiguration.allowedOrderQuantities
		);
	}

	return Number(
		getMinQuantity(
			skuUnitOfMeasure
				? settings?.productConfiguration?.minOrderQuantity
				: Math.ceil(settings?.productConfiguration?.minOrderQuantity),
			getMultipleQuantity(
				skuUnitOfMeasure?.incrementalOrderQuantity,
				settings?.productConfiguration?.multipleOrderQuantity,
				skuUnitOfMeasure?.precision || 0
			),
			skuUnitOfMeasure?.precision || 0
		)
	);
}

function AddToCart({
	accountId: initialAccountId,
	cartId: initialCartId,
	cartUUID: initialCartUUID,
	channel,
	cpInstance: initialCpInstance,
	disabled: initialDisabled,
	productId,
	settings,
	showOrderTypeModal,
	showOrderTypeModalURL,
}) {
	const account = useCommerceAccount({id: initialAccountId});
	const cart = useCommerceCart(
		{
			UUID: initialCartUUID,
			id: initialCartId,
		},
		channel.groupId
	);
	const [cpInstance, setCpInstance] = useState({
		...initialCpInstance,
		quantity: getQuantity(settings, initialCpInstance.skuUnitOfMeasure),
		validQuantity: true,
	});
	const inputRef = useRef(null);

	const inputDisabled = useMemo(() => {
		if (
			initialDisabled ||
			!account?.id ||
			cpInstance.disabled ||
			cpInstance.published === false ||
			cpInstance.purchasable === false ||
			(cpInstance.availability?.stockQuantity !== undefined &&
				cpInstance.backOrderAllowed === false &&
				cpInstance.availability?.stockQuantity <= 0)
		) {
			return true;
		}

		return false;
	}, [account, cpInstance, initialDisabled]);

	const buttonDisabled = useMemo(() => {
		if (inputDisabled || !cpInstance.quantity) {
			return true;
		}

		return false;
	}, [cpInstance, inputDisabled]);

	useEffect(() => {
		setCpInstance({
			...initialCpInstance,
			quantity: getQuantity(settings, initialCpInstance.skuUnitOfMeasure),
			validQuantity: true,
		});
	}, [initialCpInstance, settings]);

	const handleCPInstanceReplaced = useCallback(
		({cpInstance: incomingCpInstance}) => {
			function updateInCartState(inCart) {
				setCpInstance((cpInstance) => ({
					...cpInstance,
					availability: incomingCpInstance.availability,
					backOrderAllowed: incomingCpInstance.backOrderAllowed,
					disabled: incomingCpInstance.disabled,
					inCart,
					published: incomingCpInstance.published,
					purchasable: incomingCpInstance.purchasable,
					skuId: incomingCpInstance.skuId,
					skuOptions: Array.isArray(incomingCpInstance.skuOptions)
						? incomingCpInstance.skuOptions
						: JSON.parse(incomingCpInstance.skuOptions),
					stockQuantity:
						incomingCpInstance.availability.stockQuantity,
				}));
			}

			if (cart.id) {
				updateInCartState(cpInstance.inCart);
			}
			else {
				updateInCartState(false);
			}
		},
		[cart.id, cpInstance]
	);

	useEffect(() => {
		function handleQuantityChanged({quantity, skuId}) {
			setCpInstance((cpInstance) => ({
				...cpInstance,
				inCart:
					skuId === cpInstance.skuId || skuId === ALL
						? Boolean(quantity)
						: cpInstance.inCart,
			}));
		}

		function handleUOMChanged({unitOfMeasure}) {
			if (cart.id) {
				CartResource.getItemsByCartId(cart.id).then(({items}) => {
					let inCart = false;

					if (unitOfMeasure) {
						inCart = items.some(
							({skuId, skuUnitOfMeasure}) =>
								cpInstance.skuId === skuId &&
								skuUnitOfMeasure?.key &&
								unitOfMeasure?.key === skuUnitOfMeasure?.key
						);
					}
					else {
						inCart = items.some(({skuId, skuUnitOfMeasure}) => {
							return (
								cpInstance.skuId === skuId &&
								!skuUnitOfMeasure?.key
							);
						});
					}

					setCpInstance((cpInstance) => ({
						...cpInstance,
						inCart,
						skuUnitOfMeasure: unitOfMeasure,
					}));
				});
			}
			else {
				setCpInstance((cpInstance) => ({
					...cpInstance,
					inCart: false,
					skuUnitOfMeasure: unitOfMeasure,
				}));
			}
		}

		Liferay.on(CART_PRODUCT_QUANTITY_CHANGED, handleQuantityChanged);

		Liferay.on(
			`${settings.namespace}${CP_INSTANCE_CHANGED}`,
			handleCPInstanceReplaced
		);

		Liferay.on(
			`${settings.namespace}${CP_UNIT_OF_MEASURE_SELECTOR_CHANGED}`,
			handleUOMChanged
		);

		return () => {
			Liferay.detach(
				CART_PRODUCT_QUANTITY_CHANGED,
				handleQuantityChanged
			);

			Liferay.detach(
				`${settings.namespace}${CP_INSTANCE_CHANGED}`,
				handleCPInstanceReplaced
			);

			Liferay.detach(
				`${settings.namespace}${CP_UNIT_OF_MEASURE_SELECTOR_CHANGED}`,
				handleUOMChanged
			);
		};
	}, [cart.id, cpInstance.skuId, handleCPInstanceReplaced, settings]);

	const spaceDirection = settings.inline ? 'ml' : 'mt';
	const spacer = 0;

	return (
		
{ setCpInstance((cpInstance) => ({ ...cpInstance, quantity, validQuantity: !errors.length, })); Liferay.fire( `${settings.namespace}${CP_QUANTITY_SELECTOR_CHANGED}`, {errors, quantity} ); }} quantity={cpInstance.quantity} ref={inputRef} size={settings.size} step={settings.productConfiguration?.multipleOrderQuantity} unitOfMeasure={cpInstance.skuUnitOfMeasure} /> {settings.showUnitOfMeasureSelector && ( )}
{ setCpInstance((cpInstance) => ({ ...cpInstance, inCart: true, })); }} onClick={ cpInstance.validQuantity ? null : (event) => { event.preventDefault(); inputRef.current?.focus(); } } settings={settings} showOrderTypeModal={showOrderTypeModal} showOrderTypeModalURL={showOrderTypeModalURL} />
); } AddToCart.propTypes = { accountId: PropTypes.number.isRequired, cartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), cpInstance: PropTypes.shape({ skuId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, skuOptions: PropTypes.array, }), disabled: PropTypes.bool, productId: PropTypes.number, settings: PropTypes.shape({ alignment: PropTypes.oneOf(['center', 'left', 'right', 'full-width']), inline: PropTypes.bool, namespace: PropTypes.string, productConfiguration: PropTypes.shape({ allowedOrderQuantities: PropTypes.arrayOf(PropTypes.number), maxOrderQuantity: PropTypes.number, minOrderQuantity: PropTypes.number, multipleOrderQuantity: PropTypes.number, }), showUnitOfMeasureSelector: PropTypes.bool, size: PropTypes.oneOf(['lg', 'md', 'sm']), }), showOrderTypeModal: PropTypes.bool, showOrderTypeModalURL: PropTypes.string, }; export default AddToCart;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy