@riosst100/pwa-marketplace 3.1.9 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/i18n/en_US.json +30 -1
  2. package/i18n/id_ID.json +30 -1
  3. package/package.json +1 -1
  4. package/src/components/AboutUs/aboutUs.js +9 -0
  5. package/src/components/AboutUs/index.js +1 -0
  6. package/src/components/AgeVerification/ageVerificationModal.js +163 -0
  7. package/src/components/AgeVerification/ageVerificationModal.module.css +85 -0
  8. package/src/components/AgeVerification/ageVerificationModal.shimmer.js +21 -0
  9. package/src/components/AgeVerification/index.js +2 -0
  10. package/src/components/AgeVerification/sellerCoupon.js +119 -0
  11. package/src/components/AgeVerification/sellerCouponCheckout.js +164 -0
  12. package/src/components/HelpCenter/helpCenter.js +95 -23
  13. package/src/components/HelpCenter/helpcenter.module.css +15 -2
  14. package/src/components/HelpCenter/questionDetail.js +1 -1
  15. package/src/components/LiveChat/MessagesModal.js +345 -0
  16. package/src/components/LiveChat/chatContent.js +3 -2
  17. package/src/components/MaintenancePage/maintenancePage.js +2 -2
  18. package/src/components/SellerCoupon/sellerCouponCheckout.js +2 -2
  19. package/src/components/SellerDetail/sellerDetail.js +37 -35
  20. package/src/components/WebsiteSwitcher/websiteSwitcherItem.js +17 -7
  21. package/src/intercept.js +7 -0
  22. package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +23 -3
  23. package/src/overwrites/venia-ui/lib/components/Footer/footer.js +21 -22
  24. package/src/overwrites/venia-ui/lib/components/Footer/sampleData.js +35 -31
  25. package/src/overwrites/venia-ui/lib/components/Header/header.js +2 -2
  26. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +105 -3
  27. package/src/overwrites/venia-ui/lib/components/StoreCodeRoute/storeCodeRoute.js +55 -53
  28. package/src/talons/HelpCenter/helpCenter.gql.js +50 -39
  29. package/src/talons/HelpCenter/useHelpCenter.js +67 -7
  30. package/src/talons/Seller/seller.gql.js +90 -0
  31. package/src/talons/Seller/useSeller.js +102 -4
  32. package/src/talons/SellerProducts/useProductContent.js +4 -4
@@ -1,5 +1,8 @@
1
- import React, { Fragment, Suspense, useState } from 'react';
1
+ import React, { Suspense, useState } from 'react';
2
2
  import { FormattedMessage } from 'react-intl';
3
+ import { useIntl } from 'react-intl';
4
+ import { useToasts } from '@magento/peregrine/lib/Toasts/useToasts';
5
+ import { useUserContext } from '@magento/peregrine/lib/context/user';
3
6
  import { useSeller } from '@riosst100/pwa-marketplace/src/talons/Seller/useSeller';
4
7
  import ErrorView from '@magento/venia-ui/lib/components/ErrorView';
5
8
  import mapSeller from '@riosst100/pwa-marketplace/src/util/mapSeller';
@@ -9,22 +12,15 @@ import SellerProducts from '../SellerProducts';
9
12
  import SellerInformation from '../SellerInformation';
10
13
  import Reviews from '../SellerReview';
11
14
  import {
12
- Verify,
13
15
  Sms,
14
- Message,
15
- Heart,
16
- Share,
17
- BoxTick,
18
- Star1,
19
16
  Shop,
20
17
  } from 'iconsax-react';
21
18
 
22
19
  import { Share2 } from 'react-feather';
23
20
 
24
21
  import Image from '@magento/venia-ui/lib/components/Image';
25
- import Button from '@magento/venia-ui/lib/components/Button';
26
- import cn from 'classnames';
27
22
  import Rating from '@riosst100/pwa-marketplace/src/components/commons/Rating';
