@redotech/redo-hydrogen 1.0.2 → 1.1.1
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/CHANGELOG.md +23 -0
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/types.d.ts +17 -4
- package/package.json +1 -1
- package/src/components/redo-checkout-buttons.tsx +23 -8
- package/src/providers/redo-coverage-client.tsx +69 -4
- package/src/types.ts +27 -5
- package/src/utils/cart.ts +0 -1
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CartReturn
|
|
1
|
+
import { CartReturn } from '@shopify/hydrogen';
|
|
2
2
|
import { ReactNode, DependencyList } from 'react';
|
|
3
3
|
import { ProductVariant } from '@shopify/hydrogen-react/storefront-api-types';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
@@ -10,9 +10,13 @@ interface RedoCoverageClient {
|
|
|
10
10
|
disable(): Promise<boolean>;
|
|
11
11
|
get loading(): boolean;
|
|
12
12
|
get enabled(): boolean;
|
|
13
|
-
get
|
|
13
|
+
get eligible(): boolean;
|
|
14
|
+
get price(): number | undefined;
|
|
15
|
+
get storeId(): string | undefined;
|
|
16
|
+
get cart(): CartReturn | undefined;
|
|
14
17
|
get cartProduct(): CartProductVariantFragment | undefined;
|
|
15
18
|
get cartAttribute(): CartAttributeKey | undefined;
|
|
19
|
+
get errors(): RedoError[] | undefined;
|
|
16
20
|
}
|
|
17
21
|
type CartInfoToEnable = {
|
|
18
22
|
productId: string;
|
|
@@ -26,6 +30,17 @@ type RedoContextValue = {
|
|
|
26
30
|
storeId?: string;
|
|
27
31
|
cartInfoToEnable?: CartInfoToEnable;
|
|
28
32
|
cart?: CartReturn;
|
|
33
|
+
errors?: RedoError[];
|
|
34
|
+
};
|
|
35
|
+
declare enum RedoErrorType {
|
|
36
|
+
ApiBadRequest = "API_BAD_REQUEST",
|
|
37
|
+
ApiServerError = "API_SERVER_ERROR",
|
|
38
|
+
ApiUnknownError = "API_UNKNOWN_ERROR"
|
|
39
|
+
}
|
|
40
|
+
type RedoError = {
|
|
41
|
+
type: RedoErrorType;
|
|
42
|
+
message: string;
|
|
43
|
+
context: any;
|
|
29
44
|
};
|
|
30
45
|
|
|
31
46
|
declare const RedoProvider: ({ cart, storeId, children }: {
|
|
@@ -37,8 +52,6 @@ declare const useRedoCoverageClient: () => RedoCoverageClient;
|
|
|
37
52
|
|
|
38
53
|
declare const RedoCheckoutButtons: (props: {
|
|
39
54
|
cart: CartReturn;
|
|
40
|
-
storefront: Storefront;
|
|
41
|
-
storeId: string;
|
|
42
55
|
children?: ReactNode;
|
|
43
56
|
onClick?: (enabled: boolean) => void;
|
|
44
57
|
}) => react_jsx_runtime.JSX.Element;
|
package/package.json
CHANGED
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
CartForm,
|
|
4
4
|
CartActionInput,
|
|
5
5
|
CartReturn,
|
|
6
|
-
Storefront,
|
|
7
6
|
} from "@shopify/hydrogen";
|
|
8
7
|
import { useRedoCoverageClient } from "../providers/redo-coverage-client";
|
|
9
8
|
import { CartInfoToEnable, RedoCoverageClient } from "../types";
|
|
10
9
|
import { REDO_PUBLIC_API_HOSTNAME } from "../utils/security";
|
|
10
|
+
import { CurrencyCode } from "@shopify/hydrogen-react/storefront-api-types";
|
|
11
11
|
|
|
12
12
|
type CheckoutButtonUIResponse = {
|
|
13
13
|
html: string;
|
|
@@ -45,6 +45,10 @@ const getButtonsToShow = ({
|
|
|
45
45
|
ui: json
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
if(!ui) {
|
|
49
|
+
return reject(null);
|
|
50
|
+
}
|
|
51
|
+
|
|
48
52
|
return resolve(ui);
|
|
49
53
|
});
|
|
50
54
|
});
|
|
@@ -59,10 +63,19 @@ const applyButtonVariables = ({
|
|
|
59
63
|
cart: CartReturn,
|
|
60
64
|
ui: CheckoutButtonUIResponse
|
|
61
65
|
}): CheckoutButtonUIResponse | null => {
|
|
66
|
+
if(!redoCoverageClient.eligible || !redoCoverageClient.price) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let currencyCode: CurrencyCode = cart.cost.totalAmount.currencyCode;
|
|
71
|
+
if(currencyCode === 'XXX') {
|
|
72
|
+
currencyCode = 'USD';
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
const cartContainsRedo = !!(cart.lines.nodes.some((cartItem) => cartItem.merchandise?.product?.vendor === 're:do'));
|
|
63
76
|
const combinedPrice = new Intl.NumberFormat('en-US', {
|
|
64
77
|
style: 'currency',
|
|
65
|
-
currency:
|
|
78
|
+
currency: currencyCode
|
|
66
79
|
}).format(Number(cart.cost.totalAmount.amount) + (cartContainsRedo ? 0 : redoCoverageClient.price));
|
|
67
80
|
|
|
68
81
|
if(!combinedPrice || !combinedPrice.length || combinedPrice.includes('NaN')) {
|
|
@@ -89,14 +102,12 @@ const findAncestor = (
|
|
|
89
102
|
|
|
90
103
|
const RedoCheckoutButtons = (props: {
|
|
91
104
|
cart: CartReturn;
|
|
92
|
-
storefront: Storefront;
|
|
93
|
-
storeId: string;
|
|
94
105
|
children?: ReactNode;
|
|
95
106
|
onClick?: (enabled: boolean) => void;
|
|
96
107
|
}) => {
|
|
97
108
|
const redoCoverageClient = useRedoCoverageClient();
|
|
98
|
-
let cart =
|
|
99
|
-
let checkoutUrl = cart
|
|
109
|
+
let cart = redoCoverageClient.cart;
|
|
110
|
+
let checkoutUrl = redoCoverageClient.cart?.checkoutUrl || '/checkout';
|
|
100
111
|
let [redoProductToAdd, setRedoProductToAdd] =
|
|
101
112
|
useState<CartInfoToEnable | null>(null);
|
|
102
113
|
let [checkoutButtonsUI, setCheckoutButtonsUI] = useState<CheckoutButtonUIResponse | null>(
|
|
@@ -105,12 +116,16 @@ const RedoCheckoutButtons = (props: {
|
|
|
105
116
|
|
|
106
117
|
useEffect(() => {
|
|
107
118
|
(async () => {
|
|
108
|
-
|
|
119
|
+
if(!redoCoverageClient.eligible || !cart || !redoCoverageClient.storeId) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const buttons = await getButtonsToShow({ redoCoverageClient, cart, storeId: redoCoverageClient.storeId });
|
|
109
124
|
if(buttons) {
|
|
110
125
|
setCheckoutButtonsUI(buttons);
|
|
111
126
|
}
|
|
112
127
|
})();
|
|
113
|
-
}, [cart, redoCoverageClient.price]);
|
|
128
|
+
}, [cart, redoCoverageClient.eligible, redoCoverageClient.price, redoCoverageClient.storeId]);
|
|
114
129
|
|
|
115
130
|
const wrapperClickHandler = async (e: MouseEvent) => {
|
|
116
131
|
let clickedElement = e.target as HTMLElement;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useFetcher } from "@remix-run/react";
|
|
2
2
|
import { CartReturn } from "@shopify/hydrogen";
|
|
3
3
|
import { createContext, ReactNode, useContext, useEffect, useState } from "react";
|
|
4
|
-
import { CartProductVariantFragment, CartAttributeKey, CartInfoToEnable, RedoContextValue, RedoCoverageClient } from "../types";
|
|
4
|
+
import { CartProductVariantFragment, CartAttributeKey, CartInfoToEnable, RedoContextValue, RedoCoverageClient, RedoError, RedoErrorType } from "../types";
|
|
5
5
|
import { REDO_PUBLIC_API_HOSTNAME } from "../utils/security";
|
|
6
6
|
import { addProductToCartIfNeeded, removeProductFromCartIfNeeded, setCartRedoEnabledAttribute, useFetcherWithPromise } from "../utils/cart";
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ const DEFAULT_REDO_CONTEXT_VALUE: RedoContextValue = {
|
|
|
10
10
|
loading: true,
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const RedoContext = createContext(DEFAULT_REDO_CONTEXT_VALUE);
|
|
13
|
+
const RedoContext = createContext<RedoContextValue>(DEFAULT_REDO_CONTEXT_VALUE);
|
|
14
14
|
|
|
15
15
|
const RedoProvider = ({
|
|
16
16
|
cart,
|
|
@@ -25,8 +25,21 @@ const RedoProvider = ({
|
|
|
25
25
|
const [cartAttribute, setCartAttribute] = useState<CartAttributeKey>();
|
|
26
26
|
const [cartInfoToEnable, setCartInfoToEnable] = useState<CartInfoToEnable>();
|
|
27
27
|
const [loading, setLoading] = useState<boolean>(true);
|
|
28
|
+
const [errors, setErrors] = useState<RedoError[]>([]);
|
|
29
|
+
|
|
30
|
+
const logUniqueError = (newError: RedoError) => {
|
|
31
|
+
if(errors.find((err) => err.type === newError.type)) {
|
|
32
|
+
} else {
|
|
33
|
+
setErrors([...errors, newError]);
|
|
34
|
+
}
|
|
35
|
+
return newError;
|
|
36
|
+
}
|
|
28
37
|
|
|
29
38
|
useEffect(() => {
|
|
39
|
+
if(!cart || !storeId) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
fetch(`https://${REDO_PUBLIC_API_HOSTNAME}/v2.2/stores/${storeId}/coverage-products`, {
|
|
31
44
|
method: 'POST',
|
|
32
45
|
headers: {
|
|
@@ -64,13 +77,47 @@ const RedoProvider = ({
|
|
|
64
77
|
})
|
|
65
78
|
})
|
|
66
79
|
.then(async (res) => {
|
|
80
|
+
if(res.status === 500) {
|
|
81
|
+
logUniqueError({
|
|
82
|
+
type: RedoErrorType.ApiServerError,
|
|
83
|
+
message: "Internal server error occured when getting available coverage products from Redo API.. Check your inputs are correct and storeId have been configured. Reach out to Redo support if the issue persists.",
|
|
84
|
+
context: {
|
|
85
|
+
json: await res.json()
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
} else if(res.status === 400) {
|
|
90
|
+
logUniqueError({
|
|
91
|
+
type: RedoErrorType.ApiBadRequest,
|
|
92
|
+
message: "Bad request when getting available coverage products from Redo API. Check that the passed in cart is of the correct type Cart/CartReturn and includes all of the correct cart information.",
|
|
93
|
+
context: {
|
|
94
|
+
json: await res.json()
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
} else if(res.status !== 200) {
|
|
99
|
+
logUniqueError({
|
|
100
|
+
type: RedoErrorType.ApiUnknownError,
|
|
101
|
+
message: "Unkown error occured while getting available coverage products from Redo API.",
|
|
102
|
+
context: {
|
|
103
|
+
status: res.status,
|
|
104
|
+
json: await res.json()
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
67
110
|
let json = await res.json();
|
|
68
111
|
|
|
69
112
|
setLoading(false);
|
|
70
113
|
|
|
114
|
+
if(!json?.coverageProducts?.[0]?.cartInfoToEnable) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
71
118
|
setCartInfoToEnable(json.coverageProducts[0].cartInfoToEnable);
|
|
72
119
|
})
|
|
73
|
-
}, [cart]);
|
|
120
|
+
}, [cart, storeId]);
|
|
74
121
|
|
|
75
122
|
const contextVal: RedoContextValue = {
|
|
76
123
|
enabled: true,
|
|
@@ -78,6 +125,7 @@ const RedoProvider = ({
|
|
|
78
125
|
storeId,
|
|
79
126
|
cartInfoToEnable,
|
|
80
127
|
cart,
|
|
128
|
+
errors: (errors?.length && errors.length > 0) ? errors : undefined
|
|
81
129
|
};
|
|
82
130
|
|
|
83
131
|
return (
|
|
@@ -138,17 +186,34 @@ const useRedoCoverageClient = (): RedoCoverageClient => {
|
|
|
138
186
|
get loading() {
|
|
139
187
|
return redoContext.loading;
|
|
140
188
|
},
|
|
189
|
+
get eligible() {
|
|
190
|
+
return !this.loading && !!this.price && !!this.cartProduct;
|
|
191
|
+
},
|
|
141
192
|
get enabled() {
|
|
142
193
|
return redoContext.enabled;
|
|
143
194
|
},
|
|
144
195
|
get price() {
|
|
145
|
-
|
|
196
|
+
let priceToEnable = redoContext.cartInfoToEnable?.selectedVariant?.price?.amount;
|
|
197
|
+
if(!priceToEnable || Number(priceToEnable).toString() === 'NaN') {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return Number(priceToEnable);
|
|
202
|
+
},
|
|
203
|
+
get cart() {
|
|
204
|
+
return redoContext.cart;
|
|
146
205
|
},
|
|
147
206
|
get cartProduct() {
|
|
148
207
|
return redoContext.cartInfoToEnable?.selectedVariant;
|
|
149
208
|
},
|
|
150
209
|
get cartAttribute() {
|
|
151
210
|
return redoContext.cartInfoToEnable?.cartAttribute
|
|
211
|
+
},
|
|
212
|
+
get storeId() {
|
|
213
|
+
return redoContext.storeId;
|
|
214
|
+
},
|
|
215
|
+
get errors() {
|
|
216
|
+
return redoContext.errors;
|
|
152
217
|
}
|
|
153
218
|
}
|
|
154
219
|
};
|
package/src/types.ts
CHANGED
|
@@ -12,9 +12,13 @@ interface RedoCoverageClient {
|
|
|
12
12
|
disable(): Promise<boolean>;
|
|
13
13
|
get loading(): boolean;
|
|
14
14
|
get enabled(): boolean;
|
|
15
|
-
get
|
|
16
|
-
get
|
|
17
|
-
get
|
|
15
|
+
get eligible(): boolean;
|
|
16
|
+
get price(): number | undefined;
|
|
17
|
+
get storeId(): string | undefined;
|
|
18
|
+
get cart(): CartReturn | undefined;
|
|
19
|
+
get cartProduct(): CartProductVariantFragment | undefined;
|
|
20
|
+
get cartAttribute(): CartAttributeKey | undefined;
|
|
21
|
+
get errors(): RedoError[] | undefined;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
type CartInfoToEnable = {
|
|
@@ -29,13 +33,31 @@ type RedoContextValue = {
|
|
|
29
33
|
loading: boolean,
|
|
30
34
|
storeId?: string,
|
|
31
35
|
cartInfoToEnable?: CartInfoToEnable,
|
|
32
|
-
cart?: CartReturn
|
|
36
|
+
cart?: CartReturn,
|
|
37
|
+
errors?: RedoError[],
|
|
33
38
|
};
|
|
34
39
|
|
|
40
|
+
enum RedoErrorType {
|
|
41
|
+
ApiBadRequest = "API_BAD_REQUEST",
|
|
42
|
+
ApiServerError = "API_SERVER_ERROR",
|
|
43
|
+
ApiUnknownError = "API_UNKNOWN_ERROR"
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type RedoError = {
|
|
47
|
+
type: RedoErrorType,
|
|
48
|
+
message: string,
|
|
49
|
+
context: any
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
RedoErrorType,
|
|
54
|
+
}
|
|
55
|
+
|
|
35
56
|
export type {
|
|
36
57
|
CartAttributeKey,
|
|
37
58
|
CartInfoToEnable,
|
|
38
59
|
RedoContextValue,
|
|
39
60
|
RedoCoverageClient,
|
|
40
|
-
CartProductVariantFragment
|
|
61
|
+
CartProductVariantFragment,
|
|
62
|
+
RedoError
|
|
41
63
|
}
|
package/src/utils/cart.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { FetcherWithComponents, useFetcher } from "@remix-run/react";
|
|
2
2
|
import { CartInfoToEnable } from "../types";
|
|
3
3
|
import { CartForm, CartReturn } from "@shopify/hydrogen";
|
|
4
|
-
import { CartLine } from "@shopify/hydrogen-react/storefront-api-types";
|
|
5
4
|
import type { AppData } from '@remix-run/react/dist/data';
|
|
6
5
|
import React from 'react'
|
|
7
6
|
|