@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 CHANGED
@@ -1,407 +1,24 @@
1
1
  # Rebuy + Hydrogen Overview
2
2
 
3
- Rebuy + Hydrogen package is a web development framework used for building Shopify custom storefronts. It includes providers, components, and tooling you need to get started so you can spend your time creating intelligent shopping experiences.
3
+ Rebuy + Hydrogen package is a web development framework used for building Shopify custom storefronts. It includes providers, components, and tooling you need to get started so you can spend your time creating intelligent shopping experiences.
4
4
 
5
5
  ## How Rebuy + Hydrogen Works
6
6
 
7
- Rebuy + Hydrogen is a lightweight framework for creating personalized shopping experiences that are lightening fast. The framework is composed of:
8
- 1. Providers - these components build contextual objects that are used during network calls.
9
- 2. Containers - these components get personalized data from Rebuy and pass the resulting information to children components via props.
10
- 3. Components - these components can be used to render various merchandising UI.
11
- 4. Events - these components automatically track user behaviors.
7
+ Rebuy + Hydrogen is a lightweight framework for creating personalized shopping experiences that are lightening fast. The framework is composed of:
8
+
9
+ 1. **Providers** - these components build contextual objects that are used during network calls.
10
+ 2. **Containers** - these components get personalized data from Rebuy and pass the resulting information to children components via props.
11
+ 3. **Components** - these components can be used to render various merchandising UI.
12
+ 4. **Events** - these components automatically track user behaviors.
12
13
 
13
14
  ### Data Sources
14
15
 
15
- Rebuy + Hydrogen is powered by Rebuy's Data Sources, which allows for AI-powered product recommendations, custom defined rulesets, or mixture of human and computer derived output. Data Sources are created in Rebuy's App Admin, and the data source URL is used in your Hydrogen project. Separating the logic from the codebase allows non-technical team members to manage the merchandising rules without the need for costly code changes or redeployments.
16
+ Rebuy + Hydrogen is powered by Rebuy's Data Sources, which allows for AI-powered product recommendations, custom defined rulesets, or mixture of human and computer derived output. Data Sources are created in Rebuy's App Admin, and the data source URL is used in your Hydrogen project. Separating the logic from the codebase allows non-technical team members to manage the merchandising rules without the need for costly code changes or redeployments.
16
17
 
17
18
  ### User Interfaces
18
19
 
