@riosst100/pwa-marketplace 3.2.4 → 3.2.6

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 (29) hide show
  1. package/package.json +1 -1
  2. package/src/components/AgeVerification/ageVerificationModal.js +2 -2
  3. package/src/components/AgeVerification/index.js +1 -1
  4. package/src/components/BecomeSeller/becomeSeller.js +13 -10
  5. package/src/components/BecomeSellerPage/becomeSellerPage.js +29 -10
  6. package/src/components/BecomeSellerPage/becomeSellerPage.module.css +21 -0
  7. package/src/components/RFQ/index.js +3 -2
  8. package/src/components/RFQ/modalRfq.js +186 -68
  9. package/src/components/RFQPage/orderRow.js +84 -249
  10. package/src/components/RFQPage/orderRow.module.css +146 -0
  11. package/src/components/RFQPage/quoteDetail.js +173 -86
  12. package/src/components/RFQPage/quoteList.js +87 -65
  13. package/src/components/SocialLogin/googleSignInButton.js +50 -0
  14. package/src/components/SocialLogin/index.js +1 -10
  15. package/src/components/SocialLogin/socialLogin.js +28 -23
  16. package/src/components/WebsiteSwitcher/websiteSwitcherItem.js +21 -7
  17. package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +3 -1
  18. package/src/overwrites/venia-ui/lib/components/CreateAccount/createAccount.js +4 -4
  19. package/src/overwrites/venia-ui/lib/components/CreateAccountPage/createAccountPage.js +28 -9
  20. package/src/overwrites/venia-ui/lib/components/CreateAccountPage/createAccountPage.module.css +12 -2
  21. package/src/overwrites/venia-ui/lib/components/OrderHistoryPage/orderRow.js +1 -1
  22. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +1 -0
  23. package/src/overwrites/venia-ui/lib/components/SignIn/signIn.js +23 -30
  24. package/src/overwrites/venia-ui/lib/components/SignInPage/signInPage.js +22 -9
  25. package/src/overwrites/venia-ui/lib/components/SignInPage/signInPage.module.css +10 -0
  26. package/src/talons/RFQ/rfq.gql.js +162 -0
  27. package/src/talons/RFQ/useRFQ.js +81 -0
  28. package/src/talons/SocialLogin/socialLogin.gql.js +106 -0
  29. package/src/talons/SocialLogin/useSocialLogin.js +169 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riosst100/pwa-marketplace",
3
3
  "author": "riosst100@gmail.com",
4
- "version": "3.2.4",
4
+ "version": "3.2.6",
5
5
  "main": "src/index.js",
