@ordergroove/offers 2.43.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.
- package/CHANGELOG.md +11 -0
- package/dist/bundle-report.html +55 -53
- package/dist/offers.js +38 -38
- package/dist/offers.js.map +4 -4
- package/package.json +2 -2
- package/src/components/FrequencyStatus.js +13 -10
- package/src/components/Offer.js +33 -14
- package/src/components/OptinButton.js +2 -2
- package/src/components/OptinSelect.js +5 -5
- package/src/components/OptinStatus.js +15 -9
- package/src/components/Price.js +3 -3
- package/src/components/SelectFrequency.js +11 -6
- package/src/components/TestWizard.js +45 -41
- package/src/components/UpsellModal.js +9 -3
- package/src/components/__tests__/Offer.spec.js +0 -19
- package/src/components/__tests__/OptinStatus.spec.js +17 -4
- package/src/core/__tests__/actions.spec.js +47 -1
- package/src/core/__tests__/base.spec.js +0 -77
- package/src/core/__tests__/offerRequest.spec.js +2 -1
- package/src/core/__tests__/selectors.spec.js +7 -7
- package/src/core/actions-preview.js +6 -3
- package/src/core/actions.js +22 -13
- package/src/core/base.js +0 -23
- package/src/core/experiments.js +2 -4
- package/src/core/offerRequest.js +1 -1
- package/src/core/{reducer.js → reducer.ts} +30 -10
- package/src/core/{selectors.js → selectors.ts} +73 -57
- package/src/core/types/api.ts +71 -0
- package/src/core/types/reducer.ts +95 -0
- package/src/core/types/utility.ts +1 -0
- package/src/core/utils.ts +32 -15
- package/src/make-api.js +1 -1
- package/src/shopify/__tests__/productPlan.spec.js +18 -8
- package/src/shopify/__tests__/reducers/config.spec.js +497 -0
- package/src/shopify/__tests__/shopifyReducer.spec.js +68 -611
- package/src/shopify/__tests__/utils.spec.js +68 -1
- package/src/shopify/reducers/config.ts +223 -0
- package/src/shopify/reducers/productPlans.ts +3 -10
- package/src/shopify/shopifyMiddleware.ts +2 -9
- package/src/shopify/{shopifyReducer.js → shopifyReducer.ts} +52 -221
- package/src/shopify/types/shopify.ts +1 -1
- package/src/shopify/utils.ts +70 -0
- package/src/types.ts +0 -16
|
@@ -31,17 +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
|
-
import {
|
|
38
|
-
|
|
39
|
-
getSellingPlans,
|
|
40
|
-
DEFAULT_PAY_AS_YOU_GO_GROUP_NAME
|
|
41
|
-
} from './reducers/productPlans';
|
|
45
|
+
import { sellingPlanAllocationsReducer, getSellingPlans } from './reducers/productPlans';
|
|
46
|
+
import config from './reducers/config';
|
|
42
47
|
import { experimentsReducer } from '../core/experiments';
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
import {
|
|
49
|
+
getPayAsYouGoSellingPlanGroup,
|
|
50
|
+
getPayAsYouGoSellingPlanGroups,
|
|
51
|
+
sellingPlansToEveryPeriod,
|
|
52
|
+
sellingPlansToFrequencies,
|
|
53
|
+
getPrepaidShipments
|
|
54
|
+
} from './utils';
|
|
55
|
+
import { EmptyObject } from '../core/types/utility';
|
|
45
56
|
|
|
46
57
|
const overrideLineKey = (state, productId, newValue) => {
|
|
47
58
|
const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
|
|
@@ -51,7 +62,11 @@ const overrideLineKey = (state, productId, newValue) => {
|
|
|
51
62
|
return state;
|
|
52
63
|
};
|
|
53
64
|
|
|
54
|
-
export const getDefaultSellingPlan = (
|
|
65
|
+
export const getDefaultSellingPlan = (
|
|
66
|
+
sellingPlans: string[],
|
|
67
|
+
frequenciesEveryPeriod: string[],
|
|
68
|
+
defaultFrequency: string | undefined
|
|
69
|
+
) => {
|
|
55
70
|
if (!defaultFrequency) {
|
|
56
71
|
return null;
|
|
57
72
|
}
|
|
@@ -73,23 +88,27 @@ export const getDefaultSellingPlan = (sellingPlans, frequenciesEveryPeriod, defa
|
|
|
73
88
|
return defaultFrequency;
|
|
74
89
|
};
|
|
75
90
|
|
|
76
|
-
export const mapExistingOptinsFromOfferResponse = (
|
|
91
|
+
export const mapExistingOptinsFromOfferResponse = (
|
|
92
|
+
state: OptedInState,
|
|
93
|
+
offerEl: OfferElement | EmptyObject,
|
|
94
|
+
frequencyConfig: ReceiveOfferPayload['frequencyConfig']
|
|
95
|
+
) =>
|
|
77
96
|
state.map(it => {
|
|
78
97
|
if (isOgFrequency(it?.frequency)) {
|
|
79
98
|
return {
|
|
80
99
|
...it,
|
|
81
|
-
frequency: hasShopifySellingPlans(
|
|
100
|
+
frequency: hasShopifySellingPlans(frequencyConfig?.frequencies, frequencyConfig?.frequenciesEveryPeriod)
|
|
82
101
|
? mapFrequencyToSellingPlan(
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
frequencyConfig?.frequencies,
|
|
103
|
+
frequencyConfig?.frequenciesEveryPeriod,
|
|
85
104
|
it.frequency
|
|
86
105
|
) ||
|
|
87
106
|
mapFrequencyToSellingPlan(
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
frequencyConfig?.frequencies,
|
|
108
|
+
frequencyConfig?.frequenciesEveryPeriod,
|
|
90
109
|
offerEl?.defaultFrequency
|
|
91
110
|
) ||
|
|
92
|
-
getFirstSellingPlan(
|
|
111
|
+
getFirstSellingPlan(frequencyConfig?.frequencies)
|
|
93
112
|
: it.frequency
|
|
94
113
|
};
|
|
95
114
|
}
|
|
@@ -98,14 +117,16 @@ export const mapExistingOptinsFromOfferResponse = (state, offerEl) =>
|
|
|
98
117
|
});
|
|
99
118
|
|
|
100
119
|
export const reduceNewOptinsFromOfferResponse = (
|
|
101
|
-
{ autoship = {}, autoship_by_default = {}, default_frequencies = {}, in_stock = {} },
|
|
102
|
-
existingOptins,
|
|
103
|
-
offerEl
|
|
120
|
+
{ autoship = {}, autoship_by_default = {}, default_frequencies = {}, in_stock = {} }: ReceiveOfferPayload,
|
|
121
|
+
existingOptins: OptedInState,
|
|
122
|
+
offerEl: OfferElement | EmptyObject,
|
|
123
|
+
frequencyConfig: ReceiveOfferPayload['frequencyConfig']
|
|
104
124
|
) =>
|
|
105
125
|
Object.keys(autoship).reduce((acc, id) => {
|
|
106
126
|
if (!existingOptins.some(it => it.id === id)) {
|
|
107
127
|
if (!(autoship[id] && autoship_by_default[id] && in_stock[id])) return acc;
|
|
108
|
-
const {
|
|
128
|
+
const { frequencies: sellingPlans, frequenciesEveryPeriod } = frequencyConfig;
|
|
129
|
+
const { defaultFrequency } = offerEl || {};
|
|
109
130
|
const psdf = default_frequencies[id];
|
|
110
131
|
let frequency;
|
|
111
132
|
|
|
@@ -129,57 +150,21 @@ export const reduceNewOptinsFromOfferResponse = (
|
|
|
129
150
|
return acc;
|
|
130
151
|
}, []);
|
|
131
152
|
|
|
132
|
-
const getExperimentSellingPlanGroup = product => {
|
|
133
|
-
return product?.selling_plan_groups.find(group => group.app_id?.startsWith(EXPERIMENT_SHOPIFY_APP_ID_PREFIX));
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const getOGSellingPlanGroup = product => {
|
|
137
|
-
// retrieve an OG Default or PSFL Selling Plan Group, preferring to return PSFL groups if they exist
|
|
138
|
-
const productSpecificFrequencySellingPlanGroup = product?.selling_plan_groups.find(
|
|
139
|
-
isProductSpecificFrequencySellingPlanGroup
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
productSpecificFrequencySellingPlanGroup ||
|
|
144
|
-
getDefaultSubscriptionSellingPlanGroup(product) ||
|
|
145
|
-
getExperimentSellingPlanGroup(product)
|
|
146
|
-
);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const getOGSellingPlanGroups = product => {
|
|
150
|
-
const sellingPlanGroups = (product?.selling_plan_groups || []).filter(
|
|
151
|
-
group =>
|
|
152
|
-
isDefaultSellingPlanGroup(group) ||
|
|
153
|
-
isProductSpecificFrequencySellingPlanGroup(group) ||
|
|
154
|
-
group.app_id?.startsWith(EXPERIMENT_SHOPIFY_APP_ID_PREFIX)
|
|
155
|
-
);
|
|
156
|
-
return sellingPlanGroups;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const getDefaultSubscriptionSellingPlanGroup = product => {
|
|
160
|
-
// retrieve the OG Default Selling Plan Group
|
|
161
|
-
return product?.selling_plan_groups.find(isDefaultSellingPlanGroup);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
const isDefaultSellingPlanGroup = group => group.name === DEFAULT_PAY_AS_YOU_GO_GROUP_NAME;
|
|
165
|
-
|
|
166
|
-
const isProductSpecificFrequencySellingPlanGroup = group => group.name.startsWith('og_psfl');
|
|
167
|
-
|
|
168
153
|
const productOrVariantInStockReducer = (acc, cur) => ({
|
|
169
154
|
...overrideLineKey(acc, cur.id, cur.available),
|
|
170
155
|
[cur.id]: cur.available
|
|
171
156
|
});
|
|
172
157
|
|
|
173
|
-
const productTrue = (acc, [id]) => ({ ...acc, [id]: true });
|
|
158
|
+
const productTrue = (acc: Record<string, boolean>, [id]: [string, string[]]) => ({ ...acc, [id]: true });
|
|
174
159
|
|
|
175
160
|
const reduceProductCartLine = (acc, cur) => {
|
|
176
161
|
const productId = safeProductId(cur.key);
|
|
177
162
|
return { ...acc, [cur.key]: acc[productId] || null };
|
|
178
163
|
};
|
|
179
164
|
|
|
180
|
-
export const autoshipEligible = (state = {}, action) => {
|
|
165
|
+
export const autoshipEligible = (state: AutoshipEligibleState = {}, action): AutoshipEligibleState => {
|
|
181
166
|
if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
|
|
182
|
-
return Object.entries(action.payload).reduce(productTrue, state);
|
|
167
|
+
return Object.entries(action.payload as ReceiveProductPlansPayload).reduce(productTrue, state);
|
|
183
168
|
}
|
|
184
169
|
if (constants.SETUP_CART === action.type) {
|
|
185
170
|
const { payload: cart } = action;
|
|
@@ -189,7 +174,7 @@ export const autoshipEligible = (state = {}, action) => {
|
|
|
189
174
|
const {
|
|
190
175
|
payload: { product }
|
|
191
176
|
} = action;
|
|
192
|
-
const applicableSellingPlanGroups =
|
|
177
|
+
const applicableSellingPlanGroups = getPayAsYouGoSellingPlanGroups(product?.selling_plan_groups);
|
|
193
178
|
|
|
194
179
|
const ogSellingPlanIds = new Set(
|
|
195
180
|
applicableSellingPlanGroups.flatMap(group => group.selling_plans.map(sellingPlan => sellingPlan.id)) ?? []
|
|
@@ -215,152 +200,6 @@ export const autoshipEligible = (state = {}, action) => {
|
|
|
215
200
|
return state;
|
|
216
201
|
};
|
|
217
202
|
|
|
218
|
-
export function textToFreq(text) {
|
|
219
|
-
const period = ['day', 'week', 'month'].findIndex(it => text.toLowerCase().includes(it)) + 1;
|
|
220
|
-
const every = (text.match(/(\d+)/) || ['', 1])[1];
|
|
221
|
-
if (every && period) {
|
|
222
|
-
return `${every}_${period}`;
|
|
223
|
-
}
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function sellingPlansToEveryPeriod(sellingPlanGroup) {
|
|
228
|
-
return sellingPlanGroup?.selling_plans
|
|
229
|
-
?.map(({ options }) => options || [])
|
|
230
|
-
.flat()
|
|
231
|
-
.map(({ value }) => textToFreq(value));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function sellingPlansToText(sellingPlanGroup, frequencies) {
|
|
235
|
-
return sellingPlanGroup.options?.[0]?.values || frequencies;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function sellingPlansToFrequencies(sellingPlanGroup) {
|
|
239
|
-
return sellingPlanGroup?.selling_plans?.map(({ id }) => `${id}`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function getPrepaidShipments(sellingPlan) {
|
|
243
|
-
const shipments = sellingPlan?.options.find(({ name }) => name === 'Shipment amount')?.value.split(' ')[0];
|
|
244
|
-
return shipments ? Number(shipments) : undefined;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function getPrepaidSellingPlans(prepaidSellingPlanGroups) {
|
|
248
|
-
return prepaidSellingPlanGroups.reduce((acc, cur) => {
|
|
249
|
-
const variant = cur.name.split('-')[1];
|
|
250
|
-
|
|
251
|
-
const sellingPlanInfo = cur.selling_plans.map(sellingPlanObject => {
|
|
252
|
-
return {
|
|
253
|
-
numberShipments: getPrepaidShipments(sellingPlanObject),
|
|
254
|
-
sellingPlan: String(sellingPlanObject.id)
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
return { ...acc, [variant]: sellingPlanInfo };
|
|
259
|
-
}, {});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export const config = (
|
|
263
|
-
state = {
|
|
264
|
-
frequencies: [],
|
|
265
|
-
offerType: 'radio',
|
|
266
|
-
frequenciesEveryPeriod: []
|
|
267
|
-
},
|
|
268
|
-
action
|
|
269
|
-
) => {
|
|
270
|
-
if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
|
|
271
|
-
const frequencies = [...new Set(Object.values(action.payload).map(Object.keys).flat())];
|
|
272
|
-
return {
|
|
273
|
-
...state,
|
|
274
|
-
frequencies
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (constants.SETUP_PRODUCT === action.type) {
|
|
279
|
-
const {
|
|
280
|
-
payload: { product, currency }
|
|
281
|
-
} = action;
|
|
282
|
-
let configToAdd = {};
|
|
283
|
-
// pay as you go selling plans
|
|
284
|
-
const sellingPlanGroup = getOGSellingPlanGroup(product);
|
|
285
|
-
const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
|
|
286
|
-
if (frequencies?.length) {
|
|
287
|
-
const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
|
|
288
|
-
const frequenciesText = sellingPlanGroup.options?.[0]?.values || frequencies;
|
|
289
|
-
let defaultFrequency = state.defaultFrequency;
|
|
290
|
-
|
|
291
|
-
if (defaultFrequency && isOgFrequency(defaultFrequency)) {
|
|
292
|
-
defaultFrequency =
|
|
293
|
-
mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, defaultFrequency) ||
|
|
294
|
-
getFirstSellingPlan(frequencies) ||
|
|
295
|
-
defaultFrequency;
|
|
296
|
-
}
|
|
297
|
-
configToAdd = {
|
|
298
|
-
frequencies,
|
|
299
|
-
frequenciesEveryPeriod,
|
|
300
|
-
frequenciesText,
|
|
301
|
-
...(defaultFrequency ? { defaultFrequency } : {}),
|
|
302
|
-
hasProductSpecificFrequencies:
|
|
303
|
-
state.hasProductSpecificFrequencies || isProductSpecificFrequencySellingPlanGroup(sellingPlanGroup)
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// prepaid selling plans
|
|
308
|
-
const prepaidSellingPlanGroups = product?.selling_plan_groups.filter(group => /^Prepaid-.*/.test(group.name));
|
|
309
|
-
if (prepaidSellingPlanGroups.length) {
|
|
310
|
-
configToAdd = {
|
|
311
|
-
...configToAdd,
|
|
312
|
-
prepaidSellingPlans: { ...state.prepaidSellingPlans, ...getPrepaidSellingPlans(prepaidSellingPlanGroups) }
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
return {
|
|
316
|
-
...state,
|
|
317
|
-
...configToAdd,
|
|
318
|
-
storeCurrency: currency
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (constants.RECEIVE_OFFER === action.type) {
|
|
323
|
-
const {
|
|
324
|
-
payload: { offer: offerEl }
|
|
325
|
-
} = action;
|
|
326
|
-
const {
|
|
327
|
-
defaultFrequency,
|
|
328
|
-
config: { frequencies: sellingPlans, frequenciesEveryPeriod, prepaidSellingPlans = {} } = {},
|
|
329
|
-
product
|
|
330
|
-
} = offerEl || {};
|
|
331
|
-
|
|
332
|
-
// We don't want to be setting the default frequency to a prepaid selling plan
|
|
333
|
-
if (prepaidSellingPlans[product?.id]?.some(({ sellingPlan }) => sellingPlan === defaultFrequency)) {
|
|
334
|
-
return { ...state, defaultFrequency: getFirstSellingPlan(sellingPlans) || defaultFrequency };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (!isOgFrequency(defaultFrequency))
|
|
338
|
-
return {
|
|
339
|
-
...state,
|
|
340
|
-
defaultFrequency: defaultFrequency
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
return {
|
|
344
|
-
...state,
|
|
345
|
-
defaultFrequency:
|
|
346
|
-
mapFrequencyToSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) ||
|
|
347
|
-
getFirstSellingPlan(sellingPlans) ||
|
|
348
|
-
defaultFrequency
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (constants.RECEIVE_MERCHANT_SETTINGS === action.type) {
|
|
353
|
-
return {
|
|
354
|
-
...state,
|
|
355
|
-
merchantSettings: {
|
|
356
|
-
...action.payload
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return state;
|
|
362
|
-
};
|
|
363
|
-
|
|
364
203
|
export const inStock = (state = {}, action) => {
|
|
365
204
|
if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
|
|
366
205
|
return Object.entries(action.payload).reduce(productTrue, state);
|
|
@@ -391,18 +230,9 @@ export const inStock = (state = {}, action) => {
|
|
|
391
230
|
|
|
392
231
|
export const offer = (state = {}, _action) => state;
|
|
393
232
|
|
|
394
|
-
function getFrequencyForPrepaidShipments({ prepaidShipments, offer: offerEl, product }) {
|
|
395
|
-
if (prepaidShipments) {
|
|
396
|
-
const productId = safeProductId(product.id);
|
|
397
|
-
const plan = offerEl?.config.prepaidSellingPlans[productId]?.find(p => p.numberShipments === prepaidShipments);
|
|
398
|
-
return plan ? plan.sellingPlan : null;
|
|
399
|
-
}
|
|
400
|
-
return offerEl?.config.frequencies[0];
|
|
401
|
-
}
|
|
402
|
-
|
|
403
233
|
function getOptedInItem(cartItem) {
|
|
404
234
|
const prepaidShipments = getPrepaidShipments(cartItem.selling_plan_allocation.selling_plan);
|
|
405
|
-
const item = {
|
|
235
|
+
const item: OptInItem = {
|
|
406
236
|
id: cartItem.key,
|
|
407
237
|
frequency: `${cartItem.selling_plan_allocation.selling_plan.id}`
|
|
408
238
|
};
|
|
@@ -412,7 +242,7 @@ function getOptedInItem(cartItem) {
|
|
|
412
242
|
return item;
|
|
413
243
|
}
|
|
414
244
|
|
|
415
|
-
export const optedin = (state = [], action) => {
|
|
245
|
+
export const optedin = (state: OptedInState = [], action): OptedInState => {
|
|
416
246
|
if (constants.SETUP_CART === action.type) {
|
|
417
247
|
const cart = action.payload;
|
|
418
248
|
return state
|
|
@@ -421,16 +251,17 @@ export const optedin = (state = [], action) => {
|
|
|
421
251
|
}
|
|
422
252
|
|
|
423
253
|
if (constants.RECEIVE_OFFER === action.type) {
|
|
424
|
-
const
|
|
425
|
-
const
|
|
426
|
-
const
|
|
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);
|
|
427
258
|
|
|
428
259
|
return [...existingOptins, ...newOptins];
|
|
429
260
|
}
|
|
430
261
|
|
|
431
262
|
if (constants.SETUP_PRODUCT === action.type) {
|
|
432
263
|
const { product } = action.payload;
|
|
433
|
-
const sellingPlanGroup =
|
|
264
|
+
const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(product?.selling_plan_groups);
|
|
434
265
|
if (!sellingPlanGroup) {
|
|
435
266
|
return state;
|
|
436
267
|
}
|
|
@@ -460,7 +291,7 @@ export const optedin = (state = [], action) => {
|
|
|
460
291
|
return rest.concat({
|
|
461
292
|
...oldone,
|
|
462
293
|
...payload.product,
|
|
463
|
-
frequency:
|
|
294
|
+
frequency: payload.frequency
|
|
464
295
|
});
|
|
465
296
|
}
|
|
466
297
|
|
|
@@ -25,7 +25,7 @@ export interface ShopifySellingPlansEntity {
|
|
|
25
25
|
options?: ShopifyOptionsEntity[] | null;
|
|
26
26
|
recurring_deliveries: boolean;
|
|
27
27
|
price_adjustments?: ShopifyPriceAdjustmentsEntity[] | null;
|
|
28
|
-
|
|
28
|
+
group: ShopifySellingPlanGroupsEntity;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface ShopifySellingPlanGroupsEntity {
|
package/src/shopify/utils.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ShopifySellingPlanGroupsEntity, ShopifySellingPlansEntity } from './types/shopify';
|
|
2
|
+
|
|
1
3
|
export const money = (val: number, currency: string) =>
|
|
2
4
|
val === null
|
|
3
5
|
? ''
|
|
@@ -7,3 +9,71 @@ export const money = (val: number, currency: string) =>
|
|
|
7
9
|
}).format(val / 100);
|
|
8
10
|
|
|
9
11
|
export const percentage = val => `${val}%`;
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_PAY_AS_YOU_GO_GROUP_NAME = 'Subscribe and Save';
|
|
14
|
+
const EXPERIMENT_SHOPIFY_APP_ID_PREFIX = 'ordergroove-subscribe-and-save-';
|
|
15
|
+
|
|
16
|
+
// returns the non-prepaid OG selling plan to use for displaying frequencies
|
|
17
|
+
export const getPayAsYouGoSellingPlanGroup = (sellingPlanGroups: ShopifySellingPlanGroupsEntity[] = []) => {
|
|
18
|
+
// retrieve an OG Default or PSFL Selling Plan Group, preferring to return PSFL groups if they exist
|
|
19
|
+
const productSpecificFrequencySellingPlanGroup = sellingPlanGroups.find(isProductSpecificFrequencySellingPlanGroup);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
productSpecificFrequencySellingPlanGroup ||
|
|
23
|
+
sellingPlanGroups.find(isDefaultSellingPlanGroup) ||
|
|
24
|
+
sellingPlanGroups.find(isExperimentSellingPlanGroup)
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// returns OG selling plan groups that are not prepaid
|
|
29
|
+
export const getPayAsYouGoSellingPlanGroups = (sellingPlanGroups: ShopifySellingPlanGroupsEntity[] = []) => {
|
|
30
|
+
return sellingPlanGroups.filter(
|
|
31
|
+
group =>
|
|
32
|
+
isDefaultSellingPlanGroup(group) ||
|
|
33
|
+
isProductSpecificFrequencySellingPlanGroup(group) ||
|
|
34
|
+
isExperimentSellingPlanGroup(group)
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const isDefaultSellingPlanGroup = (group: ShopifySellingPlanGroupsEntity) =>
|
|
39
|
+
// we need to check name or app_id - the app_id is newer and not all selling plans have it
|
|
40
|
+
// the default group name is only applied to the default selling plan group
|
|
41
|
+
// flex incentives selling plan groups have a different name but the same subscribe-and-save app ID
|
|
42
|
+
group.name === DEFAULT_PAY_AS_YOU_GO_GROUP_NAME || group.app_id === 'ordergroove-subscribe-and-save';
|
|
43
|
+
|
|
44
|
+
export const isProductSpecificFrequencySellingPlanGroup = (group: ShopifySellingPlanGroupsEntity) =>
|
|
45
|
+
group.name.startsWith('og_psfl') || group.app_id === 'ordergroove-product-specific-frequency-list';
|
|
46
|
+
|
|
47
|
+
export const isExperimentSellingPlanGroup = (group: ShopifySellingPlanGroupsEntity) =>
|
|
48
|
+
// @ts-expect-error 18047
|
|
49
|
+
group.app_id?.startsWith(EXPERIMENT_SHOPIFY_APP_ID_PREFIX);
|
|
50
|
+
|
|
51
|
+
export const getPayAsYouGoSellingPlan = (sellingPlans: ShopifySellingPlansEntity[]) => {
|
|
52
|
+
const group = getPayAsYouGoSellingPlanGroup(sellingPlans.map(plan => plan.group));
|
|
53
|
+
return sellingPlans.find(plan => plan.group === group);
|
|
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
|
-
}
|