19
- Rebuy + Hydrogen uses a React container pattern to separate the data fetching logic and state managment from the presentional components. As such, container components do not render user interface elements. They simply pass their own properties, as well as data received from Rebuy, to their children components for consumption. This allows you to build your own user interface with your existing components, or use Rebuy's out of the box components to get up and running quickly.
20
-
21
-
22
- # Getting Started
23
-
24
- In this tutorial, you'll extend your Hydrogen app with the Rebuy + Hydrogen framework. After completing this tutorial you'll have accomplished the following:
25
-
26
- - Installed the Rebuy + Hydrogen framework
27
- - Configured your environment
28
- - Generated AI-powered product recommendations
29
-
30
- ## Requirements
31
-
32
- In order to complete this tutorial, you will need the following:
33
- - [Shopify](https://www.shopify.com/) account
34
- - [Rebuy](https://www.rebuyengine.com/) installed in your Shopify store
35
- - [Hydrogen App](https://shopify.dev/custom-storefronts/hydrogen/getting-started/create#step-1-create-a-new-hydrogen-storefront)
36
-
37
- ### Install Dependencies
38
- Additionally, you will need the following dependencies:
39
- - [npm](https://www.npmjs.com/)
40
- - [Node.js](https://nodejs.org/en/) (version 16.5.0 or higher)
41
-
42
-
43
- # Step 1: Create a new Hydrogen app
44
-
45
- You can create a Hydrogen app locally using `yarn`, `npm`, or `npx`.
46
-
47
- If you want to [integrate with an existing React framework](https://shopify.dev/custom-storefronts/hydrogen/framework/integrate-react-frameworks), like [Next.js](https://nextjs.org/) or [Gatsby](https://www.gatsbyjs.com/), then you can add the [@shopify/hydrogen](https://www.npmjs.com/package/@shopify/hydrogen) `npm` package to your project.
48
-
49
- #### 1. Navigate to your working directoy
50
- Navigate to the directory where you want to create your project and run the following command:
51
- ```bash
52
- cd <directory>
53
- ```
54
-
55
- #### 2. Create your project
56
- Create a new Hydrogen project with a default template linked to a demo-store with the following command:
57
- ```bash
58
- npm init @shopify/hydrogen -- --template demo-store
59
- ```
60
-
61
- #### 3. Name your project
62
- Follow the prompts to name your new Hydrogen application.
63
-
64
-
65
- # Step 2: Link your Shopify Storefront
66
- Connect your Hydrogen app to your Shopify storefront by updating the properties in the `hydrogen.config.js` file. You will need to [generate a Storefront API access token](https://help.shopify.com/en/manual/apps/custom-apps?shpxid=5ef5d325-B992-4F4F-3A6D-0FACECA2B482#install-the-app-and-get-the-api-access-tokens) for your Hydrogen app to communicate with your Shopify store.
67
-
68
- #### 1. Open your `hydrogen.config.js` file
69
- Open `hydrogen.config.js` file located in the root of your project directory.
70
-
71
- #### 2. Update your config file
72
- Edit your `hydrogen.config.js` file with the following changes and save the file:
73
- - Update `storeDomain` to specify your store's domain name.
74
- - Update `storefrontToken` to specify your Storefront API access token.
75
- - Update `storefrontApiVersion` to specify the Storefront API version that you want to use.
76
-
77
- ```javascript
78
- export default defineConfig({
79
- shopify: {
80
- defaultCountryCode: 'US',
81
- defaultLanguageCode: 'EN',
82
- storeDomain: 'YOUR_STORE_NAME.myshopify.com',
83
- storefrontToken: 'YOUR_STOREFRONT_ACCESS_TOKEN',
84
- storefrontApiVersion: '2022-07',
85
- },
86
- });
87
- ```
88
-
89
-
90
- # Step 3: Start a development server
91
- Create a new local development server to start testing your changes.
92
-
93
- #### 1. Navigate to your project directoy
94
- Navigate to the root of your project directory:
95
- ```bash
96
- cd <directory>
97
- ```
98
-
99
- #### 2. Start the development server
100
- Start up a new local development server on `localhost`:
101
- ```bash
102
- npm run dev
103
- ```
104
-
105
- The development environment will open automatically at [http://localhost:3000](http://localhost:3000).
106
-
107
-
108
- # Step 4: Install Rebuy + Hydrogen Package
109
- Once you have successfully created your new Hydrogen app and linked it to your Shopify store, it's now time to install the Rebuy + Hydrogen package. If you currently have your development server running, you can terminate the server with the following command `Ctrl` + `C`.
110
-
111
- #### 1. Navigate to your project directoy
112
- Navigate to the root of your project directory:
113
- ```bash
114
- cd <directory>
115
- ```
116
-
117
- #### 2. Install the Rebuy + Hydrogen package
118
- Install the Rebuy + Hydrogen package from [npm](https://www.npmjs.com/) with the following command:
119
- ```bash
120
- npm install @rebuy/rebuy-hydrogen
121
- ```
122
-
123
- #### 3. Update your `.env` with your Rebuy public API key
124
- You can find your [Rebuy public API key](https://rebuyengine.com/account/keys) in Rebuy admin > Account > API Keys. Once located, update your `.env` with the following:
125
- ```
126
- PUBLIC_REBUY_API_KEY=yourRebuyAPIKey
127
- ```
128
- *If your project does not have an `.env` file, you can create a new one and save it in the project root directory.*
129
-
130
-
131
- # Step 5: Build Personalized Shopping Experiences
132
- Once you have included Rebuy + Hydrogen in your project, it's time to start building dynamic, personalized shopping experiences! In this tutorial, we will accomplish the following:
133
-
134
- - Customize the product details page
135
- - Display a list of AI-powered product recommendations
136
- - Display a list of recently viewed products
137
-
138
- #### 1. Create a Recommended Products Component
139
- Create a new file in `src/components` called `ProductRecommendations.jsx`, and add the following to the file:
140
-
141
- ```javascript
142
- import {
143
- ProductOptionsProvider,
144
- AddToCartButton,
145
- } from '@shopify/hydrogen';
146
- import ProductCard from './ProductCard';
147
- import {
148
- BUTTON_PRIMARY_CLASSES,
149
- } from './Button.client';
150
-
151
- function AddToCartMarkup({product}) {
152
- const selectedVariant = product.variants.nodes[0];
153
- const isOutOfStock = !selectedVariant.availableForSale;
154
-
155
- return (
156
- <div className="space-y-2 mb-8">
157
- <AddToCartButton
158
- className={BUTTON_PRIMARY_CLASSES}
159
- disabled={isOutOfStock}
160
- product={product}
161
- variantId={selectedVariant.id}
162
- attributes={[
163
- {key: '_source', value: 'Rebuy'},
164
- {key: '_attribution', value: 'Product Recommendations'
165
- }]}
166
- >
167
- {isOutOfStock ? 'Out of stock' : 'Add to bag'}
168
- </AddToCartButton>
169
- </div>
170
- );
171
- }
172
-
173
- export default function ProductRecommendations(props) {
174
- const {product, products, metadata} = props;
175
-
176
- console.log('ProductRecommendations component properties:', props);
177
-
178
- return(
179
- <>
180
- <h1 className="font-bold text-4xl md:text-5xl text-gray-900 mb-6 mt-6">If you like {product.title}, you'll also love these</h1>
181
- <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
182
- {products.map((product) => (
183
- <li key={product.id}>
184
- <ProductOptionsProvider data={product} initialVariantId={product.variants.nodes[0].id}>
185
- <ProductCard product={product} />
186
- <AddToCartMarkup product={product} />
187
- </ProductOptionsProvider>
188
- </li>
189
- ))}
190
- </ul>
191
- </>
192
- )
193
- }
194
- ```
195
-
196
- #### 2. Create a Recently Viewed Products Component
197
- Create a new file in `src/components` called `RecentlyViewedProducts.jsx`, and add the following to the file:
198
-
199
- ```javascript
200
- import {
201
- ProductOptionsProvider,
202
- AddToCartButton,
203
- } from '@shopify/hydrogen';
204
- import ProductCard from './ProductCard';
205
- import {
206
- BUTTON_PRIMARY_CLASSES,
207
- } from './Button.client';
208
-
209
- function AddToCartMarkup({product}) {
210
- const selectedVariant = product.variants.nodes[0];
211
- const isOutOfStock = !selectedVariant.availableForSale;
212
-
213
- return (
214
- <div className="space-y-2 mb-8">
215
- <AddToCartButton
216
- className={BUTTON_PRIMARY_CLASSES}
217
- disabled={isOutOfStock}
218
- product={product}
219
- variantId={selectedVariant.id}
220
- attributes={[
221
- {key: '_source', value: 'Rebuy'},
222
- {key: '_attribution', value: 'Recently Viewed Product'
223
- }]}
224
- >
225
- {isOutOfStock ? 'Out of stock' : 'Add to bag'}
226
- </AddToCartButton>
227
- </div>
228
- );
229
- }
230
-
231
- export default function RecentlyViewedProducts(props) {
232
- const {product, products, metadata} = props;
233
-
234
- console.log('RecentlyViewedProducts component properties:', props);
235
-
236
- return(
237
- <>
238
- <h1 className="font-bold text-4xl md:text-5xl text-gray-900 mb-6 mt-6">Recently Viewed</h1>
239
- <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
240
- {products.map((product) => (
241
- <li key={product.id}>
242
- <ProductOptionsProvider data={product} initialVariantId={product.variants.nodes[0].id}>
243
- <ProductCard product={product} />
244
- <AddToCartMarkup product={product} />
245
- </ProductOptionsProvider>
246
- </li>
247
- ))}
248
- </ul>
249
- </>
250
- )
251
- }
252
- ```
253
-
254
- #### 3. Install the RebuyContextProvider
255
- Open `/src/App.server.jsx` file and install the `RebuyContextProvider`, so all Rebuy API calls are enriched with contextual information. Open the file and make the following edits:
256
-
257
- ##### Import the provider
258
- ```javascript
259
- import {RebuyContextProvider} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
260
- ```
261
-
262
- ##### Add RebuyContextProvider component
263
- Wrap the `<Route>` component with the `RebuyContextProvider`, as follows:
264
- ```html
265
- <RebuyContextProvider>
266
- <Route path="*" page={<NotFound />} />
267
- </RebuyContextProvider>
268
- ```
269
-
270
- #### 4. Customize the Product Details Page
271
- Let's include our new components to the `ProductDetails` client component, located in `src/components/ProductDetails.client.jsx`. We will be wrapping our new components with a `RebuyWidgetContainer` components, which will give us an interface to request data from Rebuy.
272
-
273
- ##### Import your new UI components
274
- Import the `ProductRecommendations` and `RecentlyViewedProducts` components in to the `ProductDetails.client.jsx` file.
275
- ```javascript
276
- import ProductRecommendations from './ProductRecommendations';
277
- import RecentlyViewedProducts from './RecentlyViewedProducts';
278
- ```
279
-
280
- ##### Import Rebuy components
281
- Import the `RebuyWidgetContainer` and `RebuyProductViewed` components in to the `ProductDetails.client.jsx` file.
282
- ```javascript
283
- import RebuyWidgetContainer from '@rebuy/rebuy-hydrogen/RebuyWidgetContainer.client';
284
- import RebuyProductViewed from '@rebuy/rebuy-hydrogen/RebuyProductViewed.client';
285
- ```
286
-
287
- ##### Add imported components in to the `ProductDetails` functional component
288
- Before the closing `</ProductOptionsProvider>` tag, insert our imported components:
289
- ```html
290
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/recommended" limit="6">
291
- <ProductRecommendations />
292
- </RebuyWidgetContainer>
293
-
294
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/viewed" limit="6">
295
- <RecentlyViewedProducts />
296
- </RebuyWidgetContainer>
297
-
298
- <RebuyProductViewed product={product} />
299
- ```
300
-
301
- ##### Review your changes
302
- When you look at the product details page, you should now see two new sections:
303
- 1. AI-powered product recommendations
304
- 2. Recently viewed products
305
-
306
- Additionally, we included the `<RebuyProductViewed product={product} />` component, which will automatically send a `user -> viewed -> product` event to Rebuy. This event is powering the recently viewed component in real-time! As you navigate the site, your browsing history will be tracked and display for easy navigation to previous product pages!
307
-
308
-
309
- # Documentation
310
- Below you will find documentation on each of the components included in the Rebuy + Hydrogen package.
311
-
312
- ## RebuyContextProvider
313
- The `RebuyContextProvider` is a React provider component. It is responsible for collecting environment and state-based information as a `RebuyContext`. It will gather information about the current URL, session UTM parameters, as well as cart information such as the cart token, subtotal, line items, etc. As a provider component, no UI is rendered, it simply creates a React context value that can be requested by any child component.
314
-
315
- The `RebuyContextProvider` is a client component, which means it renders in the browser. It has two dependencies, which are `useUrl` and `useCart`, so the component should be located high up in the component tree but **below** the `ShopifyProvider` and `CartProvider`.
316
-
317
- The recommended location is in `src/App.server.jsx` inside `<Router>` and wrapping **both** `<FileRoutes>` and the `<Route>` components, which makes the `RebuyContext` automatically available on all pages across the site.
318
-
319
- #### Import the RebuyContextProvider
320
- ```javascript
321
- import {RebuyContextProvider} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
322
- ```
323
-
324
- #### Wrap the Route compontent with RebuyContextProvider
325
- ```html
326
- <Router>
327
- <RebuyContextProvider>
328
- <FileRoutes
329
- basePath={countryCode ? `/${countryCode}/` : undefined}
330
- routes={routes}
331
- />
332
- <Route path="*" page={<NotFound />} />
333
- </RebuyContextProvider>
334
- </Router>
335
- ```
336
-
337
- #### Access RebuyContext
338
- Rebuy + Hydrogen components will automatically include and use `RebuyContext`, but if you would like to use this context information in your own components, you certainly can!
339
-
340
- ```javascript
341
- import React from 'react';
342
- import {RebuyContext} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
343
-
344
- // Access context parameters with React's useContext method
345
- const contextParameters = React.useContext(RebuyContext);
346
- ```
347
-
348
- ## RebuyWidgetContainer
349
- The `RebuyWidgetContainer` is a React container component. This component is responsible for fetching data and state management. It does not render any UI components, and will pass along its properties and data received from Rebuy to all of their children components. This abstraction allows you to use your existing application components or Rebuy's out of the box components.
350
-
351
- #### Component properties
352
- The component accepts argments via props to allow for different API responses. All props listed below are optional, if no `dataSource` prop is provided then `'/api/v1/products/recommended'` will be used.
353
-
354
- | Property | Value |
355
- | -------- | ----- |
356
- | dataSource | `{String}` A relative URL path of your Rebuy data source (i.e. '/api/v1/products/recommended) |
357
- | product | `{Object}` A product object with an `id` |
358
- | productId | `{String}` A product ID string in graphQL URL format |
359
- | variant | `{Object}` A variant object with an `id` |
360
- | variantId | `{String}` A variant ID string in graphQL URL format |
361
- | collection | `{Object}` A collection object with an `id` |
362
- | collectionId | `{String}` A collection ID string in graphQL URL format |
363
- | limit | `{Number}` A number of products to return from Rebuy's API |
364
- | options | `{Object}` An object with key, value pairs of Rebuy REST API arguments (i.e. metafields: yes) |
365
-
366
- #### Children components properties
367
- All child components will inherit the container props, in addition to two container generated props `products` and `metadata`. So if you've provided a `product={product}` prop to the container, then each child compontent will have the following properties `product`, `products`, and `metadata`.
368
-
369
- | Property | Value |
370
- | -------- | ----- |
371
- | products | `{Array}` An array of product objects returned from Rebuy's API |
372
- | metadata | `{Object}` An object with metadata about the input data, rules matched, rules not matched, excluded products, and cache info |
373
-
374
- #### Example implementation
375
- In the following example, we are using an example compontent called `ProductRecommendations` which is a UI component used to render data provided to it via props by the `RebuyWidgetContainer` component.
376
-
377
- When the components are rendered, it will display `6` products powered by Rebuy's AI algorithm based on the provided input `product`. So if the input product was ketchup, you would likely see recommended products like mustard, mayo, or other condiments.
378
-
379
- ```javascript
380
- import ProductRecommendations from './ProductRecommendations';
381
- import RebuyWidgetContainer from '@rebuy/rebuy-hydrogen/RebuyWidgetContainer.client';
382
-
383
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/recommended" limit="6">
384
- <ProductRecommendations />
385
- </RebuyWidgetContainer>
386
- ```
387
-
388
- ## RebuyProductViewed
389
- The `RebuyProductViewed` is a client component, which helps automate customer browsing behavior. This behavior is used to train the AI as well as power recently viewed products. This component would typically live in your `ProductDetails` component, so as a customer is browsing your site we automatically record the product pages they are viewing.
390
-
391
- #### Component properties
392
- The component accepts argments via props to allow for registering different products that have been viewed. One of the following properties are required to fire the event.
393
-
394
- | Property | Value |
395
- | -------- | ----- |
396
- | product | `{Object}` A product object with an `id` |
397
- | productId | `{String}` A product ID string in graphQL URL format |
398
- | productHandle | `{String}` A product handle string |
399
-
400
- #### Example implementation
401
- The following example would be included in the `ProductDetails.client.jsx` component to automatically register customer product view events. The current product displayed is passed to the `RebuyProductViewed` component via props as `product={product}`.
20
+ Rebuy + Hydrogen uses a React container pattern to separate the data fetching logic and state management from the presentational components. As such, container components do not render user interface elements. They simply pass their own properties, as well as data received from Rebuy, to their children components for consumption. This allows you to build your own user interface with your existing components, or use Rebuy's out of the box components to get up and running quickly.
402
21
 
403
- ```javascript
404
- import RebuyProductViewed from '@rebuy/rebuy-hydrogen/RebuyProductViewed.client';
22
+ ### Getting Started
405
23
 
406
- <RebuyProductViewed product={product} />
407
- ```
24
+ To get started, please visit our [Rebuy + Shopify Hydrogen documentation](https://developers.rebuyengine.com/reference/shopify-hydrogen-getting-started).
@@ -0,0 +1,188 @@
1
+ import {
2
+ AddToCartButton,
3
+ Image,
4
+ Link,
5
+ Money,
6
+ ProductOptionsProvider,
7
+ useMoney,
8
+ } from '@shopify/hydrogen';
9
+ import clsx from 'clsx';
10
+ import { useState } from 'react';
11
+ import { Section, Text } from '~/components';
12
+ import { Button } from '~/components/elements';
13
+ import { isDiscounted } from '~/lib/utils';
14
+
15
+ const CompareAtPrice = ({ data, className }) => {
16
+ const { currencyNarrowSymbol, withoutTrailingZerosAndCurrency } =
17
+ useMoney(data);
18
+
19
+ const styles = clsx('strike', className);
20
+
21
+ return (
22
+ <span className={styles}>
23
+ {currencyNarrowSymbol}
24
+ {withoutTrailingZerosAndCurrency}
25
+ </span>
26
+ );
27
+ };
28
+
29
+ const AddToCartMarkup = ({
30
+ product,
31
+ selectedVariant = product.variants.nodes[0],
32
+ }) => {
33
+ const isOutOfStock = !selectedVariant.availableForSale;
34
+
35
+ return (
36
+ <AddToCartButton
37
+ disabled={isOutOfStock}
38
+ variantId={selectedVariant?.id}
39
+ quantity={1}
40
+ accessibleAddingToCartLabel="Adding item to your cart"
41
+ type="button"
42
+ attributes={[
43
+ { key: '_source', value: 'Rebuy' },
44
+ { key: '_attribution', value: 'Product Recommendations' },
45
+ ]}
46
+ >
47
+ <Button
48
+ width="full"
49
+ variant={isOutOfStock ? 'secondary' : 'primary'}
50
+ as="span"
51
+ className="px-0"
52
+ >
53
+ {isOutOfStock ? 'Out of stock' : 'Add'}
54
+ </Button>
55
+ </AddToCartButton>
56
+ );
57
+ };
58
+
59
+ const RebuyProductPrice = ({ selectedVariant = {} }) => {
60
+ const { priceV2: price, compareAtPriceV2: compareAtPrice } =
61
+ selectedVariant;
62
+
63
+ return (
64
+ price && (
65
+ <div className="gap-4">
66
+ <Text className="flex gap-2">
67
+ <Money withoutTrailingZeros data={price} />
68
+ {isDiscounted(price, compareAtPrice) && (
69
+ <CompareAtPrice
70
+ className={'opacity-50'}
71
+ data={compareAtPrice}
72
+ />
73
+ )}
74
+ </Text>
75
+ </div>
76
+ )
77
+ );
78
+ };
79
+
80
+ const VariantSelector = ({ product, handleSelectedVariant }) => {
81
+ return (
82
+ product?.variants.nodes.length > 1 && (
83
+ <div className="">
84
+ <select
85
+ className="w-full py-1 rounded"
86
+ name=""
87
+ onChange={(e) =>
88
+ handleSelectedVariant(product, e.target.value)
89
+ }
90
+ >
91
+ <optgroup label={getOptionsLabel(product)}>
92
+ {product.variants.nodes.map(({ id, title }) => (
93
+ <option key={id + '-variant'} value={id}>
94
+ {title}
95
+ </option>
96
+ ))}
97
+ </optgroup>
98
+ </select>
99
+ </div>
100
+ )
101
+ );
102
+ };
103
+
104
+ const getOptionsLabel = (product) => {
105
+ const options = product.variants.nodes[0].selectedOptions;
106
+ const optionsFromKeys = Object.keys(options[0]);
107
+ const optionsFromValues = options.map((option) => option.name);
108
+ const useValues = optionsFromKeys.every((key) =>
109
+ ['name', 'value'].includes(key)
110
+ );
111
+
112
+ // Return delimited label for available option(s) e.g. Color / Size, Scent, etc
113
+ return (useValues ? optionsFromValues : optionsFromKeys).join(' / ');
114
+ };
115
+
116
+ const RebuyProductCard = ({ product }) => {
117
+ const [selectedVariant, setSelectedVariant] = useState(
118
+ product.variants.nodes[0]
119
+ );
120
+ const { image } = selectedVariant;
121
+ const handleSelectedVariant = (product, variant_id) => {
122
+ const updatedVariant = product.variants.nodes.find(
123
+ (variant) => variant.id === variant_id
124
+ );
125
+
126
+ setSelectedVariant(updatedVariant);
127
+ };
128
+
129
+ return (
130
+ <div className="grid grid-cols-2 grid-rows-1 gap-6">
131
+ <Link to={`/products/${product.handle}`}>
132
+ {image && (
133
+ <Image
134
+ className="w-full object-cover fadeIn"
135
+ data={image}
136
+ alt={image.altText || `Picture of ${product.title}`}
137
+ />
138
+ )}
139
+ </Link>
140
+ <div className="grid gap-1 items-start">
141
+ <Link to={`/products/${product.handle}`}>
142
+ <Text>{product.title}</Text>
143
+ </Link>
144
+ <RebuyProductPrice selectedVariant={selectedVariant} />
145
+ <VariantSelector
146
+ product={product}
147
+ handleSelectedVariant={handleSelectedVariant}
148
+ />
149
+ <AddToCartMarkup
150
+ product={product}
151
+ selectedVariant={selectedVariant}
152
+ />
153
+ </div>
154
+ </div>
155
+ );
156
+ };
157
+
158
+ export const RebuyCompleteTheLook = ({
159
+ product = {},
160
+ products = [],
161
+ // eslint-disable-next-line no-unused-vars
162
+ metadata = {},
163
+ title = `These pair with ${product.title}`,
164
+ className = '',
165
+ }) => {
166
+ const styles = clsx('', className);
167
+
168
+ return (
169
+ products.length > 0 && (
170
+ <Section heading={title} padding="n" className={styles}>
171
+ <ul className="grid gap-8">
172
+ {products.map((product) => (
173
+ <li key={product.id}>
174
+ <ProductOptionsProvider
175
+ data={product}
176
+ initialVariantId={product.variants.nodes[0].id}
177
+ >
178
+ <RebuyProductCard product={product} />
179
+ </ProductOptionsProvider>
180
+ </li>
181
+ ))}
182
+ </ul>
183
+ </Section>
184
+ )
185
+ );
186
+ };
187
+
188
+ export default RebuyCompleteTheLook;