6
6
  "pwa-studio": {
7
7
  "targets": {
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
  import modalClasses from '@riosst100/pwa-marketplace/src/components/AgeVerification/ageVerificationModal.module.css';
4
- import { AgeVerificationModalShimmer } from '@riosst100/pwa-marketplace/src/components/SellerCoupon';
4
+ // import { AgeVerificationModalShimmer } from '@riosst100/pwa-marketplace/src/components/SellerCoupon';
5
5
 
6
6
  // Reuse day diff logic
7
7
  const dayDiff = (toDate) => {
@@ -92,7 +92,7 @@ const AgeVerificationModal = ({
92
92
  <button type="button" className={modalClasses.closeBtn} onClick={handleClose} aria-label="Close coupon modal">×</button>
93
93
  </div>
94
94
  <div className={modalClasses.body}>
95
- {couponLoading && <AgeVerificationModalShimmer />}
95
+ {/* {couponLoading && <AgeVerificationModalShimmer />} */}
96
96
  {!couponLoading && couponError && <p className={modalClasses.metaText}>Failed to load coupons.</p>}
97
97
  {!couponLoading && !couponError && !items.length && <p className={modalClasses.metaText}>No coupons available.</p>}
98
98
  {!couponLoading && !couponError && items.length > 0 && (
@@ -1,2 +1,2 @@
1
1
  export { default } from './ageVerificationModal';
2
- export { default as AgeVerificationModalShimmer } from './ageVerificationModal.shimmer';
2
+ // export { default as AgeVerificationModalShimmer } from './ageVerificationModal.shimmer';
@@ -25,6 +25,9 @@ import Postcode from '@magento/venia-ui/lib/components/Postcode';
25
25
  import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
26
26
  import { Link } from 'react-router-dom';
27
27
  import cn from 'classnames';
28
+ import { BrowserPersistence } from '@magento/peregrine/lib/util';
29
+
30
+ const storage = new BrowserPersistence();
28
31
 
29
32
  const BecomeSeller = props => {
30
33
  const talonProps = useBecomeSeller({
@@ -47,6 +50,9 @@ const BecomeSeller = props => {
47
50
  const { formatMessage } = useIntl();
48
51
  const classes = useStyle(defaultClasses, props.classes);
49
52
 
53
+ const websiteCountry = storage.getItem('website_code') && storage.getItem('website_code') != "base" ? '/' + storage.getItem('website_code') : '';
54
+ const sellerDashboardUrl = process.env.MAGENTO_BACKEND_URL ? process.env.MAGENTO_BACKEND_URL + websiteCountry : '';
55
+
50
56
  const submitButton = (
51
57
  <Button
52
58
  className={cn(classes.submitButton, 'w-full')}
@@ -89,12 +95,12 @@ const BecomeSeller = props => {
89
95
  onSubmit={handleSubmit}
90
96
  onChange={handleChange}
91
97
  >
92
- <h2 data-cy="BecomeSeller-title" className="capitalize text-lg font-medium mb-[70px]">
98
+ {/* <h2 data-cy="BecomeSeller-title" className="capitalize text-lg font-medium mb-[70px]">
93
99
  <FormattedMessage
94
100
  id={'becomeSeller.becomeSellerText'}
95
101
  defaultMessage={'Register as Seller'}
96
102
  />
97
- </h2>
103
+ </h2> */}
98
104
  <FormError errors={Array.from(errors.values())} />
99
105
  <h3 className='text-[16px] leading-5 font-medium text-left mb-[30px]'>
100
106
  <FormattedMessage
@@ -416,22 +422,19 @@ const BecomeSeller = props => {
416
422
  {submitButton}
417
423
  </div>
418
424
  <div className='block'>
419
- <span className='text-xs font-normal'>
425
+ <span className='text-sm font-normal'>
420
426
  <FormattedMessage
421
427
  id={'createAccount.alreadyHaveAccount'}
422
- defaultMessage={"Already have an account ?"}
428
+ defaultMessage={"Already have an seller account?"}
423
429
  />
424
430
  </span>
425
431
  {' '}
426
- <Link
427
- to={'/sign-in'}
428
- className='text-xs font-normal text-blue-700 underline'
429
- >
432
+ <a href={sellerDashboardUrl + "/lofmarketplace/seller/login"} className='text-sm font-normal text-blue-700 underline'>
430
433
  <FormattedMessage
431
434
  id={'createAccount.SignInAsSeller'}
432
- defaultMessage={"Sign in as seller now "}
435
+ defaultMessage={"Sign In to Seller Dashboard"}
433
436
  />
434
- </Link>
437
+ </a>
435
438
  </div>
436
439
  </Form>
437
440
  ) : '';
@@ -7,7 +7,7 @@ import { useStyle } from '@magento/venia-ui/lib/classify';
7
7
  import BecomeSeller from '@riosst100/pwa-marketplace/src/components/BecomeSeller';
8
8
  import { StoreTitle } from '@magento/venia-ui/lib/components/Head';
9
9
  import defaultClasses from './becomeSellerPage.module.css';
10
-
10
+ import cn from 'classnames';
11
11
 
12
12
  const BecomeSellerPage = props => {
13
13
  const classes = useStyle(defaultClasses, props.classes);
@@ -17,15 +17,34 @@ const BecomeSellerPage = props => {
17
17
  const { isSeller } = becomeSellerProps
18
18
 
19
19
  return !isSeller ? (
20
- <div className={classes.root}>
21
- <StoreTitle>
22
- {formatMessage({
23
- id: 'becomeSellerPage.title',
24
- defaultMessage: 'Become a Seller'
25
- })}
26
- </StoreTitle>
27
- <div className="lg_border-2 lg_border-solid lg_border-subtle lg_pb-md lg_rounded-md !border !border-gray-100 !rounded-lg">
28
- <BecomeSeller {...becomeSellerProps} />
20
+ <div className={classes.rootContainer}>
21
+ <div className={cn(classes.leftContent, '!py-[60px]')}>
22
+ <StoreTitle>
23
+ {formatMessage({
24
+ id: 'signInPage.title',
25
+ defaultMessage: 'Sign In'
26
+ })}
27
+ </StoreTitle>
28
+ <div className={cn(classes.leftContentContainer, '')}>
29
+ <div class="auth-left">
30
+ <img src="https://img.freepik.com/premium-vector/verified-seller-illustration-concept-white-background_701961-240.jpg" width="100%" />
31
+ <p style={{
32
+ "fontSize": "20px",
33
+ "fontWeight": 500
34
+ }}>Create a seller account and start selling your collectibles today.</p>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div className={classes.root}>
39
+ <StoreTitle>
40
+ {formatMessage({
41
+ id: 'becomeSellerPage.title',
42
+ defaultMessage: 'Become a Seller'
43
+ })}
44
+ </StoreTitle>
45
+ <div className="lg_border-2 lg_border-solid lg_border-subtle lg_rounded-md !border !border-gray-100 !rounded-lg">
46
+ <BecomeSeller {...becomeSellerProps} />
47
+ </div>
29
48
  </div>
30
49
  </div>
31
50
  ) : ''
@@ -1,4 +1,15 @@
1
1
  .root {
2
+ composes: gap-y-md from global;
3
+ composes: grid from global;
4
+ /* composes: justify-center from global; */
5
+ composes: px-0 from global;
6
+ composes: py-md from global;
7
+ composes: text-center from global;
8
+ grid-template-columns: minmax(auto, 80%);
9
+ justify-content: right;
10
+ }
11
+
12
+ .leftContent {
2
13
  composes: gap-y-md from global;
3
14
  composes: grid from global;
4
15
  composes: justify-center from global;
@@ -10,4 +21,14 @@
10
21
 
11
22
  .header {
12
23
  composes: font-serif from global;
24
+ }
25
+
26
+ .leftContentContainer {
27
+ text-align: left;
28
+ }
29
+
30
+ .rootContainer {
31
+ justify-content: space-around;
32
+ display: flex;
33
+ align-items: normal;
13
34
  }
@@ -4,13 +4,14 @@ import { useIntl } from 'react-intl';
4
4
  import ModalRFQ from './modalRfq';
5
5
  import cn from 'classnames';
6
6
 
7
- const RFQ = ({ disabled, classes: customClasses = {} }) => {
7
+ const RFQ = ({ productId, disabled, classes: customClasses = {} }) => {
8
+
8
9
  const [open, setOpen] = useState(false);
9
10
  const { formatMessage } = useIntl();
10
11
 
11
12
  return (
12
13
  <>
13
- <ModalRFQ open={open} setOpen={setOpen} />
14
+ <ModalRFQ open={open} setOpen={setOpen} productId={productId} />
14
15
 
15
16
  <Button
16
17
  data-cy="ProductFullDetail-RFQ"
@@ -1,4 +1,4 @@
1
- import React, { useState, useC } from 'react';
1
+ import React, { useCallback, useState } from 'react';
2
2
  import Button from '@magento/venia-ui/lib/components/Button';
3
3
  import Modal from '@riosst100/pwa-marketplace/src/components/Modal';
4
4
  import { X } from 'react-feather';
@@ -13,35 +13,108 @@ import { useIntl } from 'react-intl';
13
13
  import { useDropzone } from "react-dropzone";
14
14
  import { DocumentUpload } from 'iconsax-react';
15
15
  import cn from 'classnames';
16
+ import { useToasts } from '@magento/peregrine/lib/Toasts/useToasts';
17
+ import { useRFQ } from '@riosst100/pwa-marketplace/src/talons/RFQ/useRFQ';
16
18
 
17
19
  import "react-datepicker/dist/react-datepicker.css";
18
20
 
19
21
  const RFQModalForm = (props) => {
20
22
 
21
- const { open = false, setOpen = () => { } } = props;
23
+ const { productId, open = false, setOpen = () => { } } = props;
24
+
22
25
  const { formatMessage } = useIntl();
26
+ const [, { addToast }] = useToasts();
27
+ const { handleCreateQuickRfq, createState } = useRFQ();
23
28
 
24
29
  const [startDate, setStartDate] = useState(new Date());
25
30
 
26
- // Upload FIle
31
+ // Upload File with remove capability
32
+ const [uploadedFiles, setUploadedFiles] = useState([]);
33
+ const onDrop = useCallback((accepted) => {
34
+ setUploadedFiles(prev => [...prev, ...accepted]);
35
+ }, []);
36
+
27
37
  const {
28
- acceptedFiles,
29
38
  getRootProps,
30
39
  getInputProps,
31
40
  isDragActive
32
- } = useDropzone();
41
+ } = useDropzone({ onDrop });
33
42
 
34
- const files = acceptedFiles.map((file) => (
35
- <div key={file.path} className='block font-medium mb-1'>
36
- {file.path} - {Math.round(file.size / 1000)} kB
43
+ const handleRemoveFile = useCallback((index) => {
44
+ setUploadedFiles(prev => prev.filter((_, i) => i !== index));
45
+ }, []);
46
+
47
+ const files = uploadedFiles.map((file, idx) => (
48
+ <div key={(file.path || file.name) + idx} className='flex items-center justify-between gap-2 mb-1'>
49
+ <span className='block font-medium'>
50
+ {(file.path || file.name)} - {Math.round(file.size / 1000)} kB
51
+ </span>
52
+ <button
53
+ type='button'
54
+ className='text-red-600 text-sm underline'
55
+ onClick={() => handleRemoveFile(idx)}
56
+ >
57
+ Remove
58
+ </button>
37
59
  </div>
38
60
  ));
39
61
 
62
+ const handleSubmit = useCallback(async values => {
63
+ console.log('[RFQ] submit values', values);
64
+ const input = {
65
+ product_id: productId,
66
+ quantity: values.quote_qty ? Number(values.quote_qty) : undefined,
67
+ comment: values.quote_comment,
68
+ customer_phone: values.phone_number,
69
+ price_per_product: values.quote_price_per_unit
70
+ ? Number(values.quote_price_per_unit)
71
+ : undefined,
72
+ date_need_quote: startDate ? startDate.toISOString() : undefined,
73
+ files: uploadedFiles.length
74
+ ? uploadedFiles.map(file => ({
75
+ file_name: (file && (file.name || file.path)) || '',
76
+ // Browsers do not expose real file system paths; use name as a stable placeholder
77
+ file_path: (file && (file.path || file.name)) || '',
78
+ // Ensure a valid MIME string; default if missing
79
+ file_type: (file && file.type) ? file.type : 'application/octet-stream'
80
+ }))
81
+ : undefined
82
+ };
83
+
84
+ console.log('[RFQ] input payload', input);
85
+
86
+ Object.keys(input).forEach(key => input[key] === undefined && delete input[key]);
87
+
88
+ try {
89
+ const result = await handleCreateQuickRfq(input);
90
+ console.log('[RFQ] mutation result', result);
91
+ addToast({
92
+ type: 'success',
93
+ message: formatMessage({
94
+ id: 'productFullDetail.rfqSuccess',
95
+ defaultMessage: 'Quote request sent successfully.'
96
+ }),
97
+ timeout: 3000
98
+ });
99
+ setOpen(false);
100
+ } catch (error) {
101
+ console.error('[RFQ] createQuickRfq failed', error);
102
+ addToast({
103
+ type: 'error',
104
+ message: formatMessage({
105
+ id: 'productFullDetail.rfqFailed',
106
+ defaultMessage: 'Unable to send quote request. Please try again.'
107
+ }),
108
+ timeout: 4000
109
+ });
110
+ }
111
+ }, [uploadedFiles, addToast, formatMessage, handleCreateQuickRfq, setOpen, startDate]);
112
+
40
113
  return (
41
114
  <>
42
115
  <Modal
43
116
  open={open}
44
- className='!p-[30px] md_min-w-[550px] modal_bid'
117
+ className='!p-[20px] md_min-w-[550px] modal_bid'
45
118
  >
46
119
  <div className='modal_bid-content'>
47
120
  <div className='header_title-modal flex justify-between mb-4'>
@@ -59,59 +132,71 @@ const RFQModalForm = (props) => {
59
132
  </button>
60
133
  </div>
61
134
 
62
- <div className='flex flex-col gap-y-[16px]'>
135
+ <div className='flex flex-col gap-y-[8px]'>
63
136
  <Form
64
137
  data-cy="form_bid"
65
- className="flex flex-col gap-y-4"
138
+ className="flex flex-col gap-y-2"
66
139
  initialValues={{}}
67
- onSubmit={() => { }}
68
- onChange={() => { }}
140
+ onSubmit={handleSubmit}
141
+ onChange={values => {
142
+ console.log('[RFQ] form change', values);
143
+ }}
144
+ onSubmitFailure={(errors, values) => {
145
+ console.warn('[RFQ] form submit failed', { errors, values });
146
+ }}
69
147
  >
70
- <Field
71
- id="quote_price_per_unit_field"
72
- label={
73
- formatMessage({
74
- id: 'productFullDetail.QuotePricePerUnit',
75
- defaultMessage:
76
- 'Quote Price Per Unit'
77
- })
78
- }
79
- >
80
- <TextInput
81
- id="quote_price_per_unit"
82
- field="quote_price_per_unit"
83
- validate={isRequired}
84
- validateOnBlur
85
- mask={value => value && value.trim()}
86
- maskOnBlur={true}
87
- data-cy="quote_price_per_unit"
88
- aria-label={'quote_price_per_unit'}
89
- placeholder={'10.00'}
90
- />
91
- </Field>
92
-
93
- <Field
94
- id="quote_qty_field"
95
- label={
96
- formatMessage({
97
- id: 'productFullDetail.QuoteQuantity',
98
- defaultMessage:
99
- 'Quote Quantity'
100
- })
101
- }
102
- >
103
- <TextInput
104
- id="quote_qty"
105
- field="quote_qty"
106
- validate={isRequired}
107
- validateOnBlur
108
- mask={value => value && value.trim()}
109
- maskOnBlur={true}
110
- data-cy="quote_qty"
111
- aria-label={'quote_qty'}
112
- placeholder={'10'}
113
- />
114
- </Field>
148
+ {({ formApi }) => (
149
+ <>
150
+ <div className="flex flex-col md_flex-row gap-2">
151
+ <div className="md_flex-1">
152
+ <Field
153
+ id="quote_price_per_unit_field"
154
+ label={
155
+ formatMessage({
156
+ id: 'productFullDetail.QuotePricePerUnit',
157
+ defaultMessage:
158
+ 'Quote Price Per Unit'
159
+ })
160
+ }
161
+ >
162
+ <TextInput
163
+ id="quote_price_per_unit"
164
+ field="quote_price_per_unit"
165
+ validate={isRequired}
166
+ validateOnBlur
167
+ mask={value => value && value.trim()}
168
+ maskOnBlur={true}
169
+ data-cy="quote_price_per_unit"
170
+ aria-label={'quote_price_per_unit'}
171
+ placeholder={'10.00'}
172
+ />
173
+ </Field>
174
+ </div>
175
+ <div className="md_flex-1">
176
+ <Field
177
+ id="quote_qty_field"
178
+ label={
179
+ formatMessage({
180
+ id: 'productFullDetail.QuoteQuantity',
181
+ defaultMessage:
182
+ 'Quote Quantity'
183
+ })
184
+ }
185
+ >
186
+ <TextInput
187
+ id="quote_qty"
188
+ field="quote_qty"
189
+ validate={isRequired}
190
+ validateOnBlur
191
+ mask={value => value && value.trim()}
192
+ maskOnBlur={true}
193
+ data-cy="quote_qty"
194
+ aria-label={'quote_qty'}
195
+ placeholder={'10'}
196
+ />
197
+ </Field>
198
+ </div>
199
+ </div>
115
200
 
116
201
  <Field
117
202
  id="date_need_quote_field"
@@ -149,7 +234,7 @@ const RFQModalForm = (props) => {
149
234
  maskOnBlur={true}
150
235
  data-cy="phone_number"
151
236
  aria-label={'phone_number'}
152
- placeholder={'10'}
237
+ placeholder={'(123) 456-7890'}
153
238
  />
154
239
  </Field>
155
240
 
@@ -178,7 +263,7 @@ const RFQModalForm = (props) => {
178
263
  <section className="wfp--dropzone">
179
264
  <div
180
265
  {...getRootProps({ isDragActive })}
181
- className='border border-blue-600 rounded-md border-dashed flex flex-col justify-center items-center py-6'
266
+ className='border border-blue-600 rounded-md border-dashed flex flex-col justify-center items-center py-3'
182
267
  >
183
268
  <input {...getInputProps()} />
184
269
  <DocumentUpload
@@ -187,7 +272,7 @@ const RFQModalForm = (props) => {
187
272
  />
188
273
  <div className='mt-2 text-blue-600'>Drop files or click here to upload</div>
189
274
  </div>
190
- {files && (
275
+ {files && files.length > 0 && (
191
276
  <div className="wfp--dropzone__file-list mt-4">
192
277
  <ul>{files}</ul>
193
278
  </div>
@@ -201,7 +286,11 @@ const RFQModalForm = (props) => {
201
286
  classes={{
202
287
  content: 'capitalize text-[16px] font-medium'
203
288
  }}
204
- onClick={() => setOpen(false)}
289
+ type="button"
290
+ onClick={() => {
291
+ console.log('[RFQ] Cancel clicked, closing modal');
292
+ setOpen(false);
293
+ }}
205
294
  >
206
295
  {
207
296
  formatMessage({
@@ -216,22 +305,47 @@ const RFQModalForm = (props) => {
216
305
  classes={{
217
306
  content: 'capitalize text-[16px] font-medium'
218
307
  }}
308
+ type="button"
309
+ onPress={() => {
310
+ console.log('[RFQ] Submit pressed, calling formApi.submitForm()');
311
+ formApi.submitForm();
312
+ }}
313
+ disabled={createState?.loading}
314
+ data-cy="rfq-submit"
219
315
  >
220
- {
221
- formatMessage({
222
- id: 'productFullDetail.SendRequest',
223
- defaultMessage:
224
- 'Send Request'
316
+ {createState?.loading
317
+ ? formatMessage({
318
+ id: 'productFullDetail.SendingRequest',
319
+ defaultMessage: 'Sending...'
225
320
  })
226
- }
321
+ : formatMessage({
322
+ id: 'productFullDetail.SendRequest',
323
+ defaultMessage: 'Send Request'
324
+ })}
227
325
  </Button>
228
326
  </div>
327
+ </>
328
+ )}
229
329
  </Form>
230
330
  </div>
231
331
  </div>
232
332
  </Modal>
233
333
  <style jsx="true">
234
334
  {`
335
+ /* Modal container adjustments to prevent clipping */
336
+ .modal_bid-content {
337
+ max-height: 90vh;
338
+ overflow-y: auto;
339
+ }
340
+ @media (min-width: 768px) {
341
+ .md_min-w-[550px] {
342
+ min-width: 550px;
343
+ }
344
+ }
345
+ /* Ensure inner form sections don't overflow */
346
+ .wfp--dropzone {
347
+ max-width: 100%;
348
+ }
235
349
  .react-datepicker__input-container>input:focus-visible,
236
350
  .react-datepicker__input-container>input:focus,
237
351
  .react-datepicker__input-container>input:active {
@@ -240,6 +354,10 @@ const RFQModalForm = (props) => {
240
354
  .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {
241
355
  left: 25px;
242
356
  }
357
+ /* Keep datepicker within viewport */
358
+ .react-datepicker-popper {
359
+ z-index: 1000;
360
+ }
243
361
  `}
244
362
  </style>
245
363
  </>