@ordergroove/offers 2.44.0 → 2.44.1-alpha-PR-1166-3.30

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.
Files changed (38) hide show
  1. package/dist/bundle-report.html +52 -49
  2. package/dist/offers.js +38 -38
  3. package/dist/offers.js.map +4 -4
  4. package/package.json +2 -2
  5. package/src/components/FrequencyStatus.js +13 -10
  6. package/src/components/Offer.js +33 -14
  7. package/src/components/OptinButton.js +2 -2
  8. package/src/components/OptinSelect.js +5 -5
  9. package/src/components/OptinStatus.js +15 -9
  10. package/src/components/Price.js +3 -3
  11. package/src/components/SelectFrequency.js +11 -6
  12. package/src/components/TestWizard.js +45 -41
  13. package/src/components/UpsellModal.js +9 -3
  14. package/src/components/__tests__/Offer.spec.js +0 -19
  15. package/src/components/__tests__/OptinStatus.spec.js +17 -4
  16. package/src/core/__tests__/actions.spec.js +47 -1
  17. package/src/core/__tests__/base.spec.js +0 -77
  18. package/src/core/__tests__/offerRequest.spec.js +2 -1
  19. package/src/core/__tests__/selectors.spec.js +7 -7
  20. package/src/core/actions-preview.js +6 -3
  21. package/src/core/actions.js +22 -13
  22. package/src/core/base.js +0 -23
  23. package/src/core/offerRequest.js +1 -1
  24. package/src/core/{reducer.js → reducer.ts} +30 -10
  25. package/src/core/{selectors.js → selectors.ts} +73 -57
  26. package/src/core/types/api.ts +71 -0
  27. package/src/core/types/reducer.ts +95 -0
  28. package/src/core/types/utility.ts +1 -0
  29. package/src/core/utils.ts +32 -15
  30. package/src/make-api.js +1 -1
  31. package/src/shopify/__tests__/reducers/config.spec.js +497 -0
  32. package/src/shopify/__tests__/shopifyReducer.spec.js +65 -610
  33. package/src/shopify/__tests__/utils.spec.js +24 -1
  34. package/src/shopify/reducers/config.ts +223 -0
  35. package/src/shopify/shopifyMiddleware.ts +2 -9
  36. package/src/shopify/{shopifyReducer.js → shopifyReducer.ts} +45 -177
  37. package/src/shopify/utils.ts +25 -0
  38. package/src/types.ts +0 -16
@@ -58,7 +58,8 @@ export const setPreviewStandardOffer = (isPreview, productId, offer) =>
58
58
  }
59
59
  }
60
60
  },
61
- offer
61
+ offer,
62
+ productId
62
63
  )
63
64
  );
64
65
  };
@@ -94,7 +95,8 @@ export const setPreviewUpsellOffer = (isPreview, productId, offer) =>
94
95
  autoship_by_default: { [productId]: false },
95
96
  modifiers: {}
96
97
  },
97
- offer
98
+ offer,
99
+ productId
98
100
  )
99
101
  );
100
102
  await dispatch(
@@ -194,7 +196,8 @@ export const setPreviewPrepaid = (isPreview, productId, offer) =>
194
196
  }
195
197
  }
196
198
  },
197
- offer
199
+ offer,
200
+ productId
198
201
  )
199
202
  );
