Guest User

Untitled

a guest
Jan 20th, 2024
249
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 51.30 KB | None | 0 0
  1. import React, { useMemo, Fragment, Suspense, useState, useEffect } from 'react';
  2. import { Link, useHistory } from 'react-router-dom';
  3. import { FormattedMessage, useIntl } from 'react-intl';
  4. import { arrayOf, bool, number, shape, string } from 'prop-types';
  5. import { Form } from 'informed';
  6. import {
  7. Info,
  8. ChevronRight,
  9. ChevronDown as ArrowDown,
  10. ChevronUp as ArrowUp,
  11. Check
  12. } from 'react-feather';
  13. import LoadingIndicator from '../LoadingIndicator';
  14. // import Price from '@magento/venia-ui/lib/components/Price';
  15.  
  16. import { useProductFullDetail } from '../../talons/ProductFullDetail/useProductFullDetail';
  17. import { isProductConfigurable } from '@magento/peregrine/lib/util/isProductConfigurable';
  18.  
  19. import { useStyle } from '@magento/venia-ui/lib/classify';
  20. import Breadcrumbs from '../Breadcrumbs';
  21. import Carousel from '../ProductImageCarousel';
  22. import FormError from '@magento/venia-ui/lib/components/FormError';
  23. import QuantityStepper from '@magento/venia-ui/lib/components/QuantityStepper';
  24. import RichContent from '@magento/venia-ui/lib/components/RichContent/richContent';
  25. import { ProductOptionsShimmer } from '@magento/venia-ui/lib/components/ProductOptions';
  26. import CustomAttributes from '@magento/venia-ui/lib/components/ProductFullDetail/CustomAttributes';
  27. import defaultClasses from './productFullDetail.module.css';
  28. import shareIcon from './ShareIcon.svg';
  29. import reviewsIcon from './ReviewsIcon.svg';
  30. import Field from '../Field';
  31. import timeIcon from './TimeIcon.svg';
  32.  
  33. import CompleteLook from './components/CompleteLook';
  34. import LinkButton from '@magento/venia-ui/lib/components/LinkButton';
  35. import Promise from './components/Promise';
  36.  
  37. import DropdownBlock from './components/DropdownBlock';
  38. import TrustedPartners from './components/TrustedPartners';
  39. import apiClient from '../../api';
  40.  
  41. import TrendingProducts from '../HomePage/components/TrendingProducts';
  42. import ShopPdpCategory from './components/PDPCategory/ShopByPdpCategory';
  43. import Price, { CurrencyCodeUI } from '../Price/price';
  44. import TryAtHomeSection from './components/TryAtHomeSection';
  45. import './index.css';
  46. import { BrowserPersistence } from '@magento/peregrine/lib/util';
  47. import ProductTags from '../Common/productTags';
  48. import { useWindowSize } from '@magento/peregrine';
  49. import whatsapp from './images/whatsapp.png';
  50. import RelatedProducts from './RelatedProducts';
  51. import CustomizableOptions from './components/CustomizableOptions';
  52. import ProductDetailsBlock from './components/ProductDetailsBlock';
  53. import { useSelector } from 'react-redux';
  54. const WishlistButton = React.lazy(() => import('../Wishlist/AddToListButton'));
  55. import { gql, useQuery } from '@apollo/client';
  56. import SimilarProduct from './SimilarProduct';
  57. import GlowPromise from '../HomePage/components/GlowPromise';
  58. import ForeverGlow from './components/ForeverGlow';
  59. const Options = React.lazy(() =>
  60. import('@magento/venia-ui/lib/components/ProductOptions')
  61. );
  62. // Correlate a GQL error message to a field. GQL could return a longer error
  63. // string but it may contain contextual info such as product id. We can use
  64. // parts of the string to check for which field to apply the error.
  65. const ERROR_MESSAGE_TO_FIELD_MAPPING = {
  66. 'The requested qty is not available': 'quantity',
  67. 'Product that you are trying to add is not available.': 'quantity',
  68. "The product that was requested doesn't exist.": 'quantity'
  69. };
  70.  
  71. // Field level error messages for rendering.
  72. const ERROR_FIELD_TO_MESSAGE_MAPPING = {
  73. quantity: 'The requested quantity is not available.'
  74. };
  75.  
  76. const storage = new BrowserPersistence();
  77.  
  78. const ProductFullDetail = props => {
  79. const { product } = props;
  80. const [showShare, setShowShare] = useState(false);
  81. const [showModal, setShowModal] = useState(false);
  82. const handleCloseModal = () => {
  83. setShowModal(!showModal);
  84. };
  85.  
  86. const talonProps = useProductFullDetail({ product });
  87.  
  88. const {
  89. breadcrumbCategoryId,
  90. errorMessage,
  91. handleAddToCart,
  92. handleCustomizableProductToCart,
  93. handleSelectionChange,
  94. isOutOfStock,
  95. isAddToCartDisabled,
  96. isSupportedProductType,
  97. mediaGalleryEntries,
  98. productDetails,
  99. customAttributes,
  100. wishlistButtonProps,
  101. setFormApi,
  102. handleSubmit,
  103. isBusy,
  104. clearErrors,
  105. isAddedItem,
  106. setIsAddedItem,
  107. handlePincode,
  108. checkDeliveryDate,
  109. pincode,
  110. deliveryDate,
  111. parentCategoryId,
  112. designFamilyValue,
  113. crossSellProducts
  114. } = talonProps;
  115.  
  116. const { formatMessage } = useIntl();
  117.  
  118. const windowSize = useWindowSize();
  119. const isMobile = windowSize.innerWidth <= 640;
  120.  
  121. const pinCode = storage.getItem('pin_code');
  122.  
  123. const customData = useSelector(state => state.custom);
  124.  
  125. const GET_CART_DETAILS = gql`
  126. query GetCartDetails($cartId: String!) {
  127. cart(cart_id: $cartId) {
  128. id
  129. items {
  130. uid
  131. product {
  132. uid
  133. sku
  134. }
  135. }
  136. }
  137. }
  138. `;
  139.  
  140. const cart = useSelector(state => state.cart);
  141. const { cartId } = cart;
  142.  
  143. const { data, loading } = useQuery(GET_CART_DETAILS, {
  144. fetchPolicy: 'cache-and-network',
  145. nextFetchPolicy: 'cache-first',
  146. skip: !cartId,
  147. variables: {
  148. cartId: cartId
  149. }
  150. });
  151.  
  152. const GET_CUSTOMER = gql`
  153. query getCustomer {
  154. customer {
  155. addresses {
  156. postcode
  157. default_shipping
  158. }
  159. }
  160. }
  161. `;
  162.  
  163. const { data: getCustomerData, error: errorCustomer } = useQuery(
  164. GET_CUSTOMER
  165. );
  166.  
  167. const customerDetails =
  168. getCustomerData &&
  169. getCustomerData.customer.addresses.find(i =>
  170. i.default_shipping == true ? i : null
  171. );
  172. const customerPostcode = customerDetails && customerDetails.postcode;
  173.  
  174. const { storeUserInfo } = customData;
  175.  
  176. const isStoreUser = storeUserInfo?.isStorefrontUser;
  177.  
  178. useEffect(() => {
  179. handlePincode(customerPostcode || pinCode);
  180. }, []);
  181.  
  182. useEffect(() => {
  183. if (pincode && pincode.length >= 6) {
  184. checkDeliveryDate(productDetails.sku);
  185. }
  186. }, [pincode]);
  187.  
  188. const history = useHistory();
  189.  
  190. useEffect(() => {
  191. if (isAddedItem) {
  192. history.push('cart');
  193. setIsAddedItem(false);
  194. }
  195. }, [isAddedItem]);
  196.  
  197. const classes = useStyle(defaultClasses, props.classes);
  198. const options = isProductConfigurable(product) ? (
  199. <Suspense fallback={<ProductOptionsShimmer />}>
  200. <Options
  201. onSelectionChange={handleSelectionChange}
  202. options={product.configurable_options}
  203. />
  204. </Suspense>
  205. ) : null;
  206.  
  207. const breadcrumbs = breadcrumbCategoryId ? (
  208. <Breadcrumbs
  209. categoryId={breadcrumbCategoryId}
  210. currentProduct={productDetails.name}
  211. />
  212. ) : null;
  213.  
  214. // Fill a map with field/section -> error.
  215. const errors = new Map();
  216.  
  217. if (errorMessage) {
  218. Object.keys(ERROR_MESSAGE_TO_FIELD_MAPPING).forEach(key => {
  219. if (errorMessage.includes(key)) {
  220. const target = ERROR_MESSAGE_TO_FIELD_MAPPING[key];
  221. const message = ERROR_FIELD_TO_MESSAGE_MAPPING[target];
  222. errors.set(target, message);
  223. }
  224. });
  225.  
  226. // Handle cases where a user token is invalid or expired. Preferably
  227. // this would be handled elsewhere with an error code and not a string.
  228. if (errorMessage.includes('The current user cannot')) {
  229. errors.set('form', [
  230. new Error(
  231. formatMessage({
  232. id: 'productFullDetail.errorToken',
  233. defaultMessage:
  234. 'There was a problem with your cart. Please sign in again and try adding the item once more.'
  235. })
  236. )
  237. ]);
  238. }
  239.  
  240. // Handle cases where a cart wasn't created properly.
  241. if (
  242. errorMessage.includes('Variable "$cartId" got invalid value null')
  243. ) {
  244. errors.set('form', [
  245. new Error(
  246. formatMessage({
  247. id: 'productFullDetail.errorCart',
  248. defaultMessage:
  249. 'There was a problem with your cart. Please refresh the page and try adding the item once more.'
  250. })
  251. )
  252. ]);
  253. }
  254.  
  255. // An unknown error should still present a readable message.
  256. if (!errors.size) {
  257. errors.set('form', [
  258. new Error(
  259. formatMessage({
  260. id: 'productFullDetail.errorUnknown',
  261. defaultMessage:
  262. 'Could not add item to cart. Please check required options and try again.'
  263. })
  264. )
  265. ]);
  266. }
  267. }
  268.  
  269. var readyToShip = false;
  270.  
  271. if (Array.isArray(customAttributes)) {
  272. customAttributes.filter(customAttribute => {
  273. if (customAttribute.attribute_metadata.code === 'ready_to_ship') {
  274. if (
  275. customAttribute?.selected_attribute_options
  276. ?.attribute_option[0]?.label == 'Yes'
  277. ) {
  278. readyToShip = true;
  279. }
  280. }
  281. });
  282. }
  283.  
  284. var isVirtualTryOn = false;
  285. var glowSku = '';
  286.  
  287. if (Array.isArray(customAttributes)) {
  288. customAttributes.filter(customAttribute => {
  289. if (
  290. customAttribute.attribute_metadata.code === 'is_virtual_try_on'
  291. ) {
  292. if (
  293. customAttribute?.selected_attribute_options
  294. ?.attribute_option[0]?.label == 'Yes'
  295. ) {
  296. isVirtualTryOn = true;
  297. }
  298. }
  299. });
  300. const glowSkuAttribute = customAttributes.find(
  301. customAttribute =>
  302. customAttribute.attribute_metadata.code === 'glow_sku'
  303. );
  304. if (glowSkuAttribute) {
  305. glowSku = glowSkuAttribute.entered_attribute_value?.value;
  306. }
  307. }
  308.  
  309. const customAttributesDetails = useMemo(() => {
  310. const list = [];
  311. const pagebuilder = [];
  312. const skuAttribute = {
  313. attribute_metadata: {
  314. uid: 'attribute_sku',
  315. used_in_components: ['PRODUCT_DETAILS_PAGE'],
  316. ui_input: {
  317. ui_input_type: 'TEXT'
  318. },
  319. label: formatMessage({
  320. id: 'global.sku',
  321. defaultMessage: 'SKU'
  322. })
  323. },
  324. entered_attribute_value: {
  325. value: productDetails.sku
  326. }
  327. };
  328. if (Array.isArray(customAttributes)) {
  329. customAttributes.forEach(customAttribute => {
  330. if (
  331. customAttribute.attribute_metadata.ui_input
  332. .ui_input_type === 'PAGEBUILDER'
  333. ) {
  334. pagebuilder.push(customAttribute);
  335. } else {
  336. list.push(customAttribute);
  337. }
  338. });
  339. }
  340. list.unshift(skuAttribute);
  341. return {
  342. list: list,
  343. pagebuilder: pagebuilder
  344. };
  345. }, [customAttributes, productDetails.sku, formatMessage]);
  346.  
  347. const cartDetails = data ? data.cart.items : null;
  348. const handleProductModal = e => {
  349. var isMatched =
  350. cartDetails &&
  351. cartDetails.find(item => item.product.sku == product.sku);
  352. if (isMatched) {
  353. e.preventDefault();
  354. setShowModal(!showModal);
  355. }
  356. };
  357.  
  358. const cartCallToActionText = !isOutOfStock ? (
  359. isStoreUser ? (
  360. <FormattedMessage
  361. id="productFullDetail.addItemToCart1"
  362. defaultMessage="ADD TO CART"
  363. />
  364. ) : (
  365. <FormattedMessage
  366. id="productFullDetail.addItemToCart1"
  367. defaultMessage="BUY NOW"
  368. />
  369. )
  370. ) : (
  371. <FormattedMessage
  372. id="productFullDetail.itemOutOfStock"
  373. defaultMessage="Out of Stock"
  374. />
  375. );
  376.  
  377. const cartActionContent = isSupportedProductType ? (
  378. <button
  379. className="rounded buy_now_button"
  380. data-cy="ProductFullDetail-addToCartButton"
  381. disabled={isAddToCartDisabled}
  382. priority="high"
  383. type="submit"
  384. style={{
  385. background: 'linear-gradient(90deg, #C7A46F 0%, #886C44 100%)',
  386. width: '99%',
  387. height: '40px',
  388. color: '#fff',
  389. letterSpacing: 4
  390. }}
  391. >
  392. {cartCallToActionText}
  393. </button>
  394. ) : (
  395. <div className={classes.unavailableContainer}>
  396. <Info />
  397. <p>
  398. <FormattedMessage
  399. id={'productFullDetail.unavailableProduct'}
  400. defaultMessage={
  401. 'This product is currently unavailable for purchase.'
  402. }
  403. />
  404. </p>
  405. </div>
  406. );
  407.  
  408. const shortDescription = productDetails.shortDescription ? (
  409. <div className="text-[#656565]">
  410. <RichContent html={productDetails.shortDescription.html} />
  411. </div>
  412. ) : null;
  413.  
  414. const pageBuilderAttributes = customAttributesDetails.pagebuilder.length ? (
  415. <section className={classes.detailsPageBuilder}>
  416. <CustomAttributes
  417. classes={{ list: classes.detailsPageBuilderList }}
  418. customAttributes={customAttributesDetails.pagebuilder}
  419. showLabels={false}
  420. />
  421. </section>
  422. ) : null;
  423.  
  424. // let selectedProductColorId = null;
  425. // const handleProductSelected = productColorId => {
  426. // setSelectedProductColorId(productColorId);
  427. // };
  428.  
  429. const productDetailsAc = (
  430. <div dangerouslySetInnerHTML={{ __html: product.description || '' }} />
  431. );
  432.  
  433. const [breakDown, setBreakDown] = useState([]);
  434. const [selectedSize, setSelectedSize] = useState(null);
  435. const [selectedColor, setSelectedColor] = useState(null);
  436.  
  437. const [isCustomizable, setIsCustomizable] = useState(false);
  438.  
  439. const [extraPrice, setExtraPrice] = useState(null);
  440.  
  441. useEffect(() => {
  442. getProductBreakDown();
  443. }, [selectedSize]);
  444.  
  445. const getProductBreakDown = async () => {
  446. try {
  447. const customOptionId = storage.getItem('size_id');
  448. const optionValue = storage.getItem('size');
  449. const ringSize = selectedSize?.title;
  450. const goldColor = selectedColor?.title;
  451. const response = await apiClient.get(
  452. apiClient.Urls.productBreakDown(
  453. product.id,
  454. customOptionId,
  455. optionValue,
  456. ringSize,
  457. goldColor
  458. )
  459. );
  460.  
  461. if (response.success && response.data) {
  462. setBreakDown(response.data);
  463. }
  464. } catch (error) {
  465. console.log('error', error);
  466. }
  467. };
  468.  
  469. const productPriceBreakdownAc = (
  470. <div>
  471. {breakDown.content &&
  472. breakDown.content.map((i, index) => {
  473. return (
  474. i.variant_type && (
  475. <div
  476. className="flex justify-between p-1"
  477. key={index}
  478. >
  479. <span
  480. className={
  481. classes.nameColumn +
  482. ' font-bold !w-auto'
  483. }
  484. >
  485. {i.variant_type}
  486. </span>
  487. <span><CurrencyCodeUI currencyCode={productDetails?.price?.currency} />{Math.round(i.variant_price)}</span>
  488. </div>
  489. )
  490. );
  491. })}
  492. <div className="flex justify-between p-1">
  493. <span className={classes.nameColumn + ' font-bold !w-auto'}>
  494. Value Addition
  495. </span>
  496. <span><CurrencyCodeUI currencyCode={productDetails?.price?.currency} /> {Math.round(breakDown.valueAddition)}</span>
  497. </div>
  498. <div className="flex justify-between p-1">
  499. <span className={classes.nameColumn + ' font-bold !w-auto'}>
  500. Tax
  501. </span>
  502. <span><CurrencyCodeUI currencyCode={productDetails?.price?.currency} /> {Math.round(breakDown.tax)}</span>
  503. </div>
  504. </div>
  505. );
  506.  
  507. const hiddenTags = [
  508. 'setting_type',
  509. 'is_on_sale',
  510. 'ready_to_ship',
  511. 'best_seller',
  512. 'new_launch',
  513. 'make_on_order',
  514. 'design_family',
  515. 'is_outlet_available',
  516. 'is_virtual_try_on'
  517. ];
  518.  
  519. const productCustomAttributeData = (
  520. <div>
  521. <table>
  522. <tbody>
  523. {customAttributes &&
  524. customAttributes.map((i, index) => {
  525. return (
  526. !hiddenTags.includes(
  527. i?.attribute_metadata?.code
  528. ) && (
  529. <tr key={index}>
  530. <td
  531. className={
  532. classes.nameColumn +
  533. ' font-semibold'
  534. }
  535. >
  536. {i?.attribute_metadata?.label}
  537. </td>
  538. <td>
  539. {i?.selected_attribute_options
  540. ?.attribute_option?.length
  541. ? i?.selected_attribute_options?.attribute_option.map(
  542. (j, index) => {
  543. return index ==
  544. i
  545. ?.selected_attribute_options
  546. ?.attribute_option
  547. ?.length -
  548. 1
  549. ? j.label
  550. : j.label + ', ';
  551. }
  552. )
  553. : i?.entered_attribute_value
  554. ?.value
  555. ? i?.entered_attribute_value
  556. ?.value
  557. : ''}
  558. </td>
  559. </tr>
  560. )
  561. );
  562. })}
  563. </tbody>
  564. </table>
  565. </div>
  566. );
  567.  
  568. const disclaimerAc = (
  569. <div>
  570. <p>
  571. Actual product dimensions may vary, please refer to the
  572. dimensions of the product for exact size. Color of the product
  573. may slightly vary from the image. We here by convey that final
  574. prices might vary depending on weight and size. A credit note or
  575. refund will be issued for any Difference value of less amount
  576. and If any access value incurred to be paid by customers. Our
  577. customer care team will inform you on prior before dispatching
  578. the product with complete details.
  579. </p>
  580. </div>
  581. );
  582.  
  583. const facebookShareLink = () => {
  584. const currentUri = encodeURIComponent(window.location.href);
  585. const shareUrl1 = currentUri;
  586. return `https://www.facebook.com/dialog/share?display=popup&href=${shareUrl1}&redirect_uri=${currentUri}`;
  587. };
  588. const whatsappShareLink = () => {
  589. const text = encodeURIComponent(window.location.href);
  590. return `https://api.whatsapp.com/send?text=${text}`;
  591. };
  592.  
  593. const twitterShareLink = () => {
  594. const text = encodeURIComponent(window.location.href);
  595. return `https://twitter.com/intent/tweet?text=${text}`;
  596. };
  597.  
  598. const emailShareLink = () => {
  599. const text = encodeURIComponent(shareContent);
  600. const subject = encodeURIComponent(shareTitle);
  601. return `mailto:?body=${text}&subject=${subject}`;
  602. };
  603.  
  604. const [earnedPoints, setEarnedPoints] = useState(null);
  605.  
  606. useEffect(() => {
  607. getEarnedPoints();
  608. }, []);
  609.  
  610. const [isDefaultProduct, setDefaultProduct] = useState(true);
  611.  
  612. const getEarnedPoints = async () => {
  613. try {
  614. const response = await apiClient.post(apiClient.Urls.getPoints, {
  615. params: {
  616. sku: product.sku
  617. }
  618. });
  619.  
  620. if (response.success) {
  621. setEarnedPoints(response.points);
  622. }
  623. } catch (error) {
  624. console.log('error', error);
  625. }
  626. };
  627. return (
  628. <Fragment>
  629. {!isMobile && breadcrumbs}
  630. <Form
  631. className={classes.root}
  632. style={{ paddingTop: 0 }}
  633. data-cy="ProductFullDetail-root"
  634. onSubmit={
  635. isCustomizable
  636. ? handleCustomizableProductToCart
  637. : handleAddToCart
  638. }
  639. >
  640. <SimilarProduct
  641. showModal={showModal}
  642. handleAddToCart={
  643. isCustomizable
  644. ? handleCustomizableProductToCart
  645. : handleAddToCart
  646. }
  647. setShowModal={setShowModal}
  648. handleCloseModal={handleCloseModal}
  649. />
  650.  
  651. <section className={classes.imageCarousel + ' max-w-[640px]'}>
  652. <ProductTags product={product} page={'PDP'} />
  653. <Carousel
  654. images={mediaGalleryEntries}
  655. item={product}
  656. isVirtualTryOn={isVirtualTryOn}
  657. selectedColor={selectedColor}
  658. />
  659. </section>
  660. <div
  661. className={
  662. classes.productDesc +
  663. ' overflow-y-scroll overflow-x-auto'
  664. }
  665. >
  666. <section
  667. className={classes.title}
  668. style={{ paddingTop: 0 }}
  669. >
  670. <div className="relative">
  671. <p className="flex justify-between text-[12px] text-[#939BA5] uppercase">
  672. SKU {glowSku}
  673. </p>
  674. {earnedPoints ? (
  675. <h3 className="bg-[#363636] product_points">
  676. <span className="selected_product">
  677. Buy this & earn upto
  678. </span>
  679. <span className="points_section">
  680. {earnedPoints}
  681. </span>
  682. points
  683. </h3>
  684. ) : (
  685. <div className="min-h-[50px]" />
  686. )}
  687. <div className="flex">
  688. <h1
  689. aria-live="polite"
  690. className={
  691. classes.productName +
  692. ' text-[22px] font-ivyModeRegular'
  693. }
  694. data-cy="ProductFullDetail-productName"
  695. >
  696. {productDetails.name}
  697. </h1>
  698.  
  699. <div className="flex absolute top-[30px] right-0">
  700. <Suspense fallback={null}>
  701. <div className="flex items-center relative">
  702. <div
  703. onClick={() =>
  704. setShowShare(!showShare)
  705. }
  706. className="cursor-pointer"
  707. >
  708. <img
  709. src={shareIcon}
  710. class="mr-[20px]"
  711. alt="Share Icon"
  712. />
  713. </div>
  714. {showShare && (
  715. <div className="rounded-[6px] absolute bg-[#fff] border-[#C1A87E] border-[1px] flex flex-col right-[10px] top-[25px]">
  716. <a
  717. href={facebookShareLink()}
  718. target="blank"
  719. title="Share on Facebook"
  720. class="flex px-4 py-2 text-sm text-gray-800 border-b hover:bg-blue-100"
  721. >
  722. <svg
  723. aria-hidden="true"
  724. focusable="false"
  725. data-prefix="fab"
  726. data-icon="facebook-messenger"
  727. class="w-5 h-5 mr-4 text-[#227aff]"
  728. role="img"
  729. xmlns="http://www.w3.org/2000/svg"
  730. viewBox="0 0 512 512"
  731. >
  732. <path
  733. fill="currentColor"
  734. d="M256.55 8C116.52 8 8 110.34 8 248.57c0 72.3 29.71 134.78 78.07 177.94 8.35 7.51 6.63 11.86 8.05 58.23A19.92 19.92 0 0 0 122 502.31c52.91-23.3 53.59-25.14 62.56-22.7C337.85 521.8 504 423.7 504 248.57 504 110.34 396.59 8 256.55 8zm149.24 185.13l-73 115.57a37.37 37.37 0 0 1-53.91 9.93l-58.08-43.47a15 15 0 0 0-18 0l-78.37 59.44c-10.46 7.93-24.16-4.6-17.11-15.67l73-115.57a37.36 37.36 0 0 1 53.91-9.93l58.06 43.46a15 15 0 0 0 18 0l78.41-59.38c10.44-7.98 24.14 4.54 17.09 15.62z"
  735. />
  736. </svg>
  737. <span class="text-gray-600">
  738. Facebook
  739. </span>
  740. </a>
  741. <a
  742. href={twitterShareLink()}
  743. target="blank"
  744. title="Share on Twitter"
  745. class="flex px-4 py-2 text-sm text-gray-800 border-b hover:bg-blue-100"
  746. >
  747. <svg
  748. aria-hidden="true"
  749. focusable="false"
  750. data-prefix="fab"
  751. data-icon="twitter-square"
  752. class="w-5 h-5 mr-4 text-[#329bff]"
  753. role="img"
  754. xmlns="http://www.w3.org/2000/svg"
  755. viewBox="0 0 448 512"
  756. >
  757. <path
  758. fill="currentColor"
  759. d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-48.9 158.8c.2 2.8.2 5.7.2 8.5 0 86.7-66 186.6-186.6 186.6-37.2 0-71.7-10.8-100.7-29.4 5.3.6 10.4.8 15.8.8 30.7 0 58.9-10.4 81.4-28-28.8-.6-53-19.5-61.3-45.5 10.1 1.5 19.2 1.5 29.6-1.2-30-6.1-52.5-32.5-52.5-64.4v-.8c8.7 4.9 18.9 7.9 29.6 8.3a65.447 65.447 0 0 1-29.2-54.6c0-12.2 3.2-23.4 8.9-33.1 32.3 39.8 80.8 65.8 135.2 68.6-9.3-44.5 24-80.6 64-80.6 18.9 0 35.9 7.9 47.9 20.7 14.8-2.8 29-8.3 41.6-15.8-4.9 15.2-15.2 28-28.8 36.1 13.2-1.4 26-5.1 37.8-10.2-8.9 13.1-20.1 24.7-32.9 34z"
  760. />
  761. </svg>
  762. <span class="text-gray-600">
  763. Twitter
  764. </span>
  765. </a>
  766. <a
  767. href={whatsappShareLink()}
  768. target="blank"
  769. title="Share on Whatsapp"
  770. class="flex px-4 py-2 text-sm text-gray-800 border-b hover:bg-blue-100"
  771. >
  772. <svg
  773. xmlns="http://www.w3.org/2000/svg"
  774. width="16"
  775. height="16"
  776. class="w-5 h-5 mr-4 text-[#25D366] bi bi-whatsapp"
  777. fill="currentColor"
  778. viewBox="0 0 16 16"
  779. >
  780. <path d="M13.601 2.326A7.854 7.854 0 0 0 7.994 0C3.627 0 .068 3.558.064 7.926c0 1.399.366 2.76 1.057 3.965L0 16l4.204-1.102a7.933 7.933 0 0 0 3.79.965h.004c4.368 0 7.926-3.558 7.93-7.93A7.898 7.898 0 0 0 13.6 2.326zM7.994 14.521a6.573 6.573 0 0 1-3.356-.92l-.24-.144-2.494.654.666-2.433-.156-.251a6.56 6.56 0 0 1-1.007-3.505c0-3.626 2.957-6.584 6.591-6.584a6.56 6.56 0 0 1 4.66 1.931 6.557 6.557 0 0 1 1.928 4.66c-.004 3.639-2.961 6.592-6.592 6.592zm3.615-4.934c-.197-.099-1.17-.578-1.353-.646-.182-.065-.315-.099-.445.099-.133.197-.513.646-.627.775-.114.133-.232.148-.43.05-.197-.1-.836-.308-1.592-.985-.59-.525-.985-1.175-1.103-1.372-.114-.198-.011-.304.088-.403.087-.088.197-.232.296-.346.1-.114.133-.198.198-.33.065-.134.034-.248-.015-.347-.05-.099-.445-1.076-.612-1.47-.16-.389-.323-.335-.445-.34-.114-.007-.247-.007-.38-.007a.729.729 0 0 0-.529.247c-.182.198-.691.677-.691 1.654 0 .977.71 1.916.81 2.049.098.133 1.394 2.132 3.383 2.992.47.205.84.326 1.129.418.475.152.904.129 1.246.08.38-.058 1.171-.48 1.338-.943.164-.464.164-.86.114-.943-.049-.084-.182-.133-.38-.232z" />{' '}
  781. </svg>
  782. <span class="text-gray-600">
  783. Whatsapp
  784. </span>
  785. </a>
  786. </div>
  787. )}
  788. </div>
  789.  
  790.  
  791. </Suspense>
  792. </div>
  793. </div>
  794. </div>
  795. {/* <div>
  796. <img
  797. src={reviewsIcon}
  798. className="mr-3"
  799. alt="Reviews icon"
  800. />
  801. <p className="text-xs">102 Reviews</p>
  802. </div> */}
  803. <div className="flex items-end mt-[15px]">
  804. <p
  805. data-cy="ProductFullDetail-productPrice"
  806. className={
  807. classes.productPrice + ' text-[22px]'
  808. }
  809. >
  810. <Price
  811. currencyCode={productDetails.price.currency}
  812. value={
  813. productDetails.price.value + extraPrice
  814. }
  815. />
  816. </p>
  817. {/* <s
  818. data-cy="ProductFullDetail-productPrice"
  819. className={
  820. classes.productPrice +
  821. ' text-xs py-1 ml-1 text-[#35383A]'
  822. }
  823. >
  824. <Price
  825. currencyCode={productDetails.price.currency}
  826. value={productDetails.price.value}
  827. />
  828. </s> */}
  829. </div>
  830. {shortDescription}
  831. <CustomizableOptions
  832. setExtraPrice={setExtraPrice}
  833. sku={product.sku}
  834. setIsCustomizable={setIsCustomizable}
  835. customAttributes={customAttributes}
  836. setDefaultProduct={setDefaultProduct}
  837. checkDeliveryDate={checkDeliveryDate}
  838. setSelectedSize={setSelectedSize}
  839. setSelectedColor={setSelectedColor}
  840. />
  841. <div className="max-w-[392px] mt-[10px] ">
  842. <div
  843. getApi={setFormApi}
  844. className={
  845. 'border-[#e1e6eb] border-[1px] flex justify-between px-2 items-center'
  846. }
  847. >
  848. <input
  849. autoComplete="pincode"
  850. field="pincode"
  851. id="pincode"
  852. className="h-[40px] px-[10px] text-[14px] focus:border-none"
  853. value={pincode}
  854. placeholder="Enter Pin Code"
  855. onChange={e => {
  856. storage.setItem(
  857. 'pin_code',
  858. e.target.value,
  859. null
  860. );
  861. handlePincode(e.target.value);
  862. }}
  863. />
  864. <LinkButton
  865. data-cy="Newsletter-submitButton"
  866. className={
  867. ' max-h-[35px] py-2 focus:outline-none'
  868. }
  869. // type="submit"
  870. disabled={isBusy}
  871. // onClick={clearErrors}
  872. onClick={() =>
  873. checkDeliveryDate(productDetails.sku)
  874. }
  875. >
  876. <ChevronRight
  877. size={20}
  878. className="rounded-full border border-[#C77F44] text-[#C77F44]"
  879. />
  880. </LinkButton>
  881. </div>
  882. <p>{deliveryDate}</p>
  883. </div>
  884. <p className="text-sm flex items-center my-[20px]">
  885. <span className="mr-[5px]">Need help ?</span>
  886.  
  887. <a
  888. href="https://wa.me/+919943212511"
  889. target={'_blank'}
  890. data-cy="ScheduleAppointment-link"
  891. className="flex items-center"
  892. >
  893. <span className="flex text-[#C77F44] text-xs cursor-pointer tracking-[1.2px] mr-[5px]">
  894. CHAT WITH US ON WHATSAPP
  895. </span>
  896. <img
  897. src={whatsapp}
  898. alt="whatsapp_icon"
  899. className="w-[20px] h-[20px]"
  900. />
  901. {/* <svg
  902. aria-hidden="true"
  903. focusable="false"
  904. data-prefix="fab"
  905. data-icon="linkedin"
  906. class="w-5 h-5 mr-4 text-[#C77F44]"
  907. role="img"
  908. xmlns="http://www.w3.org/2000/svg"
  909. viewBox="0 0 32 32"
  910. >
  911. <path
  912. d=" M19.11 17.205c-.372 0-1.088 1.39-1.518 1.39a.63.63 0 0 1-.315-.1c-.802-.402-1.504-.817-2.163-1.447-.545-.516-1.146-1.29-1.46-1.963a.426.426 0 0 1-.073-.215c0-.33.99-.945.99-1.49 0-.143-.73-2.09-.832-2.335-.143-.372-.214-.487-.6-.487-.187 0-.36-.043-.53-.043-.302 0-.53.115-.746.315-.688.645-1.032 1.318-1.06 2.264v.114c-.015.99.472 1.977 1.017 2.78 1.23 1.82 2.506 3.41 4.554 4.34.616.287 2.035.888 2.722.888.817 0 2.15-.515 2.478-1.318.13-.33.244-.73.244-1.088 0-.058 0-.144-.03-.215-.1-.172-2.434-1.39-2.678-1.39zm-2.908 7.593c-1.747 0-3.48-.53-4.942-1.49L7.793 24.41l1.132-3.337a8.955 8.955 0 0 1-1.72-5.272c0-4.955 4.04-8.995 8.997-8.995S25.2 10.845 25.2 15.8c0 4.958-4.04 8.998-8.998 8.998zm0-19.798c-5.96 0-10.8 4.842-10.8 10.8 0 1.964.53 3.898 1.546 5.574L5 27.176l5.974-1.92a10.807 10.807 0 0 0 16.03-9.455c0-5.958-4.842-10.8-10.802-10.8z"
  913. fill-rule="evenodd"
  914. />
  915. </svg> */}
  916. </a>
  917. </p>
  918. {readyToShip && isDefaultProduct && (
  919. <div className="flex text-sm mt-[15px]">
  920. <div className="flex w-1/2 gap-2">
  921. <img src={timeIcon} alt="Time Icon" />
  922. <span className="text-xs uppercase tracking-[1.2px]">
  923. {' '}
  924. Ready to ship
  925. </span>
  926. </div>
  927. <div className="flex gap-2 w-1/2">
  928. <div
  929. data-cy="ScheduleAppointment-link"
  930. onClick={() => {
  931. const element = document.getElementById(
  932. 'try-at-store'
  933. );
  934. if (element) {
  935. element.scrollIntoView({
  936. behavior: 'smooth'
  937. });
  938. }
  939. }}
  940. >
  941. <span className="text-[#C77F44] text-xs uppercase cursor-pointer tracking-[1.2px]">
  942. Find in store
  943. </span>
  944. </div>
  945. </div>
  946. </div>
  947. )}
  948. </section>
  949. <FormError
  950. classes={{
  951. root: classes.formErrors
  952. }}
  953. errors={errors.get('form') || []}
  954. />
  955. <section className={classes.options}>{options}</section>
  956. <section className={classes.quantity + ' hidden'}>
  957. <span
  958. data-cy="ProductFullDetail-quantityTitle"
  959. className={classes.quantityTitle}
  960. >
  961. <FormattedMessage
  962. id={'global.quantity'}
  963. defaultMessage={'Quantity'}
  964. />
  965. </span>
  966. <QuantityStepper
  967. classes={{ root: classes.quantityRoot }}
  968. min={1}
  969. message={errors.get('quantity')}
  970. />
  971. </section>
  972. <section
  973. className={classes.actions}
  974. style={{ paddingTop: 0 }}
  975. >
  976. {!isMobile && (
  977. <div
  978. className="lg_pr-[74px]"
  979. onClick={handleProductModal}
  980. >
  981. {cartActionContent}
  982. <Suspense fallback={null}>
  983. <WishlistButton {...wishlistButtonProps} />
  984. </Suspense>
  985. </div>
  986. )}
  987. {/* <p className="text-sm mt-2.5">
  988. Want a get Notification on a{' '}
  989. <span className="font-bold text-sm">
  990. Price drop
  991. </span>{' '}
  992. <span className="text-[#C77F44] text-xs">
  993. NOTIFY ME
  994. </span>
  995. </p> */}
  996. <div className={classes.dropdownBlock}>
  997. <DropdownBlock
  998. name="Product Details"
  999. content={
  1000. <ProductDetailsBlock
  1001. selectedSize={selectedSize}
  1002. selectedColor={selectedColor}
  1003. classes={classes}
  1004. customAttributes={customAttributes}
  1005. />
  1006. }
  1007. />
  1008. <DropdownBlock
  1009. name="Product Story"
  1010. content={productDetailsAc}
  1011. />
  1012. {breakDown.content ? (
  1013. <DropdownBlock
  1014. name="Product Price / Breakdown"
  1015. content={productPriceBreakdownAc}
  1016. />
  1017. ) : null}
  1018. <DropdownBlock
  1019. name="Disclaimers"
  1020. content={disclaimerAc}
  1021. />
  1022. </div>
  1023. </section>
  1024. </div>
  1025. {isMobile && (
  1026. <div className={classes.cartActionButton}>
  1027. <div className="px-3 h-[46px] m-auto w-full" >
  1028. {cartActionContent}
  1029. <Suspense fallback={null}>
  1030. <WishlistButton {...wishlistButtonProps} style={{ color: "red" }} />
  1031. </Suspense>
  1032. <button type="button">Click Me</button>
  1033. </div>
  1034. </div>
  1035. )}
  1036. {pageBuilderAttributes}
  1037. </Form>
  1038. <ForeverGlow />
  1039. <div className="flex justify-center">
  1040. {/* <Promise /> */}
  1041.  
  1042. <GlowPromise />
  1043. </div>
  1044. <div className="flex p-[1rem] justify-center mt-10">
  1045. {crossSellProducts && crossSellProducts.length > 0 && (
  1046. <div className="w-[100%] bg-[#F7F2EC] rounded-lg">
  1047. <CompleteLook
  1048. wishlistButtonProps={wishlistButtonProps}
  1049. productDetails={productDetails}
  1050. products={crossSellProducts}
  1051. />
  1052. </div>
  1053. )}
  1054. </div>
  1055. {designFamilyValue && parentCategoryId ? (
  1056. <RelatedProducts
  1057. df_id={designFamilyValue}
  1058. category_id={parentCategoryId}
  1059. current={product.sku}
  1060. />
  1061. ) : null}
  1062. <div id="try-at-store">
  1063. <TryAtHomeSection
  1064. sku={product.sku}
  1065. pincode={pincode}
  1066. handlePincode={handlePincode}
  1067. />
  1068. </div>
  1069.  
  1070. {/* <div className="flex p-[1rem] justify-center mt-10">
  1071. <div className="w-[100%]">
  1072. <TrustedPartners />
  1073. </div>
  1074. </div> */}
  1075. {/* <div className="flex justify-center mt-10">
  1076. <div className="text-center">
  1077. <h1 className="text-[42px] text-[#774E4F] font-medium font-ivyModeRegular">
  1078. Watch How It is Designed
  1079. </h1>
  1080. <p className="text-sm text-[#181A1D]">
  1081. Lorem ipsum dolor sit amet, consetetur sadipscing
  1082. </p>
  1083. </div>
  1084. </div> */}
  1085. {/* <div className="flex justify-center p-[1rem]">
  1086. <div className="w-[100%] h-[600px] border-2 border-solid bg-[#F7F2EC]">
  1087. <iframe
  1088. width="100%"
  1089. height="100%"
  1090. src="https://www.youtube.com/embed/J-N4qrH6Lgk"
  1091. title="Making of a Masterpiece"
  1092. frameborder="0"
  1093. allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  1094. allowfullscreen
  1095. />
  1096. </div>
  1097. </div> */}
  1098. <TrendingProducts />
  1099.  
  1100. <ShopPdpCategory />
  1101. </Fragment>
  1102. );
  1103. };
  1104.  
  1105. ProductFullDetail.propTypes = {
  1106. classes: shape({
  1107. cartActions: string,
  1108. description: string,
  1109. descriptionTitle: string,
  1110. details: string,
  1111. detailsPageBuilder: string,
  1112. detailsPageBuilderList: string,
  1113. detailsTitle: string,
  1114. imageCarousel: string,
  1115. options: string,
  1116. productName: string,
  1117. productPrice: string,
  1118. quantity: string,
  1119. quantityTitle: string,
  1120. quantityRoot: string,
  1121. root: string,
  1122. title: string,
  1123. unavailableContainer: string
  1124. }),
  1125. product: shape({
  1126. __typename: string,
  1127. id: number,
  1128. stock_status: string,
  1129. sku: string.isRequired,
  1130. price: shape({
  1131. regularPrice: shape({
  1132. amount: shape({
  1133. currency: string.isRequired,
  1134. value: number.isRequired
  1135. })
  1136. }).isRequired
  1137. }).isRequired,
  1138. media_gallery_entries: arrayOf(
  1139. shape({
  1140. uid: string,
  1141. label: string,
  1142. position: number,
  1143. disabled: bool,
  1144. file: string.isRequired
  1145. })
  1146. ),
  1147. description: string,
  1148. short_description: shape({
  1149. html: string,
  1150. __typename: string
  1151. })
  1152. }).isRequired
  1153. };
  1154.  
  1155. export default ProductFullDetail;
  1156.  
Advertisement
Add Comment
Please, Sign In to add comment