@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
@@ -1,4 +1,4 @@
1
- import { getPayAsYouGoSellingPlanGroups, money, percentage } from '../utils';
1
+ import { getPayAsYouGoSellingPlanGroups, money, percentage, textToFreq } from '../utils';
2
2
 
3
3
  describe('Shopify Utils', () => {
4
4
  describe('Money', () => {
@@ -26,6 +26,29 @@ describe('Shopify Utils', () => {
26
26
  expect(formattedPrice).toBe('10%');
27
27
  });
28
28
  });
29
+
30
+ describe('textToFreq', () => {
31
+ it('textToFreq should return freq', () => {
32
+ expect(textToFreq('DAY')).toEqual('1_1');
33
+ expect(textToFreq('DAYS')).toEqual('1_1');
34
+ expect(textToFreq('DAYLY')).toEqual('1_1');
35
+ expect(textToFreq('1 DAY')).toEqual('1_1');
36
+ expect(textToFreq('2 DAYS')).toEqual('2_1');
37
+ expect(textToFreq('2 day(s)')).toEqual('2_1');
38
+
39
+ expect(textToFreq('week')).toEqual('1_2');
40
+ expect(textToFreq('weekly')).toEqual('1_2');
41
+ expect(textToFreq('1 week')).toEqual('1_2');
42
+ expect(textToFreq('2 weeks')).toEqual('2_2');
43
+ expect(textToFreq('2 week(s)')).toEqual('2_2');
44
+
45
+ expect(textToFreq('MONTH')).toEqual('1_3');
46
+ expect(textToFreq('MONTHLY')).toEqual('1_3');
47
+ expect(textToFreq('1 month')).toEqual('1_3');
48
+ expect(textToFreq('2 months')).toEqual('2_3');
49
+ expect(textToFreq('2 month(s)')).toEqual('2_3');
50
+ });
51
+ });
29
52
  });
30
53
 
