@redotech/redo-hydrogen 1.1.0 → 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/dist/types.d.ts CHANGED
@@ -10,11 +10,13 @@ interface RedoCoverageClient {
10
10
  disable(): Promise<boolean>;
11
11
  get loading(): boolean;
12
12
  get enabled(): boolean;
13
- get price(): number;
13
+ get eligible(): boolean;
14
+ get price(): number | undefined;
14
15
  get storeId(): string | undefined;
15
16
  get cart(): CartReturn | undefined;
16
17
  get cartProduct(): CartProductVariantFragment | undefined;
17
18
  get cartAttribute(): CartAttributeKey | undefined;
19
+ get errors(): RedoError[] | undefined;
18
20
  }
19
21
  type CartInfoToEnable = {
20
22
  productId: string;
@@ -28,6 +30,17 @@ type RedoContextValue = {
28
30
  storeId?: string;
29
31
  cartInfoToEnable?: CartInfoToEnable;
30
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;
31
44
  };
32
45
 
33
46
  declare const RedoProvider: ({ cart, storeId, children }: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redotech/redo-hydrogen",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Utilities to enable and disable Redo coverage on Hydrogen stores",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -7,6 +7,7 @@ import {
7
7
  import { useRedoCoverageClient } from "../providers/redo-coverage-client";
8
8
  import { CartInfoToEnable, RedoCoverageClient } from "../types";
9
9
  import { REDO_PUBLIC_API_HOSTNAME } from "../utils/security";
10
+ import { CurrencyCode } from "@shopify/hydrogen-react/storefront-api-types";
10
11
 
11
12
  type CheckoutButtonUIResponse = {
12
13
  html: string;
@@ -44,6 +45,10 @@ const getButtonsToShow = ({
44
45
  ui: json
45
46
  });
46
47
 
48
+ if(!ui) {
49
+ return reject(null);
50
+ }
51
+
47
52
  return resolve(ui);
48
53
  });
49
54
  });
@@ -58,10 +63,19 @@ const applyButtonVariables = ({
58
63
  cart: CartReturn,
59
64
  ui: CheckoutButtonUIResponse
60
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
+
61
75
  const cartContainsRedo = !!(cart.lines.nodes.some((cartItem) => cartItem.merchandise?.product?.vendor === 're:do'));
62
76
  const combinedPrice = new Intl.NumberFormat('en-US', {
63
77
  style: 'currency',
64
- currency: cart.cost.totalAmount.currencyCode
78
+ currency: currencyCode
65
79
  }).format(Number(cart.cost.totalAmount.amount) + (cartContainsRedo ? 0 : redoCoverageClient.price));
66
80
 
67
81
  if(!combinedPrice || !combinedPrice.length || combinedPrice.includes('NaN')) {
@@ -102,7 +116,7 @@ const RedoCheckoutButtons = (props: {
102
116
 
103
117
  useEffect(() => {
104
118
  (async () => {
105
- if(!redoCoverageClient.storeId || !cart) {
119
+ if(!redoCoverageClient.eligible || !cart || !redoCoverageClient.storeId) {
106
120
  return;
107
121
  }
108
122
 
@@ -111,7 +125,7 @@ const RedoCheckoutButtons = (props: {
111
125
  setCheckoutButtonsUI(buttons);
112
126
  }
113
127
  })();
114
- }, [cart, redoCoverageClient.price, redoCoverageClient.storeId]);
128
+ }, [cart, redoCoverageClient.eligible, redoCoverageClient.price, redoCoverageClient.storeId]);
115
129
 
116
130
  const wrapperClickHandler = async (e: MouseEvent) => {
117
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
 
@@ -25,9 +25,18 @@ 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(() => {
30
- if(!cart) {
39
+ if(!cart || !storeId) {
31
40
  return;
32
41
  }
33
42
 
@@ -68,6 +77,36 @@ const RedoProvider = ({
68
77
  })
69
78
  })
70
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
+
71
110
  let json = await res.json();
72
111
 
73
112
  setLoading(false);
@@ -78,7 +117,7 @@ const RedoProvider = ({
78
117
 
79
118
  setCartInfoToEnable(json.coverageProducts[0].cartInfoToEnable);
80
119
  })
81
- }, [cart]);
120
+ }, [cart, storeId]);
82
121
 
83
122
  const contextVal: RedoContextValue = {
84
123
  enabled: true,
@@ -86,6 +125,7 @@ const RedoProvider = ({
86
125
  storeId,
87
126
  cartInfoToEnable,
88
127
  cart,
128
+ errors: (errors?.length && errors.length > 0) ? errors : undefined
89
129
  };
90
130
 
91
131
  return (
@@ -146,11 +186,19 @@ const useRedoCoverageClient = (): RedoCoverageClient => {
146
186
  get loading() {
147
187
  return redoContext.loading;
148
188
  },
189
+ get eligible() {
190
+ return !this.loading && !!this.price && !!this.cartProduct;
191
+ },
149
192
  get enabled() {
150
193
  return redoContext.enabled;
151
194
  },
152
195
  get price() {
153
- return Number(redoContext.cartInfoToEnable?.selectedVariant.price.amount);
196
+ let priceToEnable = redoContext.cartInfoToEnable?.selectedVariant?.price?.amount;
197
+ if(!priceToEnable || Number(priceToEnable).toString() === 'NaN') {
198
+ return undefined;
199
+ }
200
+
201
+ return Number(priceToEnable);
154
202
  },
155
203
  get cart() {
156
204
  return redoContext.cart;
@@ -163,6 +211,9 @@ const useRedoCoverageClient = (): RedoCoverageClient => {
163
211
  },
164
212
  get storeId() {
165
213
  return redoContext.storeId;
214
+ },
215
+ get errors() {
216
+ return redoContext.errors;
166
217
  }
167
218
  }
168
219
  };
package/src/types.ts CHANGED
@@ -12,11 +12,13 @@ interface RedoCoverageClient {
12
12
  disable(): Promise<boolean>;
13
13
  get loading(): boolean;
14
14
  get enabled(): boolean;
15
- get price(): number;
15
+ get eligible(): boolean;
16
+ get price(): number | undefined;
16
17
  get storeId(): string | undefined;
17
18
  get cart(): CartReturn | undefined;
18
- get cartProduct(): CartProductVariantFragment | undefined
19
- get cartAttribute(): CartAttributeKey | undefined
19
+ get cartProduct(): CartProductVariantFragment | undefined;
20
+ get cartAttribute(): CartAttributeKey | undefined;
21
+ get errors(): RedoError[] | undefined;
20
22
  }
21
23
 
22
24
  type CartInfoToEnable = {
@@ -31,13 +33,31 @@ type RedoContextValue = {
31
33
  loading: boolean,
32
34
  storeId?: string,
33
35
  cartInfoToEnable?: CartInfoToEnable,
34
- cart?: CartReturn
36
+ cart?: CartReturn,
37
+ errors?: RedoError[],
35
38
  };
36
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
+
37
56
  export type {
38
57
  CartAttributeKey,
39
58
  CartInfoToEnable,
40
59
  RedoContextValue,
41
60
  RedoCoverageClient,
42
- CartProductVariantFragment
61
+ CartProductVariantFragment,
62
+ RedoError
43
63
  }