@rebuy/rebuy-hydrogen 1.0.3 → 2.1.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.
- package/README.md +11 -394
- package/RebuyCompleteTheLook.client.jsx +188 -0
- package/RebuyContextProvider.client.jsx +184 -92
- package/RebuyContexts.client.jsx +3 -0
- package/RebuyProductAddOnCard.client.jsx +87 -0
- package/RebuyProductAddOns.client.jsx +228 -0
- package/RebuyProductRecommendations.client.jsx +63 -0
- package/RebuyProductViewed.client.jsx +48 -28
- package/RebuyRecentlyViewedProducts.client.jsx +65 -0
- package/RebuyWidgetContainer.client.jsx +116 -77
- package/package.json +26 -11
@@ -0,0 +1,63 @@
|
|
1
|
+
import { AddToCartButton, ProductOptionsProvider } from '@shopify/hydrogen';
|
2
|
+
import { Section } from '~/components';
|
3
|
+
import { ProductCard } from '~/components/cards';
|
4
|
+
import { Button } from '~/components/elements';
|
5
|
+
|
6
|
+
const AddToCartMarkup = ({ product }) => {
|
7
|
+
const selectedVariant = product.variants.nodes[0];
|
8
|
+
const isOutOfStock = !selectedVariant.availableForSale;
|
9
|
+
|
10
|
+
return (
|
11
|
+
<div className="space-y-2 mb-8">
|
12
|
+
<AddToCartButton
|
13
|
+
disabled={isOutOfStock}
|
14
|
+
product={product}
|
15
|
+
variantId={selectedVariant?.id}
|
16
|
+
quantity={1}
|
17
|
+
accessibleAddingToCartLabel="Adding item to your cart"
|
18
|
+
type="button"
|
19
|
+
attributes={[
|
20
|
+
{ key: '_source', value: 'Rebuy' },
|
21
|
+
{ key: '_attribution', value: 'Product Recommendations' },
|
22
|
+
]}
|
23
|
+
>
|
24
|
+
<Button
|
25
|
+
width="full"
|
26
|
+
variant={isOutOfStock ? 'secondary' : 'primary'}
|
27
|
+
as="span"
|
28
|
+
>
|
29
|
+
{isOutOfStock ? 'Out of stock' : 'Add to bag'}
|
30
|
+
</Button>
|
31
|
+
</AddToCartButton>
|
32
|
+
</div>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
|
36
|
+
export const RebuyProductRecommendations = ({ product, products }) => {
|
37
|
+
const title = `Like ${product.title}? You may also like:`;
|
38
|
+
|
39
|
+
return (
|
40
|
+
products.length > 0 && (
|
41
|
+
<Section heading={title} padding="y">
|
42
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 px-6 md:px-8 lg:px-12">
|
43
|
+
{products.map((product) => (
|
44
|
+
<li key={product.id}>
|
45
|
+
<ProductOptionsProvider
|
46
|
+
data={product}
|
47
|
+
initialVariantId={product.variants.nodes[0].id}
|
48
|
+
>
|
49
|
+
<ProductCard
|
50
|
+
product={product}
|
51
|
+
className={'w-100'}
|
52
|
+
/>
|
53
|
+
<AddToCartMarkup product={product} />
|
54
|
+
</ProductOptionsProvider>
|
55
|
+
</li>
|
56
|
+
))}
|
57
|
+
</ul>
|
58
|
+
</Section>
|
59
|
+
)
|
60
|
+
);
|
61
|
+
};
|
62
|
+
|
63
|
+
export default RebuyProductRecommendations;
|
@@ -1,42 +1,62 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
import { RebuyClient } from '@rebuy/rebuy';
|
3
2
|
import * as Utilities from '@rebuy/rebuy/utilities';
|
4
|
-
import
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
5
4
|
|
6
5
|
const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
|
7
6
|
|
8
|
-
export
|
9
|
-
const { product, productId, productHandle
|
7
|
+
export const RebuyProductViewed = ({ children, ...props }) => {
|
8
|
+
const { product, productId, productHandle } = props;
|
10
9
|
|
10
|
+
// eslint-disable-next-line no-unused-vars
|
11
11
|
const [event, setEvent] = useState(null);
|
12
|
+
const [Rebuy, setRebuy] = useState(null);
|
13
|
+
const [initialized, setInitialized] = useState(false);
|
14
|
+
const shopifyProductId = product?.id ?? productId ?? null;
|
15
|
+
|
16
|
+
const request = useMemo(() => {
|
17
|
+
const request = {
|
18
|
+
parameters: {},
|
19
|
+
};
|
20
|
+
|
21
|
+
if (shopifyProductId) {
|
22
|
+
request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(
|
23
|
+
shopifyProductId,
|
24
|
+
'Product'
|
25
|
+
);
|
26
|
+
} else if (productHandle) {
|
27
|
+
request.parameters.shopify_product_handle = productHandle;
|
28
|
+
}
|
29
|
+
|
30
|
+
return request;
|
31
|
+
}, [productHandle, shopifyProductId]);
|
12
32
|
|
13
|
-
|
33
|
+
useEffect(() => {
|
34
|
+
const recordView = async () => {
|
35
|
+
const response = await Rebuy.trackProductViewed(request.parameters);
|
14
36
|
|
15
|
-
|
16
|
-
|
17
|
-
};
|
37
|
+
setEvent(response.data);
|
38
|
+
};
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
} else if (productId) {
|
22
|
-
request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(productId, 'Product');
|
23
|
-
} else if (productHandle) {
|
24
|
-
request.parameters.shopify_product_handle = productHandle;
|
25
|
-
}
|
40
|
+
if (!initialized) {
|
41
|
+
const client = new RebuyClient(REBUY_API_KEY);
|
26
42
|
|
27
|
-
|
28
|
-
|
29
|
-
return;
|
30
|
-
}
|
43
|
+
setRebuy(client);
|
44
|
+
setInitialized(true);
|
31
45
|
|
32
|
-
|
46
|
+
// Abort
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
// Initialized
|
33
51
|
recordView();
|
34
|
-
}, []);
|
52
|
+
}, [Rebuy, request, initialized]);
|
53
|
+
|
54
|
+
if (Object.keys(request.parameters).length === 0) {
|
55
|
+
console.warn('No parameters!');
|
56
|
+
return;
|
57
|
+
}
|
35
58
|
|
36
|
-
|
37
|
-
|
38
|
-
setEvent(response.data);
|
39
|
-
};
|
59
|
+
return children;
|
60
|
+
};
|
40
61
|
|
41
|
-
|
42
|
-
}
|
62
|
+
export default RebuyProductViewed;
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { AddToCartButton, ProductOptionsProvider } from '@shopify/hydrogen';
|
2
|
+
import { Section } from '~/components';
|
3
|
+
import { ProductCard } from '~/components/cards';
|
4
|
+
import { Button } from '~/components/elements';
|
5
|
+
|
6
|
+
const AddToCartMarkup = ({ product }) => {
|
7
|
+
const selectedVariant = product.variants.nodes[0];
|
8
|
+
const isOutOfStock = !selectedVariant.availableForSale;
|
9
|
+
|
10
|
+
return (
|
11
|
+
<div className="space-y-2 mb-8">
|
12
|
+
<AddToCartButton
|
13
|
+
disabled={isOutOfStock}
|
14
|
+
product={product}
|
15
|
+
variantId={selectedVariant?.id}
|
16
|
+
quantity={1}
|
17
|
+
accessibleAddingToCartLabel="Adding item to your cart"
|
18
|
+
type="button"
|
19
|
+
attributes={[
|
20
|
+
{ key: '_source', value: 'Rebuy' },
|
21
|
+
{ key: '_attribution', value: 'Recently Viewed Product' },
|
22
|
+
]}
|
23
|
+
>
|
24
|
+
<Button
|
25
|
+
width="full"
|
26
|
+
variant={isOutOfStock ? 'secondary' : 'primary'}
|
27
|
+
as="span"
|
28
|
+
>
|
29
|
+
{isOutOfStock ? 'Out of stock' : 'Add to bag'}
|
30
|
+
</Button>
|
31
|
+
</AddToCartButton>
|
32
|
+
</div>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
|
36
|
+
export const RebuyRecentlyViewedProducts = ({ product, products }) => {
|
37
|
+
const title = 'Recently Viewed';
|
38
|
+
// Filter current product from recently viewed
|
39
|
+
const filtered = products.filter((recent) => recent.id !== product.id);
|
40
|
+
|
41
|
+
return (
|
42
|
+
filtered.length > 0 && (
|
43
|
+
<Section heading={title} padding="y">
|
44
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16 px-6 md:px-8 lg:px-12">
|
45
|
+
{filtered.map((product) => (
|
46
|
+
<li key={product.id}>
|
47
|
+
<ProductOptionsProvider
|
48
|
+
data={product}
|
49
|
+
initialVariantId={product.variants.nodes[0].id}
|
50
|
+
>
|
51
|
+
<ProductCard
|
52
|
+
product={product}
|
53
|
+
className={'w-100'}
|
54
|
+
/>
|
55
|
+
<AddToCartMarkup product={product} />
|
56
|
+
</ProductOptionsProvider>
|
57
|
+
</li>
|
58
|
+
))}
|
59
|
+
</ul>
|
60
|
+
</Section>
|
61
|
+
)
|
62
|
+
);
|
63
|
+
};
|
64
|
+
|
65
|
+
export default RebuyRecentlyViewedProducts;
|
@@ -1,97 +1,136 @@
|
|
1
|
-
import
|
2
|
-
import { RebuyContext } from '
|
3
|
-
import { flattenConnection } from '@shopify/hydrogen';
|
4
|
-
|
1
|
+
import { RebuyClient } from '@rebuy/rebuy';
|
2
|
+
import { RebuyContext } from '@rebuy/rebuy-hydrogen/RebuyContexts.client';
|
5
3
|
import * as Utilities from '@rebuy/rebuy/utilities';
|
6
|
-
import
|
4
|
+
import { flattenConnection, useCart } from '@shopify/hydrogen';
|
5
|
+
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
7
6
|
|
8
7
|
const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
|
9
8
|
|
10
|
-
export
|
11
|
-
const {
|
12
|
-
|
9
|
+
export const RebuyWidgetContainer = ({ children, ...props }) => {
|
10
|
+
const {
|
11
|
+
collection,
|
12
|
+
collectionId,
|
13
|
+
dataSource,
|
14
|
+
limit,
|
15
|
+
options,
|
16
|
+
product,
|
17
|
+
productId,
|
18
|
+
variant,
|
19
|
+
variantId,
|
20
|
+
} = props;
|
13
21
|
|
14
|
-
const
|
22
|
+
const cart = useCart();
|
23
|
+
const { contextParameters } = useContext(RebuyContext);
|
15
24
|
const [products, setProducts] = useState([]);
|
16
|
-
const [metadata, setMetadata] = useState(
|
17
|
-
|
18
|
-
const
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
const
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
30
|
-
|
31
|
-
if (product) {
|
32
|
-
request.parameters.shopify_product_ids = Utilities.getIdFromGraphUrl(product.id, 'Product');
|
33
|
-
} else if (productId) {
|
34
|
-
request.parameters.shopify_product_ids = Utilities.getIdFromGraphUrl(productId, 'Product');
|
35
|
-
}
|
36
|
-
|
37
|
-
if (variant) {
|
38
|
-
request.parameters.shopify_variant_ids = Utilities.getIdFromGraphUrl(variant.id, 'ProductVariant');
|
39
|
-
} else if (variantId) {
|
40
|
-
request.parameters.shopify_variant_ids = Utilities.getIdFromGraphUrl(variantId, 'ProductVariant');
|
41
|
-
}
|
42
|
-
|
43
|
-
if (collection) {
|
44
|
-
request.parameters.shopify_collection_ids = Utilities.getIdFromGraphUrl(collection.id, 'Collection');
|
45
|
-
} else if (collectionId) {
|
46
|
-
request.parameters.shopify_collection_ids = Utilities.getIdFromGraphUrl(collectionId, 'Collection');
|
47
|
-
}
|
25
|
+
const [metadata, setMetadata] = useState({});
|
26
|
+
const [Rebuy, setRebuy] = useState(null);
|
27
|
+
const [initialized, setInitialized] = useState(false);
|
28
|
+
const isCartReady = ['uninitialized', 'idle'].includes(cart.status); // uninitialized, creating, fetching, updating, idle
|
29
|
+
const shopifyProductId = product?.id ?? productId ?? null;
|
30
|
+
const shopifyVariantId = variant?.id ?? variantId ?? null;
|
31
|
+
const shopifyCollectionId = collection?.id ?? collectionId ?? null;
|
32
|
+
|
33
|
+
// Initialize Rebuy API client
|
34
|
+
useEffect(() => {
|
35
|
+
if (!Rebuy) {
|
36
|
+
const client = new RebuyClient(REBUY_API_KEY);
|
37
|
+
client.setContextParameters(contextParameters);
|
48
38
|
|
49
|
-
|
50
|
-
|
51
|
-
|
39
|
+
if (options) {
|
40
|
+
client.setDefaultParameters(options);
|
41
|
+
}
|
52
42
|
|
53
|
-
|
54
|
-
|
55
|
-
|
43
|
+
setRebuy(client);
|
44
|
+
setInitialized(true);
|
45
|
+
}
|
46
|
+
}, [Rebuy, contextParameters, options]);
|
56
47
|
|
48
|
+
// Update context params
|
57
49
|
useEffect(() => {
|
58
|
-
|
50
|
+
if (!Rebuy) return;
|
51
|
+
|
52
|
+
Rebuy.setContextParameters(contextParameters);
|
53
|
+
}, [Rebuy, contextParameters]);
|
54
|
+
|
55
|
+
// Memoize request object on prop changes (e.g. product)
|
56
|
+
const request = useMemo(() => {
|
57
|
+
const request = {
|
58
|
+
url: dataSource || '/api/v1/products/recommended',
|
59
|
+
parameters: {},
|
60
|
+
};
|
61
|
+
|
62
|
+
if (shopifyProductId) {
|
63
|
+
request.parameters.shopify_product_ids =
|
64
|
+
Utilities.getIdFromGraphUrl(shopifyProductId, 'Product');
|
65
|
+
}
|
66
|
+
|
67
|
+
if (shopifyVariantId) {
|
68
|
+
request.parameters.shopify_variant_ids =
|
69
|
+
Utilities.getIdFromGraphUrl(shopifyVariantId, 'ProductVariant');
|
70
|
+
}
|
71
|
+
|
72
|
+
if (shopifyCollectionId) {
|
73
|
+
request.parameters.shopify_collection_ids =
|
74
|
+
Utilities.getIdFromGraphUrl(shopifyCollectionId, 'Collection');
|
75
|
+
}
|
76
|
+
|
77
|
+
if (limit) {
|
78
|
+
request.parameters.limit = limit;
|
79
|
+
}
|
80
|
+
|
81
|
+
return request;
|
59
82
|
}, [
|
60
|
-
product,
|
61
|
-
productId,
|
62
|
-
variant,
|
63
|
-
variantId,
|
64
|
-
collection,
|
65
|
-
collectionId,
|
66
83
|
dataSource,
|
67
84
|
limit,
|
68
|
-
|
85
|
+
shopifyCollectionId,
|
86
|
+
shopifyProductId,
|
87
|
+
shopifyVariantId,
|
69
88
|
]);
|
70
89
|
|
71
|
-
|
72
|
-
|
90
|
+
// Update product recommendations on cart or request change
|
91
|
+
useEffect(() => {
|
92
|
+
let ignore = false;
|
93
|
+
|
94
|
+
// Initializing...
|
95
|
+
if (!initialized || !Rebuy || !isCartReady) return;
|
96
|
+
|
97
|
+
const fetchData = async () => {
|
98
|
+
const { data, metadata } = await Rebuy.getStorefrontData(
|
99
|
+
request.url,
|
100
|
+
request.parameters
|
101
|
+
);
|
102
|
+
|
103
|
+
data.forEach((product) => {
|
104
|
+
product.variants = {
|
105
|
+
nodes: flattenConnection(product.variants),
|
106
|
+
};
|
107
|
+
});
|
108
|
+
|
109
|
+
// Set state only if mounted
|
110
|
+
if (!ignore) {
|
111
|
+
setProducts(data);
|
112
|
+
setMetadata(metadata);
|
113
|
+
}
|
114
|
+
};
|
115
|
+
|
116
|
+
fetchData();
|
73
117
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
});
|
118
|
+
// Unmounted?
|
119
|
+
return () => {
|
120
|
+
ignore = true;
|
121
|
+
};
|
122
|
+
}, [Rebuy, initialized, request, isCartReady]);
|
80
123
|
|
81
|
-
|
82
|
-
|
83
|
-
|
124
|
+
const childrenWithProps = (props) =>
|
125
|
+
React.Children.map(children, (child) =>
|
126
|
+
React.isValidElement(child)
|
127
|
+
? React.cloneElement(child, props)
|
128
|
+
: child
|
129
|
+
);
|
84
130
|
|
85
|
-
const
|
86
|
-
return React.Children.map(children, (child) => {
|
87
|
-
if (React.isValidElement(child)) {
|
88
|
-
return React.cloneElement(child, data);
|
89
|
-
}
|
90
|
-
return child;
|
91
|
-
});
|
92
|
-
};
|
131
|
+
const childProps = { ...props, products, metadata, key: product.id };
|
93
132
|
|
94
|
-
|
133
|
+
return childrenWithProps(childProps);
|
134
|
+
};
|
95
135
|
|
96
|
-
|
97
|
-
}
|
136
|
+
export default RebuyWidgetContainer;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rebuy/rebuy-hydrogen",
|
3
3
|
"description": "This is the default library for Rebuy + Shopify Hydrogen",
|
4
|
-
"version": "1.0
|
4
|
+
"version": "2.1.0",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Rebuy, Inc.",
|
7
7
|
"type": "module",
|
@@ -10,10 +10,12 @@
|
|
10
10
|
},
|
11
11
|
"files": [
|
12
12
|
"README.md",
|
13
|
+
"**/*.js",
|
13
14
|
"**/*.jsx"
|
14
15
|
],
|
15
16
|
"scripts": {
|
16
|
-
"
|
17
|
+
"format": "npm run lint -- --fix",
|
18
|
+
"lint": "eslint . --ext .js,.jsx,.json",
|
17
19
|
"prepare": "husky install",
|
18
20
|
"test": "echo \"Error: no test specified\" && exit 1"
|
19
21
|
},
|
@@ -34,26 +36,39 @@
|
|
34
36
|
},
|
35
37
|
"homepage": "https://bitbucket.org/rebuyengine/npm-rebuy-hydrogen#readme",
|
36
38
|
"devDependencies": {
|
37
|
-
"@commitlint/cli": "^17.0
|
38
|
-
"@commitlint/config-conventional": "^17.0
|
39
|
+
"@commitlint/cli": "^17.2.0",
|
40
|
+
"@commitlint/config-conventional": "^17.2.0",
|
39
41
|
"@semantic-release/changelog": "^6.0.1",
|
40
42
|
"@semantic-release/commit-analyzer": "^9.0.2",
|
41
43
|
"@semantic-release/git": "^10.0.1",
|
42
44
|
"@semantic-release/npm": "^9.0.1",
|
43
45
|
"@semantic-release/release-notes-generator": "^10.0.3",
|
44
|
-
"@shopify/hydrogen": "^1.
|
46
|
+
"@shopify/hydrogen": "^1.6.1",
|
45
47
|
"conventional-changelog-eslint": "^3.0.9",
|
46
48
|
"cz-conventional-changelog": "^3.3.0",
|
47
|
-
"cz-customizable": "^6.
|
48
|
-
"eslint": "^8.
|
49
|
-
"eslint-
|
49
|
+
"cz-customizable": "^6.9.2",
|
50
|
+
"eslint": "^8.26.0",
|
51
|
+
"eslint-config-prettier": "^8.5.0",
|
52
|
+
"eslint-plugin-import": "^2.26.0",
|
53
|
+
"eslint-plugin-json": "^3.1.0",
|
54
|
+
"eslint-plugin-prettier": "^4.2.1",
|
55
|
+
"eslint-plugin-react": "^7.31.10",
|
56
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
50
57
|
"husky": "^8.0.1",
|
51
|
-
"
|
58
|
+
"lint-staged": "^13.0.3",
|
59
|
+
"prettier": "^2.7.1",
|
60
|
+
"semantic-release": "^19.0.5"
|
52
61
|
},
|
53
62
|
"dependencies": {
|
54
|
-
"@rebuy/rebuy": "^1.
|
63
|
+
"@rebuy/rebuy": "^1.3.9"
|
55
64
|
},
|
56
65
|
"peerDependencies": {
|
57
|
-
"@shopify/hydrogen": "^1.
|
66
|
+
"@shopify/hydrogen": "^1.6.1"
|
67
|
+
},
|
68
|
+
"lint-staged": {
|
69
|
+
"**/*.{js,jsx,json}": [
|
70
|
+
"npm run format",
|
71
|
+
"prettier --write"
|
72
|
+
]
|
58
73
|
}
|
59
74
|
}
|