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

META-INF.resources.js.DiagramWithAutomapping.Diagram.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 ClayLoadingIndicator from '@clayui/loading-indicator';
import {useIsMounted} from '@liferay/frontend-js-react-web';
import classNames from 'classnames';
import {useCommerceAccount, useCommerceCart} from 'commerce-frontend-js';
import {openToast} from 'frontend-js-web';
import PropTypes from 'prop-types';
import React, {
	useCallback,
	useEffect,
	useLayoutEffect,
	useRef,
	useState,
} from 'react';

import AdminTooltipContent from '../components/AdminTooltipContent';
import DiagramFooter from '../components/DiagramFooter';
import Sequence from '../components/Sequence';
import StorefrontTooltipContent from '../components/StorefrontTooltipContent';
import TooltipProvider from '../components/TooltipProvider';
import {DIAGRAM_TABLE_EVENTS} from '../utilities/constants';
import {
	deleteMappedProduct,
	getMappedProducts,
	saveMappedProduct,
} from '../utilities/data';
import {formatMappedProduct} from '../utilities/index';
import D3Handler from './D3Handler';

import '../../css/diagram.scss';
import {useEscapeKeyHandler} from '../utilities/hooks';

function Diagram({
	cartId: initialCartId,
	channelGroupId,
	channelId,
	commerceAccountId: initialAccountId,
	commerceCurrencyCode,
	datasetDisplayId,
	imageURL,
	isAdmin,
	orderUUID,
	pinsCSSSelectors,
	productBaseURL,
	productId,
}) {
	const [mappedProducts, setMappedProducts] = useState(null);
	const [tooltipData, setTooltipData] = useState(false);
	const [currentZoom, setCurrentZoom] = useState(1);
	const [expanded, setExpanded] = useState(false);
	const [labels, setLabels] = useState([]);
	const [selectedText, setSelectedText] = useState(null);
	const [highlightedTexts, setHighlightedTexts] = useState([]);
	const chartInstanceRef = useRef(null);
	const commerceAccount = useCommerceAccount({id: initialAccountId});
	const commerceCart = useCommerceCart({id: initialCartId});
	const isMounted = useIsMounted();
	const svgRef = useRef(null);
	const wrapperRef = useRef(null);
	const zoomHandlerRef = useRef(null);

	useEscapeKeyHandler(
		expanded,
		!!tooltipData,
		() => setExpanded(false),
		() => setTooltipData(null)
	);

	useEffect(() => {
		getMappedProducts(
			productId,
			!isAdmin && channelId,
			'',
			1,
			200,
			commerceAccount.id
		).then(({items}) => setMappedProducts(items));
	}, [channelId, isAdmin, productId, commerceAccount]);

	useEffect(() => {
		chartInstanceRef.current?.updatePins(mappedProducts);
	}, [mappedProducts]);

	useEffect(() => {
		if (!tooltipData) {
			setSelectedText(null);
		}
	}, [tooltipData]);

	const handleClickOnLabel = useCallback(
		({target}) => {
			const sequence = target.textContent;

			const mappedProduct = mappedProducts.find(
				(mappedProduct) => mappedProduct.sequence === sequence
			);

			const selectedPin = mappedProduct ? {mappedProduct} : null;

			setTooltipData({selectedPin, sequence, target});
			setSelectedText(target);
		},
		[mappedProducts]
	);

	const handleMouseEnterOnLabel = useCallback(
		({target}) => {
			const hightlightedLabels = labels.filter(
				(label) => label.textContent === target.textContent
			);

			setHighlightedTexts(hightlightedLabels);
		},
		[labels]
	);

	const handleMouseLeaveOnLabel = () => {
		setHighlightedTexts([]);
	};

	useEffect(() => {
		labels.forEach((label) => {
			label.addEventListener('click', handleClickOnLabel);
			label.addEventListener('mouseenter', handleMouseEnterOnLabel);
			label.addEventListener('mouseleave', handleMouseLeaveOnLabel);
		});

		return () => {
			labels.forEach((label) => {
				label.removeEventListener('click', handleClickOnLabel);
				label.removeEventListener(
					'mouseenter',
					handleMouseEnterOnLabel
				);
				label.removeEventListener(
					'mouseleave',
					handleMouseLeaveOnLabel
				);
			});
		};
	}, [handleClickOnLabel, handleMouseEnterOnLabel, labels]);

	useEffect(() => {
		function handleSelectPinByTable({diagramProductId, product}) {
			if (diagramProductId === productId) {
				const pinNode = labels.find(
					(label) => label.textContent === product.sequence
				);
				const selectedProduct = mappedProducts.find(
					(mappedProduct) =>
						mappedProduct.sequence === product.sequence
				);

				if (selectedProduct) {
					setHighlightedTexts([]);

					chartInstanceRef.current.recenterOnPin(pinNode).then(() => {
						setTooltipData({
							selectedPin: {mappedProduct: selectedProduct},
							sequence: pinNode.textContent,
							target: pinNode,
						});
						setSelectedText(pinNode);
					});
				}
			}
		}

		function handlePinHighlightByTable({diagramProductId, sequence}) {
			if (diagramProductId === productId) {
				handleMouseEnterOnLabel({target: {textContent: sequence}});
			}
		}

		function handleRemovePinHighlightByTable({diagramProductId}) {
			if (diagramProductId === productId) {
				setHighlightedTexts([]);
			}
		}

		function handlePinsUpdatedByTable() {
			getMappedProducts(
				productId,
				!isAdmin && channelId,
				'',
				1,
				200,
				commerceAccount.id
			).then(({items}) => setMappedProducts(items));
		}

		Liferay.on(DIAGRAM_TABLE_EVENTS.SELECT_PIN, handleSelectPinByTable);
		Liferay.on(
			DIAGRAM_TABLE_EVENTS.HIGHLIGHT_PIN,
			handlePinHighlightByTable
		);
		Liferay.on(
			DIAGRAM_TABLE_EVENTS.REMOVE_PIN_HIGHLIGHT,
			handleRemovePinHighlightByTable
		);
		Liferay.on(
			DIAGRAM_TABLE_EVENTS.TABLE_UPDATED,
			handlePinsUpdatedByTable
		);

		return () => {
			Liferay.detach(
				DIAGRAM_TABLE_EVENTS.SELECT_PIN,
				handleSelectPinByTable
			);
			Liferay.detach(
				DIAGRAM_TABLE_EVENTS.HIGHLIGHT_PIN,
				handlePinHighlightByTable
			);
			Liferay.detach(
				DIAGRAM_TABLE_EVENTS.REMOVE_PIN_HIGHLIGHT,
				handleRemovePinHighlightByTable
			);
			Liferay.detach(
				DIAGRAM_TABLE_EVENTS.TABLE_UPDATED,
				handlePinsUpdatedByTable
			);
		};
	}, [
		channelId,
		commerceAccount,
		isAdmin,
		handleMouseEnterOnLabel,
		labels,
		mappedProducts,
		productId,
	]);

	useLayoutEffect(() => {
		chartInstanceRef.current = new D3Handler(
			svgRef.current,
			imageURL,
			isAdmin,
			pinsCSSSelectors,
			setLabels,
			(scale) => {
				setTooltipData(null);

				setCurrentZoom(scale);
			},
			zoomHandlerRef.current
		);
	}, [imageURL, isAdmin, pinsCSSSelectors]);

	const handleMappedProductSave = (
		type,
		quantity,
		sequence,
		linkedProduct
	) => {
		const linkedProductDetails = formatMappedProduct(
			type,
			quantity,
			sequence,
			linkedProduct
		);

		const update = Boolean(tooltipData.selectedPin?.mappedProduct.id);

		return saveMappedProduct(
			update ? tooltipData.selectedPin.mappedProduct.id : null,
			linkedProductDetails,
			sequence,
			productId
		)
			.then((newMappedProduct) => {
				if (!isMounted()) {
					return;
				}

				setMappedProducts((mappedProducts) => {
					const updatedMappedProducts = mappedProducts.map(
						(mappedProduct) =>
							mappedProduct.sequence === newMappedProduct.sequence
								? {
										...mappedProduct,
										mappedProduct:
											newMappedProduct.mappedProduct,
										quantity: newMappedProduct.quantity,
									}
								: mappedProduct
					);

					return update
						? updatedMappedProducts.map((updatedMappedProduct) =>
								updatedMappedProduct.id === newMappedProduct.id
									? newMappedProduct
									: updatedMappedProduct
							)
						: [...updatedMappedProducts, newMappedProduct];
				});

				openToast({
					message: update
						? Liferay.Language.get('pin-updated')
						: Liferay.Language.get('pin-created'),
					type: 'success',
				});
			})
			.catch((error) => {
				openToast({
					message: error.message || error,
					type: 'danger',
				});

				throw error;
			});
	};

	const handleMappedProductDelete = () => {
		return deleteMappedProduct(tooltipData.selectedPin.mappedProduct.id)
			.then(() => {
				if (!isMounted()) {
					return;
				}

				setMappedProducts((mappedProducts) =>
					mappedProducts.filter(
						(mappedProduct) =>
							mappedProduct.id !==
							tooltipData.selectedPin.mappedProduct.id
					)
				);

				openToast({
					message: Liferay.Language.get('pin-deleted'),
					type: 'success',
				});
			})
			.catch((error) => {
				openToast({
					message: error.message || error,
					type: 'danger',
				});

				throw error;
			});
	};

	return (
		
{highlightedTexts.map((highlightedText) => ( ))} {selectedText && ( )}
{tooltipData && ( setTooltipData(null)} target={tooltipData.target} > {isAdmin ? ( setTooltipData(null)} datasetDisplayId={datasetDisplayId} onDelete={handleMappedProductDelete} onSave={handleMappedProductSave} productId={productId} readOnlySequence={true} {...tooltipData} /> ) : ( )} )}
); } Diagram.propTypes = { cartId: PropTypes.string, channelGroupId: PropTypes.string, channelId: PropTypes.string, commerceAccountId: PropTypes.string, commerceCurrencyCode: PropTypes.string, datasetDisplayId: PropTypes.string, diagramId: PropTypes.string.isRequired, imageURL: PropTypes.string.isRequired, isAdmin: PropTypes.bool.isRequired, orderUUID: PropTypes.string, pinsCSSSelectors: PropTypes.array.isRequired, productBaseURL: PropTypes.string, productId: PropTypes.string.isRequired, }; export default Diagram;




© 2015 - 2024 Weber Informatics LLC | Privacy Policy