31
54
  describe('selling plan queries', () => {
@@ -0,0 +1,223 @@
1
+ import * as constants from '../../core/constants';
2
+ import { getFirstSellingPlan, isOgFrequency, mapFrequencyToSellingPlan, safeProductId } from '../../core/utils';
3
+ import type {
4
+ ConfigState,
5
+ ReceiveMerchantSettingsPayload,
6
+ ReceiveOfferPayload,
7
+ ReceiveProductPlansPayload,
8
+ SetupProductPayload
9
+ } from '../../core/types/reducer';
10
+
11
+ import {
12
+ getPayAsYouGoSellingPlanGroup,
13
+ isProductSpecificFrequencySellingPlanGroup,
14
+ sellingPlansToEveryPeriod,
15
+ sellingPlansToFrequencies,
16
+ getPrepaidShipments
17
+ } from '../utils';
18
+ import { ShopifySellingPlanGroupsEntity, ShopifyVariantsEntity } from '../types/shopify';
19
+
20
+ const config = (
21
+ state: ConfigState = {
22
+ frequencies: [],
23
+ offerType: 'radio',
24
+ frequenciesEveryPeriod: [],
25
+ productFrequencies: {}
26
+ },
27
+ action
28
+ ): ConfigState => {
29
+ if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
30
+ const frequencies = [
31
+ ...new Set(
32
+ Object.values(action.payload as ReceiveProductPlansPayload)
33
+ .map(Object.keys)
34
+ .flat()
35
+ )
36
+ ];
37
+ return {
38
+ ...state,
39
+ frequencies
40
+ };
41
+ }
42
+
43
+ if (constants.SETUP_PRODUCT === action.type) {
44
+ const {
45
+ payload: { product, currency }
46
+ } = action as { payload: SetupProductPayload };
47
+ let configToAdd: ConfigState = {};
48
+
49
+ // get the frequency values for the old config structure
50
+ // leaving for now for backwards compatibility, but they should eventually be removed in favor of productFrequencies
51
+ let globalFrequencies = getFrequencies(product.selling_plan_groups, state);
52
+ if (globalFrequencies?.frequencies?.length) {
53
+ configToAdd = {
54
+ ...globalFrequencies,
55
+ hasProductSpecificFrequencies:
56
+ state.hasProductSpecificFrequencies ||
57
+ isProductSpecificFrequencySellingPlanGroup(getPayAsYouGoSellingPlanGroup(product.selling_plan_groups))
58
+ };
59
+ }
60
+
61
+ let productFrequencies = product.variants?.reduce(
62
+ (acc, variant) => reduceSellingPlansToFrequencies(acc, variant, product.selling_plan_groups, state),
63
+ {}
64
+ );
65
+
66
+ configToAdd = {
67
+ ...configToAdd,
68
+
69
+ productFrequencies: {
70
+ ...state.productFrequencies,
71
+ ...productFrequencies
72
+ }
73
+ };
74
+
75
+ // prepaid selling plans
76
+ const prepaidSellingPlanGroups = product?.selling_plan_groups.filter(group => /^Prepaid-.*/.test(group.name));
77
+ if (prepaidSellingPlanGroups.length) {
78
+ configToAdd = {
79
+ ...configToAdd,
80
+ prepaidSellingPlans: { ...state.prepaidSellingPlans, ...getPrepaidSellingPlans(prepaidSellingPlanGroups) }
81
+ };
82
+ }
83
+ return {
84
+ ...state,
85
+ ...configToAdd,
86
+ storeCurrency: currency
87
+ };
88
+ }
89
+
90
+ if (constants.RECEIVE_OFFER === action.type) {
91
+ const {
92
+ payload: { offer: offerEl }
93
+ } = action as { payload: ReceiveOfferPayload };
94
+ const { defaultFrequency, product } = offerEl || {};
95
+ const { frequencies: sellingPlans, frequenciesEveryPeriod, prepaidSellingPlans = {} } = state;
96
+
97
+ // productFrequencies does not have entries for the cart ID
98
+ // eligible frequencies apply to cart entries for the product
99
+ const productId = safeProductId(product?.id);
100
+ const currentProductFrequencies = state.productFrequencies[productId];
101
+
102
+ return {
103
+ ...state,
104
+ // populate this field for backwards compatibility
105
+ // this should eventually be removed in favor of productFrequencies
106
+ defaultFrequency: getUpdatedDefaultFrequency(
107
+ productId,
108
+ defaultFrequency,
109
+ prepaidSellingPlans,
110
+ sellingPlans,
111
+ frequenciesEveryPeriod
112
+ ),
113
+ productFrequencies: {
114
+ ...state.productFrequencies,
115
+ [productId]: {
116
+ ...currentProductFrequencies,
117
+ defaultFrequency: getUpdatedDefaultFrequency(
118
+ productId,
119
+ defaultFrequency,
120
+ prepaidSellingPlans,
121
+ currentProductFrequencies?.frequencies,
122
+ currentProductFrequencies?.frequenciesEveryPeriod
123
+ )
124
+ }
125
+ }
126
+ };
127
+ }
128
+
129
+ if (constants.RECEIVE_MERCHANT_SETTINGS === action.type) {
130
+ return {
131
+ ...state,
132
+ merchantSettings: {
133
+ ...(action.payload as ReceiveMerchantSettingsPayload)
134
+ }
135
+ };
136
+ }
137
+
138
+ return state;
139
+ };
140
+
141
+ function getFrequencies(
142
+ productSellingPlanGroups: ShopifySellingPlanGroupsEntity[],
143
+ state: { defaultFrequency?: string }
144
+ ) {
145
+ const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(productSellingPlanGroups);
146
+ const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
147
+ if (frequencies?.length) {
148
+ const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
149
+ const frequenciesText = sellingPlanGroup.options?.[0]?.values || frequencies;
150
+ let defaultFrequency = state?.defaultFrequency;
151
+
152
+ if (defaultFrequency && isOgFrequency(defaultFrequency)) {
153
+ defaultFrequency =
154
+ mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, defaultFrequency) ||
155
+ getFirstSellingPlan(frequencies) ||
156
+ defaultFrequency;
157
+ }
158
+ return {
159
+ frequencies,
160
+ frequenciesEveryPeriod,
161
+ frequenciesText,
162
+ ...(defaultFrequency ? { defaultFrequency } : {})
163
+ };
164
+ }
165
+ return null;
166
+ }
167
+
168
+ function reduceSellingPlansToFrequencies(
169
+ acc: ConfigState['productFrequencies'],
170
+ variant: ShopifyVariantsEntity,
171
+ sellingPlanGroups: ShopifySellingPlanGroupsEntity[],
172
+ state: ConfigState
173
+ ) {
174
+ const sellingPlanGroupIdsForProduct = variant.selling_plan_allocations.map(all => all.selling_plan_group_id);
175
+ const applicableSellingPlanGroups = sellingPlanGroups.filter(group =>
176
+ sellingPlanGroupIdsForProduct.includes(group.id)
177
+ );
178
+ const frequencies = getFrequencies(applicableSellingPlanGroups, state.productFrequencies[variant.id]);
179
+ if (frequencies) {
180
+ acc[variant.id] = frequencies;
181
+ }
182
+ return acc;
183
+ }
184
+
185
+ function getUpdatedDefaultFrequency(
186
+ productId: string,
187
+ offerElementDefaultFrequency: string,
188
+ prepaidSellingPlans: ConfigState['prepaidSellingPlans'],
189
+ frequencies: string[] | undefined = [],
190
+ frequenciesEveryPeriod: string[] | undefined = []
191
+ ) {
192
+ // We don't want to be setting the default frequency to a prepaid selling plan
193
+ if (prepaidSellingPlans[productId]?.some(({ sellingPlan }) => sellingPlan === offerElementDefaultFrequency)) {
194
+ return getFirstSellingPlan(frequencies) || offerElementDefaultFrequency;
195
+ }
196
+
197
+ if (!isOgFrequency(offerElementDefaultFrequency)) {
198
+ return offerElementDefaultFrequency;
199
+ }
200
+
201
+ return (
202
+ mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, offerElementDefaultFrequency) ||
203
+ getFirstSellingPlan(frequencies) ||
204
+ offerElementDefaultFrequency
205
+ );
206
+ }
207
+
208
+ function getPrepaidSellingPlans(prepaidSellingPlanGroups) {
209
+ return prepaidSellingPlanGroups.reduce((acc, cur) => {
210
+ const variant = cur.name.split('-')[1];
211
+
212
+ const sellingPlanInfo = cur.selling_plans.map(sellingPlanObject => {
213
+ return {
214
+ numberShipments: getPrepaidShipments(sellingPlanObject),
215
+ sellingPlan: String(sellingPlanObject.id)
216
+ };
217
+ });
218
+
219
+ return { ...acc, [variant]: sellingPlanInfo };
220
+ }, {});
221
+ }
222
+
223
+ export default config;
@@ -18,6 +18,7 @@ import { makeSubscribedSelector } from '../core/selectors';
18
18
  import { getOrCreateHidden, safeProductId } from '../core/utils';
