@ordergroove/offers 2.29.0 → 2.29.1-alpha-PR-707-2.113
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/README.md +22 -1
- package/dist/bundle-report.html +72 -59
- package/dist/examples.js +262 -105
- package/dist/examples.js.map +2 -2
- package/dist/offers.js +82 -32
- package/dist/offers.js.map +3 -3
- package/examples/index.js +230 -218
- package/package.json +2 -2
- package/src/components/PrepaidData.js +110 -0
- package/src/components/PrepaidToggle.js +108 -0
- package/src/components/Price.js +6 -6
- package/src/components/Select.js +6 -1
- package/src/components/__tests__/PrepaidData.spec.js +173 -0
- package/src/components/__tests__/PrepaidToggle.spec.js +115 -0
- package/src/components/__tests__/Price.spec.js +96 -0
- package/src/core/__tests__/adapters.spec.js +232 -1
- package/src/core/__tests__/descriptors.spec.js +56 -0
- package/src/core/__tests__/reducer.spec.js +153 -1
- package/src/core/__tests__/selectors.spec.js +34 -1
- package/src/core/actions.js +5 -0
- package/src/core/adapters.js +48 -2
- package/src/core/constants.js +1 -0
- package/src/core/descriptors.js +7 -1
- package/src/core/reducer.js +35 -14
- package/src/core/selectors.js +32 -0
- package/src/core/utils.ts +16 -0
- package/src/make-api.js +4 -0
- package/src/shopify/__tests__/productPlan.spec.js +513 -0
- package/src/shopify/__tests__/shopifyReducer.spec.js +630 -19
- package/src/shopify/__tests__/utils.spec.js +25 -0
- package/src/shopify/reducers/productPlans.ts +134 -0
- package/src/shopify/shopifyMiddleware.ts +3 -0
- package/src/shopify/shopifyReducer.js +96 -47
- package/src/shopify/shopifyTrackingMiddleware.ts +1 -1
- package/src/shopify/types/productPlan.ts +11 -0
- package/src/shopify/types/shopify.ts +98 -0
- package/src/shopify/utils.ts +3 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { money, percentage } from '../utils';
|
|
2
|
+
|
|
3
|
+
describe('Shopify Utils', () => {
|
|
4
|
+
describe('Money', () => {
|
|
5
|
+
it('Should return formatted greater than $1 money price', () => {
|
|
6
|
+
const price = 1000;
|
|
7
|
+
const formattedPrice = money(price);
|
|
8
|
+
expect(formattedPrice).toBe('$10.00');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('Should return formatted lower than $1 money price', () => {
|
|
12
|
+
const price = 50;
|
|
13
|
+
const formattedPrice = money(price);
|
|
14
|
+
expect(formattedPrice).toBe('$.50');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('Percentage', () => {
|
|
19
|
+
it('Should return percentage', () => {
|
|
20
|
+
const price = 10;
|
|
21
|
+
const formattedPrice = percentage(price);
|
|
22
|
+
expect(formattedPrice).toBe('10%');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { money, percentage } from '../utils';
|
|
2
|
+
import { ShopifyProductEntity, ShopifySellingPlanAllocationsEntity, ShopifySellingPlansEntity } from '../types/shopify';
|
|
3
|
+
import { ProductPlanEntity } from '../types/productPlan';
|
|
4
|
+
|
|
5
|
+
export const isPrepaidAllocation = (allocation: ShopifySellingPlanAllocationsEntity) =>
|
|
6
|
+
Array.isArray(allocation.selling_plan?.options) &&
|
|
7
|
+
allocation.selling_plan?.options.some(option => option?.name === 'Shipment amount');
|
|
8
|
+
|
|
9
|
+
export const getPrepaidShipmentsNumberFromOptions = options => {
|
|
10
|
+
if (options && options.length > 1) {
|
|
11
|
+
const shipmentAmountArray = options.find(option => option?.name === 'Shipment amount').value.split(' ');
|
|
12
|
+
return shipmentAmountArray.length > 0 ? +shipmentAmountArray[0] : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getAllocationFrequency = (allocation: ShopifySellingPlanAllocationsEntity) => {
|
|
19
|
+
// now frequency every_period will match with selling_plan_id so no need to convert it
|
|
20
|
+
return (allocation.selling_plan_id || allocation.selling_plan?.id).toString();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const getAllocationRegularPrice = (allocation: ShopifySellingPlanAllocationsEntity) => {
|
|
24
|
+
return money(allocation.compare_at_price);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const getAllocationSubscriptionPrice = (allocation: ShopifySellingPlanAllocationsEntity) => {
|
|
28
|
+
if (isPrepaidAllocation(allocation)) {
|
|
29
|
+
const prepaidShipmentsPerBilling = getPrepaidShipmentsNumberFromOptions(allocation.selling_plan?.options);
|
|
30
|
+
const pricePerShipment = allocation.price / prepaidShipmentsPerBilling;
|
|
31
|
+
return money(pricePerShipment);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return money(allocation.price);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getPrepaidPercentage = (allocation: ShopifySellingPlanAllocationsEntity, pricePerShipment: number) => {
|
|
38
|
+
return ((allocation.compare_at_price - pricePerShipment) * 100) / allocation.compare_at_price;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const getAllocationDiscountRate = (allocation: ShopifySellingPlanAllocationsEntity) => {
|
|
42
|
+
if (isPrepaidAllocation(allocation)) {
|
|
43
|
+
const prepaidShipmentsPerBilling = getPrepaidShipmentsNumberFromOptions(allocation.selling_plan?.options);
|
|
44
|
+
const pricePerShipment = allocation.price / prepaidShipmentsPerBilling;
|
|
45
|
+
const prepaidPercentageSavings = getPrepaidPercentage(allocation, pricePerShipment);
|
|
46
|
+
|
|
47
|
+
return percentage(prepaidPercentageSavings);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let formatted_discount = '';
|
|
51
|
+
|
|
52
|
+
if (allocation.price_adjustments[0]?.value_type === 'percentage') {
|
|
53
|
+
formatted_discount = percentage(allocation.price_adjustments[0].value);
|
|
54
|
+
} else if (allocation.price_adjustments[0]?.value) {
|
|
55
|
+
formatted_discount = money(allocation.price_adjustments[0].value);
|
|
56
|
+
} else if (allocation.compare_at_price) {
|
|
57
|
+
formatted_discount = money(allocation.compare_at_price - allocation.price);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return formatted_discount;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const getAllocationNumberOfShipments = (allocation: ShopifySellingPlanAllocationsEntity) => {
|
|
64
|
+
if (isPrepaidAllocation(allocation)) {
|
|
65
|
+
return getPrepaidShipmentsNumberFromOptions(allocation.selling_plan?.options);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const addPrepaidPriceAndSavings = (
|
|
71
|
+
allocation: ShopifySellingPlanAllocationsEntity,
|
|
72
|
+
productPlan: ProductPlanEntity,
|
|
73
|
+
payAsYouGoPlan: ShopifySellingPlansEntity
|
|
74
|
+
) => {
|
|
75
|
+
const prepaidShipmentsPerBilling = getPrepaidShipmentsNumberFromOptions(allocation.selling_plan?.options);
|
|
76
|
+
const pricePerShipment = allocation.price / prepaidShipmentsPerBilling;
|
|
77
|
+
const prepaidSaving = allocation.compare_at_price - pricePerShipment;
|
|
78
|
+
const prepaidPercentageSavings = getPrepaidPercentage(allocation, pricePerShipment);
|
|
79
|
+
const payAsYouGoAdjustment = payAsYouGoPlan?.price_adjustments?.[0];
|
|
80
|
+
const payAsYouGoPercentage =
|
|
81
|
+
payAsYouGoAdjustment && payAsYouGoAdjustment.value_type === 'percentage' ? payAsYouGoAdjustment.value : null;
|
|
82
|
+
|
|
83
|
+
productPlan['regularPrepaidPrice'] = money(allocation.price);
|
|
84
|
+
productPlan['prepaidSavingsPerShipment'] = money(prepaidSaving);
|
|
85
|
+
productPlan['prepaidSavingsTotal'] = money(prepaidSaving * prepaidShipmentsPerBilling);
|
|
86
|
+
|
|
87
|
+
if (payAsYouGoPercentage && prepaidPercentageSavings) {
|
|
88
|
+
productPlan['prepaidExtraSavingsPercentage'] = percentage(prepaidPercentageSavings - payAsYouGoPercentage);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return productPlan;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const DEFAULT_PAY_AS_YOU_GO_GROUP_NAME = 'Subscribe and Save';
|
|
95
|
+
|
|
96
|
+
export const mapSellingPlanToDiscount = (
|
|
97
|
+
allocation: ShopifySellingPlanAllocationsEntity,
|
|
98
|
+
sellingPlans: ShopifySellingPlansEntity[] = []
|
|
99
|
+
) => {
|
|
100
|
+
if (!allocation.selling_plan) {
|
|
101
|
+
allocation.selling_plan = sellingPlans.find(plan => plan.id === allocation.selling_plan_id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const productPlan: ProductPlanEntity = {
|
|
105
|
+
frequency: getAllocationFrequency(allocation),
|
|
106
|
+
regularPrice: getAllocationRegularPrice(allocation),
|
|
107
|
+
subscriptionPrice: getAllocationSubscriptionPrice(allocation),
|
|
108
|
+
discountRate: getAllocationDiscountRate(allocation),
|
|
109
|
+
prepaidShipments: getAllocationNumberOfShipments(allocation)
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (isPrepaidAllocation(allocation)) {
|
|
113
|
+
const payAsYouGoPlan = sellingPlans.find(
|
|
114
|
+
plan => plan.group_name === DEFAULT_PAY_AS_YOU_GO_GROUP_NAME && plan.options.length === 1
|
|
115
|
+
);
|
|
116
|
+
return addPrepaidPriceAndSavings(allocation, productPlan, payAsYouGoPlan);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return productPlan;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const sellingPlanAllocationsReducer = (acc, cur, sellingPlans = []) => [
|
|
123
|
+
...acc,
|
|
124
|
+
mapSellingPlanToDiscount(cur, sellingPlans)
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
export const getSellingPlans = (product: ShopifyProductEntity) =>
|
|
128
|
+
product.selling_plan_groups.reduce(
|
|
129
|
+
(allGroups, group) => [
|
|
130
|
+
...allGroups,
|
|
131
|
+
...group.selling_plans.map(selling_plan => ({ ...selling_plan, group_name: group.name }))
|
|
132
|
+
],
|
|
133
|
+
[]
|
|
134
|
+
);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
OPTIN_PRODUCT,
|
|
7
7
|
OPTOUT_PRODUCT,
|
|
8
8
|
PRODUCT_CHANGE_FREQUENCY,
|
|
9
|
+
PRODUCT_CHANGE_PREPAID_SHIPMENTS,
|
|
9
10
|
RECEIVE_OFFER,
|
|
10
11
|
REQUEST_OFFER,
|
|
11
12
|
SETUP_CART,
|
|
@@ -345,6 +346,8 @@ export default function shopifyMiddleware(store) {
|
|
|
345
346
|
case OPTOUT_PRODUCT:
|
|
346
347
|
case PRODUCT_CHANGE_FREQUENCY:
|
|
347
348
|
synchronizeCartOptin(action, store);
|
|
349
|
+
// eslint-disable-next-line no-fallthrough
|
|
350
|
+
case PRODUCT_CHANGE_PREPAID_SHIPMENTS:
|
|
348
351
|
case REQUEST_OFFER:
|
|
349
352
|
case RECEIVE_OFFER:
|
|
350
353
|
case SETUP_PRODUCT:
|
|
@@ -21,36 +21,24 @@ import baseReducer, {
|
|
|
21
21
|
previewUpsellOffer,
|
|
22
22
|
productToSubscribe,
|
|
23
23
|
sessionId,
|
|
24
|
-
templates
|
|
24
|
+
templates,
|
|
25
|
+
prepaidShipmentsSelected
|
|
25
26
|
} from '../core/reducer';
|
|
26
27
|
import {
|
|
27
28
|
getFirstSellingPlan,
|
|
28
29
|
hasShopifySellingPlans,
|
|
29
30
|
isOgFrequency,
|
|
30
31
|
mapFrequencyToSellingPlan,
|
|
31
|
-
safeProductId
|
|
32
|
+
safeProductId,
|
|
33
|
+
getMatchingProductIfExists
|
|
32
34
|
} from '../core/utils';
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (allocation.price_adjustments[0]?.value_type === 'percentage') {
|
|
41
|
-
formatted_discount = percentage(allocation.price_adjustments[0].value);
|
|
42
|
-
} else if (allocation.price_adjustments[0]?.value) {
|
|
43
|
-
formatted_discount = money(allocation.price_adjustments[0].value);
|
|
44
|
-
} else if (allocation.compare_at_price) {
|
|
45
|
-
formatted_discount = money(allocation.compare_at_price - allocation.price);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (formatted_discount) {
|
|
49
|
-
return [money(allocation.compare_at_price), formatted_discount, money(allocation.price)];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return [money(allocation.price), '', money(allocation.price)];
|
|
53
|
-
};
|
|
36
|
+
import { getObjectStructuredProductPlans } from '../core/adapters';
|
|
37
|
+
import {
|
|
38
|
+
sellingPlanAllocationsReducer,
|
|
39
|
+
getSellingPlans,
|
|
40
|
+
DEFAULT_PAY_AS_YOU_GO_GROUP_NAME
|
|
41
|
+
} from './reducers/productPlans';
|
|
54
42
|
|
|
55
43
|
const overrideLineKey = (state, productId, newValue) => {
|
|
56
44
|
const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
|
|
@@ -140,7 +128,7 @@ export const reduceNewOptinsFromOfferResponse = (
|
|
|
140
128
|
|
|
141
129
|
const getOGSellingPlanGroup = product => {
|
|
142
130
|
const sellingPlanGroup = product?.selling_plan_groups.find(group => {
|
|
143
|
-
return group.name ===
|
|
131
|
+
return group.name === DEFAULT_PAY_AS_YOU_GO_GROUP_NAME;
|
|
144
132
|
});
|
|
145
133
|
return sellingPlanGroup;
|
|
146
134
|
};
|
|
@@ -152,12 +140,6 @@ const productOrVariantInStockReducer = (acc, cur) => ({
|
|
|
152
140
|
|
|
153
141
|
const productTrue = (acc, [id]) => ({ ...acc, [id]: true });
|
|
154
142
|
|
|
155
|
-
const sellingPlanAllocationsReducer = (acc, cur) => ({
|
|
156
|
-
...acc,
|
|
157
|
-
// now frequency every_period will match with selling_plan_id so no need to convert it
|
|
158
|
-
[cur.selling_plan_id || cur.selling_plan?.id]: mapSellingPlanToDiscount(cur)
|
|
159
|
-
});
|
|
160
|
-
|
|
161
143
|
const reduceProductCartLine = (acc, cur) => {
|
|
162
144
|
const productId = safeProductId(cur.key);
|
|
163
145
|
return { ...acc, [cur.key]: acc[productId] || null };
|
|
@@ -210,6 +192,26 @@ export function sellingPlansToFrequencies(sellingPlanGroup) {
|
|
|
210
192
|
return sellingPlanGroup?.selling_plans?.map(({ id }) => `${id}`);
|
|
211
193
|
}
|
|
212
194
|
|
|
195
|
+
function getPrepaidShipments(sellingPlan) {
|
|
196
|
+
const shipments = sellingPlan?.options.find(({ name }) => name === 'Shipment amount')?.value.split(' ')[0];
|
|
197
|
+
return shipments ? Number(shipments) : undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getPrepaidSellingPlans(prepaidSellingPlanGroups) {
|
|
201
|
+
return prepaidSellingPlanGroups.reduce((acc, cur) => {
|
|
202
|
+
const variant = cur.name.split('-')[1];
|
|
203
|
+
|
|
204
|
+
const sellingPlanInfo = cur.selling_plans.map(sellingPlanObject => {
|
|
205
|
+
return {
|
|
206
|
+
numberShipments: getPrepaidShipments(sellingPlanObject),
|
|
207
|
+
sellingPlan: String(sellingPlanObject.id)
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return { ...acc, [variant]: sellingPlanInfo };
|
|
212
|
+
}, {});
|
|
213
|
+
}
|
|
214
|
+
|
|
213
215
|
export const config = (
|
|
214
216
|
state = {
|
|
215
217
|
frequencies: [],
|
|
@@ -236,8 +238,10 @@ export const config = (
|
|
|
236
238
|
const {
|
|
237
239
|
payload: { offer: offerEl, product }
|
|
238
240
|
} = action;
|
|
241
|
+
let configToAdd = {};
|
|
242
|
+
// pay as you go selling plans
|
|
239
243
|
const sellingPlanGroup = getOGSellingPlanGroup(product);
|
|
240
|
-
const frequencies = sellingPlanGroup
|
|
244
|
+
const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
|
|
241
245
|
if (frequencies?.length) {
|
|
242
246
|
const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
|
|
243
247
|
const frequenciesText = sellingPlanGroup.options?.[0]?.values || frequencies;
|
|
@@ -249,15 +253,26 @@ export const config = (
|
|
|
249
253
|
getFirstSellingPlan(frequencies) ||
|
|
250
254
|
defaultFrequency;
|
|
251
255
|
}
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
...state,
|
|
255
|
-
frequenciesEveryPeriod,
|
|
256
|
+
configToAdd = {
|
|
256
257
|
frequencies,
|
|
258
|
+
frequenciesEveryPeriod,
|
|
257
259
|
frequenciesText,
|
|
258
260
|
...(defaultFrequency ? { defaultFrequency } : {})
|
|
259
261
|
};
|
|
260
262
|
}
|
|
263
|
+
|
|
264
|
+
// prepaid selling plans
|
|
265
|
+
const prepaidSellingPlanGroups = product?.selling_plan_groups.filter(group => /^Prepaid-.*/.test(group.name));
|
|
266
|
+
if (prepaidSellingPlanGroups.length) {
|
|
267
|
+
configToAdd = {
|
|
268
|
+
...configToAdd,
|
|
269
|
+
prepaidSellingPlans: getPrepaidSellingPlans(prepaidSellingPlanGroups)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
...state,
|
|
274
|
+
...configToAdd
|
|
275
|
+
};
|
|
261
276
|
}
|
|
262
277
|
|
|
263
278
|
if (constants.RECEIVE_OFFER === action.type) {
|
|
@@ -310,20 +325,33 @@ export const inStock = (state = {}, action) => {
|
|
|
310
325
|
|
|
311
326
|
export const offer = (state = {}, action) => state;
|
|
312
327
|
|
|
328
|
+
function getFrequencyForPrepaidShipments({ prepaidShipments, offer: offerEl, product }) {
|
|
329
|
+
if (prepaidShipments) {
|
|
330
|
+
const productId = product.id;
|
|
331
|
+
const plan = offerEl.config.prepaidSellingPlans[productId]?.find(p => p.numberShipments === prepaidShipments);
|
|
332
|
+
return plan ? plan.sellingPlan : null;
|
|
333
|
+
}
|
|
334
|
+
return offerEl.config.frequencies[0];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function getOptedInItem(cartItem) {
|
|
338
|
+
const prepaidShipments = getPrepaidShipments(cartItem.selling_plan_allocation.selling_plan);
|
|
339
|
+
const item = {
|
|
340
|
+
id: cartItem.key,
|
|
341
|
+
frequency: `${cartItem.selling_plan_allocation.selling_plan.id}`
|
|
342
|
+
};
|
|
343
|
+
if (prepaidShipments) {
|
|
344
|
+
item.prepaidShipments = prepaidShipments;
|
|
345
|
+
}
|
|
346
|
+
return item;
|
|
347
|
+
}
|
|
348
|
+
|
|
313
349
|
export const optedin = (state = [], action) => {
|
|
314
350
|
if (constants.SETUP_CART === action.type) {
|
|
315
351
|
const cart = action.payload;
|
|
316
352
|
return state
|
|
317
353
|
.filter(it => !it.id.includes(':'))
|
|
318
|
-
.concat(
|
|
319
|
-
cart.items.reduce(
|
|
320
|
-
(acc, cur) =>
|
|
321
|
-
cur.selling_plan_allocation
|
|
322
|
-
? [...acc, { id: cur.key, frequency: `${cur.selling_plan_allocation.selling_plan.id}` }]
|
|
323
|
-
: acc,
|
|
324
|
-
[]
|
|
325
|
-
)
|
|
326
|
-
);
|
|
354
|
+
.concat(cart.items.reduce((acc, cur) => (cur.selling_plan_allocation ? [...acc, getOptedInItem(cur)] : acc), []));
|
|
327
355
|
}
|
|
328
356
|
|
|
329
357
|
if (constants.RECEIVE_OFFER === action.type) {
|
|
@@ -354,6 +382,19 @@ export const optedin = (state = [], action) => {
|
|
|
354
382
|
});
|
|
355
383
|
}
|
|
356
384
|
|
|
385
|
+
if (constants.PRODUCT_CHANGE_PREPAID_SHIPMENTS === action.type) {
|
|
386
|
+
const { payload } = action;
|
|
387
|
+
// core reducer sets prepaid shipments
|
|
388
|
+
const newState = coreOptedin(state, action);
|
|
389
|
+
// get the new frequency (selling plan) that matches the prepaid shipments
|
|
390
|
+
const [oldone, rest] = getMatchingProductIfExists(newState, payload.product);
|
|
391
|
+
return rest.concat({
|
|
392
|
+
...oldone,
|
|
393
|
+
...payload.product,
|
|
394
|
+
frequency: getFrequencyForPrepaidShipments(payload)
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
357
398
|
return coreOptedin(state, action);
|
|
358
399
|
};
|
|
359
400
|
|
|
@@ -364,11 +405,18 @@ export const productPlans = (state = {}, action) => {
|
|
|
364
405
|
const {
|
|
365
406
|
payload: { product }
|
|
366
407
|
} = action;
|
|
408
|
+
|
|
409
|
+
const sellingPlans = getSellingPlans(product);
|
|
410
|
+
|
|
367
411
|
return (
|
|
412
|
+
// We consider the product here as well for cases where we don't have any variants
|
|
368
413
|
[product, ...product?.variants]?.reduce(
|
|
369
414
|
(acc, cur) => ({
|
|
370
415
|
...acc,
|
|
371
|
-
[cur.id]: cur.selling_plan_allocations?.reduce(
|
|
416
|
+
[cur.id]: cur.selling_plan_allocations?.reduce(
|
|
417
|
+
(accumulator, current) => sellingPlanAllocationsReducer(accumulator, current, sellingPlans),
|
|
418
|
+
[]
|
|
419
|
+
)
|
|
372
420
|
}),
|
|
373
421
|
state
|
|
374
422
|
) || state
|
|
@@ -382,7 +430,7 @@ export const productPlans = (state = {}, action) => {
|
|
|
382
430
|
cur.selling_plan_allocation
|
|
383
431
|
? {
|
|
384
432
|
...acc,
|
|
385
|
-
[cur.key]: sellingPlanAllocationsReducer(
|
|
433
|
+
[cur.key]: sellingPlanAllocationsReducer([], cur.selling_plan_allocation)
|
|
386
434
|
}
|
|
387
435
|
: acc,
|
|
388
436
|
state
|
|
@@ -390,7 +438,7 @@ export const productPlans = (state = {}, action) => {
|
|
|
390
438
|
);
|
|
391
439
|
}
|
|
392
440
|
if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
|
|
393
|
-
return
|
|
441
|
+
return getObjectStructuredProductPlans(action.payload);
|
|
394
442
|
}
|
|
395
443
|
return state;
|
|
396
444
|
};
|
|
@@ -420,7 +468,8 @@ const reducer = combineReducers({
|
|
|
420
468
|
productPlans,
|
|
421
469
|
productToSubscribe,
|
|
422
470
|
sessionId,
|
|
423
|
-
templates
|
|
471
|
+
templates,
|
|
472
|
+
prepaidShipmentsSelected
|
|
424
473
|
});
|
|
425
474
|
|
|
426
475
|
export default function shopifyReducer(state, action) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OPTIN_PRODUCT, OPTOUT_PRODUCT, PRODUCT_CHANGE_FREQUENCY, RECEIVE_OFFER
|
|
1
|
+
import { OPTIN_PRODUCT, OPTOUT_PRODUCT, PRODUCT_CHANGE_FREQUENCY, RECEIVE_OFFER } from '../core/constants';
|
|
2
2
|
import { getTrackingEvent, getSubscribedFrequency } from './shopifyMiddleware';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ProductPlanEntity {
|
|
2
|
+
frequency: string;
|
|
3
|
+
regularPrice: string;
|
|
4
|
+
subscriptionPrice: string;
|
|
5
|
+
discountRate: string;
|
|
6
|
+
prepaidShipments: number;
|
|
7
|
+
regularPrepaidPrice?: string;
|
|
8
|
+
prepaidSavingsPerShipment?: string;
|
|
9
|
+
prepaidSavingsTotal?: string;
|
|
10
|
+
prepaidExtraSavingsPercentage?: string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface ShopifyPriceAdjustmentsEntity {
|
|
2
|
+
position: number;
|
|
3
|
+
price?: number;
|
|
4
|
+
order_count?: null;
|
|
5
|
+
value_type?: string;
|
|
6
|
+
value?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ShopifyAllOptionsEntity {
|
|
10
|
+
name: string;
|
|
11
|
+
position: number;
|
|
12
|
+
values?: string[] | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ShopifyOptionsEntity {
|
|
16
|
+
name: string;
|
|
17
|
+
position: number;
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ShopifySellingPlansEntity {
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
description?: null;
|
|
25
|
+
options?: ShopifyOptionsEntity[] | null;
|
|
26
|
+
recurring_deliveries: boolean;
|
|
27
|
+
price_adjustments?: ShopifyPriceAdjustmentsEntity[] | null;
|
|
28
|
+
group_name?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ShopifySellingPlanGroupsEntity {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
options?: ShopifyAllOptionsEntity[] | null;
|
|
35
|
+
selling_plans?: ShopifySellingPlansEntity[] | null;
|
|
36
|
+
app_id?: null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ShopifySellingPlanAllocationsEntity {
|
|
40
|
+
price_adjustments?: ShopifyPriceAdjustmentsEntity[] | null;
|
|
41
|
+
price: number;
|
|
42
|
+
compare_at_price: number;
|
|
43
|
+
per_delivery_price: number;
|
|
44
|
+
selling_plan_id: number;
|
|
45
|
+
selling_plan_group_id: string;
|
|
46
|
+
selling_plan?: ShopifySellingPlansEntity;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ShopifyVariantsEntity {
|
|
50
|
+
id: number;
|
|
51
|
+
title: string;
|
|
52
|
+
option1: string;
|
|
53
|
+
option2?: null;
|
|
54
|
+
option3?: null;
|
|
55
|
+
sku: string;
|
|
56
|
+
requires_shipping: boolean;
|
|
57
|
+
taxable: boolean;
|
|
58
|
+
featured_image?: null;
|
|
59
|
+
available: boolean;
|
|
60
|
+
name: string;
|
|
61
|
+
public_title: string;
|
|
62
|
+
options?: string[] | null;
|
|
63
|
+
price: number;
|
|
64
|
+
weight: number;
|
|
65
|
+
compare_at_price?: null;
|
|
66
|
+
inventory_management: string;
|
|
67
|
+
barcode: string;
|
|
68
|
+
requires_selling_plan: boolean;
|
|
69
|
+
selling_plan_allocations?: ShopifySellingPlanAllocationsEntity[] | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ShopifyProductEntity {
|
|
73
|
+
id: number;
|
|
74
|
+
title: string;
|
|
75
|
+
handle: string;
|
|
76
|
+
description: string;
|
|
77
|
+
published_at: string;
|
|
78
|
+
created_at: string;
|
|
79
|
+
vendor: string;
|
|
80
|
+
type: string;
|
|
81
|
+
tags?: null[] | null;
|
|
82
|
+
price: number;
|
|
83
|
+
price_min: number;
|
|
84
|
+
price_max: number;
|
|
85
|
+
available: boolean;
|
|
86
|
+
price_varies: boolean;
|
|
87
|
+
compare_at_price?: null;
|
|
88
|
+
compare_at_price_min: number;
|
|
89
|
+
compare_at_price_max: number;
|
|
90
|
+
compare_at_price_varies: boolean;
|
|
91
|
+
variants?: ShopifyVariantsEntity[] | null;
|
|
92
|
+
images?: null[] | null;
|
|
93
|
+
featured_image?: null;
|
|
94
|
+
options?: ShopifyAllOptionsEntity[] | null;
|
|
95
|
+
url: string;
|
|
96
|
+
requires_selling_plan: boolean;
|
|
97
|
+
selling_plan_groups?: ShopifySellingPlanGroupsEntity[] | null;
|
|
98
|
+
}
|