200
203
  await dispatch({
@@ -1,7 +1,8 @@
1
1
  import { resolveAuth } from '@ordergroove/auth';
2
2
  import * as constants from './constants';
3
3
  import { api } from './api';
4
- import { safeOgFrequency, safeProductId } from './utils';
4
+ import { safeOgFrequency } from './utils';
5
+ import { makeFrequencyForPrepaidShipmentsSelector, makeProductFrequenciesSelector } from './selectors';
5
6
 
6
7
  export const optinProduct = (product, frequency, offer) => ({
7
8
  type: constants.OPTIN_PRODUCT,
@@ -23,10 +24,14 @@ export const productChangeFrequency = (product, frequency, offer) => ({
23
24
  payload: { product, frequency, offer }
24
25
  });
25
26
 
26
- export const productChangePrepaidShipments = (product, prepaidShipments, offer) => ({
27
- type: constants.PRODUCT_CHANGE_PREPAID_SHIPMENTS,
28
- payload: { product, prepaidShipments, offer }
29
- });
27
+ export const productChangePrepaidShipments = (product, prepaidShipments, offer) => (dispatch, getState) => {
28
+ // this is a thunk so that we access the state for the selector
29
+ const frequency = makeFrequencyForPrepaidShipmentsSelector(product, prepaidShipments)(getState());
30
+ dispatch({
31
+ type: constants.PRODUCT_CHANGE_PREPAID_SHIPMENTS,
32
+ payload: { product, prepaidShipments, offer, frequency }
33
+ });
34
+ };
30
35
 
31
36
  export const cartProductKeyHasChanged = (oldCartProductKey, newCartProductKey) => ({
32
37
  type: constants.CART_PRODUCT_KEY_HAS_CHANGED,
@@ -183,10 +188,13 @@ export const requestSessionId = () => (dispatch, getState) => {
183
188
  return sessionId;
184
189
  };
185
190
 
186
- export const receiveOffer = (response, offer) => ({
187
- type: constants.RECEIVE_OFFER,
188
- payload: { ...response, offer }
189
- });
191
+ export const receiveOffer = (response, offer, productId) => (dispatch, getState) => {
192
+ const frequencyConfig = makeProductFrequenciesSelector(productId)(getState());
193
+ dispatch({
194
+ type: constants.RECEIVE_OFFER,
195
+ payload: { ...response, offer, frequencyConfig }
196
+ });
197
+ };
190
198
 
191
199
  export const fetchResponseError = err => ({
192
200
  type: constants.FETCH_RESPONSE_ERROR,
@@ -226,18 +234,19 @@ export const receiveConvertOneTime = (response, product) => ({
226
234
 
227
235
  export const createIu = (product, order, quantity, subscribed = false, initialFrequency = null) =>
228
236
  function createIuThunk(dispatch, getState) {
237
+ const state = getState();
229
238
  const {
230
239
  auth,
231
- config,
232
240
  environment: { legoUrl },
233
241
  previewUpsellOffer,
234
242
  offerId: offer,
235
243
  sessionId
236
- } = getState();
244
+ } = state;
237
245
 
238
246
  if (!auth) return dispatch(unauthorized('No auth set.'));
239
247
 
240
- const frequency = safeOgFrequency(initialFrequency, config);
248
+ const { frequencies, frequenciesEveryPeriod } = makeProductFrequenciesSelector(product.id)(state);
249
+ const frequency = safeOgFrequency(initialFrequency, frequencies, frequenciesEveryPeriod);
241
250
 
242
251
  const requestAction = requestCreateOneTime(product, order, quantity, offer);
243
252
 
@@ -301,7 +310,7 @@ export const setProductToSubscribe = (product, productToSubscribe) => ({
301
310
  });
302
311
 
303
312
  /**
304
- * @param {import('../types').MerchantSettings} settings
313
+ * @param {import('../core/types/api').MerchantSettings} settings
305
314
  */
306
315
  export const receiveMerchantSettings = settings => ({
307
316
  type: constants.RECEIVE_MERCHANT_SETTINGS,
package/src/core/base.js CHANGED
@@ -1,30 +1,7 @@
1
1
  import { LitElement } from 'lit-element';
2
- import { kebabCase } from './selectors';
3
2
 
4
3
  export const withTemplate = Base =>
5
4
  class extends Base {
6
- /**
7
- *
8
- * @param {*} key html attribute key name
9
- * @param {*} optional configKey key name in configuration object
10
- */
11
- getOption(key, configKey = key) {
12
- const attrName = kebabCase(key);
13
- if (this.hasAttribute(attrName)) {
14
- const attr = this.getAttribute(attrName);
15
- if (attr.toString().toLowerCase() === 'true') return true;
16
- if (attr.toString().toLowerCase() === 'false') return false;
17
- return attr;
18
- }
19
-
20
- if (this.template && this.template.config && typeof this.template.config[configKey] !== 'undefined')
21
- return this.template.config[configKey];
22
-
23
- if (this.config && typeof this.config[configKey] !== 'undefined') return this.config[configKey];
24
-
25
- return undefined;
26
- }
27
-
28
5
  applyTemplate(template) {
29
6
  this.template = template;
30
7
  // update innerHTML if change (performance)
@@ -24,7 +24,7 @@ export function offerRequestMiddleware(store) {
24
24
  action.payload.searchParams
25
25
  )
26
26
  .then(
27
- response => store.dispatch(receiveOffer(response, action.payload.offer)),
27
+ response => store.dispatch(receiveOffer(response, action.payload.offer, productId)),
28
28
  err => store.dispatch(fetchResponseError(err))
29
29
  )
30
30
  .finally(() => store.dispatch(fetchDone(action)));
@@ -6,7 +6,21 @@ import { getObjectStructuredProductPlans } from './adapters';
6
6
  import { safeProductId, getMatchingProductIfExists } from './utils';
7
7
  import { experimentsReducer } from './experiments';
8
8
 
9
- export const optedin = (state = [], action) => {
9
+ import {
10
+ AutoshipByDefaultState,
11
+ AutoshipEligibleState,
12
+ ConfigState,
13
+ IncentiveObject,
14
+ IncentivesState,
15
+ NextUpcomingOrderState,
16
+ OptedInState,
17
+ OptedOutState,
18
+ PrepaidShipmentsSelectedState,
19
+ ReceiveOfferPayload
20
+ } from './types/reducer';
21
+ import { EmptyObject } from './types/utility';
22
+
23
+ export const optedin = (state: OptedInState = [], action): OptedInState => {
10
24
  switch (action.type) {
11
25
  case constants.LOCAL_STORAGE_CLEAR:
12
26
  return [];
@@ -49,7 +63,7 @@ export const optedin = (state = [], action) => {
49
63
  }
50
64
  };
51
65
 
52
- export const optedout = (state = [], action) => {
66
+ export const optedout = (state: OptedOutState = [], action): OptedOutState => {
53
67
  switch (action.type) {
54
68
  case constants.LOCAL_STORAGE_CLEAR:
55
69
  return [];
@@ -77,7 +91,7 @@ export const optedout = (state = [], action) => {
77
91
  }
78
92
  };
79
93
 
80
- export const nextUpcomingOrder = (state = {}, { type, payload }) => {
94
+ export const nextUpcomingOrder = (state: NextUpcomingOrderState = {}, { type, payload }): NextUpcomingOrderState => {
81
95
  switch (type) {
82
96
  case constants.RECEIVE_ORDERS:
83
97
  return payload && payload.count > 0
@@ -107,7 +121,7 @@ export const nextUpcomingOrder = (state = {}, { type, payload }) => {
107
121
  }
108
122
  };
109
123
 
110
- export const autoshipEligible = (state = {}, action) => {
124
+ export const autoshipEligible = (state: AutoshipEligibleState = {}, action): AutoshipEligibleState => {
111
125
  switch (action.type) {
112
126
  case constants.RECEIVE_OFFER:
113
127
  return {
@@ -153,7 +167,10 @@ const mapIncentive = (incentive, incentiveDisplay) => {
153
167
  }));
154
168
  };
155
169
 
156
- export const incentives = (state = {}, action) => {
170
+ export const incentives = (
171
+ state: IncentivesState = {},
172
+ action: { type: string; payload: ReceiveOfferPayload }
173
+ ): IncentivesState => {
157
174
  switch (action.type) {
158
175
  case constants.RECEIVE_OFFER:
159
176
  return {
@@ -164,7 +181,7 @@ export const incentives = (state = {}, action) => {
164
181
  [uniqueProductId]: Object.entries(action.payload.incentives)
165
182
  .filter(([productId]) => productId === uniqueProductId)
166
183
  .reduce(
167
- (incentiveObj, [, { initial, ongoing }]) => ({
184
+ (incentiveObj: IncentiveObject | EmptyObject, [, { initial, ongoing }]) => ({
168
185
  ...incentiveObj,
169
186
  initial: [
170
187
  ...(incentiveObj.initial || []),
@@ -392,11 +409,11 @@ export const locale = (
392
409
  };
393
410
 
394
411
  export const config = (
395
- state = {
412
+ state: ConfigState = {
396
413
  offerType: 'radio'
397
414
  },
398
415
  action
399
- ) => {
416
+ ): ConfigState => {
400
417
  switch (action.type) {
401
418
  case constants.SET_CONFIG:
402
419
  return {
@@ -447,7 +464,7 @@ export const previewPrepaidOffer = (state = false, action) => {
447
464
  }
448
465
  };
449
466
 
450
- export const autoshipByDefault = (state = [], action) => {
467
+ export const autoshipByDefault = (state: AutoshipByDefaultState = {}, action): AutoshipByDefaultState => {
451
468
  switch (action.type) {
452
469
  case constants.RECEIVE_OFFER:
453
470
  return {
@@ -492,7 +509,10 @@ export const productPlans = (state = {}, action) => {
492
509
  }
493
510
  };
494
511
 
495
- export const prepaidShipmentsSelected = (state = {}, action) => {
512
+ export const prepaidShipmentsSelected = (
513
+ state: PrepaidShipmentsSelectedState = {},
514
+ action
515
+ ): PrepaidShipmentsSelectedState => {
496
516
  switch (action.type) {
497
517
  // Given that, in the cart, products will have a composed id (<productId>:<cartId>) and that every time
498
518
  // a product changes in the cart we need to sync these changes back with the eComm platform, this operation
@@ -2,11 +2,17 @@ import { createSelector } from 'reselect';
2
2
  import memoize from 'lodash.memoize';
3
3
  import { stringifyFrequency } from './api';
4
4
  import platform from '../platform';
5
- import { mapFrequencyToSellingPlan, safeProductId } from '../core/utils';
5
+ import { mapFrequencyToSellingPlan, safeProductId } from './utils';
6
+ import { OfferElement, State } from './types/reducer';
6
7
 
7
8
  memoize.Cache = Map;
8
9
 
9
- function arraysEqual(a, b) {
10
+ type BaseProduct = {
11
+ id: string;
12
+ components?: string[];
13
+ };
14
+
15
+ function arraysEqual<T>(a: T[], b: T[]) {
10
16
  if (a === b) return true;
11
17
  if (a === null || b === null) return false;
12
18
  if (a.length !== b.length) return false;
@@ -22,14 +28,14 @@ function arraysEqual(a, b) {
22
28
  return true;
23
29
  }
24
30
 
25
- function resolveFrequency(sellingPlans, frequenciesEveryPeriod, frequency) {
31
+ function resolveFrequency(sellingPlans: string[], frequenciesEveryPeriod: string[], frequency) {
26
32
  const ogFrequency = stringifyFrequency(frequency);
27
33
  if (!platform.shopify_selling_plans) return ogFrequency;
28
34
  return mapFrequencyToSellingPlan(sellingPlans, frequenciesEveryPeriod, ogFrequency);
29
35
  }
30
36
 
31
- export const isSameProduct = (a, b) => {
32
- if (a === b) return true;
37
+ export const isSameProduct = <T extends BaseProduct, S extends BaseProduct>(a: T, b: S) => {
38
+ if ((a as BaseProduct) === b) return true;
33
39
  if (typeof a === 'object' && typeof b === 'object' && a && b) {
34
40
  if (a.id === b.id) {
35
41
  if (!(Array.isArray(a.components) && Array.isArray(b.components))) {
@@ -47,28 +53,23 @@ export const isSameProduct = (a, b) => {
47
53
  * Returns a list of opted in products id from the state
48
54
  * @param {object} state
49
55
  */
50
- export const optedinSelector = state => state.optedin || [];
56
+ export const optedinSelector = (state: State) => state.optedin || [];
51
57
 
52
- export const optedoutSelector = state => state.optedout || [];
58
+ const optedoutSelector = (state: State) => state.optedout || [];
53
59
 
54
- export const autoshipSelector = state => state.autoshipByDefault || {};
60
+ export const autoshipSelector = (state: State) => state.autoshipByDefault || {};
55
61
 
56
- export const defaultFrequenciesSelector = state => state.defaultFrequencies || {};
62
+ const defaultFrequenciesSelector = (state: State) => state.defaultFrequencies || {};
57
63
 
58
- export const sellingPlansSelector = state => state?.config?.frequencies || [];
59
-
60
- export const frequenciesEveryPeriodSelector = state => state?.config?.frequenciesEveryPeriod || [];
61
-
62
- export const prepaidSellingPlansSelector = state => state?.config?.prepaidSellingPlans || [];
63
- export const prepaidShipmentsSelectedSelector = state => state?.prepaidShipmentsSelected || {};
64
+ const prepaidSellingPlansSelector = (state: State) => state?.config?.prepaidSellingPlans || [];
65
+ const prepaidShipmentsSelectedSelector = (state: State) => state?.prepaidShipmentsSelected || {};
64
66
 
65
67
  /**
66
68
  * Creates a function with state arguments that return the true when
67
69
  * productId is in the optedin array or not in optedout or autoship by default
68
- * @param {String} productId
69
70
  */
70
71
  export const makeOptedinSelector = memoize(
71
- product =>
72
+ (product: BaseProduct) =>
72
73
  createSelector(optedinSelector, optedoutSelector, autoshipSelector, (optedin, optedout, autoshipByDefault) => {
73
74
  const entry = optedin.find(b => isSameProduct(product, b));
74
75
  if (entry) {
@@ -87,10 +88,9 @@ export const makeOptedinSelector = memoize(
87
88
  /**
88
89
  * Creates a function with state arguments that return the true when
89
90
  * productId is in the optedin array
90
- * @param {String} productId
91
91
  */
92
92
  export const makeSubscribedSelector = memoize(
93
- product =>
93
+ (product: BaseProduct) =>
94
94
  createSelector(optedinSelector, optedin => {
95
95
  const entry = optedin.find(b => isSameProduct(product, b));
96
96
  if (entry) {
@@ -102,13 +102,13 @@ export const makeSubscribedSelector = memoize(
102
102
  );
103
103
 
104
104
  export const makePrepaidSubscribedSelector = memoize(
105
- product =>
105
+ (product: BaseProduct) =>
106
106
  createSelector(optedinSelector, optedin => optedin.some(b => isSameProduct(product, b) && b.prepaidShipments)),
107
107
  product => JSON.stringify(product)
108
108
  );
109
109
 
110
110
  export const makePrepaidShipmentsSelectedSelector = memoize(
111
- product =>
111
+ (product: BaseProduct) =>
112
112
  createSelector(
113
113
  prepaidShipmentsSelectedSelector,
114
114
  prepaidShipmentsSelected => prepaidShipmentsSelected[product.id] || null
@@ -119,23 +119,28 @@ export const makePrepaidShipmentsSelectedSelector = memoize(
119
119
  /**
120
120
  * Creates a function with state arguments that return the true when
121
121
  * productId is in the optedout array
122
- * @param {String} productId
123
122
  */
124
- export const makeOptedoutSelector = memoize(productId =>
125
- createSelector(optedoutSelector, optedout => optedout.find(b => isSameProduct(productId, b)))
123
+ export const makeOptedoutSelector = memoize((product: BaseProduct) =>
124
+ createSelector(optedoutSelector, optedout => optedout.find(b => isSameProduct(product, b)))
126
125
  );
127
126
 
128
- export const frequencySelector = state => state.frequency;
127
+ export const frequencySelector = (state: State) => state.frequency;
129
128
 
130
- export const makeProductFrequencySelector = memoize(productId =>
131
- createSelector(makeOptedinSelector(productId), productOptin => (productOptin && productOptin.frequency) || null)
129
+ export const makeProductFrequencyOptedInSelector = memoize((product: BaseProduct) =>
130
+ createSelector(
131
+ makeOptedinSelector(product),
132
+ productOptin => (productOptin && 'frequency' in productOptin && productOptin.frequency) || null
133
+ )
132
134
  );
133
135
 
134
- export const makeProductPrepaidShipmentsOptedInSelector = memoize(product =>
135
- createSelector(makeOptedinSelector(product), productOptin => (productOptin && productOptin.prepaidShipments) || null)
136
+ export const makeProductPrepaidShipmentsOptedInSelector = memoize((product: BaseProduct) =>
137
+ createSelector(
138
+ makeOptedinSelector(product),
139
+ productOptin => (productOptin && 'prepaidShipments' in productOptin && productOptin.prepaidShipments) || null
140
+ )
136
141
  );
137
142
 
138
- export const makeProductPrepaidShipmentOptionsSelector = memoize(productId =>
143
+ export const makeProductPrepaidShipmentOptionsSelector = memoize((productId: string) =>
139
144
  createSelector(prepaidSellingPlansSelector, prepaidSellingPlans => {
140
145
  const shipmentsList =
141
146
  prepaidSellingPlans[safeProductId(productId)]?.map(({ numberShipments }) => numberShipments) || [];
@@ -144,49 +149,60 @@ export const makeProductPrepaidShipmentOptionsSelector = memoize(productId =>
144
149
  );
145
150
 
146
151
  /**
147
- * Creates a function with state arguments that returns stringified frequency
148
- * if default frequency exists for the product
149
- * @param {String} productId
152
+ * If the product has a product-specific default frequency, return that frequency
150
153
  */
151
- export const makeProductDefaultFrequencySelector = memoize(productId =>
154
+ export const makeProductSpecificDefaultFrequencySelector = memoize((productId: string) =>
152
155
  createSelector(
153
156
  defaultFrequenciesSelector,
154
- sellingPlansSelector,
155
- frequenciesEveryPeriodSelector,
156
- (defaultFrequencies, sellingPlans, frequenciesEveryPeriod) =>
157
+ makeProductFrequenciesSelector(productId),
158
+ (defaultFrequencies, { frequencies: sellingPlans = [], frequenciesEveryPeriod = [] }) =>
157
159
  (defaultFrequencies[safeProductId(productId)] &&
158
160
  resolveFrequency(sellingPlans, frequenciesEveryPeriod, defaultFrequencies[safeProductId(productId)])) ||
159
161
  null
160
162
  )
161
163
  );
162
164
 
165
+ export const makeProductFrequencyOptionsSelector = (productId: string) =>
166
+ createSelector(makeProductFrequenciesSelector(productId), productFrequencies => productFrequencies.frequencies);
167
+
168
+ export const makeProductDefaultFrequencySelector = (productId: string) =>
169
+ createSelector(makeProductFrequenciesSelector(productId), productFrequencies => productFrequencies.defaultFrequency);
170
+
171
+ export const makeProductFrequenciesSelector = (productId: string) =>
172
+ createSelector(
173
+ (state: State) => state?.config?.productFrequencies || {},
174
+ productFrequencies => {
175
+ return productFrequencies[safeProductId(productId)] || {};
176
+ }
177
+ );
178
+
179
+ export const makeFrequencyForPrepaidShipmentsSelector = (product: BaseProduct, prepaidShipments: number) =>
180
+ createSelector(
181
+ prepaidSellingPlansSelector,
182
+ makeProductFrequenciesSelector(product.id),
183
+ (prepaidSellingPlans, { frequencies }) => {
184
+ if (prepaidShipments) {
185
+ const productId = safeProductId(product.id);
186
+ const plan = prepaidSellingPlans[productId]?.find(p => p.numberShipments === prepaidShipments);
187
+ return plan ? plan.sellingPlan : null;
188
+ }
189
+ return frequencies[0];
190
+ }
191
+ );
192
+
163
193
  /**
164
194
  * Convert a string from camel case to kebab case.
165
- * @param {String} string
166
- * @returns {String}
167
195
  */
168
- export const kebabCase = string => {
196
+ export const kebabCase = (string: string) => {
169
197
  return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
170
198
  };
171
199
 
172
- /**
173
- * Resolve element configuration by looking into html attribute or state config
174
- *
175
- * @param {Object} state
176
- * @param {HTMLElement} element
177
- * @param {String} key
178
- * @returns {Object}
179
- */
180
- export const configSelector = (state, element, key, defaultValue) => ({
181
- [key]:
182
- (state.config && state.config[key]) ||
183
- (element && element.hasAttribute && element.hasAttribute(kebabCase(key)) && element[key]) ||
184
- (element.offer && typeof (element.offer[key] !== 'undefined') && element.offer[key]) ||
185
- defaultValue
186
- });
200
+ export const getFallbackValue = (element: HTMLElement & { offer: OfferElement }, key: string, defaultValue?) =>
201
+ (element && element.hasAttribute && element.hasAttribute(kebabCase(key)) && element[key]) ||
202
+ (element.offer && typeof (element.offer[key] !== 'undefined') && element.offer[key]) ||
203
+ defaultValue;
187
204
 
188
205
  /**
189
206
  * Returns a list of opted in products id from the state
190
- * @param {object} state
191
207
  */
192
- export const templatesSelector = state => ({ templates: state.templates || [] });
208
+ export const templatesSelector = (state: State) => ({ templates: state.templates || [] });
@@ -0,0 +1,71 @@
1
+ export type Order = {
2
+ merchant: string;
3
+ customer: string;
4
+ payment: string;
5
+ shipping_address: string;
6
+ public_id: string;
7
+ sub_total: string;
8
+ tax_total: string;
9
+ shipping_total: string;
10
+ discount_total: string;
11
+ total: string;
12
+ created: string;
13
+ updated: string;
14
+ place: string;
15
+ cancelled: string | null;
16
+ tries: number;
17
+ generic_error_count: number;
18
+ status: number;
19
+ type: number;
20
+ order_merchant_id: string | null;
21
+ rejected_message: string | null;
22
+ extra_data: unknown;
23
+ locked: boolean;
24
+ oos_free_shipping: boolean;
25
+ currency_code: string;
26
+ };
27
+
28
+ export type Incentive = {
29
+ object: string;
30
+ field: string;
31
+ type: string;
32
+ value: number;
33
+ };
34
+
35
+ type ExperimentVariant = {
36
+ public_id: string;
37
+ parameters: any;
38
+ weight: number;
39
+ };
40
+
41
+ type ExperimentConfig = {
42
+ public_id: string;
43
+ variants: ExperimentVariant[];
44
+ };
45
+
46
+ export interface MerchantSettings {
47
+ currency_code: string;
48
+ multicurrency_enabled: boolean;
49
+ experiments?: ExperimentConfig;
50
+ }
51
+
52
+ export type OfferResponse = {
53
+ result: string;
54
+ module_view: {
55
+ regular: string;
56
+ };
57
+ modifiers: Record<string, { coupon_code: string }>;
58
+ autoship: Record<string, boolean>;
59
+ in_stock: Record<string, boolean>;
60
+ autoship_by_default: Record<string, boolean>;
61
+ default_frequencies: Record<string, { every: number; every_period: number }>;
62
+ eligibility_groups: Record<string, string[]>;
63
+ incentives: Record<
64
+ string,
65
+ {
66
+ ongoing: string[];
67
+ initial: string[];
68
+ }
69
+ >;
70
+ incentives_display: Record<string, Incentive>;
71
+ };
@@ -0,0 +1,95 @@
1
+ import { Order, Incentive as ApiIncentive, MerchantSettings, OfferResponse } from './api';
2
+ import { type Offer } from '../../components/Offer';
3
+ import { ShopifyCart, ShopifyProductEntity } from '../../shopify/types/shopify';
4
+ import reducer from '../reducer';
5
+
6
+ export type State = ReturnType<typeof reducer>;
7
+
8
+ // state types
9
+
10
+ export type NextUpcomingOrderState = Partial<
11
+ Order & {
12
+ place: Date;
13
+ products: string[];
14
+ }
15
+ >;
16
+
17
+ export type IncentivesState = Record<string, IncentiveObject>;
18
+
19
+ type Incentive = ApiIncentive & {
20
+ id: string;
21
+ };
22
+
23
+ export type IncentiveObject = {
24
+ initial: Incentive[];
25
+ ongoing: Incentive[];
26
+ };
27
+
28
+ export type ConfigState = Partial<{
29
+ /**
30
+ * @deprecated use productFrequencies instead
31
+ */
32
+ frequencies: string[];
33
+ offerType: string;
34
+ /**
35
+ * @deprecated use productFrequencies instead
36
+ */
37
+ frequenciesEveryPeriod: string[];
38
+ merchantSettings: MerchantSettings;
39
+ /**
40
+ * @deprecated use productFrequencies instead
41
+ */
42
+ defaultFrequency: string;
43
+ /**
44
+ * @deprecated use productFrequencies instead
45
+ */
46
+ frequenciesText: string[];
47
+ hasProductSpecificFrequencies: boolean;
48
+ prepaidSellingPlans: Record<string, { numberShipments: number; sellingPlan: string }[]>;
49
+ storeCurrency: string;
50
+ productFrequencies: Record<string, ProductFrequencyConfig>;
51
+ }>;
52
+
53
+ type ProductFrequencyConfig = {
54
+ frequencies?: string[];
55
+ frequenciesEveryPeriod?: string[];
56
+ frequenciesText?: string[];
57
+ defaultFrequency?: string;
58
+ };
59
+
60
+ export type AutoshipEligibleState = Record<string, boolean>;
61
+
62
+ export type PrepaidShipmentsSelectedState = Record<string, number>;
63
+
64
+ export type OptInItem = { id: string; frequency: string; prepaidShipments?: number; components?: string[] };
65
+
66
+ export type OptedInState = OptInItem[];
67
+
68
+ export type OptedOutState = { id: string }[];
69
+
70
+ export type AutoshipByDefaultState = Record<string, boolean>;
71
+
72
+ // payload types
73
+
74
+ export type ReceiveOfferPayload = OfferResponse & {
75
+ offer: OfferElement;
76
+ frequencyConfig: {
77
+ frequencies?: string[];
78
+ frequenciesEveryPeriod?: string[];
79
+ frequenciesText?: string[];
80
+ };
81
+ };
82
+
83
+ export type OfferElement = InstanceType<typeof Offer> & { config: ConfigState };
84
+
85
+ export type ReceiveProductPlansPayload = Record<string, string[]>;
86
+
87
+ export type SetupProductPayload = {
88
+ product: ShopifyProductEntity;
89
+ offer: OfferElement;
90
+ currency: string;
91
+ };
92
+
93
+ export type SetupCartPayload = ShopifyCart;
94
+
95
+ export type ReceiveMerchantSettingsPayload = MerchantSettings;
@@ -0,0 +1 @@
1
+ export type EmptyObject = Record<string, never>;