19
19
  import { getTrackingKey } from './shopifyTrackingMiddleware';
20
20
  import { ShopifyCart, ShopifyProductEntity } from './types/shopify';
21
+ import { SetupProductPayload, SetupCartPayload, OfferElement } from '../core/types/reducer';
21
22
 
22
23
  const SHOPIFY_ROOT = window.Shopify?.routes?.root || '/';
23
24
  const CART_PAGE_URL = '/cart';
@@ -25,14 +26,6 @@ const CART_JS_URL = `${SHOPIFY_ROOT}cart.js`;
25
26
  const CART_CHANGE_URL = `${SHOPIFY_ROOT}cart/change.js`;
26
27
  const PRODUCTS_URL = `${SHOPIFY_ROOT}products/`;
27
28
 
28
- type SetupProductPayload = {
29
- product: ShopifyProductEntity;
30
- offer: any;
31
- currency: string;
32
- };
33
-
34
- type SetupCartPayload = ShopifyCart;
35
-
36
29
  /**
37
30
  * List of section DOM elements to update via section-rendering api https://shopify.dev/api/section-rendering
38
31
  */
@@ -344,7 +337,7 @@ export function getSubscribedFrequency(productId, store) {
344
337
  *
345
338
  * @param store
346
339
  */
347
- function synchronizeSellingPlan(store: any, offerElement?: HTMLElement) {
340
+ function synchronizeSellingPlan(store: any, offerElement?: OfferElement) {
348
341
  if (offerElement?.isCart) return; // hidden inputs are used when product page, not cart.
349
342
  if (!offerElement?.shouldEnableOffer) return; // do not set a selling plan if we're hiding the offer
350
343
 
@@ -31,16 +31,28 @@ import {
31
31
  safeProductId,
32
32
  getMatchingProductIfExists
33
33
  } from '../core/utils';
34
+ import type {
35
+ AutoshipEligibleState,
36
+ OfferElement,
37
+ OptedInState,
38
+ OptInItem,
39
+ ReceiveOfferPayload,
40
+ ReceiveProductPlansPayload
41
+ } from '../core/types/reducer';
34
42
 
35
43
  import { getObjectStructuredProductPlans } from '../core/adapters';
36
44
 
37
45
  import { sellingPlanAllocationsReducer, getSellingPlans } from './reducers/productPlans';
46
+ import config from './reducers/config';
38
47
  import { experimentsReducer } from '../core/experiments';
39
48
  import {
40
49
  getPayAsYouGoSellingPlanGroup,
41
50
  getPayAsYouGoSellingPlanGroups,
42
- isProductSpecificFrequencySellingPlanGroup
51
+ sellingPlansToEveryPeriod,
52
+ sellingPlansToFrequencies,
53
+ getPrepaidShipments
43
54
  } from './utils';
55
+ import { EmptyObject } from '../core/types/utility';
44
56
 
45
57
  const overrideLineKey = (state, productId, newValue) => {
46
58
  const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
@@ -50,7 +62,11 @@ const overrideLineKey = (state, productId, newValue) => {
50
62
  return state;
51
63
  };
52
64
 
53
- export const getDefaultSellingPlan = (sellingPlans, frequenciesEveryPeriod, defaultFrequency) => {
65
+ export const getDefaultSellingPlan = (
66
+ sellingPlans: string[],
67
+ frequenciesEveryPeriod: string[],
68
+ defaultFrequency: string | undefined
69
+ ) => {
54
70
  if (!defaultFrequency) {
55
71
  return null;
56
72
  }
@@ -72,23 +88,27 @@ export const getDefaultSellingPlan = (sellingPlans, frequenciesEveryPeriod, defa
72
88
  return defaultFrequency;
73
89
  };
74
90
 
75
- export const mapExistingOptinsFromOfferResponse = (state, offerEl) =>
91
+ export const mapExistingOptinsFromOfferResponse = (
92
+ state: OptedInState,
93
+ offerEl: OfferElement | EmptyObject,
94
+ frequencyConfig: ReceiveOfferPayload['frequencyConfig']
95
+ ) =>
76
96
  state.map(it => {
77
97
  if (isOgFrequency(it?.frequency)) {
78
98
  return {
79
99
  ...it,
80
- frequency: hasShopifySellingPlans(offerEl?.config?.frequencies, offerEl?.config?.frequenciesEveryPeriod)
100
+ frequency: hasShopifySellingPlans(frequencyConfig?.frequencies, frequencyConfig?.frequenciesEveryPeriod)
81
101
  ? mapFrequencyToSellingPlan(
82
- offerEl?.config?.frequencies,
83
- offerEl?.config?.frequenciesEveryPeriod,
102
+ frequencyConfig?.frequencies,
103
+ frequencyConfig?.frequenciesEveryPeriod,
84
104
  it.frequency
85
105
  ) ||
86
106
  mapFrequencyToSellingPlan(
87
- offerEl?.config?.frequencies,
88
- offerEl?.config?.frequenciesEveryPeriod,
107
+ frequencyConfig?.frequencies,
108
+ frequencyConfig?.frequenciesEveryPeriod,
89
109
  offerEl?.defaultFrequency
90
110
  ) ||
91
- getFirstSellingPlan(offerEl?.config?.frequencies)
111
+ getFirstSellingPlan(frequencyConfig?.frequencies)
92
112
  : it.frequency
93
113
  };
94
114
  }
@@ -97,14 +117,16 @@ export const mapExistingOptinsFromOfferResponse = (state, offerEl) =>
97
117
  });
98
118
 
99
119
  export const reduceNewOptinsFromOfferResponse = (
100
- { autoship = {}, autoship_by_default = {}, default_frequencies = {}, in_stock = {} },
101
- existingOptins,
102
- offerEl
120
+ { autoship = {}, autoship_by_default = {}, default_frequencies = {}, in_stock = {} }: ReceiveOfferPayload,
121
+ existingOptins: OptedInState,
122
+ offerEl: OfferElement | EmptyObject,
123
+ frequencyConfig: ReceiveOfferPayload['frequencyConfig']
103
124
  ) =>
104
125
  Object.keys(autoship).reduce((acc, id) => {
105
126
  if (!existingOptins.some(it => it.id === id)) {
106
127
  if (!(autoship[id] && autoship_by_default[id] && in_stock[id])) return acc;
107
- const { config: { frequencies: sellingPlans, frequenciesEveryPeriod } = {}, defaultFrequency } = offerEl || {};
128
+ const { frequencies: sellingPlans, frequenciesEveryPeriod } = frequencyConfig;
129
+ const { defaultFrequency } = offerEl || {};
108
130
  const psdf = default_frequencies[id];
109
131
  let frequency;
110
132
 
@@ -133,16 +155,16 @@ const productOrVariantInStockReducer = (acc, cur) => ({
133
155
  [cur.id]: cur.available
134
156
  });
135
157
 
136
- const productTrue = (acc, [id]) => ({ ...acc, [id]: true });
158
+ const productTrue = (acc: Record<string, boolean>, [id]: [string, string[]]) => ({ ...acc, [id]: true });
137
159
 
138
160
  const reduceProductCartLine = (acc, cur) => {
139
161
  const productId = safeProductId(cur.key);
140
162
  return { ...acc, [cur.key]: acc[productId] || null };
141
163
  };
142
164
 
143
- export const autoshipEligible = (state = {}, action) => {
165
+ export const autoshipEligible = (state: AutoshipEligibleState = {}, action): AutoshipEligibleState => {
144
166
  if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
145
- return Object.entries(action.payload).reduce(productTrue, state);
167
+ return Object.entries(action.payload as ReceiveProductPlansPayload).reduce(productTrue, state);
146
168
  }
147
169
  if (constants.SETUP_CART === action.type) {
148
170
  const { payload: cart } = action;
@@ -178,152 +200,6 @@ export const autoshipEligible = (state = {}, action) => {
178
200
  return state;
179
201
  };
180
202
 
181
- export function textToFreq(text) {
182
- const period = ['day', 'week', 'month'].findIndex(it => text.toLowerCase().includes(it)) + 1;
183
- const every = (text.match(/(\d+)/) || ['', 1])[1];
184
- if (every && period) {
185
- return `${every}_${period}`;
186
- }
187
- return null;
188
- }
189
-
190
- export function sellingPlansToEveryPeriod(sellingPlanGroup) {
191
- return sellingPlanGroup?.selling_plans
192
- ?.map(({ options }) => options || [])
193
- .flat()
194
- .map(({ value }) => textToFreq(value));
195
- }
196
-
197
- export function sellingPlansToText(sellingPlanGroup, frequencies) {
198
- return sellingPlanGroup.options?.[0]?.values || frequencies;
199
- }
200
-
201
- export function sellingPlansToFrequencies(sellingPlanGroup) {
202
- return sellingPlanGroup?.selling_plans?.map(({ id }) => `${id}`);
203
- }
204
-
205
- function getPrepaidShipments(sellingPlan) {
206
- const shipments = sellingPlan?.options.find(({ name }) => name === 'Shipment amount')?.value.split(' ')[0];
207
- return shipments ? Number(shipments) : undefined;
208
- }
209
-
210
- function getPrepaidSellingPlans(prepaidSellingPlanGroups) {
211
- return prepaidSellingPlanGroups.reduce((acc, cur) => {
212
- const variant = cur.name.split('-')[1];
213
-
214
- const sellingPlanInfo = cur.selling_plans.map(sellingPlanObject => {
215
- return {
216
- numberShipments: getPrepaidShipments(sellingPlanObject),
217
- sellingPlan: String(sellingPlanObject.id)
218
- };
219
- });
220
-
221
- return { ...acc, [variant]: sellingPlanInfo };
222
- }, {});
223
- }
224
-
225
- export const config = (
226
- state = {
227
- frequencies: [],
228
- offerType: 'radio',
229
- frequenciesEveryPeriod: []
230
- },
231
- action
232
- ) => {
233
- if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
234
- const frequencies = [...new Set(Object.values(action.payload).map(Object.keys).flat())];
235
- return {
236
- ...state,
237
- frequencies
238
- };
239
- }
240
-
241
- if (constants.SETUP_PRODUCT === action.type) {
242
- const {
243
- payload: { product, currency }
244
- } = action;
245
- let configToAdd = {};
246
- // pay as you go selling plans
247
- const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(product?.selling_plan_groups);
248
- const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
249
- if (frequencies?.length) {
250
- const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
251
- const frequenciesText = sellingPlanGroup.options?.[0]?.values || frequencies;
252
- let defaultFrequency = state.defaultFrequency;
253
-
254
- if (defaultFrequency && isOgFrequency(defaultFrequency)) {
255
- defaultFrequency =
256
- mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, defaultFrequency) ||
257
- getFirstSellingPlan(frequencies) ||
258
- defaultFrequency;
259
- }
260
- configToAdd = {
261
- frequencies,
262
- frequenciesEveryPeriod,
263
- frequenciesText,
264
- ...(defaultFrequency ? { defaultFrequency } : {}),
265
- hasProductSpecificFrequencies:
266
- state.hasProductSpecificFrequencies || isProductSpecificFrequencySellingPlanGroup(sellingPlanGroup)
267
- };
268
- }
269
-
270
- // prepaid selling plans
271
- const prepaidSellingPlanGroups = product?.selling_plan_groups.filter(group => /^Prepaid-.*/.test(group.name));
272
- if (prepaidSellingPlanGroups.length) {
273
- configToAdd = {
274
- ...configToAdd,
275
- prepaidSellingPlans: { ...state.prepaidSellingPlans, ...getPrepaidSellingPlans(prepaidSellingPlanGroups) }
276
- };
277
- }
278
- return {
279
- ...state,
280
- ...configToAdd,
281
- storeCurrency: currency
282
- };
283
- }
284
-
285
- if (constants.RECEIVE_OFFER === action.type) {
286
- const {
287
- payload: { offer: offerEl }
288
- } = action;
289
- const {
290
- defaultFrequency,
291
- config: { frequencies: sellingPlans, frequenciesEveryPeriod, prepaidSellingPlans = {} } = {},
292
- product
293
- } = offerEl || {};
294
-
295
- // We don't want to be setting the default frequency to a prepaid selling plan
296
- if (prepaidSellingPlans[product?.id]?.some(({ sellingPlan }) => sellingPlan === defaultFrequency)) {
297
- return { ...state, defaultFrequency: getFirstSellingPlan(sellingPlans) || defaultFrequency };
298
- }
299
-
300
- if (!isOgFrequency(defaultFrequency))
301
- return {
302
- ...state,
303
- defaultFrequency: defaultFrequency
304
- };
305
-
306
- return {
307
- ...state,
308
- defaultFrequency:
309
- mapFrequencyToSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) ||
310
- getFirstSellingPlan(sellingPlans) ||
311
- defaultFrequency
312
- };
313
- }
314
-
315
- if (constants.RECEIVE_MERCHANT_SETTINGS === action.type) {
316
- return {
317
- ...state,
318
- merchantSettings: {
319
- ...action.payload
320
- }
321
- };
322
- }
323
-
324
- return state;
325
- };
326
-
327
203
  export const inStock = (state = {}, action) => {
328
204
  if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
329
205
  return Object.entries(action.payload).reduce(productTrue, state);
@@ -354,18 +230,9 @@ export const inStock = (state = {}, action) => {
354
230
 
355
231
  export const offer = (state = {}, _action) => state;
356
232
 
357
- function getFrequencyForPrepaidShipments({ prepaidShipments, offer: offerEl, product }) {
358
- if (prepaidShipments) {
359
- const productId = safeProductId(product.id);
360
- const plan = offerEl?.config.prepaidSellingPlans[productId]?.find(p => p.numberShipments === prepaidShipments);
361
- return plan ? plan.sellingPlan : null;
362
- }
363
- return offerEl?.config.frequencies[0];
364
- }
365
-
366
233
  function getOptedInItem(cartItem) {
367
234
  const prepaidShipments = getPrepaidShipments(cartItem.selling_plan_allocation.selling_plan);
368
- const item = {
235
+ const item: OptInItem = {
369
236
  id: cartItem.key,
370
237
  frequency: `${cartItem.selling_plan_allocation.selling_plan.id}`
371
238
  };
@@ -375,7 +242,7 @@ function getOptedInItem(cartItem) {
375
242
  return item;
376
243
  }
377
244
 
378
- export const optedin = (state = [], action) => {
245
+ export const optedin = (state: OptedInState = [], action): OptedInState => {
379
246
  if (constants.SETUP_CART === action.type) {
380
247
  const cart = action.payload;
381
248
  return state
@@ -384,9 +251,10 @@ export const optedin = (state = [], action) => {
384
251
  }
385
252
 
386
253
  if (constants.RECEIVE_OFFER === action.type) {
387
- const { offer: offerEl = {}, ...payload } = action.payload;
388
- const existingOptins = mapExistingOptinsFromOfferResponse(state, offerEl);
389
- const newOptins = reduceNewOptinsFromOfferResponse(payload, existingOptins, offerEl);
254
+ const payload = action.payload as ReceiveOfferPayload;
255
+ const { offer: offerEl = {}, frequencyConfig } = payload;
256
+ const existingOptins = mapExistingOptinsFromOfferResponse(state, offerEl, frequencyConfig);
257
+ const newOptins = reduceNewOptinsFromOfferResponse(payload, existingOptins, offerEl, frequencyConfig);
390
258
 
391
259
  return [...existingOptins, ...newOptins];
392
260
  }
@@ -423,7 +291,7 @@ export const optedin = (state = [], action) => {
423
291
  return rest.concat({
424
292
  ...oldone,
425
293
  ...payload.product,
426
- frequency: getFrequencyForPrepaidShipments(payload)
294
+ frequency: payload.frequency
427
295
  });
428
296
  }
429
297
 
@@ -52,3 +52,28 @@ export const getPayAsYouGoSellingPlan = (sellingPlans: ShopifySellingPlansEntity
52
52
  const group = getPayAsYouGoSellingPlanGroup(sellingPlans.map(plan => plan.group));
53
53
  return sellingPlans.find(plan => plan.group === group);
54
54
  };
55
+
56
+ export function sellingPlansToFrequencies(sellingPlanGroup: ShopifySellingPlanGroupsEntity) {
57
+ return sellingPlanGroup?.selling_plans?.map(({ id }) => `${id}`);
58
+ }
59
+
60
+ export function sellingPlansToEveryPeriod(sellingPlanGroup: ShopifySellingPlanGroupsEntity) {
61
+ return sellingPlanGroup?.selling_plans
62
+ ?.map(({ options }) => options || [])
63
+ .flat()
64
+ .map(({ value }) => textToFreq(value));
65
+ }
66
+
67
+ export function textToFreq(text) {
68
+ const period = ['day', 'week', 'month'].findIndex(it => text.toLowerCase().includes(it)) + 1;
69
+ const every = (text.match(/(\d+)/) || ['', 1])[1];
70
+ if (every && period) {
71
+ return `${every}_${period}`;
72
+ }
73
+ return null;
74
+ }
75
+
76
+ export function getPrepaidShipments(sellingPlan) {
77
+ const shipments = sellingPlan?.options.find(({ name }) => name === 'Shipment amount')?.value.split(' ')[0];
78
+ return shipments ? Number(shipments) : undefined;
79
+ }
package/src/types.ts DELETED
@@ -1,16 +0,0 @@
1
- export type ExperimentVariant = {
2
- public_id: string;
3
- parameters: any;
4
- weight: number;
5
- };
6
-
7
- export type ExperimentConfig = {
8
- public_id: string;
9
- variants: ExperimentVariant[];
10
- };
11
-
12
- export interface MerchantSettings {
13
- currency_code: string;
14
- multicurrency_enabled: boolean;
15
- experiments?: ExperimentConfig;
16
- }