@riosst100/pwa-marketplace 1.0.1 → 1.0.2
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.
- package/package.json +1 -1
- package/src/components/BecomeSeller/becomeSeller.js +3 -3
- package/src/components/BecomeSeller/becomeSeller.module.css +2 -2
- package/src/components/BecomeSellerLink/becomeSellerLink.js +28 -0
- package/src/components/BecomeSellerLink/becomeSellerLink.module.css +13 -0
- package/src/components/BecomeSellerPage/becomeSellerPage.js +3 -2
- package/src/components/BecomeSellerPage/becomeSellerPage.module.css +21 -0
- package/src/components/WebsiteSwitcher/websiteSwitcher.js +109 -0
- package/src/components/WebsiteSwitcher/websiteSwitcher.module.css +111 -0
- package/src/components/WebsiteSwitcher/websiteSwitcher.shimmer.js +6 -0
- package/src/components/WebsiteSwitcher/websiteSwitcherItem.js +47 -0
- package/src/components/WebsiteSwitcher/websiteSwitcherItem.module.css +20 -0
- package/src/intercept.js +30 -37
- package/src/overwrites/venia-ui/lib/components/Header/header.js +2 -2
- package/src/overwrites/venia-ui/lib/components/Header/storeSwitcher.js +2 -2
- package/src/talons/BecomeSeller/becomeSeller.gql.js +136 -0
- package/src/talons/BecomeSeller/useBecomeSeller.js +288 -0
- package/src/talons/BecomeSellerLink/useBecomeSellerLink.js +13 -0
- package/src/talons/BecomeSellerPage/useBecomeSellerPage.js +68 -0
- package/src/talons/WebsiteSwitcher/useWebsiteSwitcher.js +218 -0
- package/src/talons/WebsiteSwitcher/websiteSwitcher.gql.js +45 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
3
3
|
import { Form } from 'informed';
|
|
4
4
|
import { func, shape, string, bool } from 'prop-types';
|
|
5
|
-
import { useBecomeSeller } from '@riosst100/pwa-
|
|
5
|
+
import { useBecomeSeller } from '@riosst100/pwa-marketplace/src/talons/BecomeSeller/useBecomeSeller';
|
|
6
6
|
|
|
7
7
|
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
8
8
|
import combine from '@magento/venia-ui/lib/util/combineValidators';
|
|
@@ -68,7 +68,7 @@ const BecomeSeller = props => {
|
|
|
68
68
|
>
|
|
69
69
|
<FormattedMessage
|
|
70
70
|
id={'becomeSeller.becomeSellerText'}
|
|
71
|
-
defaultMessage={'
|
|
71
|
+
defaultMessage={'Become a Seller'}
|
|
72
72
|
/>
|
|
73
73
|
</Button>
|
|
74
74
|
);
|
|
@@ -83,7 +83,7 @@ const BecomeSeller = props => {
|
|
|
83
83
|
<h2 data-cy="BecomeSeller-title" className={classes.title}>
|
|
84
84
|
<FormattedMessage
|
|
85
85
|
id={'becomeSeller.becomeSellerText'}
|
|
86
|
-
defaultMessage={'
|
|
86
|
+
defaultMessage={'Become a Seller'}
|
|
87
87
|
/>
|
|
88
88
|
</h2>
|
|
89
89
|
<FormError errors={Array.from(errors.values())} />
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.cancelButton {
|
|
34
|
-
composes: root_lowPriority from '
|
|
34
|
+
composes: root_lowPriority from '@magento/venia-ui/lib/components/Button/button.module.css';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.submitButton {
|
|
38
|
-
composes: root_highPriority from '
|
|
38
|
+
composes: root_highPriority from '@magento/venia-ui/lib/components/Button/button.module.css';
|
|
39
39
|
|
|
40
40
|
composes: col-start-auto from global;
|
|
41
41
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
3
|
+
import defaultClasses from './becomeSellerLink.module.css';
|
|
4
|
+
import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
|
|
5
|
+
import { Link } from 'react-router-dom';
|
|
6
|
+
import { shape, string } from 'prop-types';
|
|
7
|
+
|
|
8
|
+
const BecomeSellerLink = props => {
|
|
9
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
10
|
+
const becomeSellerLabel = "Become a Seller";
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className={classes.root} data-cy="BecomeSellerLink-root">
|
|
14
|
+
<Link
|
|
15
|
+
aria-label={becomeSellerLabel}
|
|
16
|
+
to={resourceUrl('/become-seller')}
|
|
17
|
+
>{becomeSellerLabel}</Link>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default BecomeSellerLink;
|
|
23
|
+
|
|
24
|
+
BecomeSellerLink.propTypes = {
|
|
25
|
+
classes: shape({
|
|
26
|
+
root: string
|
|
27
|
+
})
|
|
28
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: grid from global;
|
|
3
|
+
composes: items-center from global;
|
|
4
|
+
composes: justify-items-start from global;
|
|
5
|
+
composes: max-w-site from global;
|
|
6
|
+
composes: mx-auto from global;
|
|
7
|
+
composes: my-0 from global;
|
|
8
|
+
composes: px-xs from global;
|
|
9
|
+
composes: py-2xs from global;
|
|
10
|
+
composes: relative from global;
|
|
11
|
+
|
|
12
|
+
composes: sm_justify-items-end from global;
|
|
13
|
+
}
|
|
@@ -2,10 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import { shape, string } from 'prop-types';
|
|
3
3
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
4
4
|
|
|
5
|
-
import { useBecomeSellerPage } from '@riosst100/pwa-
|
|
5
|
+
import { useBecomeSellerPage } from '@riosst100/pwa-marketplace/src/talons/BecomeSellerPage/useBecomeSellerPage';
|
|
6
6
|
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
7
|
-
import BecomeSeller from '@riosst100/pwa-
|
|
7
|
+
import BecomeSeller from '@riosst100/pwa-marketplace/src/components/BecomeSeller';
|
|
8
8
|
import { StoreTitle } from '@magento/venia-ui/lib/components/Head';
|
|
9
|
+
import defaultClasses from './becomeSellerPage.module.css';
|
|
9
10
|
|
|
10
11
|
const BecomeSellerPage = props => {
|
|
11
12
|
const classes = useStyle(defaultClasses, props.classes);
|
|
@@ -0,0 +1,21 @@
|
|
|
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, 512px);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.header {
|
|
12
|
+
composes: font-serif from global;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.contentContainer {
|
|
16
|
+
composes: lg_border-2 from global;
|
|
17
|
+
composes: lg_border-solid from global;
|
|
18
|
+
composes: lg_border-subtle from global;
|
|
19
|
+
composes: lg_pb-md from global;
|
|
20
|
+
composes: lg_rounded-md from global;
|
|
21
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { shape, string } from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { useWebsiteSwitcher } from '@riosst100/pwa-marketplace/src/talons/WebsiteSwitcher/useWebsiteSwitcher';
|
|
5
|
+
import { availableRoutes } from '@magento/venia-ui/lib/components/Routes/routes';
|
|
6
|
+
|
|
7
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
8
|
+
import defaultClasses from './websiteSwitcher.module.css';
|
|
9
|
+
import WebsiteSwitcherItem from './websiteSwitcherItem';
|
|
10
|
+
import Shimmer from './websiteSwitcher.shimmer';
|
|
11
|
+
|
|
12
|
+
const WebsiteSwitcher = props => {
|
|
13
|
+
const {
|
|
14
|
+
availableStores,
|
|
15
|
+
currentGroupName,
|
|
16
|
+
currentWebsiteName,
|
|
17
|
+
handleSwitchWebsite,
|
|
18
|
+
storeGroups,
|
|
19
|
+
storeMenuRef,
|
|
20
|
+
storeMenuTriggerRef,
|
|
21
|
+
storeMenuIsOpen,
|
|
22
|
+
handleTriggerClick
|
|
23
|
+
} = useWebsiteSwitcher({ availableRoutes });
|
|
24
|
+
|
|
25
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
26
|
+
const menuClassName = storeMenuIsOpen ? classes.menu_open : classes.menu;
|
|
27
|
+
|
|
28
|
+
if (!availableStores) return <Shimmer />;
|
|
29
|
+
|
|
30
|
+
if (availableStores.size <= 1) return null;
|
|
31
|
+
|
|
32
|
+
const groups = [];
|
|
33
|
+
const hasOnlyOneGroup = storeGroups.size === 1;
|
|
34
|
+
|
|
35
|
+
storeGroups.forEach((group, key) => {
|
|
36
|
+
const stores = [];
|
|
37
|
+
group.forEach(({ storeGroupName, websiteCode, websiteName, isCurrent, storeCode }) => {
|
|
38
|
+
let label = `${websiteName}`;
|
|
39
|
+
stores.push(
|
|
40
|
+
<li
|
|
41
|
+
aria-selected={currentWebsiteName}
|
|
42
|
+
role="option"
|
|
43
|
+
key={storeCode}
|
|
44
|
+
className={classes.menuItem}
|
|
45
|
+
data-cy="WebsiteSwitcher-view"
|
|
46
|
+
>
|
|
47
|
+
<WebsiteSwitcherItem
|
|
48
|
+
active={isCurrent}
|
|
49
|
+
onClick={handleSwitchWebsite}
|
|
50
|
+
option={storeCode}
|
|
51
|
+
option2={websiteCode}
|
|
52
|
+
>
|
|
53
|
+
{label}
|
|
54
|
+
</WebsiteSwitcherItem>
|
|
55
|
+
</li>
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
groups.push(
|
|
60
|
+
<ul
|
|
61
|
+
role="listbox"
|
|
62
|
+
className={classes.groupList}
|
|
63
|
+
key={key}
|
|
64
|
+
data-cy="WebsiteSwitcher-group"
|
|
65
|
+
>
|
|
66
|
+
{stores}
|
|
67
|
+
</ul>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let triggerLabel = `${currentWebsiteName}`;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className={classes.root} data-cy="WebsiteSwitcher-root">
|
|
75
|
+
<button
|
|
76
|
+
data-cy="WebsiteSwitcher-triggerButton"
|
|
77
|
+
className={classes.trigger}
|
|
78
|
+
aria-label={currentWebsiteName}
|
|
79
|
+
onClick={handleTriggerClick}
|
|
80
|
+
ref={storeMenuTriggerRef}
|
|
81
|
+
data-cy="WebsiteSwitcher-trigger"
|
|
82
|
+
aria-expanded={storeMenuIsOpen}
|
|
83
|
+
>
|
|
84
|
+
{triggerLabel || ''}
|
|
85
|
+
</button>
|
|
86
|
+
<div
|
|
87
|
+
ref={storeMenuRef}
|
|
88
|
+
className={menuClassName}
|
|
89
|
+
data-cy="WebsiteSwitcher-menu"
|
|
90
|
+
>
|
|
91
|
+
<div className={classes.groups}>{groups}</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default WebsiteSwitcher;
|
|
98
|
+
|
|
99
|
+
WebsiteSwitcher.propTypes = {
|
|
100
|
+
classes: shape({
|
|
101
|
+
groupList: string,
|
|
102
|
+
groups: string,
|
|
103
|
+
menu: string,
|
|
104
|
+
menu_open: string,
|
|
105
|
+
menuItem: string,
|
|
106
|
+
root: string,
|
|
107
|
+
trigger: string
|
|
108
|
+
})
|
|
109
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: grid from global;
|
|
3
|
+
composes: items-center from global;
|
|
4
|
+
composes: justify-items-start from global;
|
|
5
|
+
composes: max-w-site from global;
|
|
6
|
+
composes: mx-auto from global;
|
|
7
|
+
composes: my-0 from global;
|
|
8
|
+
composes: px-xs from global;
|
|
9
|
+
composes: py-2xs from global;
|
|
10
|
+
composes: relative from global;
|
|
11
|
+
|
|
12
|
+
composes: sm_justify-items-end from global;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.trigger {
|
|
16
|
+
composes: max-w-[15rem] from global;
|
|
17
|
+
composes: overflow-ellipsis from global;
|
|
18
|
+
composes: overflow-hidden from global;
|
|
19
|
+
composes: whitespace-nowrap from global;
|
|
20
|
+
|
|
21
|
+
composes: sm_max-w-full from global;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.menu {
|
|
25
|
+
composes: absolute from global;
|
|
26
|
+
composes: bg-white from global;
|
|
27
|
+
composes: border from global;
|
|
28
|
+
composes: border-solid from global;
|
|
29
|
+
composes: border-subtle from global;
|
|
30
|
+
composes: bottom-md from global;
|
|
31
|
+
composes: left-xs from global;
|
|
32
|
+
composes: max-w-[90vw] from global;
|
|
33
|
+
composes: opacity-0 from global;
|
|
34
|
+
composes: pb-2xs from global;
|
|
35
|
+
composes: pt-2xs from global;
|
|
36
|
+
composes: right-auto from global;
|
|
37
|
+
composes: rounded from global;
|
|
38
|
+
composes: shadow-menu from global;
|
|
39
|
+
composes: top-auto from global;
|
|
40
|
+
composes: w-max from global;
|
|
41
|
+
composes: z-menu from global;
|
|
42
|
+
transform: translate3d(0, -8px, 0);
|
|
43
|
+
transition-duration: 192ms;
|
|
44
|
+
transition-timing-function: var(--venia-global-anim-out);
|
|
45
|
+
transition-property: opacity, transform, visibility;
|
|
46
|
+
|
|
47
|
+
/* TODO @TW: review (B6) */
|
|
48
|
+
/* composes: invisible from global; */
|
|
49
|
+
visibility: hidden;
|
|
50
|
+
|
|
51
|
+
composes: sm_bottom-auto from global;
|
|
52
|
+
composes: sm_left-auto from global;
|
|
53
|
+
composes: sm_right-xs from global;
|
|
54
|
+
composes: sm_top-md from global;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.menu_open {
|
|
58
|
+
composes: menu;
|
|
59
|
+
|
|
60
|
+
composes: opacity-100 from global;
|
|
61
|
+
transform: translate3d(0, 4px, 0);
|
|
62
|
+
transition-duration: 224ms;
|
|
63
|
+
transition-timing-function: var(--venia-global-anim-in);
|
|
64
|
+
|
|
65
|
+
/* TODO @TW: review (B6) */
|
|
66
|
+
/* composes: visible from global; */
|
|
67
|
+
visibility: visible;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.menuItem {
|
|
71
|
+
composes: hover_bg-subtle from global;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.groups {
|
|
75
|
+
composes: max-h-[24rem] from global;
|
|
76
|
+
composes: overflow-auto from global;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.groupList {
|
|
80
|
+
composes: border-b from global;
|
|
81
|
+
composes: border-solid from global;
|
|
82
|
+
composes: border-subtle from global;
|
|
83
|
+
composes: pb-2xs from global;
|
|
84
|
+
composes: pt-2xs from global;
|
|
85
|
+
|
|
86
|
+
composes: last_border-none from global;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Mobile-specific styles.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
@media (max-width: 639px) {
|
|
94
|
+
.root:only-child {
|
|
95
|
+
grid-column: 2;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* TODO @TW: cannot compose */
|
|
99
|
+
.root:last-child .menu {
|
|
100
|
+
right: 1rem;
|
|
101
|
+
left: auto;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.menu {
|
|
105
|
+
transform: translate3d(0, 8px, 0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.menu_open {
|
|
109
|
+
transform: translate3d(0, -4px, 0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { Check } from 'react-feather';
|
|
3
|
+
import { bool, func, shape, string } from 'prop-types';
|
|
4
|
+
|
|
5
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
6
|
+
import Icon from '@magento/venia-ui/lib/components/Icon/icon';
|
|
7
|
+
import defaultClasses from './websiteSwitcherItem.module.css';
|
|
8
|
+
|
|
9
|
+
const WebsiteSwitcherItem = props => {
|
|
10
|
+
const { active, onClick, option, option2, children } = props;
|
|
11
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
12
|
+
|
|
13
|
+
const handleClick = useCallback(() => {
|
|
14
|
+
onClick(option, option2);
|
|
15
|
+
}, [option, option2, onClick]);
|
|
16
|
+
|
|
17
|
+
const activeIcon = active ? (
|
|
18
|
+
<Icon data-cy="WebsiteSwitcherItem-activeIcon" size={20} src={Check} />
|
|
19
|
+
) : null;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
data-cy="WebsiteSwitcherItem-button"
|
|
24
|
+
className={classes.root}
|
|
25
|
+
disabled={active}
|
|
26
|
+
onClick={handleClick}
|
|
27
|
+
>
|
|
28
|
+
<span className={classes.content}>
|
|
29
|
+
<span className={classes.text}>{children}</span>
|
|
30
|
+
{activeIcon}
|
|
31
|
+
</span>
|
|
32
|
+
</button>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
WebsiteSwitcherItem.propTypes = {
|
|
37
|
+
active: bool,
|
|
38
|
+
classes: shape({
|
|
39
|
+
content: string,
|
|
40
|
+
root: string,
|
|
41
|
+
text: string
|
|
42
|
+
}),
|
|
43
|
+
onClick: func,
|
|
44
|
+
option: string
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default WebsiteSwitcherItem;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: flex from global;
|
|
3
|
+
composes: items-center from global;
|
|
4
|
+
composes: w-full from global;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.content {
|
|
8
|
+
composes: gap-3 from global;
|
|
9
|
+
composes: grid from global;
|
|
10
|
+
composes: grid-cols-[1fr] from global;
|
|
11
|
+
composes: grid-flow-col from global;
|
|
12
|
+
composes: items-center from global;
|
|
13
|
+
composes: px-xs from global;
|
|
14
|
+
composes: py-2xs from global;
|
|
15
|
+
composes: w-full from global;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.text {
|
|
19
|
+
composes: text-left from global;
|
|
20
|
+
}
|
package/src/intercept.js
CHANGED
|
@@ -3,30 +3,24 @@ const moduleOverridePlugin = require('./moduleOverrideWebpackPlugin');
|
|
|
3
3
|
const { DefinePlugin } = require('webpack');
|
|
4
4
|
const { getAvailableWebsitesConfigData } = require('./Utilities/graphQL');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*/
|
|
16
|
-
module.exports = async targets => {
|
|
6
|
+
module.exports = targets => {
|
|
7
|
+
// Perform the asynchronous operation outside the tap
|
|
8
|
+
let availableWebsites;
|
|
9
|
+
|
|
10
|
+
// Use an IIFE (Immediately Invoked Function Expression) to enable async/await
|
|
11
|
+
(async () => {
|
|
12
|
+
availableWebsites = await getAvailableWebsitesConfigData();
|
|
13
|
+
})();
|
|
14
|
+
|
|
17
15
|
targets.of('@magento/pwa-buildpack').specialFeatures.tap(flags => {
|
|
18
|
-
|
|
19
|
-
* Wee need to activated esModules and cssModules to allow build pack to load our extension
|
|
20
|
-
* {@link https://magento.github.io/pwa-studio/pwa-buildpack/reference/configure-webpack/#special-flags}.
|
|
21
|
-
*/
|
|
22
|
-
flags[targets.name] = { esModules: true, cssModules: true, graphqlQueries: true };
|
|
16
|
+
flags[targets.name] = { esModules: true, cssModules: true, graphqlQueries: true };
|
|
23
17
|
});
|
|
24
18
|
|
|
25
19
|
targets.of('@magento/pwa-buildpack').webpackCompiler.tap(compiler => {
|
|
26
|
-
|
|
20
|
+
new moduleOverridePlugin(componentOverrideMapping).apply(compiler);
|
|
27
21
|
});
|
|
28
22
|
|
|
29
|
-
targets.of('@magento/pwa-buildpack').envVarDefinitions.tap(
|
|
23
|
+
targets.of('@magento/pwa-buildpack').envVarDefinitions.tap(defs => {
|
|
30
24
|
defs.sections.push({
|
|
31
25
|
name: "Default Website Code",
|
|
32
26
|
variables: [
|
|
@@ -39,32 +33,31 @@ module.exports = async targets => {
|
|
|
39
33
|
});
|
|
40
34
|
});
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
// Define the routes using the results of the asynchronous operation
|
|
37
|
+
const routes = [
|
|
38
|
+
{
|
|
39
|
+
name: "BecomeSellerRoute",
|
|
40
|
+
pattern: "/become-seller",
|
|
41
|
+
path: require.resolve("./components/BecomeSellerPage/index.js"),
|
|
42
|
+
},
|
|
43
|
+
// Add other routes as needed
|
|
44
|
+
];
|
|
43
45
|
|
|
46
|
+
// Apply DefinePlugin using the results of the asynchronous operation
|
|
44
47
|
targets.of('@magento/pwa-buildpack').webpackCompiler.tap(compiler => {
|
|
45
48
|
new DefinePlugin({
|
|
46
49
|
AVAILABLE_WEBSITES: JSON.stringify(availableWebsites.availableStoresByUserIp),
|
|
47
50
|
WEBSITE_CODE: process.env.WEBSITE_CODE
|
|
48
51
|
}).apply(compiler);
|
|
49
52
|
});
|
|
50
|
-
|
|
51
|
-
// targets.of("@magento/venia-ui").routes.tap(routes => {
|
|
52
|
-
// routes.push({
|
|
53
|
-
// name: "Become Seller",
|
|
54
|
-
// pattern: "/become-seller",
|
|
55
|
-
// path: require.resolve("../components/BecomeSeller/index.js")
|
|
56
|
-
// });
|
|
57
|
-
// return routes;
|
|
58
|
-
// });
|
|
59
|
-
};
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
54
|
+
targets.of('@magento/pwa-buildpack').specialFeatures.tap(flags => {
|
|
55
|
+
flags[targets.name] = { esModules: true, cssModules: true, graphqlQueries: true };
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Handle the routes directly within the routes tap
|
|
59
|
+
targets.of("@magento/venia-ui").routes.tap(routesArray => {
|
|
60
|
+
routesArray.push(...routes);
|
|
61
|
+
return routesArray;
|
|
69
62
|
});
|
|
70
63
|
};
|
|
@@ -14,9 +14,9 @@ import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
|
|
|
14
14
|
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
15
15
|
import defaultClasses from '@magento/venia-ui/lib/components/Header/header.module.css';
|
|
16
16
|
import StoreSwitcher from '@magento/venia-ui/lib/components/Header/storeSwitcher';
|
|
17
|
-
import WebsiteSwitcher from '@riosst100/pwa-marketplace/src/components/
|
|
17
|
+
import WebsiteSwitcher from '@riosst100/pwa-marketplace/src/components/WebsiteSwitcher/websiteSwitcher';
|
|
18
18
|
import CurrencySwitcher from '@magento/venia-ui/lib/components/Header/currencySwitcher';
|
|
19
|
-
import BecomeSellerLink from '@riosst100/pwa-marketplace/src/components/
|
|
19
|
+
import BecomeSellerLink from '@riosst100/pwa-marketplace/src/components/BecomeSellerLink/becomeSellerLink';
|
|
20
20
|
import MegaMenu from '@magento/venia-ui/lib/components/MegaMenu';
|
|
21
21
|
import PageLoadingIndicator from '@magento/venia-ui/lib/components/PageLoadingIndicator';
|
|
22
22
|
import { useIntl } from 'react-intl';
|
|
@@ -84,13 +84,13 @@ const StoreSwitcher = props => {
|
|
|
84
84
|
<button
|
|
85
85
|
data-cy="StoreSwitcher-triggerButton"
|
|
86
86
|
className={classes.trigger}
|
|
87
|
-
aria-label={currentStoreName
|
|
87
|
+
aria-label={currentStoreName}
|
|
88
88
|
onClick={handleTriggerClick}
|
|
89
89
|
ref={storeMenuTriggerRef}
|
|
90
90
|
data-cy="StoreSwitcher-trigger"
|
|
91
91
|
aria-expanded={storeMenuIsOpen}
|
|
92
92
|
>
|
|
93
|
-
{triggerLabel}
|
|
93
|
+
{triggerLabel || ''}
|
|
94
94
|
</button>
|
|
95
95
|
<div
|
|
96
96
|
ref={storeMenuRef}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { gql } from '@apollo/client';
|
|
2
|
+
import { CheckoutPageFragment } from '@magento/peregrine/lib/talons/CheckoutPage/checkoutPageFragments.gql';
|
|
3
|
+
|
|
4
|
+
export const CREATE_ACCOUNT = gql`
|
|
5
|
+
mutation BecomeSeller(
|
|
6
|
+
$email: String!
|
|
7
|
+
$firstname: String!
|
|
8
|
+
$lastname: String!
|
|
9
|
+
$password: String!
|
|
10
|
+
$is_subscribed: Boolean!
|
|
11
|
+
) {
|
|
12
|
+
createCustomer(
|
|
13
|
+
input: {
|
|
14
|
+
email: $email
|
|
15
|
+
firstname: $firstname
|
|
16
|
+
lastname: $lastname
|
|
17
|
+
password: $password
|
|
18
|
+
is_subscribed: $is_subscribed
|
|
19
|
+
}
|
|
20
|
+
) {
|
|
21
|
+
# The createCustomer mutation returns a non-nullable CustomerOutput type
|
|
22
|
+
# which requires that at least one of the sub fields be returned.
|
|
23
|
+
|
|
24
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
25
|
+
customer {
|
|
26
|
+
email
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
export const GET_CUSTOMER = gql`
|
|
33
|
+
query GetCustomerAfterCreate {
|
|
34
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
35
|
+
customer {
|
|
36
|
+
email
|
|
37
|
+
firstname
|
|
38
|
+
lastname
|
|
39
|
+
is_subscribed
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
export const SIGN_IN = gql`
|
|
45
|
+
mutation SignInAfterCreate($email: String!, $password: String!) {
|
|
46
|
+
generateCustomerToken(email: $email, password: $password) {
|
|
47
|
+
token
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
export const CREATE_CART = gql`
|
|
53
|
+
mutation CreateCartAfterAccountCreation {
|
|
54
|
+
cartId: createEmptyCart
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export const GET_CART_DETAILS = gql`
|
|
59
|
+
query GetCartDetailsAfterAccountCreation($cartId: String!) {
|
|
60
|
+
cart(cart_id: $cartId) {
|
|
61
|
+
id
|
|
62
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
63
|
+
items {
|
|
64
|
+
uid
|
|
65
|
+
prices {
|
|
66
|
+
price {
|
|
67
|
+
value
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
71
|
+
product {
|
|
72
|
+
uid
|
|
73
|
+
name
|
|
74
|
+
sku
|
|
75
|
+
small_image {
|
|
76
|
+
url
|
|
77
|
+
label
|
|
78
|
+
}
|
|
79
|
+
price {
|
|
80
|
+
regularPrice {
|
|
81
|
+
amount {
|
|
82
|
+
value
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
quantity
|
|
88
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
89
|
+
... on ConfigurableCartItem {
|
|
90
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
91
|
+
configurable_options {
|
|
92
|
+
configurable_product_option_uid
|
|
93
|
+
option_label
|
|
94
|
+
configurable_product_option_value_uid
|
|
95
|
+
value_label
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
prices {
|
|
100
|
+
grand_total {
|
|
101
|
+
value
|
|
102
|
+
currency
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
export const MERGE_CARTS = gql`
|
|
110
|
+
mutation MergeCartsAfterAccountCreation(
|
|
111
|
+
$sourceCartId: String!
|
|
112
|
+
$destinationCartId: String!
|
|
113
|
+
) {
|
|
114
|
+
mergeCarts(
|
|
115
|
+
source_cart_id: $sourceCartId
|
|
116
|
+
destination_cart_id: $destinationCartId
|
|
117
|
+
) {
|
|
118
|
+
id
|
|
119
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
120
|
+
items {
|
|
121
|
+
uid
|
|
122
|
+
}
|
|
123
|
+
...CheckoutPageFragment
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
${CheckoutPageFragment}
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
export default {
|
|
130
|
+
becomeSellerMutation: CREATE_ACCOUNT,
|
|
131
|
+
createCartMutation: CREATE_CART,
|
|
132
|
+
getCartDetailsQuery: GET_CART_DETAILS,
|
|
133
|
+
getCustomerQuery: GET_CUSTOMER,
|
|
134
|
+
mergeCartsMutation: MERGE_CARTS,
|
|
135
|
+
signInMutation: SIGN_IN
|
|
136
|
+
};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { useApolloClient, useMutation } from '@apollo/client';
|
|
3
|
+
|
|
4
|
+
import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
|
|
5
|
+
import { useUserContext } from '@magento/peregrine/lib/context/user';
|
|
6
|
+
import { useCartContext } from '@magento/peregrine/lib/context/cart';
|
|
7
|
+
import { useAwaitQuery } from '@magento/peregrine/lib/hooks/useAwaitQuery';
|
|
8
|
+
import { retrieveCartId } from '@magento/peregrine/lib/store/actions/cart';
|
|
9
|
+
import { useGoogleReCaptcha } from '@magento/peregrine/lib/hooks/useGoogleReCaptcha';
|
|
10
|
+
|
|
11
|
+
import DEFAULT_OPERATIONS from './becomeSeller.gql';
|
|
12
|
+
import { useEventingContext } from '@magento/peregrine/lib/context/eventing';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns props necessary to render BecomeSeller component. In particular this
|
|
16
|
+
* talon handles the submission flow by first doing a pre-submisson validation
|
|
17
|
+
* and then, on success, invokes the `onSubmit` prop, which is usually the action.
|
|
18
|
+
*
|
|
19
|
+
* @param {BecomeSellerQueries} props.queries queries used by the talon
|
|
20
|
+
* @param {BecomeSellerMutations} props.mutations mutations used by the talon
|
|
21
|
+
* @param {InitialValues} props.initialValues initial values to sanitize and seed the form
|
|
22
|
+
* @param {Function} props.onSubmit the post submit callback
|
|
23
|
+
* @param {Function} props.onCancel the cancel callback
|
|
24
|
+
*
|
|
25
|
+
* @returns {BecomeSellerProps}
|
|
26
|
+
*
|
|
27
|
+
* @example <caption>Importing into your project</caption>
|
|
28
|
+
* import { useForgotPassword } from '@magento/peregrine/lib/talons/BecomeSeller/useBecomeSeller.js';
|
|
29
|
+
*/
|
|
30
|
+
export const useBecomeSeller = props => {
|
|
31
|
+
const { initialValues = {}, onSubmit, onCancel } = props;
|
|
32
|
+
|
|
33
|
+
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
34
|
+
const {
|
|
35
|
+
becomeSellerMutation,
|
|
36
|
+
createCartMutation,
|
|
37
|
+
getCartDetailsQuery,
|
|
38
|
+
getCustomerQuery,
|
|
39
|
+
mergeCartsMutation,
|
|
40
|
+
signInMutation
|
|
41
|
+
} = operations;
|
|
42
|
+
const apolloClient = useApolloClient();
|
|
43
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
44
|
+
const [
|
|
45
|
+
{ cartId },
|
|
46
|
+
{ createCart, removeCart, getCartDetails }
|
|
47
|
+
] = useCartContext();
|
|
48
|
+
const [
|
|
49
|
+
{ isGettingDetails },
|
|
50
|
+
{ getUserDetails, setToken }
|
|
51
|
+
] = useUserContext();
|
|
52
|
+
|
|
53
|
+
const [, { dispatch }] = useEventingContext();
|
|
54
|
+
|
|
55
|
+
const [fetchCartId] = useMutation(createCartMutation);
|
|
56
|
+
|
|
57
|
+
const [mergeCarts] = useMutation(mergeCartsMutation);
|
|
58
|
+
|
|
59
|
+
// For create account and sign in mutations, we don't want to cache any
|
|
60
|
+
// personally identifiable information (PII). So we set fetchPolicy to 'no-cache'.
|
|
61
|
+
const [becomeSeller, { error: becomeSellerError }] = useMutation(
|
|
62
|
+
becomeSellerMutation,
|
|
63
|
+
{
|
|
64
|
+
fetchPolicy: 'no-cache'
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const [signIn, { error: signInError }] = useMutation(signInMutation, {
|
|
69
|
+
fetchPolicy: 'no-cache'
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const fetchUserDetails = useAwaitQuery(getCustomerQuery);
|
|
73
|
+
const fetchCartDetails = useAwaitQuery(getCartDetailsQuery);
|
|
74
|
+
|
|
75
|
+
const {
|
|
76
|
+
generateReCaptchaData,
|
|
77
|
+
recaptchaLoading,
|
|
78
|
+
recaptchaWidgetProps
|
|
79
|
+
} = useGoogleReCaptcha({
|
|
80
|
+
currentForm: 'CUSTOMER_CREATE',
|
|
81
|
+
formAction: 'becomeSeller'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const handleCancel = useCallback(() => {
|
|
85
|
+
onCancel();
|
|
86
|
+
}, [onCancel]);
|
|
87
|
+
|
|
88
|
+
const handleCancelKeyPress = useCallback(() => {
|
|
89
|
+
event => {
|
|
90
|
+
if (event.key === 'Enter') {
|
|
91
|
+
handleCancel();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}, [handleCancel]);
|
|
95
|
+
|
|
96
|
+
const handleSubmit = useCallback(
|
|
97
|
+
async formValues => {
|
|
98
|
+
setIsSubmitting(true);
|
|
99
|
+
try {
|
|
100
|
+
// Get source cart id (guest cart id).
|
|
101
|
+
const sourceCartId = cartId;
|
|
102
|
+
|
|
103
|
+
// Get reCaptchaV3 Data for becomeSeller mutation
|
|
104
|
+
const recaptchaDataForBecomeSeller = await generateReCaptchaData();
|
|
105
|
+
|
|
106
|
+
// Create the account and then sign in.
|
|
107
|
+
await becomeSeller({
|
|
108
|
+
variables: {
|
|
109
|
+
email: formValues.customer.email,
|
|
110
|
+
firstname: formValues.customer.firstname,
|
|
111
|
+
lastname: formValues.customer.lastname,
|
|
112
|
+
password: formValues.password,
|
|
113
|
+
is_subscribed: !!formValues.subscribe
|
|
114
|
+
},
|
|
115
|
+
...recaptchaDataForBecomeSeller
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
dispatch({
|
|
119
|
+
type: 'USER_CREATE_ACCOUNT',
|
|
120
|
+
payload: {
|
|
121
|
+
email: formValues.customer.email,
|
|
122
|
+
firstName: formValues.customer.firstname,
|
|
123
|
+
lastName: formValues.customer.lastname,
|
|
124
|
+
isSubscribed: !!formValues.subscribe
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Get reCaptchaV3 Data for signIn mutation
|
|
129
|
+
const recaptchaDataForSignIn = await generateReCaptchaData();
|
|
130
|
+
|
|
131
|
+
const signInResponse = await signIn({
|
|
132
|
+
variables: {
|
|
133
|
+
email: formValues.customer.email,
|
|
134
|
+
password: formValues.password
|
|
135
|
+
},
|
|
136
|
+
...recaptchaDataForSignIn
|
|
137
|
+
});
|
|
138
|
+
const token = signInResponse.data.generateCustomerToken.token;
|
|
139
|
+
await setToken(token);
|
|
140
|
+
|
|
141
|
+
// Clear all cart/customer data from cache and redux.
|
|
142
|
+
await apolloClient.clearCacheData(apolloClient, 'cart');
|
|
143
|
+
await apolloClient.clearCacheData(apolloClient, 'customer');
|
|
144
|
+
await removeCart();
|
|
145
|
+
|
|
146
|
+
// Create and get the customer's cart id.
|
|
147
|
+
await createCart({
|
|
148
|
+
fetchCartId
|
|
149
|
+
});
|
|
150
|
+
const destinationCartId = await retrieveCartId();
|
|
151
|
+
|
|
152
|
+
// Merge the guest cart into the customer cart.
|
|
153
|
+
await mergeCarts({
|
|
154
|
+
variables: {
|
|
155
|
+
destinationCartId,
|
|
156
|
+
sourceCartId
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Ensure old stores are updated with any new data.
|
|
161
|
+
await getUserDetails({ fetchUserDetails });
|
|
162
|
+
await getCartDetails({
|
|
163
|
+
fetchCartId,
|
|
164
|
+
fetchCartDetails
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Finally, invoke the post-submission callback.
|
|
168
|
+
if (onSubmit) {
|
|
169
|
+
onSubmit();
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
173
|
+
console.error(error);
|
|
174
|
+
}
|
|
175
|
+
setIsSubmitting(false);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
[
|
|
180
|
+
cartId,
|
|
181
|
+
generateReCaptchaData,
|
|
182
|
+
becomeSeller,
|
|
183
|
+
signIn,
|
|
184
|
+
setToken,
|
|
185
|
+
apolloClient,
|
|
186
|
+
removeCart,
|
|
187
|
+
createCart,
|
|
188
|
+
fetchCartId,
|
|
189
|
+
mergeCarts,
|
|
190
|
+
getUserDetails,
|
|
191
|
+
fetchUserDetails,
|
|
192
|
+
getCartDetails,
|
|
193
|
+
fetchCartDetails,
|
|
194
|
+
onSubmit,
|
|
195
|
+
dispatch
|
|
196
|
+
]
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const sanitizedInitialValues = useMemo(() => {
|
|
200
|
+
const { email, firstName, lastName, ...rest } = initialValues;
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
customer: { email, firstname: firstName, lastname: lastName },
|
|
204
|
+
...rest
|
|
205
|
+
};
|
|
206
|
+
}, [initialValues]);
|
|
207
|
+
|
|
208
|
+
const errors = useMemo(
|
|
209
|
+
() =>
|
|
210
|
+
new Map([
|
|
211
|
+
['becomeSellerQuery', becomeSellerError],
|
|
212
|
+
['signInMutation', signInError]
|
|
213
|
+
]),
|
|
214
|
+
[becomeSellerError, signInError]
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
errors,
|
|
219
|
+
handleCancel,
|
|
220
|
+
handleSubmit,
|
|
221
|
+
handleCancelKeyPress,
|
|
222
|
+
initialValues: sanitizedInitialValues,
|
|
223
|
+
isDisabled: isSubmitting || isGettingDetails || recaptchaLoading,
|
|
224
|
+
recaptchaWidgetProps
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/** JSDocs type definitions */
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* GraphQL queries for the create account form.
|
|
232
|
+
* This is a type used by the {@link useBecomeSeller} talon.
|
|
233
|
+
*
|
|
234
|
+
* @typedef {Object} BecomeSellerQueries
|
|
235
|
+
*
|
|
236
|
+
* @property {GraphQLAST} customerQuery query to fetch customer details
|
|
237
|
+
* @property {GraphQLAST} getCartDetailsQuery query to get cart details
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* GraphQL mutations for the create account form.
|
|
242
|
+
* This is a type used by the {@link useBecomeSeller} talon.
|
|
243
|
+
*
|
|
244
|
+
* @typedef {Object} BecomeSellerMutations
|
|
245
|
+
*
|
|
246
|
+
* @property {GraphQLAST} becomeSellerMutation mutation for creating new account
|
|
247
|
+
* @property {GraphQLAST} createCartMutation mutation for creating new cart
|
|
248
|
+
* @property {GraphQLAST} mergeCartsMutation mutation for merging carts
|
|
249
|
+
* @property {GraphQLAST} signInMutation mutation for signing
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Initial values for the create account form.
|
|
254
|
+
* This is a type used by the {@link useBecomeSeller} talon.
|
|
255
|
+
*
|
|
256
|
+
* @typedef {Object} InitialValues
|
|
257
|
+
*
|
|
258
|
+
* @property {String} email email id of the user
|
|
259
|
+
* @property {String} firstName first name of the user
|
|
260
|
+
* @property {String} lastName last name of the user
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Sanitized initial values for the create account form.
|
|
265
|
+
* This is a type used by the {@link useBecomeSeller} talon.
|
|
266
|
+
*
|
|
267
|
+
* @typedef {Object} SanitizedInitialValues
|
|
268
|
+
*
|
|
269
|
+
* @property {String} email email id of the user
|
|
270
|
+
* @property {String} firstname first name of the user
|
|
271
|
+
* @property {String} lastname last name of the user
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Object type returned by the {@link useBecomeSeller} talon.
|
|
276
|
+
* It provides props data to use when rendering the create account form component.
|
|
277
|
+
*
|
|
278
|
+
* @typedef {Object} BecomeSellerProps
|
|
279
|
+
*
|
|
280
|
+
* @property {Map} errors a map of errors to their respective mutations
|
|
281
|
+
* @property {Function} handleCancel callback function to handle form cancellations
|
|
282
|
+
* @property {Function} handleSubmit callback function to handle form submission
|
|
283
|
+
* @property {SanitizedInitialValues} initialValues initial values for the create account form
|
|
284
|
+
* @property {Boolean} isDisabled true if either details are being fetched or form is being submitted. False otherwise.
|
|
285
|
+
* @property {Object} recaptchaWidgetProps - Props for the GoogleReCaptcha component.
|
|
286
|
+
* @property {Function} recaptchaWidgetProps.containerElement - Container reference callback.
|
|
287
|
+
* @property {Boolean} recaptchaWidgetProps.shouldRender - Checks if component should be rendered.
|
|
288
|
+
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useWebsiteSwitcher = (props = {}) => {
|
|
4
|
+
const handleTriggerClick = useCallback(() => {
|
|
5
|
+
// Toggle Stores Menu.
|
|
6
|
+
// setStoreMenuIsOpen(isOpen => !isOpen);
|
|
7
|
+
// globalThis.location.assign(`${newPath || ''}`);
|
|
8
|
+
}, []);
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
handleTriggerClick
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useHistory, useLocation } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { useUserContext } from '@magento/peregrine/lib/context/user';
|
|
5
|
+
|
|
6
|
+
const validBecomeSellerParams = ['email', 'firstName', 'lastName'];
|
|
7
|
+
|
|
8
|
+
const getBecomeSellerInitialValues = search => {
|
|
9
|
+
const params = new URLSearchParams(search);
|
|
10
|
+
|
|
11
|
+
return validBecomeSellerParams.reduce(
|
|
12
|
+
(values, param) => ({ ...values, [param]: params.get(param) }),
|
|
13
|
+
{}
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {function} useBecomeSellerPage
|
|
19
|
+
*
|
|
20
|
+
* @param {String} props.signedInRedirectUrl - Url to push when user is signed in
|
|
21
|
+
* @param {String} props.signInPageUrl - Sign In Page url
|
|
22
|
+
*
|
|
23
|
+
* @returns {{
|
|
24
|
+
* becomeSellerProps: object
|
|
25
|
+
* }}
|
|
26
|
+
*/
|
|
27
|
+
export const useBecomeSellerPage = props => {
|
|
28
|
+
const { signedInRedirectUrl, signInPageUrl } = props;
|
|
29
|
+
const history = useHistory();
|
|
30
|
+
const [{ isSignedIn }] = useUserContext();
|
|
31
|
+
const { search } = useLocation();
|
|
32
|
+
|
|
33
|
+
// Keep location state in memory when pushing history and redirect to
|
|
34
|
+
// the `from` url instead when creating an account
|
|
35
|
+
const historyState = useMemo(() => {
|
|
36
|
+
return history && history.location.state ? history.location.state : {};
|
|
37
|
+
}, [history]);
|
|
38
|
+
const fromRedirectUrl = historyState.from || null;
|
|
39
|
+
|
|
40
|
+
// Redirect if user is signed in
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isSignedIn) {
|
|
43
|
+
if (fromRedirectUrl || signedInRedirectUrl) {
|
|
44
|
+
history.push(fromRedirectUrl || signedInRedirectUrl);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}, [fromRedirectUrl, history, isSignedIn, signedInRedirectUrl]);
|
|
48
|
+
|
|
49
|
+
const handleOnCancel = useCallback(() => {
|
|
50
|
+
if (signInPageUrl) {
|
|
51
|
+
history.push(signInPageUrl, historyState);
|
|
52
|
+
}
|
|
53
|
+
}, [history, historyState, signInPageUrl]);
|
|
54
|
+
|
|
55
|
+
const initialValues = useMemo(() => getBecomeSellerInitialValues(search), [
|
|
56
|
+
search
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
const becomeSellerProps = {
|
|
60
|
+
initialValues,
|
|
61
|
+
isCancelButtonHidden: false,
|
|
62
|
+
onCancel: handleOnCancel
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
becomeSellerProps
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { useLocation } from 'react-router-dom';
|
|
4
|
+
import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
|
|
5
|
+
import { useAwaitQuery } from '@magento/peregrine/lib/hooks/useAwaitQuery';
|
|
6
|
+
import { BrowserPersistence } from '@magento/peregrine/lib/util';
|
|
7
|
+
import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
|
|
8
|
+
import DEFAULT_OPERATIONS from './websiteSwitcher.gql';
|
|
9
|
+
|
|
10
|
+
const storage = new BrowserPersistence();
|
|
11
|
+
|
|
12
|
+
const mapAvailableOptions = (config, stores) => {
|
|
13
|
+
const { store_code: configCode } = config;
|
|
14
|
+
|
|
15
|
+
return stores.reduce((map, store) => {
|
|
16
|
+
const {
|
|
17
|
+
default_display_currency_code: currency,
|
|
18
|
+
locale,
|
|
19
|
+
secure_base_media_url,
|
|
20
|
+
store_code: storeCode,
|
|
21
|
+
store_group_code: storeGroupCode,
|
|
22
|
+
store_group_name: storeGroupName,
|
|
23
|
+
website_name: websiteName,
|
|
24
|
+
website_code: websiteCode,
|
|
25
|
+
store_sort_order: sortOrder
|
|
26
|
+
} = store;
|
|
27
|
+
|
|
28
|
+
const isCurrent = storeCode === configCode;
|
|
29
|
+
const option = {
|
|
30
|
+
currency,
|
|
31
|
+
isCurrent,
|
|
32
|
+
locale,
|
|
33
|
+
secure_base_media_url,
|
|
34
|
+
sortOrder,
|
|
35
|
+
storeCode,
|
|
36
|
+
storeGroupCode,
|
|
37
|
+
storeGroupName,
|
|
38
|
+
websiteName,
|
|
39
|
+
websiteCode
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return map.set(storeCode, option);
|
|
43
|
+
}, new Map());
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The useWebsiteSwitcher talon complements the WebsiteSwitcher component.
|
|
48
|
+
*
|
|
49
|
+
* @param {Array<Object>} [props.availableRoutes] - Hardcoded app routes.
|
|
50
|
+
* @param {Object} [props.operations] - GraphQL operations to be run by the hook.
|
|
51
|
+
*
|
|
52
|
+
* @returns {Map} talonProps.availableStores - Details about the available store views.
|
|
53
|
+
* @returns {String} talonProps.currentWebsiteName - Name of the current store view.
|
|
54
|
+
* @returns {Boolean} talonProps.storeMenuIsOpen - Whether the menu that this trigger toggles is open or not.
|
|
55
|
+
* @returns {Ref} talonProps.storeMenuRef - A React ref to the menu that this trigger toggles.
|
|
56
|
+
* @returns {Ref} talonProps.storeMenuTriggerRef - A React ref to the trigger element itself.
|
|
57
|
+
* @returns {Function} talonProps.handleTriggerClick - A function for handling when the trigger is clicked.
|
|
58
|
+
* @returns {Function} talonProps.handleSwitchWebsite - A function for handling when the menu item is clicked.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export const useWebsiteSwitcher = (props = {}) => {
|
|
62
|
+
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
63
|
+
const { availableRoutes = [] } = props;
|
|
64
|
+
const internalRoutes = useMemo(() => {
|
|
65
|
+
return availableRoutes.map(path => {
|
|
66
|
+
if (path.exact) {
|
|
67
|
+
return path.pattern;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}, [availableRoutes]);
|
|
71
|
+
|
|
72
|
+
const {
|
|
73
|
+
getStoreConfigData,
|
|
74
|
+
getRouteData,
|
|
75
|
+
getAvailableWebsitesData
|
|
76
|
+
} = operations;
|
|
77
|
+
const { pathname, search: searchParams } = useLocation();
|
|
78
|
+
const {
|
|
79
|
+
elementRef: storeMenuRef,
|
|
80
|
+
expanded: storeMenuIsOpen,
|
|
81
|
+
setExpanded: setStoreMenuIsOpen,
|
|
82
|
+
triggerRef: storeMenuTriggerRef
|
|
83
|
+
} = useDropdown();
|
|
84
|
+
|
|
85
|
+
const { data: storeConfigData } = useQuery(getStoreConfigData, {
|
|
86
|
+
fetchPolicy: 'cache-and-network',
|
|
87
|
+
nextFetchPolicy: 'cache-first'
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const fetchRouteData = useAwaitQuery(getRouteData);
|
|
91
|
+
|
|
92
|
+
const { data: availableStoresData } = useQuery(getAvailableWebsitesData, {
|
|
93
|
+
fetchPolicy: 'cache-and-network',
|
|
94
|
+
nextFetchPolicy: 'cache-first'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const currentWebsiteName = useMemo(() => {
|
|
98
|
+
if (storeConfigData) {
|
|
99
|
+
return storeConfigData.storeConfig.website_name;
|
|
100
|
+
}
|
|
101
|
+
}, [storeConfigData]);
|
|
102
|
+
|
|
103
|
+
const currentGroupName = useMemo(() => {
|
|
104
|
+
if (storeConfigData) {
|
|
105
|
+
return storeConfigData.storeConfig.store_group_name;
|
|
106
|
+
}
|
|
107
|
+
}, [storeConfigData]);
|
|
108
|
+
|
|
109
|
+
// availableStores => mapped options or empty map if undefined.
|
|
110
|
+
const availableStores = useMemo(() => {
|
|
111
|
+
return (
|
|
112
|
+
(storeConfigData &&
|
|
113
|
+
availableStoresData &&
|
|
114
|
+
mapAvailableOptions(
|
|
115
|
+
storeConfigData.storeConfig,
|
|
116
|
+
availableStoresData.availableStoresByUserIp
|
|
117
|
+
)) ||
|
|
118
|
+
new Map()
|
|
119
|
+
);
|
|
120
|
+
}, [storeConfigData, availableStoresData]);
|
|
121
|
+
|
|
122
|
+
// Create a map of sorted store views for each group.
|
|
123
|
+
const storeGroups = useMemo(() => {
|
|
124
|
+
const groups = new Map();
|
|
125
|
+
|
|
126
|
+
availableStores.forEach(store => {
|
|
127
|
+
const groupCode = store.storeGroupCode;
|
|
128
|
+
if (!groups.has(groupCode)) {
|
|
129
|
+
const groupViews = [store];
|
|
130
|
+
groups.set(groupCode, groupViews);
|
|
131
|
+
} else {
|
|
132
|
+
const groupViews = groups.get(groupCode);
|
|
133
|
+
// Insert store at configured position
|
|
134
|
+
groupViews.splice(store.sortOrder, 0, store);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return groups;
|
|
139
|
+
}, [availableStores]);
|
|
140
|
+
|
|
141
|
+
const getPathname = useCallback(
|
|
142
|
+
async storeCode => {
|
|
143
|
+
if (pathname === '' || pathname === '/') return '';
|
|
144
|
+
let newPath = '';
|
|
145
|
+
if (internalRoutes.includes(pathname)) {
|
|
146
|
+
newPath = pathname;
|
|
147
|
+
} else {
|
|
148
|
+
const { data: routeData } = await fetchRouteData({
|
|
149
|
+
fetchPolicy: 'no-cache',
|
|
150
|
+
variables: {
|
|
151
|
+
url: pathname
|
|
152
|
+
},
|
|
153
|
+
context: { headers: { store: storeCode } }
|
|
154
|
+
});
|
|
155
|
+
if (routeData.route) {
|
|
156
|
+
newPath = routeData.route.relative_url;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return newPath.startsWith('/') ? newPath.substr(1) : newPath;
|
|
160
|
+
},
|
|
161
|
+
[pathname, fetchRouteData, internalRoutes]
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const handleSwitchWebsite = useCallback(
|
|
165
|
+
// Change store view code and currency to be used in Apollo link request headers
|
|
166
|
+
async (storeCode, websiteCode) => {
|
|
167
|
+
// Do nothing when store view is not present in available stores
|
|
168
|
+
if (!availableStores.has(storeCode)) return;
|
|
169
|
+
|
|
170
|
+
storage.setItem('store_view_code', storeCode);
|
|
171
|
+
storage.setItem(
|
|
172
|
+
'store_view_currency',
|
|
173
|
+
availableStores.get(storeCode).currency
|
|
174
|
+
);
|
|
175
|
+
storage.setItem(
|
|
176
|
+
'store_view_secure_base_media_url',
|
|
177
|
+
availableStores.get(storeCode).secure_base_media_url
|
|
178
|
+
);
|
|
179
|
+
const pathName = await getPathname(storeCode);
|
|
180
|
+
|
|
181
|
+
let accessBaseWebsite = null;
|
|
182
|
+
let newSearchParams = searchParams;
|
|
183
|
+
let newWebsiteCode = websiteCode;
|
|
184
|
+
|
|
185
|
+
if (newWebsiteCode == process.env.WEBSITE_CODE) {
|
|
186
|
+
newWebsiteCode = null;
|
|
187
|
+
newSearchParams = searchParams ? searchParams+'&access_base_website=1' : '?access_base_website=1';
|
|
188
|
+
accessBaseWebsite = '?access_base_website=1';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const newPath = pathName ? `/${pathName}${newSearchParams}` : (accessBaseWebsite ? `/${pathName}${accessBaseWebsite}` : '');
|
|
192
|
+
|
|
193
|
+
if (newWebsiteCode) {
|
|
194
|
+
globalThis.location.assign(`/${newWebsiteCode}${newPath || ''}`);
|
|
195
|
+
} else {
|
|
196
|
+
globalThis.location.assign(`${newPath || ''}`);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
[availableStores, getPathname, searchParams]
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const handleTriggerClick = useCallback(() => {
|
|
203
|
+
// Toggle Stores Menu.
|
|
204
|
+
setStoreMenuIsOpen(isOpen => !isOpen);
|
|
205
|
+
}, [setStoreMenuIsOpen]);
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
availableStores,
|
|
209
|
+
currentGroupName,
|
|
210
|
+
currentWebsiteName,
|
|
211
|
+
storeGroups,
|
|
212
|
+
storeMenuRef,
|
|
213
|
+
storeMenuTriggerRef,
|
|
214
|
+
storeMenuIsOpen,
|
|
215
|
+
handleTriggerClick,
|
|
216
|
+
handleSwitchWebsite
|
|
217
|
+
};
|
|
218
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { gql } from '@apollo/client';
|
|
2
|
+
|
|
3
|
+
export const GET_STORE_CONFIG_DATA = gql`
|
|
4
|
+
query getStoreConfigData {
|
|
5
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
6
|
+
storeConfig {
|
|
7
|
+
store_code
|
|
8
|
+
store_name
|
|
9
|
+
store_group_name
|
|
10
|
+
website_name
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const GET_ROUTE_DATA = gql`
|
|
16
|
+
query getRouteData($url: String!) {
|
|
17
|
+
route(url: $url) {
|
|
18
|
+
relative_url
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export const GET_AVAILABLE_WEBSITES_DATA = gql`
|
|
24
|
+
query availableStoresByUserIp {
|
|
25
|
+
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
|
26
|
+
availableStoresByUserIp {
|
|
27
|
+
default_display_currency_code
|
|
28
|
+
locale
|
|
29
|
+
secure_base_media_url
|
|
30
|
+
store_code
|
|
31
|
+
store_group_code
|
|
32
|
+
store_group_name
|
|
33
|
+
store_name
|
|
34
|
+
store_sort_order
|
|
35
|
+
website_name
|
|
36
|
+
website_code
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
getStoreConfigData: GET_STORE_CONFIG_DATA,
|
|
43
|
+
getRouteData: GET_ROUTE_DATA,
|
|
44
|
+
getAvailableWebsitesData: GET_AVAILABLE_WEBSITES_DATA
|
|
45
|
+
};
|