@ordergroove/offers 2.26.11 → 2.27.1

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 (56) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +34 -0
  3. package/build.js +3 -1
  4. package/dist/bundle-report.html +185 -116
  5. package/dist/offers.js +65 -76
  6. package/dist/offers.js.map +3 -3
  7. package/examples/cart.js +105 -0
  8. package/examples/index.html +2 -2
  9. package/examples/products/cheap-watch.js +183 -0
  10. package/examples/shopify-cart.html +26 -0
  11. package/examples/shopify-pdp.html +34 -0
  12. package/karma.conf.js +2 -1
  13. package/package.json +4 -4
  14. package/src/__tests__/offers.spec.js +35 -10
  15. package/src/components/FrequencyStatus.js +14 -11
  16. package/src/components/IncentiveText.js +2 -1
  17. package/src/components/Offer.js +14 -7
  18. package/src/components/OptinButton.js +1 -1
  19. package/src/components/OptinSelect.js +2 -2
  20. package/src/components/OptinToggle.js +2 -2
  21. package/src/components/OptoutButton.js +1 -1
  22. package/src/components/Price.js +7 -3
  23. package/src/components/Select.js +3 -13
  24. package/src/components/SelectFrequency.js +24 -6
  25. package/src/components/TestWizard.js +1 -1
  26. package/src/components/__tests__/OG.fspec.js +24 -0
  27. package/src/components/__tests__/Offer.spec.js +4 -4
  28. package/src/components/__tests__/OptinButton.spec.js +2 -2
  29. package/src/components/__tests__/OptinToggle.spec.js +2 -2
  30. package/src/components/__tests__/OptoutButton.spec.js +1 -1
  31. package/src/components/__tests__/SelectFrequency.fspec.js +1 -0
  32. package/src/components/__tests__/SelectFrequency.spec.js +1 -1
  33. package/src/components/__tests__/TestWizard.spec.js +2 -2
  34. package/src/components/__tests__/Text.spec.js +5 -1
  35. package/src/core/__tests__/actions.spec.js +6 -6
  36. package/src/core/actions.js +22 -17
  37. package/src/core/constants.js +21 -0
  38. package/src/core/descriptors.js +2 -1
  39. package/src/core/middleware.js +41 -1
  40. package/src/core/reducer.js +22 -21
  41. package/src/core/resolveProperties.js +2 -7
  42. package/src/core/selectors.js +1 -1
  43. package/src/core/store.js +17 -9
  44. package/src/core/utils.ts +67 -0
  45. package/src/index.js +46 -203
  46. package/src/make-api.js +195 -0
  47. package/src/platform.ts +9 -0
  48. package/src/shopify/__tests__/shopifyMiddleware.spec.js +126 -0
  49. package/src/shopify/__tests__/shopifyReducer.spec.js +489 -0
  50. package/src/shopify/shopifyBootstrap.ts +136 -0
  51. package/src/shopify/shopifyMiddleware.ts +336 -0
  52. package/src/shopify/shopifyReducer.js +254 -0
  53. package/tsconfig.json +35 -0
  54. package/examples/5starnutrition-main.js +0 -3
  55. package/examples/single-offer.html +0 -9
  56. package/src/init-test.js +0 -3
