@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.
@@ -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 React, { useState, useEffect } from 'react';
2
-
1
+ import { RebuyClient } from '@rebuy/rebuy';
3
2
  import * as Utilities from '@rebuy/rebuy/utilities';
4
- import RebuyClient from '@rebuy/rebuy/client';
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 default function RebuyProductViewed(props) {
9
- const { product, productId, productHandle, children } = props;
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
- const Rebuy = new RebuyClient(REBUY_API_KEY);
33
+ useEffect(() => {
34
+ const recordView = async () => {
35
+ const response = await Rebuy.trackProductViewed(request.parameters);
14
36
 
15
- const request = {
16
- parameters: {},
17
- };
37
+ setEvent(response.data);
38
+ };
18
39
 
19
- if (product) {
20
- request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(product.id, 'Product');
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
- if (Object.keys(request.parameters).length == 0) {
28
- console.log('No parameters!');
29
- return;
30
- }
43
+ setRebuy(client);
44
+ setInitialized(true);
31
45
 
32
- useEffect(() => {
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
- const recordView = async () => {
37
- const response = await Rebuy.trackProductViewed(request.parameters);
38
- setEvent(response.data);
39
- };
59
+ return children;
60
+ };
40
61
 
41
- return <>{children}</>;
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 React, { useState, useEffect, useContext } from 'react';
2
- import { RebuyContext } from './RebuyContextProvider.client';
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 RebuyClient from '@rebuy/rebuy/client';
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 default function RebuyWidgetContainer(props) {
11
- const { product, productId, variant, variantId, collection, collectionId, dataSource, limit, options, children } =
12
- props;
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 contextParameters = useContext(RebuyContext);
22
+ const cart = useCart();
23
+ const { contextParameters } = useContext(RebuyContext);
15
24
  const [products, setProducts] = useState([]);
16
- const [metadata, setMetadata] = useState([]);
17
-
18
- const Rebuy = new RebuyClient(REBUY_API_KEY);
19
-
20
- Rebuy.setContextParameters(contextParameters);
21
-
22
- const request = {
23
- url: '/api/v1/products/recommended',
24
- parameters: {},
25
- };
26
-
27
- if (dataSource) {
28
- request.url = dataSource;
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
- if (limit) {
50
- request.parameters.limit = limit;
51
- }
39
+ if (options) {
40
+ client.setDefaultParameters(options);
41
+ }
52
42
 
53
- if (options) {
54
- Rebuy.setDefaultParameters(options);
55
- }
43
+ setRebuy(client);
44
+ setInitialized(true);
45
+ }
46
+ }, [Rebuy, contextParameters, options]);
56
47
 
48
+ // Update context params
57
49
  useEffect(() => {
58
- fetchData();
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
- options,
85
+ shopifyCollectionId,
86
+ shopifyProductId,
87
+ shopifyVariantId,
69
88
  ]);
70
89
 
71
- const fetchData = async () => {
72
- const response = await Rebuy.getStorefrontData(request.url, request.parameters);
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
- const { data, metadata } = response;
75
- data.forEach((product) => {
76
- product.variants = {
77
- nodes: flattenConnection(product.variants),
78
- };
79
- });
118
+ // Unmounted?
119
+ return () => {
120
+ ignore = true;
121
+ };
122
+ }, [Rebuy, initialized, request, isCartReady]);
80
123
 
81
- setProducts(data);
82
- setMetadata(metadata);
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 childrenWithProps = (data) => {
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
- const data = Object.assign({}, props, { products, metadata });
133
+ return childrenWithProps(childProps);
134
+ };
95
135
 
96
- return <>{childrenWithProps(data)}</>;
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.3",
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
- "lint": "eslint . --ext .js,.jsx",
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.2",
38
- "@commitlint/config-conventional": "^17.0.2",
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.0.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.3.0",
48
- "eslint": "^8.17.0",
49
- "eslint-plugin-react": "^7.30.0",
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
- "semantic-release": "^19.0.3"
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.0.1"
63
+ "@rebuy/rebuy": "^1.3.9"
55
64
  },
56
65
  "peerDependencies": {
57
- "@shopify/hydrogen": "^1.0.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
  }