@ordergroove/offers 2.43.0 → 2.44.0
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 +9 -10
- package/dist/offers.js +30 -30
- package/dist/offers.js.map +4 -4
- package/package.json +2 -2
- package/src/core/experiments.js +2 -4
- package/src/shopify/__tests__/productPlan.spec.js +18 -8
- package/src/shopify/__tests__/shopifyReducer.spec.js +3 -1
- package/src/shopify/__tests__/utils.spec.js +45 -1
- package/src/shopify/reducers/productPlans.ts +3 -10
- package/src/shopify/shopifyReducer.js +9 -46
- package/src/shopify/types/shopify.ts +1 -1
- package/src/shopify/utils.ts +45 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.0",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"@ordergroove/offers-templates": "^0.9.7",
|
|
50
50
|
"@types/lodash.memoize": "^4.1.9"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "6920106790e66edb68d2f452825f9dc4be5911d4"
|
|
53
53
|
}
|
package/src/core/experiments.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { READY, RECEIVE_MERCHANT_SETTINGS, REQUEST_OFFER, SET_EXPERIMENT_VARIANT, SETUP_PRODUCT } from './constants';
|
|
2
2
|
import murmur from 'murmurhash-js';
|
|
3
3
|
import { waitFor } from './waitUntilOffersReady';
|
|
4
|
-
import {
|
|
4
|
+
import { isExperimentSellingPlanGroup } from '../shopify/utils';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Returns the index of a variant based on the provided key and variants.
|
|
@@ -77,9 +77,7 @@ function resolveShopifySetupProductWhenExperiment(variant, product, experimentSe
|
|
|
77
77
|
|
|
78
78
|
if (experimentSettings.variants.length === 0) return;
|
|
79
79
|
|
|
80
|
-
const sellingPlanGroups = product.selling_plan_groups.filter(
|
|
81
|
-
group.app_id?.startsWith(EXPERIMENT_SHOPIFY_APP_ID_PREFIX)
|
|
82
|
-
);
|
|
80
|
+
const sellingPlanGroups = product.selling_plan_groups.filter(isExperimentSellingPlanGroup);
|
|
83
81
|
|
|
84
82
|
if (sellingPlanGroups.length !== experimentSettings.variants.length) return;
|
|
85
83
|
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
getAllocationDiscountRate,
|
|
7
7
|
addPrepaidPriceAndSavings,
|
|
8
8
|
mapSellingPlanToDiscount,
|
|
9
|
-
getSellingPlans
|
|
10
|
-
DEFAULT_PAY_AS_YOU_GO_GROUP_NAME
|
|
9
|
+
getSellingPlans
|
|
11
10
|
} from '../reducers/productPlans';
|
|
11
|
+
import { DEFAULT_PAY_AS_YOU_GO_GROUP_NAME } from '../utils';
|
|
12
12
|
|
|
13
13
|
describe('Shopify productPlan Reducer', () => {
|
|
14
14
|
describe('isPrepaidAllocation', () => {
|
|
@@ -329,6 +329,9 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
329
329
|
const sellingPlans = [
|
|
330
330
|
{
|
|
331
331
|
id: 688412983572,
|
|
332
|
+
group: {
|
|
333
|
+
name: 'Prepaid-'
|
|
334
|
+
},
|
|
332
335
|
name: 'Delivered every 1 month, prepaid for 3 shipments',
|
|
333
336
|
description: null,
|
|
334
337
|
options: [
|
|
@@ -410,7 +413,10 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
410
413
|
value_type: 'price',
|
|
411
414
|
value: 4798
|
|
412
415
|
}
|
|
413
|
-
]
|
|
416
|
+
],
|
|
417
|
+
group: {
|
|
418
|
+
name: 'Prepaid-'
|
|
419
|
+
}
|
|
414
420
|
}
|
|
415
421
|
];
|
|
416
422
|
|
|
@@ -462,7 +468,9 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
462
468
|
value: 4800
|
|
463
469
|
}
|
|
464
470
|
],
|
|
465
|
-
|
|
471
|
+
group: {
|
|
472
|
+
name: 'Prepaid-'
|
|
473
|
+
}
|
|
466
474
|
},
|
|
467
475
|
{
|
|
468
476
|
id: 688412983573,
|
|
@@ -484,7 +492,9 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
484
492
|
value: 10
|
|
485
493
|
}
|
|
486
494
|
],
|
|
487
|
-
|
|
495
|
+
group: {
|
|
496
|
+
name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME
|
|
497
|
+
}
|
|
488
498
|
}
|
|
489
499
|
];
|
|
490
500
|
|
|
@@ -560,7 +570,7 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
560
570
|
|
|
561
571
|
const expectedSellingPlans = [
|
|
562
572
|
{
|
|
563
|
-
|
|
573
|
+
group: product.selling_plan_groups[0],
|
|
564
574
|
id: 688412721428,
|
|
565
575
|
options: [
|
|
566
576
|
{
|
|
@@ -576,7 +586,7 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
576
586
|
]
|
|
577
587
|
},
|
|
578
588
|
{
|
|
579
|
-
|
|
589
|
+
group: product.selling_plan_groups[1],
|
|
580
590
|
id: 688412623124,
|
|
581
591
|
options: [
|
|
582
592
|
{
|
|
@@ -587,7 +597,7 @@ describe('Shopify productPlan Reducer', () => {
|
|
|
587
597
|
]
|
|
588
598
|
},
|
|
589
599
|
{
|
|
590
|
-
|
|
600
|
+
group: product.selling_plan_groups[1],
|
|
591
601
|
id: 688412655892,
|
|
592
602
|
options: [
|
|
593
603
|
{
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
textToFreq
|
|
11
11
|
} from '../shopifyReducer';
|
|
12
12
|
import { getObjectStructuredProductPlans } from '../../core/adapters';
|
|
13
|
-
import { DEFAULT_PAY_AS_YOU_GO_GROUP_NAME } from '../
|
|
13
|
+
import { DEFAULT_PAY_AS_YOU_GO_GROUP_NAME } from '../utils';
|
|
14
14
|
|
|
15
15
|
describe('autoshipEligible', () => {
|
|
16
16
|
it('should return true for each id given action RECEIVE_PRODUCT_PLANS', () => {
|
|
@@ -1760,6 +1760,7 @@ function getSetupProductActionForPrepaidProduct(currency = 'USD') {
|
|
|
1760
1760
|
],
|
|
1761
1761
|
selling_plan_groups: [
|
|
1762
1762
|
{
|
|
1763
|
+
name: 'Prepaid-1',
|
|
1763
1764
|
selling_plans: [
|
|
1764
1765
|
{
|
|
1765
1766
|
id: 'yum selling plan id 1',
|
|
@@ -1795,6 +1796,7 @@ function getSetupProductActionForPrepaidProduct(currency = 'USD') {
|
|
|
1795
1796
|
]
|
|
1796
1797
|
},
|
|
1797
1798
|
{
|
|
1799
|
+
name: 'Prepaid-2',
|
|
1798
1800
|
selling_plans: [
|
|
1799
1801
|
{
|
|
1800
1802
|
id: 'yum selling plan id prepaid 3 shipments',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { money, percentage } from '../utils';
|
|
1
|
+
import { getPayAsYouGoSellingPlanGroups, money, percentage } from '../utils';
|
|
2
2
|
|
|
3
3
|
describe('Shopify Utils', () => {
|
|
4
4
|
describe('Money', () => {
|
|
@@ -27,3 +27,47 @@ describe('Shopify Utils', () => {
|
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
|
+
|
|
31
|
+
describe('selling plan queries', () => {
|
|
32
|
+
describe('getPayAsYouGoSellingPlanGroups', () => {
|
|
33
|
+
it('returns default group with app ID', () => {
|
|
34
|
+
expect(
|
|
35
|
+
getPayAsYouGoSellingPlanGroups([{ name: 'Subscribe and Save', app_id: 'ordergroove-subscribe-and-save' }])
|
|
36
|
+
.length
|
|
37
|
+
).toBe(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('returns default group without app ID', () => {
|
|
41
|
+
expect(getPayAsYouGoSellingPlanGroups([{ name: 'Subscribe and Save', app_id: null }]).length).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns PSI selling plan groups', () => {
|
|
45
|
+
expect(
|
|
46
|
+
getPayAsYouGoSellingPlanGroups([{ name: 'incentive-group', app_id: 'ordergroove-subscribe-and-save' }]).length
|
|
47
|
+
).toBe(1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not return prepaid selling plan groups', () => {
|
|
51
|
+
expect(
|
|
52
|
+
getPayAsYouGoSellingPlanGroups([{ name: 'Prepaid-1', app_id: 'ordergroove-prepaid-and-save' }]).length
|
|
53
|
+
).toBe(0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns PSFL group with app ID', () => {
|
|
57
|
+
expect(
|
|
58
|
+
getPayAsYouGoSellingPlanGroups([
|
|
59
|
+
{
|
|
60
|
+
// name probably still starts with og_psfl
|
|
61
|
+
// but changing it so that the app_id branch is hit
|
|
62
|
+
name: 'changed-psfl-name',
|
|
63
|
+
app_id: 'ordergroove-product-specific-frequency-list'
|
|
64
|
+
}
|
|
65
|
+
]).length
|
|
66
|
+
).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns PSFL group without app ID', () => {
|
|
70
|
+
expect(getPayAsYouGoSellingPlanGroups([{ name: 'og_psfl_1w', app_id: null }]).length).toBe(1);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { money, percentage } from '../utils';
|
|
1
|
+
import { getPayAsYouGoSellingPlan, money, percentage } from '../utils';
|
|
2
2
|
import { ShopifyProductEntity, ShopifySellingPlanAllocationsEntity, ShopifySellingPlansEntity } from '../types/shopify';
|
|
3
3
|
import { ProductPlanEntity } from '../types/productPlan';
|
|
4
4
|
|
|
@@ -92,8 +92,6 @@ export const addPrepaidPriceAndSavings = (
|
|
|
92
92
|
return productPlan;
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
export const DEFAULT_PAY_AS_YOU_GO_GROUP_NAME = 'Subscribe and Save';
|
|
96
|
-
|
|
97
95
|
export const mapSellingPlanToDiscount = (
|
|
98
96
|
allocation: ShopifySellingPlanAllocationsEntity,
|
|
99
97
|
sellingPlans: ShopifySellingPlansEntity[],
|
|
@@ -112,9 +110,7 @@ export const mapSellingPlanToDiscount = (
|
|
|
112
110
|
};
|
|
113
111
|
|
|
114
112
|
if (isPrepaidAllocation(allocation)) {
|
|
115
|
-
const payAsYouGoPlan = sellingPlans
|
|
116
|
-
plan => plan.group_name === DEFAULT_PAY_AS_YOU_GO_GROUP_NAME && plan.options.length === 1
|
|
117
|
-
);
|
|
113
|
+
const payAsYouGoPlan = getPayAsYouGoSellingPlan(sellingPlans);
|
|
118
114
|
return addPrepaidPriceAndSavings(allocation, productPlan, payAsYouGoPlan, currency);
|
|
119
115
|
}
|
|
120
116
|
|
|
@@ -130,9 +126,6 @@ export const sellingPlanAllocationsReducer = (
|
|
|
130
126
|
|
|
131
127
|
export const getSellingPlans = (product: ShopifyProductEntity) =>
|
|
132
128
|
product.selling_plan_groups.reduce<ShopifySellingPlansEntity[]>(
|
|
133
|
-
(allGroups, group) => [
|
|
134
|
-
...allGroups,
|
|
135
|
-
...group.selling_plans.map(selling_plan => ({ ...selling_plan, group_name: group.name }))
|
|
136
|
-
],
|
|
129
|
+
(allGroups, group) => [...allGroups, ...group.selling_plans.map(selling_plan => ({ ...selling_plan, group }))],
|
|
137
130
|
[]
|
|
138
131
|
);
|
|
@@ -34,14 +34,13 @@ import {
|
|
|
34
34
|
|
|
35
35
|
import { getObjectStructuredProductPlans } from '../core/adapters';
|
|
36
36
|
|
|
37
|
-
import {
|
|
38
|
-
sellingPlanAllocationsReducer,
|
|
39
|
-
getSellingPlans,
|
|
40
|
-
DEFAULT_PAY_AS_YOU_GO_GROUP_NAME
|
|
41
|
-
} from './reducers/productPlans';
|
|
37
|
+
import { sellingPlanAllocationsReducer, getSellingPlans } from './reducers/productPlans';
|
|
42
38
|
import { experimentsReducer } from '../core/experiments';
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
import {
|
|
40
|
+
getPayAsYouGoSellingPlanGroup,
|
|
41
|
+
getPayAsYouGoSellingPlanGroups,
|
|
42
|
+
isProductSpecificFrequencySellingPlanGroup
|
|
43
|
+
} from './utils';
|
|
45
44
|
|
|
46
45
|
const overrideLineKey = (state, productId, newValue) => {
|
|
47
46
|
const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
|
|
@@ -129,42 +128,6 @@ export const reduceNewOptinsFromOfferResponse = (
|
|
|
129
128
|
return acc;
|
|
130
129
|
}, []);
|
|
131
130
|
|
|
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
131
|
const productOrVariantInStockReducer = (acc, cur) => ({
|
|
169
132
|
...overrideLineKey(acc, cur.id, cur.available),
|
|
170
133
|
[cur.id]: cur.available
|
|
@@ -189,7 +152,7 @@ export const autoshipEligible = (state = {}, action) => {
|
|
|
189
152
|
const {
|
|
190
153
|
payload: { product }
|
|
191
154
|
} = action;
|
|
192
|
-
const applicableSellingPlanGroups =
|
|
155
|
+
const applicableSellingPlanGroups = getPayAsYouGoSellingPlanGroups(product?.selling_plan_groups);
|
|
193
156
|
|
|
194
157
|
const ogSellingPlanIds = new Set(
|
|
195
158
|
applicableSellingPlanGroups.flatMap(group => group.selling_plans.map(sellingPlan => sellingPlan.id)) ?? []
|
|
@@ -281,7 +244,7 @@ export const config = (
|
|
|
281
244
|
} = action;
|
|
282
245
|
let configToAdd = {};
|
|
283
246
|
// pay as you go selling plans
|
|
284
|
-
const sellingPlanGroup =
|
|
247
|
+
const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(product?.selling_plan_groups);
|
|
285
248
|
const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
|
|
286
249
|
if (frequencies?.length) {
|
|
287
250
|
const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
|
|
@@ -430,7 +393,7 @@ export const optedin = (state = [], action) => {
|
|
|
430
393
|
|
|
431
394
|
if (constants.SETUP_PRODUCT === action.type) {
|
|
432
395
|
const { product } = action.payload;
|
|
433
|
-
const sellingPlanGroup =
|
|
396
|
+
const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(product?.selling_plan_groups);
|
|
434
397
|
if (!sellingPlanGroup) {
|
|
435
398
|
return state;
|
|
436
399
|
}
|
|
@@ -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,46 @@ 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
|
+
};
|