@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
@@ -1,13 +1,33 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
import React, { createContext, useMemo } from 'react';
|
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';
|
4
|
+
import { useCart, useShop, useUrl } from '@shopify/hydrogen';
|
5
|
+
import { useEffect, useMemo, useState } from 'react';
|
6
|
+
|
7
|
+
const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
|
8
|
+
const API = '/api/v1';
|
9
|
+
|
10
|
+
const getEncodedAttributes = (attributes) =>
|
11
|
+
encodeURIComponent(
|
12
|
+
JSON.stringify(
|
13
|
+
attributes.reduce(
|
14
|
+
(merged, { key, value }) => ({ ...merged, [key]: value }),
|
15
|
+
{}
|
16
|
+
)
|
17
|
+
)
|
18
|
+
);
|
6
19
|
|
7
|
-
export const
|
8
|
-
|
9
|
-
|
20
|
+
export const RebuyContextProvider = ({ children }) => {
|
21
|
+
// Shopify
|
22
|
+
const cart = useCart();
|
23
|
+
const shop = useShop();
|
10
24
|
const url = useUrl();
|
25
|
+
|
26
|
+
// Default state
|
27
|
+
const [initialized, setInitialized] = useState(false);
|
28
|
+
const [rebuyConfig, setRebuyConfig] = useState(null);
|
29
|
+
const [config, setConfig] = useState({ shop: null });
|
30
|
+
// const [Rebuy, setRebuy] = useState(null);
|
11
31
|
const queryObject = Utilities.queryStringToObject(url.search);
|
12
32
|
const utmObject = Utilities.utmObjectFromString(url);
|
13
33
|
|
@@ -17,114 +37,186 @@ export function RebuyContextProvider({ children }) {
|
|
17
37
|
}
|
18
38
|
}
|
19
39
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
40
|
+
// Initialization
|
41
|
+
useEffect(() => {
|
42
|
+
const getRebuyConfig = async () => {
|
43
|
+
try {
|
44
|
+
const request = {
|
45
|
+
url: `${API}/user/config`,
|
46
|
+
parameters: { shop: shop.storeDomain },
|
47
|
+
};
|
48
|
+
|
49
|
+
const { data: rebuy, ...response } = await new RebuyClient(
|
50
|
+
REBUY_API_KEY
|
51
|
+
).getDataFromCDN(request.url, request.parameters);
|
52
|
+
|
53
|
+
// Missing Rebuy shop data?
|
54
|
+
if (!rebuy?.shop) {
|
55
|
+
throw new Error(
|
56
|
+
'Rebuy configuration is not properly set up - missing shop',
|
57
|
+
{ cause: response }
|
58
|
+
);
|
59
|
+
}
|
26
60
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
61
|
+
setRebuyConfig(rebuy);
|
62
|
+
} catch (err) {
|
63
|
+
console.warn('Error fetching Rebuy shop config');
|
64
|
+
console.error(err, err.cause);
|
65
|
+
}
|
66
|
+
};
|
31
67
|
|
32
|
-
|
33
|
-
|
68
|
+
const onBeforeReady = async () => {
|
69
|
+
// Initializing...
|
70
|
+
if (!rebuyConfig?.shop) {
|
71
|
+
return await getRebuyConfig();
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
onBeforeReady();
|
76
|
+
}, [rebuyConfig, shop]);
|
77
|
+
|
78
|
+
useEffect(() => {
|
79
|
+
const applyConfig = async () => {
|
80
|
+
// Still fetching Rebuy config OR config already applied. Abort!
|
81
|
+
if (!rebuyConfig || config.shop) return;
|
82
|
+
|
83
|
+
// Bring it all together
|
84
|
+
const appConfig = { ...config, ...rebuyConfig };
|
85
|
+
|
86
|
+
setConfig(appConfig);
|
87
|
+
};
|
88
|
+
|
89
|
+
applyConfig();
|
90
|
+
}, [config, rebuyConfig]);
|
91
|
+
|
92
|
+
useEffect(() => {
|
93
|
+
const onReady = () => {
|
94
|
+
// Still initializing... Abort!
|
95
|
+
if (!config.shop) return;
|
96
|
+
|
97
|
+
// Initialized!
|
98
|
+
setInitialized(true);
|
99
|
+
// const { api_key, cache_key } = config.shop;
|
100
|
+
// setRebuy(new RebuyClient(api_key, { cache_key }));
|
101
|
+
};
|
102
|
+
|
103
|
+
onReady();
|
104
|
+
}, [config]);
|
105
|
+
|
106
|
+
const contextParameters = useMemo(() => {
|
107
|
+
// Still initializing... Abort!
|
108
|
+
if (!config.shop) return null;
|
109
|
+
|
110
|
+
const { cache_key } = config.shop;
|
111
|
+
const contextParameters = {
|
112
|
+
url: url.href,
|
113
|
+
cache_key,
|
114
|
+
};
|
115
|
+
// Cart object
|
116
|
+
const cartContext = {};
|
117
|
+
|
118
|
+
// Set time
|
119
|
+
if (Object.prototype.hasOwnProperty.call(queryObject, 'time')) {
|
120
|
+
contextParameters.time = queryObject.time;
|
121
|
+
}
|
34
122
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
123
|
+
// Set Cart: token
|
124
|
+
if (cart.id) {
|
125
|
+
cartContext.token = Utilities.getIdFromGraphUrl(cart.id, 'Cart');
|
126
|
+
contextParameters.cart_token = Utilities.getIdFromGraphUrl(
|
127
|
+
cart.id,
|
128
|
+
'Cart'
|
129
|
+
);
|
130
|
+
}
|
40
131
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
cart.estimatedCost.subtotalAmount &&
|
45
|
-
cart.estimatedCost.subtotalAmount &&
|
46
|
-
cart.estimatedCost.subtotalAmount.amount
|
47
|
-
) {
|
48
|
-
cartContext.subtotal = Utilities.amountToCents(cart.estimatedCost.subtotalAmount.amount);
|
49
|
-
contextParameters.cart_subtotal = Utilities.amountToCents(cart.estimatedCost.subtotalAmount.amount);
|
50
|
-
}
|
132
|
+
// Set Cart: subtotal
|
133
|
+
if (cart.cost?.subtotalAmount?.amount) {
|
134
|
+
const { amount } = cart.cost.subtotalAmount;
|
51
135
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
contextParameters.cart_count = cart.lines.length;
|
56
|
-
contextParameters.cart_line_count = cart.lines.length;
|
57
|
-
}
|
136
|
+
cartContext.subtotal = Utilities.amountToCents(amount);
|
137
|
+
contextParameters.cart_subtotal = Utilities.amountToCents(amount);
|
138
|
+
}
|
58
139
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
contextParameters.cart_item_count = cart.totalQuantity;
|
63
|
-
}
|
140
|
+
// Set Cart: line count
|
141
|
+
if (typeof cart.lines != 'undefined') {
|
142
|
+
const totalLines = cart.lines.length;
|
64
143
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
const cartItem = cart.lines[i];
|
144
|
+
cartContext.line_count = totalLines;
|
145
|
+
contextParameters.cart_count = totalLines;
|
146
|
+
contextParameters.cart_line_count = totalLines;
|
147
|
+
}
|
70
148
|
|
71
|
-
|
149
|
+
// Set Cart: item count
|
150
|
+
if (typeof cart.totalQuantity != 'undefined') {
|
151
|
+
const { totalQuantity } = cart;
|
72
152
|
|
73
|
-
|
74
|
-
|
75
|
-
|
153
|
+
cartContext.item_count = totalQuantity;
|
154
|
+
contextParameters.cart_item_count = totalQuantity;
|
155
|
+
}
|
76
156
|
|
77
|
-
|
78
|
-
|
79
|
-
|
157
|
+
// Set Cart: line items
|
158
|
+
if (typeof cart.lines != 'undefined') {
|
159
|
+
cartContext.items = [];
|
80
160
|
|
81
|
-
|
161
|
+
for (const cartItem of cart.lines) {
|
162
|
+
const item = {
|
163
|
+
quantity: cartItem.quantity,
|
164
|
+
};
|
82
165
|
|
83
|
-
|
84
|
-
|
166
|
+
if (cartItem.product?.id) {
|
167
|
+
item.product_id = Utilities.getIdFromGraphUrl(
|
168
|
+
cartItem.product.id,
|
169
|
+
'Product'
|
170
|
+
);
|
171
|
+
}
|
85
172
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
173
|
+
if (cartItem.merchandise?.id) {
|
174
|
+
item.variant_id = Utilities.getIdFromGraphUrl(
|
175
|
+
cartItem.merchandise.id,
|
176
|
+
'ProductVariant'
|
177
|
+
);
|
90
178
|
}
|
91
179
|
|
92
|
-
|
93
|
-
|
180
|
+
if (cartItem.attributes) {
|
181
|
+
item.properties = getEncodedAttributes(cartItem.attributes);
|
182
|
+
}
|
94
183
|
|
95
|
-
|
184
|
+
// TBD: item.selling_plan
|
96
185
|
|
97
|
-
|
186
|
+
cartContext.items.push(item);
|
187
|
+
}
|
98
188
|
}
|
99
|
-
}
|
100
189
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
for (let i = 0; i < cart.attributes.length; i++) {
|
106
|
-
const key = cart.attributes[i].key;
|
107
|
-
const value = cart.attributes[i].value;
|
108
|
-
attributes[key] = value;
|
190
|
+
// Set Cart: attributes
|
191
|
+
if (cart.attributes) {
|
192
|
+
cartContext.attributes = getEncodedAttributes(cart.attributes);
|
109
193
|
}
|
110
194
|
|
111
|
-
|
112
|
-
|
195
|
+
// Set Cart: notes
|
196
|
+
if (cart.note) {
|
197
|
+
cartContext.note = cart.note;
|
198
|
+
}
|
113
199
|
|
114
|
-
|
115
|
-
|
116
|
-
cartContext.note = cart.note;
|
117
|
-
}
|
200
|
+
// Set cart
|
201
|
+
contextParameters.cart = cartContext;
|
118
202
|
|
119
|
-
|
120
|
-
|
203
|
+
return contextParameters;
|
204
|
+
}, [cart, config, queryObject, url]);
|
121
205
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
206
|
+
// Static reference (JSON) + memoization
|
207
|
+
// ^ prevent re-rendering children when context params are unchanged
|
208
|
+
const contextParametersJSON = JSON.stringify(contextParameters);
|
209
|
+
const contextValue = useMemo(
|
210
|
+
() => ({ contextParameters: JSON.parse(contextParametersJSON) }),
|
211
|
+
[contextParametersJSON]
|
127
212
|
);
|
128
213
|
|
129
|
-
|
130
|
-
|
214
|
+
// Still initializing...
|
215
|
+
if (!initialized) return null;
|
216
|
+
|
217
|
+
return (
|
218
|
+
<RebuyContext.Provider value={contextValue}>
|
219
|
+
{children}
|
220
|
+
</RebuyContext.Provider>
|
221
|
+
);
|
222
|
+
};
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import { Image, Link, Money } from '@shopify/hydrogen';
|
2
|
+
import clsx from 'clsx';
|
3
|
+
import { Text } from '~/components';
|
4
|
+
import { isDiscounted } from '~/lib/utils';
|
5
|
+
|
6
|
+
const CompareAtPrice = ({ data: compareAtPrice, className }) => {
|
7
|
+
const styles = clsx('strike', className);
|
8
|
+
|
9
|
+
return (
|
10
|
+
<Money
|
11
|
+
withoutTrailingZeros
|
12
|
+
data={compareAtPrice}
|
13
|
+
as="span"
|
14
|
+
className={styles}
|
15
|
+
/>
|
16
|
+
);
|
17
|
+
};
|
18
|
+
|
19
|
+
const RebuyProductPrice = ({ selectedVariant = {} }) => {
|
20
|
+
const { priceV2: price, compareAtPriceV2: compareAtPrice } =
|
21
|
+
selectedVariant;
|
22
|
+
|
23
|
+
return (
|
24
|
+
price && (
|
25
|
+
<div className="gap-4">
|
26
|
+
<Text className="flex gap-2">
|
27
|
+
<Money data={price} withoutTrailingZeros as="span" />
|
28
|
+
{isDiscounted(price, compareAtPrice) && (
|
29
|
+
<CompareAtPrice
|
30
|
+
data={compareAtPrice}
|
31
|
+
className={'opacity-50'}
|
32
|
+
/>
|
33
|
+
)}
|
34
|
+
</Text>
|
35
|
+
</div>
|
36
|
+
)
|
37
|
+
);
|
38
|
+
};
|
39
|
+
|
40
|
+
export const RebuyProductAddOnCard = ({
|
41
|
+
product = {},
|
42
|
+
selectedVariant = product.variants.nodes[0],
|
43
|
+
}) => {
|
44
|
+
if (!selectedVariant) {
|
45
|
+
return null;
|
46
|
+
}
|
47
|
+
|
48
|
+
const { image, availableForSale } = selectedVariant;
|
49
|
+
const isOutOfStock = !availableForSale;
|
50
|
+
|
51
|
+
return (
|
52
|
+
<div className="mb-4 flex">
|
53
|
+
<div className="flex items-center justify-center overflow-hidden mx-auto">
|
54
|
+
{image && (
|
55
|
+
<Image
|
56
|
+
className="fadeIn"
|
57
|
+
data={image}
|
58
|
+
alt={image.altText || `Picture of ${product.title}`}
|
59
|
+
width={80}
|
60
|
+
height={80}
|
61
|
+
loaderOptions={{ scale: 2 }}
|
62
|
+
/>
|
63
|
+
)}
|
64
|
+
{isOutOfStock && (
|
65
|
+
<div className="absolute top-3 left-3 rounded text-xs bg-primary/60 text-contrast py-3 px-4">
|
66
|
+
<Text>Out of stock</Text>
|
67
|
+
</div>
|
68
|
+
)}
|
69
|
+
</div>
|
70
|
+
|
71
|
+
<div className="grid grid-rows-3 ml-3">
|
72
|
+
<Text className="font-medium">{product.title}</Text>
|
73
|
+
|
74
|
+
<RebuyProductPrice selectedVariant={selectedVariant} />
|
75
|
+
|
76
|
+
<Link
|
77
|
+
to={`/products/${product?.handle}`}
|
78
|
+
className="text-xs underline text-primary/50 hover:underline hover:text-primary"
|
79
|
+
>
|
80
|
+
Learn More
|
81
|
+
</Link>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
);
|
85
|
+
};
|
86
|
+
|
87
|
+
export default RebuyProductAddOnCard;
|
@@ -0,0 +1,228 @@
|
|
1
|
+
import {
|
2
|
+
Money,
|
3
|
+
ProductOptionsProvider,
|
4
|
+
useCart,
|
5
|
+
useProductOptions,
|
6
|
+
} from '@shopify/hydrogen';
|
7
|
+
import clsx from 'clsx';
|
8
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
9
|
+
import { Heading, Text } from '~/components';
|
10
|
+
import { Button } from '~/components/elements';
|
11
|
+
import { RebuyProductAddOnCard } from './RebuyProductAddOnCard.client';
|
12
|
+
|
13
|
+
export const RebuyProductAddOns = ({
|
14
|
+
product = {},
|
15
|
+
products = [],
|
16
|
+
metadata = {},
|
17
|
+
title = '',
|
18
|
+
className = '',
|
19
|
+
}) => {
|
20
|
+
// Hooks
|
21
|
+
const { linesAdd } = useCart();
|
22
|
+
const { selectedVariant } = useProductOptions();
|
23
|
+
|
24
|
+
// State
|
25
|
+
const [addedItems, setAddedItems] = useState([]);
|
26
|
+
const [totalMoney, _setTotalMoney] = useState(selectedVariant.priceV2);
|
27
|
+
const isOutOfStock = !selectedVariant.availableForSale;
|
28
|
+
const language = metadata.widget?.language ?? {};
|
29
|
+
const componentTitle =
|
30
|
+
title ?? language?.title ?? `Popular Add-Ons for ${product.title}`;
|
31
|
+
const styles = clsx('grid gap-4 py-6 md:py-8 lg:py-12', className);
|
32
|
+
|
33
|
+
const AddToCartMarkup = ({ items, money, selectedVariant }) => {
|
34
|
+
return (
|
35
|
+
<>
|
36
|
+
<Button
|
37
|
+
onClick={() => addLineItemsToCart(items, selectedVariant)}
|
38
|
+
variant="primary"
|
39
|
+
as="button"
|
40
|
+
>
|
41
|
+
<Text
|
42
|
+
as="span"
|
43
|
+
className="flex items-center justify-center gap-2"
|
44
|
+
>
|
45
|
+
<span>Add to bag</span> <span>·</span>{' '}
|
46
|
+
<Money withoutTrailingZeros data={money} />
|
47
|
+
</Text>
|
48
|
+
</Button>
|
49
|
+
</>
|
50
|
+
);
|
51
|
+
};
|
52
|
+
|
53
|
+
const addLineItemsToCart = useCallback(
|
54
|
+
(items, selectedVariant) => {
|
55
|
+
const lineItemsToAdd = items.map((item) => ({
|
56
|
+
quantity: 1,
|
57
|
+
merchandiseId: item.variantId,
|
58
|
+
attributes: [
|
59
|
+
{ key: '_source', value: 'Rebuy' },
|
60
|
+
{ key: '_attribution', value: 'Product AddOn' },
|
61
|
+
],
|
62
|
+
}));
|
63
|
+
|
64
|
+
// Add the selected variant of the main product
|
65
|
+
lineItemsToAdd.push({
|
66
|
+
quantity: 1,
|
67
|
+
merchandiseId: selectedVariant.id,
|
68
|
+
});
|
69
|
+
|
70
|
+
// Add additional cart lines
|
71
|
+
linesAdd(lineItemsToAdd);
|
72
|
+
|
73
|
+
return;
|
74
|
+
},
|
75
|
+
[linesAdd]
|
76
|
+
);
|
77
|
+
|
78
|
+
// reference object for line data
|
79
|
+
const refItems = useMemo(() => {
|
80
|
+
const items = {};
|
81
|
+
|
82
|
+
products.forEach((product) => {
|
83
|
+
// NOTE: More work must be done if the add-ons will have variant selectors of their own
|
84
|
+
const singleVariant = product.variants?.nodes[0];
|
85
|
+
|
86
|
+
items[product.id] = {
|
87
|
+
id: product.id,
|
88
|
+
variantId: singleVariant.id,
|
89
|
+
moneyPrice: singleVariant.priceV2, // full MoneyV2 object
|
90
|
+
};
|
91
|
+
});
|
92
|
+
|
93
|
+
return items;
|
94
|
+
}, [products]);
|
95
|
+
|
96
|
+
const setTotalMoney = useCallback(
|
97
|
+
(items) => {
|
98
|
+
// get base product price
|
99
|
+
const newTotalMoney = { ...selectedVariant.priceV2 };
|
100
|
+
|
101
|
+
if (items.length === 0) {
|
102
|
+
_setTotalMoney(newTotalMoney);
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
|
106
|
+
// cast amount to number for calculation
|
107
|
+
let totalAmount = Number(newTotalMoney.amount);
|
108
|
+
// sum up the prices of the added items
|
109
|
+
items.forEach((item) => {
|
110
|
+
totalAmount += Number(item.moneyPrice.amount);
|
111
|
+
});
|
112
|
+
|
113
|
+
// cast amount back to string for MoneyV2
|
114
|
+
newTotalMoney.amount = String(totalAmount);
|
115
|
+
|
116
|
+
_setTotalMoney(newTotalMoney);
|
117
|
+
return;
|
118
|
+
},
|
119
|
+
[selectedVariant]
|
120
|
+
);
|
121
|
+
|
122
|
+
// listen for selected variant change from parent
|
123
|
+
useEffect(() => {
|
124
|
+
setTotalMoney(addedItems);
|
125
|
+
}, [addedItems, setTotalMoney]);
|
126
|
+
|
127
|
+
const handleChange = useCallback(
|
128
|
+
(event, product) => {
|
129
|
+
// const mainPrice = Number(selectedVariant.priceV2.amount);
|
130
|
+
let newAddedItems = [...addedItems];
|
131
|
+
if (event.target.checked) {
|
132
|
+
newAddedItems = [...addedItems, refItems[product.id]];
|
133
|
+
} else {
|
134
|
+
newAddedItems = addedItems.filter(
|
135
|
+
(item) => item.id !== product.id
|
136
|
+
);
|
137
|
+
}
|
138
|
+
|
139
|
+
setAddedItems(newAddedItems);
|
140
|
+
setTotalMoney(newAddedItems);
|
141
|
+
},
|
142
|
+
[addedItems, refItems, setTotalMoney]
|
143
|
+
);
|
144
|
+
|
145
|
+
return (
|
146
|
+
/* Render Product AddOns with AddToCart Button */
|
147
|
+
product &&
|
148
|
+
products.length > 0 &&
|
149
|
+
!isOutOfStock && (
|
150
|
+
<div className={styles}>
|
151
|
+
<Heading size="lead" className="px-6 md:px-8 lg:px-12">
|
152
|
+
{componentTitle}
|
153
|
+
</Heading>
|
154
|
+
{/* Product Add-Ons */}
|
155
|
+
<ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 px-6 md:px-8 lg:px-12">
|
156
|
+
{products.map((product) => (
|
157
|
+
<li key={product.id}>
|
158
|
+
<ProductOptionsProvider data={product}>
|
159
|
+
<div className="mt-2">
|
160
|
+
<label
|
161
|
+
htmlFor={product.id}
|
162
|
+
className="cursor-pointer flex items-center hover:bg-slate-200 p-2 gap-2"
|
163
|
+
>
|
164
|
+
<input
|
165
|
+
type="checkbox"
|
166
|
+
value=""
|
167
|
+
disabled={
|
168
|
+
!product.variants.nodes[0]
|
169
|
+
.availableForSale
|
170
|
+
}
|
171
|
+
name={product.title}
|
172
|
+
id={product.id}
|
173
|
+
checked={
|
174
|
+
addedItems.filter(
|
175
|
+
(item) =>
|
176
|
+
item.id === product.id
|
177
|
+
).length > 0
|
178
|
+
}
|
179
|
+
className="accent-black rounded-sm cursor-pointer"
|
180
|
+
onChange={(event) =>
|
181
|
+
handleChange(event, product)
|
182
|
+
}
|
183
|
+
/>
|
184
|
+
<RebuyProductAddOnCard
|
185
|
+
product={product}
|
186
|
+
/>
|
187
|
+
</label>
|
188
|
+
</div>
|
189
|
+
</ProductOptionsProvider>
|
190
|
+
</li>
|
191
|
+
))}
|
192
|
+
</ul>
|
193
|
+
|
194
|
+
<div className="px-6 md:px-8 lg:px-12">
|
195
|
+
{/* Subtotal */}
|
196
|
+
{Number(totalMoney.amount) >
|
197
|
+
Number(selectedVariant.priceV2.amount) && (
|
198
|
+
<Text
|
199
|
+
as="div"
|
200
|
+
className="flex gap-1 pb-4 text-gray-600"
|
201
|
+
>
|
202
|
+
Subtotal:{' '}
|
203
|
+
<Money
|
204
|
+
data={{
|
205
|
+
...totalMoney,
|
206
|
+
amount: String(
|
207
|
+
totalMoney.amount -
|
208
|
+
selectedVariant.priceV2.amount
|
209
|
+
),
|
210
|
+
}}
|
211
|
+
withoutTrailingZeros
|
212
|
+
/>
|
213
|
+
</Text>
|
214
|
+
)}
|
215
|
+
|
216
|
+
{/* AddToCart Button */}
|
217
|
+
<AddToCartMarkup
|
218
|
+
items={addedItems}
|
219
|
+
money={totalMoney}
|
220
|
+
selectedVariant={selectedVariant}
|
221
|
+
/>
|
222
|
+
</div>
|
223
|
+
</div>
|
224
|
+
)
|
225
|
+
);
|
226
|
+
};
|
227
|
+
|
228
|
+
export default RebuyProductAddOns;
|