@@ -0,0 +1,254 @@
1
+ /* eslint-disable no-case-declarations */
2
+ import { combineReducers } from 'redux';
3
+ import * as constants from '../core/constants';
4
+
5
+ import {
6
+ autoshipByDefault,
7
+ auth,
8
+ authUrl,
9
+ defaultFrequencies,
10
+ eligibilityGroups,
11
+ environment,
12
+ firstOrderPlaceDate,
13
+ incentives,
14
+ locale,
15
+ merchantId,
16
+ nextUpcomingOrder,
17
+ optedin as coreOptedin,
18
+ optedout,
19
+ previewStandardOffer,
20
+ previewUpsellOffer,
21
+ productToSubscribe,
22
+ sessionId,
23
+ templates
24
+ } from '../core/reducer';
25
+ import { safeProductId } from '../core/utils';
26
+
27
+ const money = val => (val === null ? '' : `$${val.toString().replace(/(\d\d)$/, '.$1')}`);
28
+
29
+ const percentage = val => `${val}%`;
30
+
31
+ const mapSellingPlanToDiscount = allocation => {
32
+ let formatted_discount = '';
33
+ if (allocation.price_adjustments[0]?.value_type === 'percentage') {
34
+ formatted_discount = percentage(allocation.price_adjustments[0].value);
35
+ } else if (allocation.price_adjustments[0]?.value) {
36
+ formatted_discount = money(allocation.price_adjustments[0].value);
37
+ } else if (allocation.compare_at_price) {
38
+ formatted_discount = money(allocation.compare_at_price - allocation.price);
39
+ }
40
+
41
+ if (formatted_discount) {
42
+ return [money(allocation.compare_at_price), formatted_discount, money(allocation.price)];
43
+ }
44
+
45
+ return [money(allocation.price), '', money(allocation.price)];
46
+ };
47
+
48
+ const overrideLineKey = (state, productId, newValue) => {
49
+ const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
50
+ if (keys.length) {
51
+ return { ...state, ...keys.reduce((acc, cur) => ({ ...acc, [cur]: newValue }), {}) };
52
+ }
53
+ return state;
54
+ };
55
+
56
+ const getOGSellingPlanGroup = product => {
57
+ const sellingPlanGroup = product?.selling_plan_groups.find(group => {
58
+ return group.name === 'Subscribe and Save';
59
+ });
60
+ return sellingPlanGroup;
61
+ };
62
+
63
+ const productOrVariantInStockReducer = (acc, cur) => ({
64
+ ...overrideLineKey(acc, cur.id, cur.available),
65
+ [cur.id]: cur.available
66
+ });
67
+
68
+ const productTrue = (acc, [id]) => ({ ...acc, [id]: true });
69
+
70
+ const sellingPlanAllocationsReducer = (acc, cur) => ({
71
+ ...acc,
72
+ // now frequency every_period will match with selling_plan_id so no need to convert it
73
+ [cur.selling_plan_id || cur.selling_plan?.id]: mapSellingPlanToDiscount(cur)
74
+ });
75
+
76
+ const reduceProductCartLine = (acc, cur) => {
77
+ const productId = safeProductId(cur.key);
78
+ return { ...acc, [cur.key]: acc[productId] || null };
79
+ };
80
+
81
+ export const autoshipEligible = (state = {}, action) => {
82
+ if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
83
+ return Object.entries(action.payload).reduce(productTrue, state);
84
+ }
85
+ if (constants.SETUP_CART === action.type) {
86
+ const { payload: cart } = action;
87
+ return cart.items.reduce(reduceProductCartLine, state);
88
+ }
89
+ if (constants.SETUP_PRODUCT === action.type) {
90
+ const { payload: product } = action;
91
+ return [product, ...(product?.variants || [])]?.reduce(
92
+ (acc, cur) => ({
93
+ ...overrideLineKey(acc, cur.id, cur.selling_plan_allocations?.length > 0),
94
+ [cur.id]: cur.selling_plan_allocations?.length > 0
95
+ }),
96
+ state
97
+ );
98
+ }
99
+ return state;
100
+ };
101
+
102
+ export const config = (
103
+ state = {
104
+ frequencies: [],
105
+ offerType: 'radio'
106
+ },
107
+ action
108
+ ) => {
109
+ if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
110
+ const frequencies = [
111
+ ...new Set(
112
+ Object.values(action.payload)
113
+ .map(Object.keys)
114
+ .flat()
115
+ )
116
+ ];
117
+ return {
118
+ ...state,
119
+ frequencies
120
+ };
121
+ }
122
+
123
+ if (constants.SETUP_PRODUCT === action.type) {
124
+ const product = action.payload;
125
+ const sellingPlanGroup = getOGSellingPlanGroup(product);
126
+ const frequencies = sellingPlanGroup?.selling_plans?.map(({ id }) => `${id}`);
127
+ if (frequencies?.length) {
128
+ const frequenciesText = sellingPlanGroup.options?.[0]?.values || frequencies;
129
+ return {
130
+ ...state,
131
+ defaultFrequency: frequencies[0],
132
+ frequencies,
133
+ frequenciesText
134
+ };
135
+ }
136
+ }
137
+
138
+ return state;
139
+ };
140
+
141
+ export const inStock = (state = {}, action) => {
142
+ if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
143
+ return Object.entries(action.payload).reduce(productTrue, state);
144
+ }
145
+
146
+ if (constants.SETUP_CART === action.type) {
147
+ const cart = action.payload;
148
+
149
+ return cart.items.reduce(reduceProductCartLine, state);
150
+ }
151
+
152
+ if (constants.SETUP_PRODUCT === action.type) {
153
+ const product = action.payload;
154
+
155
+ return [product, ...product?.variants]?.reduce(productOrVariantInStockReducer, state) || state;
156
+ }
157
+
158
+ return state;
159
+ };
160
+
161
+ export const offer = (state = {}, action) => state;
162
+
163
+ export const offerId = (state = '', action) => 'native-shopify-offer';
164
+
165
+ export const optedin = (state = [], action) => {
166
+ if (constants.SETUP_CART === action.type) {
167
+ const cart = action.payload;
168
+ return cart.items.reduce(
169
+ (acc, cur) =>
170
+ cur.selling_plan_allocation
171
+ ? [...acc, { id: cur.key, frequency: `${cur.selling_plan_allocation.selling_plan.id}` }]
172
+ : acc,
173
+ []
174
+ );
175
+ }
176
+ if (constants.RECEIVE_OFFER === action.type) {
177
+ const { autoship, autoship_by_default, in_stock, offer: offerEl } = action.payload;
178
+
179
+ return Object.keys(autoship).reduce(
180
+ (acc, id) =>
181
+ acc.concat(
182
+ !acc.some(it => it.id === id) && autoship[id] && autoship_by_default[id] && in_stock[id]
183
+ ? { id, frequency: offerEl.defaultFrequency }
184
+ : []
185
+ ),
186
+ state
187
+ );
188
+ }
189
+ return coreOptedin(state, action);
190
+ };
191
+
192
+ export const productOffer = (state = {}, action) => state;
193
+
194
+ export const productPlans = (state = {}, action) => {
195
+ if (constants.SETUP_PRODUCT === action.type) {
196
+ const product = action.payload;
197
+ return (
198
+ [product, ...product?.variants]?.reduce(
199
+ (acc, cur) => ({
200
+ ...acc,
201
+ [cur.id]: cur.selling_plan_allocations?.reduce(sellingPlanAllocationsReducer, {})
202
+ }),
203
+ state
204
+ ) || state
205
+ );
206
+ }
207
+ if (constants.SETUP_CART === action.type) {
208
+ const cart = action.payload;
209
+ return (
210
+ cart.items.reduce(
211
+ (acc, cur) =>
212
+ cur.selling_plan_allocation
213
+ ? {
214
+ ...acc,
215
+ [cur.key]: sellingPlanAllocationsReducer({}, cur.selling_plan_allocation)
216
+ }
217
+ : acc,
218
+ state
219
+ ) || state
220
+ );
221
+ }
222
+ if (constants.RECEIVE_PRODUCT_PLANS === action.type) {
223
+ return { ...action.payload };
224
+ }
225
+ return state;
226
+ };
227
+
228
+ export default combineReducers({
229
+ auth,
230
+ authUrl,
231
+ autoshipByDefault,
232
+ autoshipEligible,
233
+ config,
234
+ defaultFrequencies,
235
+ eligibilityGroups,
236
+ environment,
237
+ firstOrderPlaceDate,
238
+ incentives,
239
+ inStock,
240
+ locale,
241
+ merchantId,
242
+ nextUpcomingOrder,
243
+ offer,
244
+ offerId,
245
+ optedin,
246
+ optedout,
247
+ previewStandardOffer,
248
+ previewUpsellOffer,
249
+ productOffer,
250
+ productPlans,
251
+ productToSubscribe,
252
+ sessionId,
253
+ templates
254
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noEmit": true,
4
+ "composite": true,
5
+ "target": "es2019",
6
+ "module": "es2020",
7
+ "lib": ["es2020", "DOM", "DOM.Iterable"],
8
+ "rootDir": "./src",
9
+ "moduleResolution": "node",
10
+ "allowSyntheticDefaultImports": true,
11
+ "stripInternal": true,
12
+ "noImplicitOverride": true,
13
+ "allowJs": true
14
+ },
15
+ "include": [
16
+ "node_modules/lit-html/lit-html.d.ts",
17
+ "node_modules/dayjs-es/index.d.ts",
18
+ "node_modules/lodash-es/index.d.ts",
19
+ "src/**/*.ts",
20
+ "src/**/*.js"
21
+ ],
22
+ "typedocOptions": {
23
+ "name": "SMI",
24
+ "entryPoints": [
25
+ "./src/index.ts",
26
+ "./src/shopify.ts",
27
+ ],
28
+ "out": "docs/",
29
+ "readme": "none",
30
+ "disableSources": true,
31
+
32
+ "categoryOrder": ["Type aliases", "*"],
33
+ "validation": { "invalidLink": true }
34
+ }
35
+ }
@@ -1,3 +0,0 @@
1
- import offers from '../src';
2
-
3
- offers('bc678090fa6211e9b99abc764e10b970', 'prod', false);
@@ -1,9 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Ordergroove Offers</title>
5
- </head>
6
- <body id="single-offer" onload="og.offers('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging')">
7
- <og-offer product="UD729" id="regular1" preview-standard-offer></og-offer>
8
- </body>
9
- </html>
package/src/init-test.js DELETED
@@ -1,3 +0,0 @@
1
- import { initialize } from './index';
2
-
3
- initialize('some-merchant', 'staging');