23
+ import MessagesModal from '@riosst100/pwa-marketplace/src/components/LiveChat/MessagesModal';
28
24
 
29
25
  const FavoriteSellerButton = React.lazy(() => import('@riosst100/pwa-marketplace/src/components/FavoriteSeller/AddToListButton'));
30
26
 
@@ -36,8 +32,11 @@ const SellerDetail = props => {
36
32
  mapSeller
37
33
  });
38
34
 
39
- const { error, loading, seller, favoriteSellerButtonProps, couponData, couponError, couponLoading } = talonProps;
35
+ const { error, loading, seller, favoriteSellerButtonProps, couponData, couponError, couponLoading, handleSendMessage, messages, messagesLoading, handleReplyMessage, handleDeleteMessage } = talonProps;
40
36
  const history = useHistory();
37
+ const [{ isSignedIn }] = useUserContext();
38
+ const [, { addToast }] = useToasts();
39
+ const { formatMessage } = useIntl();
41
40
 
42
41
  if (loading && !seller)
43
42
  return '';
@@ -98,31 +97,24 @@ const SellerDetail = props => {
98
97
  }
99
98
  ];
100
99
 
101
- const [openChat, setOpenChat] = useState(false);
102
- const toggleChat = () => {
103
- setOpenChat(!openChat);
104
- }
105
-
106
- const addToFavourite = () => {
107
-
108
- }
109
-
110
- const getSellerAddressDisplay = (seller) => {
111
- let city = seller?.city;
112
- let country = seller?.country;
113
-
114
- let result = '';
115
- if (city && country) {
116
- result = `${city}, ${country}`;
100
+ const [isMessagesOpen, setIsMessagesOpen] = useState(false);
101
+ const openMessagesModal = () => {
102
+ if (!isSignedIn) {
103
+ addToast({
104
+ type: 'info',
105
+ message: formatMessage({
106
+ id: 'messages.loginRequired',
107
+ defaultMessage: 'Please sign in to your account to send a message to the seller.'
108
+ }),
109
+ timeout: 3000
110
+ });
111
+ return;
117
112
  }
118
- if (city && !country) {
119
- result = `${city}`;
120
- }
121
- if (!city && country) {
122
- result = `${country}`;
123
- }
124
- return result;
125
- }
113
+ setIsMessagesOpen(true);
114
+ };
115
+ const closeMessagesModal = () => setIsMessagesOpen(false);
116
+
117
+ // Address helper not used currently; removed to keep file lean
126
118
 
127
119
  return (
128
120
  <div className=' py-8'>
@@ -181,7 +173,7 @@ const SellerDetail = props => {
181
173
 
182
174
  {/* Right: action buttons */}
183
175
  <div className='flex xs_flex-wrap items-center xs_justify-center lg_justify-end gap-[10px] xs_mt-4 lg_mt-0 xs_pt-2 lg_pt-0 relative'>
184
- <button className='send-message-btn flex items-center justify-center gap-[8px] px-[14px] py-[8px] bg-white rounded-[30px] border border-solid border-[#f76b1c] min-w-[44px]'>
176
+ <button onClick={openMessagesModal} className='send-message-btn flex items-center justify-center gap-[8px] px-[14px] py-[8px] bg-white rounded-[30px] border border-solid border-[#f76b1c] min-w-[44px]'>
185
177
  <Sms color="#f76b1c" size={16} variant='Outline' className='stroke' />
186
178
  <span className='text-[#f76b1c] text-[14px] font-medium whitespace-nowrap md_block xs_hidden'>Send Message</span>
187
179
  </button>
@@ -201,6 +193,16 @@ const SellerDetail = props => {
201
193
  </button>
202
194
  </div>
203
195
  </div>
196
+ <MessagesModal
197
+ isOpen={isMessagesOpen}
198
+ onClose={closeMessagesModal}
199
+ messages={messages}
200
+ seller={seller}
201
+ onReply={handleReplyMessage}
202
+ onDelete={handleDeleteMessage}
203
+ onSend={handleSendMessage}
204
+ loading={messagesLoading}
205
+ />
204
206
  <Tabs
205
207
  data={dataTabs}
206
208
  tabContentWrapperClassName='!p-0'
@@ -10,26 +10,36 @@ const WebsiteSwitcherItem = props => {
10
10
  const { active, onClick, option, option2, children } = props;
11
11
  const classes = useStyle(defaultClasses, props.classes);
12
12
 
13
- const handleClick = useCallback(() => {
14
- onClick(option, option2);
15
- }, [option, option2, onClick]);
13
+ const { location } = globalThis;
14
+ const href = location.href;
15
+
16
+ let match = location && location.pathname.split("/")[1];
17
+
18
+ const websiteCode = option2 != "base" ? option2 : '';
19
+ if (option2 == "base") {
20
+ match = match + "/";
21
+ }
22
+ const url = href.replace(match, websiteCode);
23
+
24
+ // const handleClick = useCallback(() => {
25
+ // onClick(option, option2);
26
+ // }, [option, option2, onClick]);
16
27
 
17
28
  const activeIcon = active ? (
18
29
  <Icon data-cy="WebsiteSwitcherItem-activeIcon" size={20} src={Check} />
19
30
  ) : null;
20
31
 
21
32
  return (
22
- <button
33
+ <a
23
34
  data-cy="WebsiteSwitcherItem-button"
24
35
  className={classes.root}
25
- disabled={active}
26
- onClick={handleClick}
36
+ href={url}
27
37
  >
28
38
  <span className={classes.content}>
29
39
  <span className={classes.text}>{children}</span>
30
40
  {activeIcon}
31
41
  </span>
32
- </button>
42
+ </a>
33
43
  );
34
44
  };
35
45
 
package/src/intercept.js CHANGED
@@ -94,6 +94,13 @@ module.exports = targets => {
94
94
  path: require.resolve("./components/HelpCenter/index.js"),
95
95
  authed: false,
96
96
  },
97
+ {
98
+ exact: true,
99
+ name: "AboutUs",
100
+ pattern: "/about-us",
101
+ path: require.resolve("./components/AboutUs/index.js"),
102
+ authed: false,
103
+ },
97
104
  {
98
105
  exact: true,
99
106
  name: "HelpCenterQuestionDetail",
@@ -23,9 +23,14 @@ const Adapter = props => {
23
23
  reduxProps,
24
24
  routerProps,
25
25
  isMaintenance,
26
+ isCheckingMaintenance,
26
27
  apiBase
27
28
  } = talonProps;
28
29
 
30
+ if (isCheckingMaintenance) {
31
+ return null;
32
+ }
33
+
29
34
  // console.log('isMaintenance',isMaintenance)
30
35
 
31
36
  // useEffect(() => {
@@ -89,10 +94,23 @@ const Adapter = props => {
89
94
 
90
95
  useEffect(() => {
91
96
  if (!websiteCodeInUrl) {
92
- // history.replace(location.pathname + location.search)
97
+ history.replace(location.pathname + location.search)
93
98
  } else if (websiteCodeInUrl && websiteCodeInUrl !== currentWebsiteCode) {
94
99
  const storeCodeInUrl = websiteStores[websiteCodeInUrl];
95
100
 
101
+
102
+
103
+ // const search = location.search;
104
+
105
+ // const query = new URLSearchParams(search);
106
+ // const isAccessBaseWebsite = query.get('access_base_website') || (
107
+ // storage.getItem('access_base_website') || null
108
+ // );
109
+
110
+ // routerProps.basename = websiteCodeInUrl != process.env.WEBSITE_CODE && !isAccessBaseWebsite ? '/'+websiteCodeInUrl : '/';
111
+
112
+ // setNewRouterProps(routerProps);
113
+
96
114
  // await removeCart();
97
115
 
98
116
  // alert(storeCodeInUrl)
@@ -109,6 +127,8 @@ const Adapter = props => {
109
127
 
110
128
  // await apolloClient.clearCacheData(apolloClient, 'cart');
111
129
  // await removeCart();
130
+
131
+ // klo enabled jd redirect tros
112
132
  storage.removeItem('cartId');
113
133
 
114
134
  storage.setItem('store_view_code', storeCodeInUrl);
@@ -124,7 +144,7 @@ const Adapter = props => {
124
144
 
125
145
  // We're required to reload the page as the basename doesn't
126
146
  // change entirely without a full page reload.
127
- // history.go(0);
147
+ history.go(0);
128
148
  }
129
149
 
130
150
  if (!websiteCodeInUrl && getWebsiteByUserIp) {
@@ -164,7 +184,7 @@ const Adapter = props => {
164
184
  );
165
185
  }
166
186
  }
167
- }, [websiteCodeInUrl, getWebsiteByUserIp])
187
+ }, [websiteCodeInUrl, getWebsiteByUserIp]);
168
188
 
169
189
  // TODO: Replace with app skeleton. See PWA-547.
170
190
  if (!initialized) {
@@ -30,14 +30,25 @@ const Footer = props => {
30
30
  const linkGroups = Array.from(links, ([groupKey, linkProps]) => {
31
31
  const linkElements = Array.from(linkProps, ([text, pathInfo]) => {
32
32
  let path = pathInfo;
33
+ let url = null;
33
34
  let Component = Fragment;
34
35
  if (pathInfo && typeof pathInfo === 'object') {
35
- path = pathInfo.path;
36
- Component = pathInfo.Component;
36
+ url = pathInfo.url;
37
+ if (pathInfo.Component) {
38
+ Component = pathInfo.Component;
39
+ }
40
+
41
+ if (pathInfo.path) {
42
+ path = pathInfo.path;
43
+ }
37
44
  }
38
45
 
39
46
  const itemKey = `text: ${text} path:${path}`;
40
- const child = path ? (
47
+ const child = url ? (
48
+ <a data-cy="Footer-link" className={classes.link} href={url}>
49
+ <FormattedMessage id={text} defaultMessage={text} />
50
+ </a>
51
+ ) : path ? (
41
52
  <Link data-cy="Footer-link" className={classes.link} to={path}>
42
53
  <FormattedMessage id={text} defaultMessage={text} />
43
54
  </Link>
@@ -75,24 +86,19 @@ const Footer = props => {
75
86
  }
76
87
  }
77
88
  >
78
- {!isCheckoutPage && <div className={classes.links}>
89
+ {!isCheckoutPage && <div className={classes.links} style={{"display":"flex"}}>
79
90
  {linkGroups}
80
- <div>
91
+ {/* <div>
81
92
  <Newsletter />
82
- </div>
83
- <div className={classes.callout}>
93
+ </div> */}
94
+ {/* <div className={classes.callout}>
84
95
  <span
85
96
  data-cy="Footer-calloutHeading"
86
97
  className={classes.calloutHeading}
87
98
  >
88
- Follow Us
99
+ Connect with Us
89
100
  </span>
90
101
  <ul className={classes.socialLinks}>
91
- <li>
92
- <Link to="" className="bg-darkblue-900 rounded-full flex justify-center items-center w-[34px] h-[34px]">
93
- <Instagram size={15} color='#FFFFFF' />
94
- </Link>
95
- </li>
96
102
  <li>
97
103
  <Link to="" className="bg-darkblue-900 rounded-full flex justify-center items-center w-[34px] h-[34px]">
98
104
  <Facebook size={15} color='#FFFFFF' />
@@ -105,18 +111,11 @@ const Footer = props => {
105
111
  </li>
106
112
  <li>
107
113
  <Link to="" className="bg-darkblue-900 rounded-full flex justify-center items-center w-[34px] h-[34px]">
108
- <svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
109
- <path d="M12.9311 4.27218C12.8573 4.19836 12.7835 4.19836 12.6358 4.19836C12.4144 4.19836 12.2667 4.19836 12.0453 4.12454C10.7903 3.82925 9.90442 2.86957 9.8306 1.61459V1.31931C9.8306 1.09784 9.68295 0.950195 9.46148 0.950195H7.173C6.95154 0.950195 6.80389 1.09784 6.80389 1.31931V10.5471C6.80389 11.4329 6.06567 12.0973 5.25363 12.0973C4.73688 12.0973 4.29395 11.8758 3.99866 11.5067C3.4819 10.8423 3.62955 9.80883 4.29395 9.29208C4.58923 9.07061 4.88452 9.07061 5.4751 9.07061C5.54892 9.07061 5.69656 9.07061 5.77039 8.99679C5.84421 8.92297 5.91803 8.84915 5.91803 8.7015V6.26538C5.91803 6.11773 5.77039 5.89627 5.62274 5.89627C4.44159 5.74862 3.26044 6.04391 2.37457 6.78213C1.56253 7.52035 0.971956 8.55386 0.824312 9.80883C0.602846 11.2115 1.1196 12.6879 2.07929 13.6476C2.96515 14.6073 4.22012 15.0502 5.40128 15.0502C6.58243 15.0502 7.76358 14.6073 8.64944 13.7214C9.53531 12.8355 10.0521 11.6544 10.0521 10.3994V6.3392C10.9379 6.78213 11.8976 7.07742 12.8573 7.07742C13.0788 7.07742 13.2264 6.92978 13.2264 6.70831V4.49365C13.0788 4.41983 13.0049 4.34601 12.9311 4.27218Z" fill="white" />
110
- </svg>
111
- </Link>
112
- </li>
113
- <li>
114
- <Link to="" className="bg-darkblue-900 rounded-full flex justify-center items-center w-[34px] h-[34px]">
115
- <Linkedin size={15} color='#FFFFFF' />
114
+ <Instagram size={15} color='#FFFFFF' />
116
115
  </Link>
117
116
  </li>
118
117
  </ul>
119
- </div>
118
+ </div> */}
120
119
  </div>}
121
120
  <div className={"flex flex-row justify-between py-4 border-t border-gray-100 w-full py-[15px] mx-[auto] flex justify-between items-center" + (isCheckoutPage ? " max-w-[1000px]" : " max-w-[1300px]")}>
122
121
  <p className="text-colorDefault">{copyrightText || null}</p>
@@ -1,46 +1,50 @@
1
1
  import { ContactLink } from '@magento/venia-ui/lib/components/ContactPage';
2
2
 
3
+ const { location } = globalThis;
4
+ const origin = location.origin;
5
+
3
6
  const accountLinks = new Map()
4
- .set('Account', null)
5
- .set('Sign In', '/')
6
- .set('Register', '/')
7
- .set('Tracking Order', '/')
7
+ .set('Let Us Help You', null)
8
+ .set('Your Account', '/account-information')
9
+ .set('Your Orders', '/order-history')
10
+ .set('Help', '/help-center')
8
11
 
9
- const getToKnowUsLinks = new Map()
10
- .set('About TCG Collective', null)
11
- .set('Career', '/')
12
- .set('Contact Us', '/')
13
- .set('Help Center', '/help-center')
14
- .set('Intelectual Property Claims', '/');
12
+ const socialMediaLinks = new Map()
13
+ .set('Connect With Us', null)
14
+ .set('Facebook', '/')
15
+ .set('Twitter', '/')
16
+ .set('Instagram', '/');
15
17
 
16
- const aboutLinks = new Map()
18
+ const brandLinks = new Map()
19
+ .set('Protect and Build Your Brand', null)
20
+ .set('Sell on TCG Collective', '/')
21
+ .set('Partners/Affiliates', '/');
22
+
23
+ const getToKnowUsLinks = new Map()
24
+ .set('Get to Know Us', null)
17
25
  .set('About Us', '/about-us')
18
- .set('Our Story', null)
19
- .set('Email Signup', null)
20
- .set('Give Back', null);
26
+ .set('Careers', '/career')
27
+ .set('Contact Us', '/contact-us');
21
28
 
22
29
  const sitesLinks = new Map()
23
- .set('Sites', null)
24
- .set('International', '/')
25
- .set('Singapore', '/')
26
- .set('Malaysia', '/')
27
- .set('Indonesia', '/')
28
- .set('Thailand', '/')
29
- .set('Philippines', '/')
30
- .set('Vietnam', '/')
31
-
32
- const helpLinks = new Map()
33
- .set('Help', null)
34
- .set('Customer Service', '/customer-service')
35
- .set('Contact Us', {
36
- path: '/contact-us',
37
- Component: ContactLink
30
+ .set('Our Stores (Country/Region)', null)
31
+ .set('Global', {
32
+ url: origin + '/'
33
+ })
34
+ .set('Singapore', {
35
+ url: origin + '/sg'
36
+ })
37
+ .set('Indonesia', {
38
+ url: origin + '/id'
39
+ })
40
+ .set('Malaysia', {
41
+ url: origin + '/my'
38
42
  })
39
- .set('Order Status', null)
40
- .set('Returns', null);
41
43
 
42
44
  export const DEFAULT_LINKS = new Map()
43
45
  .set('us', getToKnowUsLinks)
46
+ .set('social-media', socialMediaLinks)
47
+ .set('brand', brandLinks)
44
48
  .set('account', accountLinks)
45
49
  .set('sites', sitesLinks);
46
50
 
@@ -46,8 +46,8 @@ const Header = props => {
46
46
  const baseUrl = data?.storeConfig?.base_media_url;
47
47
  const imageSource = data?.storeConfig?.header_logo_src;
48
48
  const LogoImageSource = `${baseUrl}/logo/${imageSource}`;
49
- const IMAGE_HEIGHT = data?.storeConfig?.logo_width || 28;
50
- const IMAGE_WIDTH = data?.storeConfig?.logo_height || 190;
49
+ const IMAGE_HEIGHT = data?.storeConfig?.logo_width || 33;
50
+ const IMAGE_WIDTH = data?.storeConfig?.logo_height || 230;
51
51
  const IMAGE_ALT = data?.storeConfig?.logo_alt || 'TCG Collective';
52
52
 
53
53
  const classes = useStyle(defaultClasses, props.classes);
@@ -1,10 +1,11 @@
1
1
  import React, { useState, useMemo, Fragment, Suspense, useRef, useEffect } from 'react';
2
- import { useQuery } from '@apollo/client';
2
+ import { useQuery, useMutation } from '@apollo/client';
3
3
  import { FormattedMessage, useIntl } from 'react-intl';
4
4
  import { arrayOf, bool, number, shape, string } from 'prop-types';
5
5
  import { Form } from 'informed';
6
6
  import { Info } from 'react-feather';
7
7
  import { useToasts } from '@magento/peregrine/lib';
8
+ import { useUserContext } from '@magento/peregrine/lib/context/user';
8
9
 
9
10
  import Price from '@magento/venia-ui/lib/components/Price';
10
11
  import { useProductFullDetail } from '@magento/peregrine/lib/talons/ProductFullDetail/useProductFullDetail';
@@ -43,6 +44,10 @@ import Collapsible from '@riosst100/pwa-marketplace/src/components/commons/Colla
43
44
  import { useLocation } from 'react-router-dom';
44
45
  import Icon from '@magento/venia-ui/lib/components/Icon';
45
46
  import { ShoppingCart } from 'react-feather';
47
+ import MessagesModal from '@riosst100/pwa-marketplace/src/components/LiveChat/MessagesModal';
48
+ import SellerOperations from '@riosst100/pwa-marketplace/src/talons/Seller/seller.gql';
49
+
50
+ import AgeVerificationModal from '@riosst100/pwa-marketplace/src/components/AgeVerification/ageVerificationModal';
46
51
 
47
52
  import { totalListings, lowestPrice } from '@riosst100/pwa-marketplace/src/components/CrossSeller/crossSellerBuy';
48
53
 
@@ -113,6 +118,7 @@ const ProductFullDetail = props => {
113
118
  } = talonProps;
114
119
 
115
120
  const [, { addToast }] = useToasts();
121
+ const [{ isSignedIn }] = useUserContext();
116
122
 
117
123
  const { formatMessage } = useIntl();
118
124
 
@@ -126,6 +132,89 @@ const ProductFullDetail = props => {
126
132
 
127
133
  const [hoveredMedia, setHoveredMedia] = useState(null);
128
134
  const [selectedPaymentType, setSelectedPaymentType] = useState(null);
135
+ const [isMessagesOpen, setIsMessagesOpen] = useState(false);
136
+
137
+ // Messages GraphQL operations
138
+ const {
139
+ getCustomerSellerMessages,
140
+ customerSendMessageMutation,
141
+ customerReplyMessageMutation,
142
+ customerDeleteMessageMutation
143
+ } = SellerOperations;
144
+
145
+ const [messageListVars] = useState({
146
+ pageSize: 500,
147
+ currentPage: 1,
148
+ filter: undefined,
149
+ sort: undefined
150
+ });
151
+
152
+ const {
153
+ data: messagesData,
154
+ loading: messagesLoading,
155
+ error: messagesError,
156
+ refetch: refetchMessages
157
+ } = useQuery(getCustomerSellerMessages, {
158
+ fetchPolicy: 'network-only',
159
+ nextFetchPolicy: 'network-only',
160
+ variables: messageListVars,
161
+ pollInterval: 10000,
162
+ notifyOnNetworkStatusChange: true
163
+ });
164
+
165
+ const [sendMessage] = useMutation(customerSendMessageMutation);
166
+ const [replyMessage] = useMutation(customerReplyMessageMutation);
167
+ const [deleteMessage] = useMutation(customerDeleteMessageMutation);
168
+
169
+ const handleSendMessage = async ({ subject, content, seller_url }) => {
170
+ const input = { subject, content, seller_url };
171
+ const result = await sendMessage({ variables: { input }, refetchQueries: [{ query: getCustomerSellerMessages, variables: messageListVars }] });
172
+ addToast({
173
+ type: 'success',
174
+ message: formatMessage({ id: 'messages.messageSent', defaultMessage: 'Message sent successfully.' }),
175
+ timeout: 3000
176
+ });
177
+ await refetchMessages({ fetchPolicy: 'network-only' });
178
+ return result?.data?.customerSendMessage;
179
+ };
180
+
181
+ const handleReplyMessage = async ({ message_id, content }) => {
182
+ const input = { message_id, content };
183
+ const result = await replyMessage({ variables: { input }, refetchQueries: [{ query: getCustomerSellerMessages, variables: messageListVars }] });
184
+ addToast({
185
+ type: 'success',
186
+ message: formatMessage({ id: 'messages.replySent', defaultMessage: 'Reply sent successfully.' }),
187
+ timeout: 3000
188
+ });
189
+ await refetchMessages({ fetchPolicy: 'network-only' });
190
+ return result?.data?.customerReplyMessage;
191
+ };
192
+
193
+ const handleDeleteMessage = async ({ id }) => {
194
+ await deleteMessage({ variables: { id }, refetchQueries: [{ query: getCustomerSellerMessages, variables: messageListVars }] });
195
+ addToast({
196
+ type: 'success',
197
+ message: formatMessage({ id: 'messages.messageDeleted', defaultMessage: 'Message deleted successfully.' }),
198
+ timeout: 3000
199
+ });
200
+ await refetchMessages({ fetchPolicy: 'network-only' });
201
+ };
202
+
203
+ const openMessagesModal = () => {
204
+ if (!isSignedIn) {
205
+ addToast({
206
+ type: 'info',
207
+ message: formatMessage({
208
+ id: 'messages.loginRequired',
209
+ defaultMessage: 'Please sign in to your account to send a message to the seller.'
210
+ }),
211
+ timeout: 3000
212
+ });
213
+ return;
214
+ }
215
+ setIsMessagesOpen(true);
216
+ };
217
+ const closeMessagesModal = () => setIsMessagesOpen(false);
129
218
 
130
219
  const options = isProductConfigurable(product) ? (
131
220
  <Suspense fallback={<ProductOptionsShimmer />}>
@@ -722,6 +811,7 @@ const ProductFullDetail = props => {
722
811
  <Fragment>
723
812
  {breadcrumbs}
724
813
  {productPreviewMessages}
814
+ {/* <AgeVerificationModal /> */}
725
815
  <Form
726
816
  className={classes.root}
727
817
  data-cy="ProductFullDetail-root"
@@ -886,14 +976,14 @@ const ProductFullDetail = props => {
886
976
  <div class="flex items-center justify-center gap-[5px] py-1 relative bg-white">
887
977
  <div class="flex items-center justify-center gap-[5px] relative">
888
978
  <div className='flex flex-wrap items-start gap-4 relative'>
889
- <Link to='#' class="flex items-center justify-center gap-[5px] py-1 px-5 relative bg-white rounded-[30px] border border-solid border-[#f76b1c]">
979
+ <button type="button" onClick={openMessagesModal} class="flex items-center justify-center gap-[5px] py-1 px-5 relative bg-white rounded-[30px] border border-solid border-[#f76b1c]">
890
980
  <div class="flex items-center justify-center gap-[10px] relative">
891
981
  <Sms color="#f76b1c" size={14} variant="Outline" className='stroke-[#f76b1c]' />
892
982
  <div class="relative xs_hidden lg_flex w-fit font-medium text-[#f76b1c] text-[14px] tracking-[0] leading-[20px] whitespace-nowrap">
893
983
  Send Message
894
984
  </div>
895
985
  </div>
896
- </Link>
986
+ </button>
897
987
  <Link to={"/seller/"+sellerDetails.url_key} class="flex items-center justify-center gap-[5px] py-1 px-5 relative bg-white rounded-[30px] border border-solid border-[#f76b1c]">
898
988
  <div class="flex items-center justify-center gap-[10px] relative">
899
989
  <Shop color="#f76b1c" size={14} variant="Outline" className='stroke-[#f76b1c]' />
@@ -951,6 +1041,16 @@ const ProductFullDetail = props => {
951
1041
  <CrossSeller/>
952
1042
  </div>
953
1043
  </section>
1044
+ <MessagesModal
1045
+ isOpen={isMessagesOpen}
1046
+ onClose={closeMessagesModal}
1047
+ messages={messagesData?.customer?.sellerMessages}
1048
+ seller={sellerDetails}
1049
+ onReply={handleReplyMessage}
1050
+ onDelete={handleDeleteMessage}
1051
+ onSend={handleSendMessage}
1052
+ loading={messagesLoading}
1053
+ />
954
1054
  </Fragment>
955
1055
  );
956
1056
  };
@@ -1006,3 +1106,5 @@ ProductFullDetail.propTypes = {
1006
1106
  };
1007
1107
 
1008
1108
  export default ProductFullDetail;
1109
+ // Messages modal attached at root to avoid layout shifts
1110
+