@nakedwines/naked-components 0.0.3
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 +33 -0
- package/build-utils/update-library.js +1 -0
- package/library/globals.js +4 -0
- package/library/index.js +69 -0
- package/package.json +26 -0
- package/src/components/badges/Badge.vue +20 -0
- package/src/components/badges/Badge.vue?1b45 +1 -0
- package/src/components/badges/Badge.vue?5424 +10 -0
- package/src/components/badges/Badge.vue?c5b6 +1 -0
- package/src/components/banners/Banner.vue +20 -0
- package/src/components/banners/Banner.vue?2006 +1 -0
- package/src/components/banners/Banner.vue?36d1 +1 -0
- package/src/components/banners/Banner.vue?9b97 +4 -0
- package/src/components/buttons/Button.vue +21 -0
- package/src/components/buttons/Button.vue?2035 +1 -0
- package/src/components/buttons/Button.vue?5c96 +1 -0
- package/src/components/buttons/Button.vue?838e +26 -0
- package/src/components/buttons/Button.vue?e88c +1 -0
- package/src/components/buttons/LoadingButton.vue +20 -0
- package/src/components/buttons/LoadingButton.vue?2f5e +17 -0
- package/src/components/buttons/LoadingButton.vue?4a65 +1 -0
- package/src/components/buttons/LoadingButton.vue?65be +1 -0
- package/src/components/buttons/Ripple.vue +20 -0
- package/src/components/buttons/Ripple.vue?130a +1 -0
- package/src/components/buttons/Ripple.vue?2026 +13 -0
- package/src/components/buttons/Ripple.vue?4835 +1 -0
- package/src/components/cards/ContentCard.vue +20 -0
- package/src/components/cards/ContentCard.vue?35c8 +10 -0
- package/src/components/cards/ContentCard.vue?7164 +1 -0
- package/src/components/cards/ContentCard.vue?ec15 +1 -0
- package/src/components/cards/bottle-cards/CaseContentsCard.vue +20 -0
- package/src/components/cards/bottle-cards/CaseContentsCard.vue?3ac7 +1 -0
- package/src/components/cards/bottle-cards/CaseContentsCard.vue?7e00 +4 -0
- package/src/components/cards/bottle-cards/CaseContentsCard.vue?dd6f +1 -0
- package/src/components/cards/bottle-cards/CaseProductCard.vue +21 -0
- package/src/components/cards/bottle-cards/CaseProductCard.vue?0cb0 +1 -0
- package/src/components/cards/bottle-cards/CaseProductCard.vue?1bd2 +1 -0
- package/src/components/cards/bottle-cards/CaseProductCard.vue?25e9 +4 -0
- package/src/components/cards/bottle-cards/CaseProductCard.vue?95f9 +1 -0
- package/src/components/cards/bottle-cards/WineBottleCard.vue +21 -0
- package/src/components/cards/bottle-cards/WineBottleCard.vue?61a8 +1 -0
- package/src/components/cards/bottle-cards/WineBottleCard.vue?7e9a +1 -0
- package/src/components/cards/bottle-cards/WineBottleCard.vue?bd3e +4 -0
- package/src/components/cards/bottle-cards/WineBottleCard.vue?bdcc +1 -0
- package/src/components/cards/bottle-cards/components/BiaText.vue +20 -0
- package/src/components/cards/bottle-cards/components/BiaText.vue?123e +1 -0
- package/src/components/cards/bottle-cards/components/BiaText.vue?3e10 +1 -0
- package/src/components/cards/bottle-cards/components/BiaText.vue?d135 +4 -0
- package/src/components/cards/bottle-cards/components/HeartRatingPanel.vue +20 -0
- package/src/components/cards/bottle-cards/components/HeartRatingPanel.vue?712f +4 -0
- package/src/components/cards/bottle-cards/components/HeartRatingPanel.vue?c55a +1 -0
- package/src/components/cards/bottle-cards/components/HeartRatingPanel.vue?ee3b +1 -0
- package/src/components/cards/bottle-cards/components/PriceBreakdownPanel.vue +20 -0
- package/src/components/cards/bottle-cards/components/PriceBreakdownPanel.vue?5052 +1 -0
- package/src/components/cards/bottle-cards/components/PriceBreakdownPanel.vue?5838 +4 -0
- package/src/components/cards/bottle-cards/components/PriceBreakdownPanel.vue?7aad +1 -0
- package/src/components/cards/bottle-cards/components/WineDetailPanel.vue +20 -0
- package/src/components/cards/bottle-cards/components/WineDetailPanel.vue?5047 +1 -0
- package/src/components/cards/bottle-cards/components/WineDetailPanel.vue?8e3b +1 -0
- package/src/components/cards/bottle-cards/components/WineDetailPanel.vue?d1ea +4 -0
- package/src/components/carousel/Carousel.vue +21 -0
- package/src/components/carousel/Carousel.vue?492f +1 -0
- package/src/components/carousel/Carousel.vue?4e80 +4 -0
- package/src/components/carousel/Carousel.vue?66a0 +1 -0
- package/src/components/carousel/Carousel.vue?72ce +1 -0
- package/src/components/chips/Chip.vue +20 -0
- package/src/components/chips/Chip.vue?7e2c +1 -0
- package/src/components/chips/Chip.vue?9563 +4 -0
- package/src/components/chips/Chip.vue?f42c +1 -0
- package/src/components/footers/sticky-footer/StickyFooter.vue +20 -0
- package/src/components/footers/sticky-footer/StickyFooter.vue?10bf +4 -0
- package/src/components/footers/sticky-footer/StickyFooter.vue?b195 +1 -0
- package/src/components/footers/sticky-footer/StickyFooter.vue?e2d9 +1 -0
- package/src/components/forms/CheckboxInput.vue +20 -0
- package/src/components/forms/CheckboxInput.vue?1713 +1 -0
- package/src/components/forms/CheckboxInput.vue?51c9 +1 -0
- package/src/components/forms/CheckboxInput.vue?feea +4 -0
- package/src/components/forms/QuantityInput.vue +20 -0
- package/src/components/forms/QuantityInput.vue?233a +25 -0
- package/src/components/forms/QuantityInput.vue?4975 +1 -0
- package/src/components/forms/QuantityInput.vue?bb30 +1 -0
- package/src/components/forms/SelectInput.vue +172 -0
- package/src/components/forms/SelectInput.vue?1d47 +1 -0
- package/src/components/forms/SelectInput.vue?642a +21 -0
- package/src/components/forms/SelectInput.vue?7590 +20 -0
- package/src/components/forms/SelectInput.vue?f210 +1 -0
- package/src/components/forms/SelectInput.vue?fc78 +1 -0
- package/src/components/icons/CustomisableHeart.vue +19 -0
- package/src/components/icons/CustomisableHeart.vue?30ab +1 -0
- package/src/components/icons/CustomisableHeart.vue?ce76 +4 -0
- package/src/components/icons/Icon.vue +21 -0
- package/src/components/icons/Icon.vue?47ee +9 -0
- package/src/components/icons/Icon.vue?6e26 +1 -0
- package/src/components/icons/Icon.vue?897e +1 -0
- package/src/components/icons/Icon.vue?e753 +1 -0
- package/src/components/modal/ProductDetailModal.vue +20 -0
- package/src/components/modal/ProductDetailModal.vue?5d3a +1 -0
- package/src/components/modal/ProductDetailModal.vue?9f0f +1 -0
- package/src/components/modal/ProductDetailModal.vue?a2a9 +4 -0
- package/src/components/popover/Popover.vue +20 -0
- package/src/components/popover/Popover.vue?088e +1 -0
- package/src/components/popover/Popover.vue?4dad +1 -0
- package/src/components/popover/Popover.vue?7034 +7 -0
- package/src/components/roundels/Roundel.vue +20 -0
- package/src/components/roundels/Roundel.vue?14bf +7 -0
- package/src/components/roundels/Roundel.vue?a97a +1 -0
- package/src/components/roundels/Roundel.vue?aa28 +1 -0
- package/src/components/shelf/Shelf.vue +20 -0
- package/src/components/shelf/Shelf.vue?50f3 +1 -0
- package/src/components/shelf/Shelf.vue?9eba +1 -0
- package/src/components/shelf/Shelf.vue?d8e7 +4 -0
- package/src/globals/assortments.js +80 -0
- package/src/globals/csrf.js +25 -0
- package/src/globals/functions.js +166 -0
- package/src/globals/products.js +251 -0
- package/src/globals/responsive.js +53 -0
- package/src/globals/validation.js +8 -0
- package/src/globals/variables.js +48 -0
- package/src/mixins/productCard.js +151 -0
- package/src/styles/js-modules/breakpoints.scss +2 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/* eslint-disable no-prototype-builtins */
|
|
2
|
+
|
|
3
|
+
import { csrf } from './csrf';
|
|
4
|
+
import { Assortments } from './assortments';
|
|
5
|
+
import { Logger } from './functions';
|
|
6
|
+
import { styleNamesIds, styleColours } from './variables';
|
|
7
|
+
import 'whatwg-fetch';
|
|
8
|
+
|
|
9
|
+
export const Products = {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fetches a list of products
|
|
13
|
+
* @param {Array} productIds the list of product IDs
|
|
14
|
+
* @returns {Array} returns an array of products
|
|
15
|
+
*/
|
|
16
|
+
getProductsById({ productIds }) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
if (!productIds) {
|
|
19
|
+
reject({ error: 'productId is required' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fetch(`/products?productIds=${productIds}`, {
|
|
24
|
+
'method': 'GET'
|
|
25
|
+
}).then(response => {
|
|
26
|
+
return resolve(response.json());
|
|
27
|
+
}).catch(error => reject({ error: error }));
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Adds a product to basket
|
|
33
|
+
* @param {Array} productId the product ID to add to basket
|
|
34
|
+
* @param {Number} quantity quantity to add to basket
|
|
35
|
+
* @returns {Object} returns a success/error response
|
|
36
|
+
*/
|
|
37
|
+
addToBasket({ productId, quantity = 1 }) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
if (!productId) {
|
|
40
|
+
reject({ error: 'productId is required' });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const headers = new Headers(csrf.header);
|
|
45
|
+
const formData = new FormData();
|
|
46
|
+
formData.append('productId', productId.toString());
|
|
47
|
+
formData.append('quantity', quantity.toString());
|
|
48
|
+
|
|
49
|
+
fetch('/customer/add-to-cart.json', {
|
|
50
|
+
'method': 'POST',
|
|
51
|
+
'headers': headers,
|
|
52
|
+
'body': formData
|
|
53
|
+
}).then(response => {
|
|
54
|
+
return resolve(response.json());
|
|
55
|
+
}).catch(error => reject({ error: error }));
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Displays an add to basket notification in the header
|
|
61
|
+
* @param {String} productName the product name to display
|
|
62
|
+
*/
|
|
63
|
+
showATBConfirmation(productName) {
|
|
64
|
+
if (window && window.Notify && window.Notify.CoinPurse) {
|
|
65
|
+
const config = {
|
|
66
|
+
settings: { purseType: 'product-added' },
|
|
67
|
+
productName: productName,
|
|
68
|
+
autoDismiss: true,
|
|
69
|
+
timeout: 4000
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
window.Notify.CoinPurse.show(config.settings, config.productName, config.autoDismiss, config.timeout);
|
|
73
|
+
|
|
74
|
+
if (window.nkd && window.nkd.headerCart) {
|
|
75
|
+
window.nkd.headerCart.reload();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
_parseProduct(data) {
|
|
81
|
+
let model = null;
|
|
82
|
+
let type;
|
|
83
|
+
let imageAssortmentKey;
|
|
84
|
+
let imageMediumCutout = 'mediumCutout';
|
|
85
|
+
|
|
86
|
+
// skip item if it's not in the right format
|
|
87
|
+
if (!data || !data.object) { Logger.log('_parseProduct skipping item', "'object' property not found"); return null; }
|
|
88
|
+
|
|
89
|
+
// wrap in try catch so we can skip an item if the data is inconsistent
|
|
90
|
+
try {
|
|
91
|
+
// use different keys if it's a case rather than a single product
|
|
92
|
+
if (data && data.object && data.object.productTypeDesc && data.object.productTypeDesc == 'Case') {
|
|
93
|
+
type = 'case-product';
|
|
94
|
+
imageAssortmentKey = 'headline';
|
|
95
|
+
} else {
|
|
96
|
+
type = 'single-product';
|
|
97
|
+
imageAssortmentKey = 'background';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
model = {
|
|
101
|
+
id: data.object.id,
|
|
102
|
+
type: type,
|
|
103
|
+
data: {
|
|
104
|
+
id: data.object.id,
|
|
105
|
+
heading: data.object.productName,
|
|
106
|
+
subHeading: null,
|
|
107
|
+
thumbnail: '/merchandising/content/wines/default-content/background.jpg',
|
|
108
|
+
button: null,
|
|
109
|
+
pricesCurrent: {
|
|
110
|
+
prices: {}
|
|
111
|
+
},
|
|
112
|
+
positiveRatingPercentage: null,
|
|
113
|
+
numberOfRatings: null,
|
|
114
|
+
angelOnly: data.object.angelOnly,
|
|
115
|
+
tickpoints: null,
|
|
116
|
+
tickpointsCase: null,
|
|
117
|
+
taste: null,
|
|
118
|
+
originForSpiritsOrBeer: data.object.originForSpiritsOrBeer,
|
|
119
|
+
style: {},
|
|
120
|
+
percentageMatch: null,
|
|
121
|
+
likeThis: null,
|
|
122
|
+
producer: {},
|
|
123
|
+
url: null,
|
|
124
|
+
productTypeDesc: null,
|
|
125
|
+
productQuantity: null,
|
|
126
|
+
scheme: {},
|
|
127
|
+
assortmentObjects: {},
|
|
128
|
+
origin: '',
|
|
129
|
+
wineGrape: '',
|
|
130
|
+
hideFromView: false,
|
|
131
|
+
hideRatings: false,
|
|
132
|
+
specialDelivery: false,
|
|
133
|
+
freeShipping: false,
|
|
134
|
+
bia: {},
|
|
135
|
+
vegan: false,
|
|
136
|
+
vegetarian: false,
|
|
137
|
+
alcoholStrength: null,
|
|
138
|
+
bottleSizeDesc: '',
|
|
139
|
+
promotionalPrice: null,
|
|
140
|
+
angelPrices: false
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
// attach ratings
|
|
144
|
+
if (data.object.hasOwnProperty('ratingData')) {
|
|
145
|
+
model.data.positiveRatingPercentage = data.object.ratingData.positiveRatingPercentage;
|
|
146
|
+
model.data.numberOfRatings = data.object.ratingData.numberOfRatings;
|
|
147
|
+
model.data.hideRatings = data.object.ratingData.hideRatings;
|
|
148
|
+
}
|
|
149
|
+
// attach pricing
|
|
150
|
+
if (data.object.pricesCurrent && data.object.pricesCurrent.prices) {
|
|
151
|
+
model.data.pricesCurrent.prices = data.object.pricesCurrent.prices;
|
|
152
|
+
// attach ATB button (customer price)
|
|
153
|
+
if (data.object.pricesCurrent.prices.customerPrice) { model.data.button = data.object.pricesCurrent.prices.customerPrice; }
|
|
154
|
+
}
|
|
155
|
+
if (data.object.assortmentObjects) {
|
|
156
|
+
// attach thumbnail image
|
|
157
|
+
let thumbnail = Assortments._getAssortmentObject(data.object.assortmentObjects, imageAssortmentKey);
|
|
158
|
+
if (thumbnail) model.data.thumbnail = thumbnail;
|
|
159
|
+
// attach bottleImage
|
|
160
|
+
let bottleImage = Assortments._getAssortmentObject(data.object.assortmentObjects, imageMediumCutout);
|
|
161
|
+
if (bottleImage) model.data.bottleImage = bottleImage;
|
|
162
|
+
// attach tickpoints
|
|
163
|
+
let tickpoints = Assortments._getAssortmentObject(data.object.assortmentObjects, 'whatWeThink');
|
|
164
|
+
if (tickpoints) model.data.tickpoints = tickpoints;
|
|
165
|
+
let tickpointsCase = Assortments._getAssortmentObject(data.object.assortmentObjects, 'whatWeSay');
|
|
166
|
+
if (tickpointsCase) model.data.tickpointsCase = tickpointsCase;
|
|
167
|
+
}
|
|
168
|
+
// attach style data
|
|
169
|
+
if (data.object.taste) {
|
|
170
|
+
model.data.style = this._getWineStyleByName(data.object.taste);
|
|
171
|
+
// attach sub style data (modern/classic)
|
|
172
|
+
if (data.object.productSubStyle) { model.data.style.subStyle = data.object.productSubStyle.toLowerCase(); }
|
|
173
|
+
}
|
|
174
|
+
// attach producer data
|
|
175
|
+
if (data.object.producer) {
|
|
176
|
+
model.data.producer.firstName = data.object.producer.firstName;
|
|
177
|
+
model.data.producer.lastName = data.object.producer.lastName;
|
|
178
|
+
model.data.producer.url = data.object.producer.url;
|
|
179
|
+
model.data.producer.urlString = data.object.producer.urlString;
|
|
180
|
+
}
|
|
181
|
+
// attach url
|
|
182
|
+
if (data.object.url) { model.data.url = data.object.url; }
|
|
183
|
+
// attach percentage match and like this
|
|
184
|
+
if (data.object.customerProductLikeThis) {
|
|
185
|
+
let likeData = data.object.customerProductLikeThis;
|
|
186
|
+
|
|
187
|
+
if (likeData.hasOwnProperty('likeThis')) { model.data.likeThis = likeData.likeThis; }
|
|
188
|
+
|
|
189
|
+
if (likeData.custProdRecommendation && likeData.custProdRecommendation.hasOwnProperty('score')) {
|
|
190
|
+
model.data.percentageMatch = likeData.custProdRecommendation.score;
|
|
191
|
+
const percVal = likeData.custProdRecommendation.score;
|
|
192
|
+
let formatted = (typeof percVal !== 'number') ? percVal : `${(percVal * 100).toFixed(0)}%`;
|
|
193
|
+
model.data.percentageMatchFormatted = formatted;
|
|
194
|
+
model.data.subHeading = formatted + ' Match';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// attach type description
|
|
198
|
+
if (data.object.productTypeDesc) { model.data.productTypeDesc = data.object.productTypeDesc; }
|
|
199
|
+
// attach product quantity
|
|
200
|
+
if (data.object.productQuantity) { model.data.productQuantity = data.object.productQuantity; }
|
|
201
|
+
// attached bottle quantity
|
|
202
|
+
if(data.object.quantity) { model.data.quantity = data.object.quantity; }
|
|
203
|
+
// attach scheme
|
|
204
|
+
if (data.object.scheme && data.object.scheme.id) { model.data.scheme = data.object.scheme; }
|
|
205
|
+
// attach assortment objects
|
|
206
|
+
if (data.object.assortmentObjects && data.object.assortmentObjects.objects && data.object.assortmentObjects.objects.length > 0) {
|
|
207
|
+
data.object.assortmentObjects.objects.forEach(ao => {
|
|
208
|
+
model.data.assortmentObjects[ao.name] = ao;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// attach shipping data
|
|
212
|
+
if (data.object.hasOwnProperty('shipAsCase')) { model.data.shipAsCase = data.object.shipAsCase; }
|
|
213
|
+
if (data.object.hasOwnProperty('packingSlots')) { model.data.packingSlots = data.object.packingSlots; }
|
|
214
|
+
// attach more info
|
|
215
|
+
if (data.object.hasOwnProperty('origin')) { model.data.origin = data.object.origin; }
|
|
216
|
+
if (data.object.hasOwnProperty('taste')) { model.data.taste = data.object.taste; }
|
|
217
|
+
if (data.object.hasOwnProperty('wineGrape')) { model.data.wineGrape = data.object.wineGrape; }
|
|
218
|
+
if (data.object.hasOwnProperty('hideFromView')) { model.data.hideFromView = data.object.hideFromView; }
|
|
219
|
+
if (data.object.hasOwnProperty('specialDelivery')) { model.data.specialDelivery = data.object.specialDelivery; }
|
|
220
|
+
if (data.object.hasOwnProperty('freeShipping')) { model.data.freeShipping = data.object.freeShipping; }
|
|
221
|
+
if (data.object.productRatingBreakdownList && data.object.productRatingBreakdownList.length > 0) { model.data.bia = data.object.productRatingBreakdownList[0]; }
|
|
222
|
+
if (data.object.hasOwnProperty('vegan')) { model.data.vegan = data.object.vegan; }
|
|
223
|
+
if (data.object.hasOwnProperty('vegetarian')) { model.data.vegetarian = data.object.vegetarian; }
|
|
224
|
+
if (data.object.hasOwnProperty('alcoholStrength')) { model.data.alcoholStrength = data.object.alcoholStrength; }
|
|
225
|
+
if (data.object.hasOwnProperty('bottleSizeDesc')) { model.data.bottleSizeDesc = data.object.bottleSizeDesc; }
|
|
226
|
+
if (data.object.hasOwnProperty('averageRating')) { model.data.averageRating = data.object.averageRating; }
|
|
227
|
+
if (data.object.hasOwnProperty('region')) { model.data.region = data.object.region; }
|
|
228
|
+
// for free bottles
|
|
229
|
+
if(data.promotionalPrice !== null && data.promotionalPrice === 0) { model.data.promotionalPrice = data.promotionalPrice; }
|
|
230
|
+
if(data.angelPrices !== null) { model.data.angelPrices = data.angelPrices; }
|
|
231
|
+
}
|
|
232
|
+
catch(error) { console.error('_parseProduct skipping item', error); return null; }
|
|
233
|
+
return model;
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// get the wine style from data prop
|
|
237
|
+
_getWineStyleByName(styleName) {
|
|
238
|
+
let result = {};
|
|
239
|
+
if (!styleName) return result;
|
|
240
|
+
if (styleNamesIds[styleName]) {
|
|
241
|
+
let styleId = styleNamesIds[styleName];
|
|
242
|
+
let colours = styleColours[styleId];
|
|
243
|
+
result = {
|
|
244
|
+
name: styleName,
|
|
245
|
+
id: styleId,
|
|
246
|
+
colours: colours
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
},
|
|
251
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Method used for creating the computed breakpoint objects
|
|
3
|
+
* for use.
|
|
4
|
+
* @param {[Object, Number]} propToUse - the prop to use for creating the object
|
|
5
|
+
* @param {Object} defaults - default settings to use
|
|
6
|
+
* @return {Object} - containing breakpoints and values
|
|
7
|
+
*/
|
|
8
|
+
export function getResponsiveOption(propToUse, defaults) {
|
|
9
|
+
if (propToUse && typeof propToUse === 'object') {
|
|
10
|
+
let obj = {};
|
|
11
|
+
for (let index = 0; index < Object.keys(defaults).length; index++) {
|
|
12
|
+
const breakpointProp = Object.keys(defaults)[index];
|
|
13
|
+
const breakpointPropValue = propToUse[breakpointProp];
|
|
14
|
+
// if propToUse declares a breakpoint, apply to this breakpoint and up
|
|
15
|
+
if (breakpointPropValue || (typeof breakpointPropValue === 'number' && breakpointPropValue === 0)) {
|
|
16
|
+
for (let remaining = index; remaining < Object.keys(defaults).length; remaining++) {
|
|
17
|
+
obj[Object.keys(defaults)[remaining]] = breakpointPropValue;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// fallback if not created yet, uses defaults equivalent value
|
|
21
|
+
else if (!obj[breakpointProp]) {
|
|
22
|
+
obj[breakpointProp] = defaults[breakpointProp];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return obj;
|
|
26
|
+
}
|
|
27
|
+
else if (propToUse && typeof propToUse === 'number') {
|
|
28
|
+
// Number defined, apply to all breakpoints
|
|
29
|
+
let obj = {};
|
|
30
|
+
for (let index = 0; index < Object.keys(defaults).length; index++) {
|
|
31
|
+
const breakpointProp = Object.keys(defaults)[index];
|
|
32
|
+
obj[breakpointProp] = propToUse;
|
|
33
|
+
}
|
|
34
|
+
return obj;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// if responsive prop not defined, just use defaults
|
|
38
|
+
return defaults;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
import { breakpoints } from './variables.js';
|
|
43
|
+
export function getCurrentBreakpoint() {
|
|
44
|
+
let ret;
|
|
45
|
+
for (let index = 0; index < Object.keys(breakpoints).length; index++) {
|
|
46
|
+
const breakProp = Object.keys(breakpoints)[index];
|
|
47
|
+
if (window.innerWidth < breakpoints[breakProp]) {
|
|
48
|
+
ret = (index === 0) ? 'xs' : Object.keys(breakpoints)[index-1];
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return ret ? ret : 'xl';
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function getValidationMessage(element, options) {
|
|
2
|
+
const defaults = {
|
|
3
|
+
fallback: 'Input not valid, please try something else'
|
|
4
|
+
};
|
|
5
|
+
const opt = Object.assign({}, defaults, options);
|
|
6
|
+
|
|
7
|
+
return element.validationMessage != null ? element.validationMessage : opt.fallback;
|
|
8
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import sassBreakpoints from '../styles/js-modules/breakpoints.scss';
|
|
2
|
+
|
|
3
|
+
export const breakpoints = {
|
|
4
|
+
xs: parseInt(sassBreakpoints.breakpointXs), // mobile min (iphone 5/se)
|
|
5
|
+
sm: parseInt(sassBreakpoints.breakpointSm), // large mobile
|
|
6
|
+
md: parseInt(sassBreakpoints.breakpointMd), // tablet
|
|
7
|
+
lg: parseInt(sassBreakpoints.breakpointLg), // desktop
|
|
8
|
+
xl: parseInt(sassBreakpoints.breakpointXl), // wide
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const styleNamesIds = {
|
|
12
|
+
'Crisp White': 'WHITE_CRISP',
|
|
13
|
+
'Fruity White': 'WHITE_FRUITY',
|
|
14
|
+
'Rich White': 'WHITE_RICH',
|
|
15
|
+
'Sweet White': 'WHITE_SWEET',
|
|
16
|
+
'Fruity Red': 'RED_FRUITY',
|
|
17
|
+
'Smooth Red': 'RED_SMOOTH',
|
|
18
|
+
'Big Red': 'RED_BIG',
|
|
19
|
+
'Rose': 'ROSE',
|
|
20
|
+
'Sparkling': 'SPARKLING',
|
|
21
|
+
'Dessert & Fortified': 'DESSERT_FORTIFIED'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const styleIdsNames = {
|
|
25
|
+
WHITE_CRISP: 'Crisp White',
|
|
26
|
+
WHITE_FRUITY: 'Fruity White',
|
|
27
|
+
WHITE_RICH: 'Rich White',
|
|
28
|
+
WHITE_SWEET: 'Sweet White',
|
|
29
|
+
RED_FRUITY: 'Fruity Red',
|
|
30
|
+
RED_SMOOTH: 'Smooth Red',
|
|
31
|
+
RED_BIG: 'Big Red',
|
|
32
|
+
ROSE: 'Rosé',
|
|
33
|
+
SPARKLING: 'Sparkling',
|
|
34
|
+
DESSERT_FORTIFIED: 'Dessert & Fortified'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const styleColours = {
|
|
38
|
+
WHITE_CRISP: { primary: '#CCD402', secondary: '#BAC106' },
|
|
39
|
+
WHITE_FRUITY: { primary: '#5A8C24', secondary: '#4F7A17' },
|
|
40
|
+
WHITE_RICH: { primary: '#E8BC02', secondary: '#D4AC04' },
|
|
41
|
+
WHITE_SWEET: { primary: '#01A6B4', secondary: '#068F9A' },
|
|
42
|
+
RED_FRUITY: { primary: '#AC0F63', secondary: '#8E0C51' },
|
|
43
|
+
RED_SMOOTH: { primary: '#E4481D', secondary: '#CF3D15' },
|
|
44
|
+
RED_BIG: { primary: '#552681', secondary: '#451B6D' },
|
|
45
|
+
ROSE: { primary: '#E86D83', secondary: '#DC6378' },
|
|
46
|
+
SPARKLING: { primary: '#AC8A4D', secondary: '#9C7C43' },
|
|
47
|
+
DESSERT_FORTIFIED: { primary: '#151432', secondary: '#06061C' },
|
|
48
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { convertStyleCodeToClassName } from '../globals/functions';
|
|
2
|
+
import { Products } from '../globals/products.js';
|
|
3
|
+
const bottleShotPlaceHolderPath = '/merchandising/content/placeholders/wine-bottle-placeholder.png';
|
|
4
|
+
|
|
5
|
+
const productCard = {
|
|
6
|
+
props: {
|
|
7
|
+
product: {
|
|
8
|
+
type: Object,
|
|
9
|
+
required: false
|
|
10
|
+
},
|
|
11
|
+
recommendationType: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: false,
|
|
14
|
+
default: 'bia'
|
|
15
|
+
},
|
|
16
|
+
styleBar: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
required: false,
|
|
19
|
+
default: false
|
|
20
|
+
},
|
|
21
|
+
type: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: false
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
computed: {
|
|
28
|
+
productData() {
|
|
29
|
+
let product = this.product;
|
|
30
|
+
|
|
31
|
+
if(product && product.data) {
|
|
32
|
+
return product.data
|
|
33
|
+
} else {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
typeBia() {
|
|
39
|
+
return this.recommendationType === 'bia' &&
|
|
40
|
+
this.productData.bia &&
|
|
41
|
+
this.productData.bia.positiveRatingPercentage;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
typePercentagematch() {
|
|
45
|
+
return this.recommendationType === 'percentageMatch' &&
|
|
46
|
+
this.productData.percentageMatchFormatted;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
percentageMatchNumber() {
|
|
50
|
+
let score = null,
|
|
51
|
+
productData = this.productData;
|
|
52
|
+
|
|
53
|
+
if(
|
|
54
|
+
productData.customerProductLikeThis &&
|
|
55
|
+
productData.customerProductLikeThis.custProdRecommendation &&
|
|
56
|
+
productData.customerProductLikeThis.custProdRecommendation.score
|
|
57
|
+
) {
|
|
58
|
+
score = productData.customerProductLikeThis.custProdRecommendation.score;
|
|
59
|
+
score * 100;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return score;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
itemPriceString() { return `${this.currencySymbol == '$' ? '$' : '£'}${this.productData.pricesCurrent.prices.customerPrice.toFixed(2)}` },
|
|
66
|
+
|
|
67
|
+
productImage() {
|
|
68
|
+
if (this.productData.bottleImage) { return window.staticImageUrl + this.productData.bottleImage; }
|
|
69
|
+
else {
|
|
70
|
+
let find = false;
|
|
71
|
+
|
|
72
|
+
if (this.productData.assortmentObjects.objects) {
|
|
73
|
+
find = this.productData.assortmentObjects.objects.findIndex(item => item.name == "mediumCutout");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if(find) {
|
|
77
|
+
return window.staticImageUrl + this.productData.assortmentObjects.objects[find].contentPath;
|
|
78
|
+
} else {
|
|
79
|
+
return window.staticImageUrl + bottleShotPlaceHolderPath;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
caseImage() {
|
|
85
|
+
// TODO: Tidy up - maybe include default placeholder image here if not available?
|
|
86
|
+
if (this.productData.bottleImage) { return window.staticImageUrl + this.productData.bottleImage; }
|
|
87
|
+
else { return window.staticImageUrl + this.productData.assortmentObjects.headline.contentPath; }
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
hasActionSlot() {
|
|
91
|
+
return !!this.$slots.action
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
methods: {
|
|
96
|
+
styleCodeClassName(product) {
|
|
97
|
+
if(product && product.style && product.style.id) {
|
|
98
|
+
return convertStyleCodeToClassName(product.style.id)
|
|
99
|
+
} else {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
bottleShot(product) {
|
|
105
|
+
if (product.bottleImage) {
|
|
106
|
+
return window.staticImageUrl + product.bottleImage;
|
|
107
|
+
} else {
|
|
108
|
+
let find = false;
|
|
109
|
+
|
|
110
|
+
if (product.assortmentObjects.objects) {
|
|
111
|
+
find = product.assortmentObjects.objects.findIndex(item => item.name == "mediumCutout");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if(find) {
|
|
115
|
+
return window.staticImageUrl + product.assortmentObjects.objects[find].contentPath;
|
|
116
|
+
} else {
|
|
117
|
+
return window.staticImageUrl + bottleShotPlaceHolderPath;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
parentElement() {
|
|
123
|
+
return this.$el.parentElement;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
atb: function() {
|
|
127
|
+
if (!this.productData.id) { return false; }
|
|
128
|
+
Products.addToBasket({ productId: this.productData.id, quantity: 1 })
|
|
129
|
+
.then(() => {
|
|
130
|
+
if (window.nkd && window.nkd.headerCart) { window.nkd.headerCart.reload(); }
|
|
131
|
+
window.notificationCentre.notify({
|
|
132
|
+
title: `1x ${this.productData.heading} added to your basket.`,
|
|
133
|
+
text: 'To checkout, please go to the basket page.',
|
|
134
|
+
group: 'header',
|
|
135
|
+
duration: 8000
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
goToUrl: function() { window.location = this.productData.url; }
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
filters: {
|
|
143
|
+
nameChopper: function(val) {
|
|
144
|
+
if (!val) { return false; }
|
|
145
|
+
const maxCharactersTitle = 42;
|
|
146
|
+
return val.length >= maxCharactersTitle ? `${val.substring(0, maxCharactersTitle)} ...` : val;
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default productCard;
|