@lancom/shared 0.0.106 → 0.0.109
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/assets/js/models/print-area.js +9 -7
- package/assets/js/models/product-layers.js +3 -2
- package/assets/js/utils/cart.js +1 -0
- package/assets/js/utils/custom-validation-rules.js +7 -0
- package/assets/js/utils/fabric/wireframe.js +4 -4
- package/assets/js/utils/fabric-helper.js +5 -3
- package/assets/js/utils/order.js +3 -3
- package/assets/js/utils/prints.js +2 -2
- package/assets/js/utils/product.js +3 -2
- package/assets/js/utils/products-grouping/by-colors.js +22 -18
- package/components/asides/contact_us/contact-us.vue +3 -0
- package/components/asides/offer_screen_printing/offer-screen-printing.vue +22 -12
- package/components/checkout/cart/cart_entity/cart_entity_color_simple_products/cart-entity-color-simple-products.mixin.js +5 -3
- package/components/checkout/cart/cart_entity/cart_entity_prints/cart-entity-prints.vue +3 -0
- package/components/common/btn.vue +8 -1
- package/components/common/coupon_select/coupon-select.vue +41 -17
- package/components/common/postcode_select/postcode-select.vue +6 -4
- package/components/common/product_side_with_print/product-side-with-print.vue +2 -2
- package/components/customer/signin_form/signin-form.vue +5 -2
- package/components/design/approve_design_tables/approve-design-tables.scss +3 -3
- package/components/design/approve_design_tables/approve-design-tables.vue +24 -13
- package/components/design/approve_design_tees/approve-design-tees.vue +18 -4
- package/components/editor/editor.vue +1 -1
- package/components/editor/editor_layers/editor_layers_toolbar/editor-layers-toolbar.vue +0 -1
- package/components/editor/editor_print_area_options/editor_print_area_option/editor-print-area-option.vue +1 -1
- package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +1 -0
- package/components/products/products_autocomplete/products-autocomplete.vue +4 -0
- package/components/products/products_filters/products-filters.vue +2 -5
- package/components/subscribe/subscribe.vue +3 -0
- package/components/the_navbar/the-navbar.scss +5 -0
- package/components/the_navbar/the-navbar.vue +1 -2
- package/mixins/product-preview.js +6 -0
- package/package.json +1 -1
- package/plugins/vee-validate.js +2 -1
- package/plugins/vue-recaptcha.js +4 -1
- package/store/product.js +2 -2
|
@@ -42,19 +42,21 @@ export const getProductPrintsAreasPrices = (product, printType) => {
|
|
|
42
42
|
return areas;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export const getPrintAreaByName = ({ printArea, editorWidth, editorHeight }, product) => {
|
|
45
|
+
export const getPrintAreaByName = ({ printArea, editorWidth, editorHeight, printSize, printAreaOffsets }, product) => {
|
|
46
46
|
const printAreas = getProductPrintAreas(product);
|
|
47
|
-
const
|
|
47
|
+
const pa = printAreas[printArea] || DEFAULT_PRINT_AREA;
|
|
48
|
+
const size = printSize || pa.printSize;
|
|
49
|
+
const offsets = printAreaOffsets || pa.printAreaOffsets;
|
|
48
50
|
const pxPerCm = (editorWidth * product.productToImageRatio) / (product.productWidthInCm || 60);
|
|
49
|
-
const widthCm =
|
|
50
|
-
const heightCm =
|
|
51
|
+
const widthCm = size ? size.width : DEFAULT_PRINT_SIZE_CM;
|
|
52
|
+
const heightCm = size ? size.height : DEFAULT_PRINT_SIZE_CM;
|
|
51
53
|
let width = widthCm * pxPerCm;
|
|
52
54
|
let height = heightCm * pxPerCm;
|
|
53
55
|
let top = 0;
|
|
54
56
|
let left = 0;
|
|
55
|
-
if (
|
|
56
|
-
top = (editorHeight - height) / (100 /
|
|
57
|
-
left = (editorWidth - width) / (100 /
|
|
57
|
+
if (offsets) {
|
|
58
|
+
top = (editorHeight - height) / (100 / offsets.top);
|
|
59
|
+
left = (editorWidth - width) / (100 / offsets.left);
|
|
58
60
|
} else {
|
|
59
61
|
left = 30;
|
|
60
62
|
top = 30;
|
|
@@ -4,6 +4,7 @@ import { generateGUID } from '@lancom/shared/assets/js/utils/guid';
|
|
|
4
4
|
export class Layer {
|
|
5
5
|
type = null;
|
|
6
6
|
colorId = null;
|
|
7
|
+
sideId = null;
|
|
7
8
|
angle = 0;
|
|
8
9
|
alignVertically = 'center';
|
|
9
10
|
alignHorizontally = 'center';
|
|
@@ -78,7 +79,7 @@ export class ArtLayer extends Layer {
|
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
|
|
81
|
-
export const getLayerModel = async ({ type, colorId, top, left, isEditMode, url, originalSize, properties = {} }) => {
|
|
82
|
+
export const getLayerModel = async ({ type, colorId, top, left, isEditMode, url, originalSize, sideId, properties = {} }) => {
|
|
82
83
|
if (!type) {
|
|
83
84
|
throw new Error('When creating a new layer, you must specify its type');
|
|
84
85
|
}
|
|
@@ -100,7 +101,7 @@ export const getLayerModel = async ({ type, colorId, top, left, isEditMode, url,
|
|
|
100
101
|
throw new Error(`Layer type '${type}' not found or not supported.`);
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
Object.assign(layer, properties);
|
|
104
|
+
Object.assign(layer, properties, { sideId });
|
|
104
105
|
return layer;
|
|
105
106
|
};
|
|
106
107
|
|
package/assets/js/utils/cart.js
CHANGED
|
@@ -10,6 +10,7 @@ export function groupSimpleProducts(entity, isGroupByColor, isPopulateEmptyProdu
|
|
|
10
10
|
color: simpleProduct.color,
|
|
11
11
|
images: entity.product?.images || [],
|
|
12
12
|
prints: entity.prints,
|
|
13
|
+
printsThumbnails: entity.printsThumbnails,
|
|
13
14
|
product: entity.product,
|
|
14
15
|
simpleProducts: [],
|
|
15
16
|
amount: 0,
|
|
@@ -3,9 +3,9 @@ import { fabric } from 'fabric';
|
|
|
3
3
|
|
|
4
4
|
const sizes = [];
|
|
5
5
|
|
|
6
|
-
export const buildWireframe = ({ width, height, editor,
|
|
6
|
+
export const buildWireframe = ({ width, height, editor, print }) => {
|
|
7
7
|
const layers = editor.getObjects();
|
|
8
|
-
const printAreaSize =
|
|
8
|
+
const printAreaSize = print.printSize;
|
|
9
9
|
const group = new fabric.Group(layers);
|
|
10
10
|
const groupBounding = group.getBoundingRect();
|
|
11
11
|
const ratioX = (width / groupBounding.width) * 0.8;
|
|
@@ -108,8 +108,8 @@ export const buildWireframe = ({ width, height, editor, area = 'a4_v', product }
|
|
|
108
108
|
], 'h');
|
|
109
109
|
verticalArrow.set({ selectable: false });
|
|
110
110
|
horizontalArrow.set({ selectable: false });
|
|
111
|
-
const W = Math.round(groupBounding.width / printAreaSize.width
|
|
112
|
-
const H = Math.round(groupBounding.height / printAreaSize.height
|
|
111
|
+
const W = Math.round(groupBounding.width / printAreaSize.width);
|
|
112
|
+
const H = Math.round(groupBounding.height / printAreaSize.height);
|
|
113
113
|
const verticalSize = new fabric.Text(`H: ${H * 10}mm`, {
|
|
114
114
|
top: bounding.top + (bounding.height / 2),
|
|
115
115
|
left: 0,
|
|
@@ -137,6 +137,8 @@ export default class FabricHelper {
|
|
|
137
137
|
setPrintArea(printArea, size, product) {
|
|
138
138
|
this.printAreaRect = getPrintAreaByName({
|
|
139
139
|
printArea: printArea?._id,
|
|
140
|
+
printSize: printArea?.printSize,
|
|
141
|
+
printAreaOffsets: printArea?.printAreaOffsets,
|
|
140
142
|
editorWidth: size.width,
|
|
141
143
|
editorHeight: size.height
|
|
142
144
|
}, product);
|
|
@@ -189,7 +191,7 @@ export default class FabricHelper {
|
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
createObject({ layer, active }) {
|
|
192
|
-
const initial = !layer.modifiedAt;
|
|
194
|
+
const initial = false; // !layer.modifiedAt;
|
|
193
195
|
return new Promise(resolve => {
|
|
194
196
|
const methods = {
|
|
195
197
|
text: 'createTextObject',
|
|
@@ -216,8 +218,8 @@ export default class FabricHelper {
|
|
|
216
218
|
});
|
|
217
219
|
}
|
|
218
220
|
|
|
219
|
-
buildWireframe({ width, height,
|
|
220
|
-
buildWireframe({ width, height, editor: this.editor,
|
|
221
|
+
buildWireframe({ width, height, print }) {
|
|
222
|
+
buildWireframe({ width, height, editor: this.editor, print });
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
checkAnyBoundingIntersection() {
|
package/assets/js/utils/order.js
CHANGED
|
@@ -43,13 +43,13 @@ export function populateProductsFields(products, pricing) {
|
|
|
43
43
|
}, []);
|
|
44
44
|
|
|
45
45
|
if (pricing) {
|
|
46
|
-
const productPricing = pricing.products[product.guid];
|
|
46
|
+
const productPricing = pricing.products[product.guid] || pricing.products[product?.product._id];
|
|
47
47
|
product.totalPrice = productPricing.totalPriceWithoutTax;
|
|
48
48
|
product.printsTotalPrice = productPricing.prints.totalPriceWithoutTax;
|
|
49
49
|
product.productsTotalPrice = productPricing.products.totalPriceWithoutTax;
|
|
50
50
|
|
|
51
51
|
(product.simpleProducts || []).forEach(sp => {
|
|
52
|
-
const spPricing = productPricing.products[sp.guid];
|
|
52
|
+
const spPricing = productPricing.products[sp.guid] || productPricing.products[sp._id];
|
|
53
53
|
if (spPricing) {
|
|
54
54
|
sp.totalPrice = spPricing.totalPriceWithoutTax;
|
|
55
55
|
sp.productCost = spPricing.priceWithoutTax;
|
|
@@ -57,7 +57,7 @@ export function populateProductsFields(products, pricing) {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
(product.prints || []).forEach(p => {
|
|
60
|
-
const pPricing = productPricing.prints[p.printArea
|
|
60
|
+
const pPricing = productPricing.prints[p.printArea?._id];
|
|
61
61
|
if (pPricing) {
|
|
62
62
|
p.totalPrice = pPricing.totalPriceWithoutTax;
|
|
63
63
|
p.printCost = pPricing.priceWithoutTax;
|
|
@@ -58,8 +58,8 @@ export function getPrintsFromLayers(layers, product) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function getPrintTypeSizePricing(printType, sizeId) {
|
|
61
|
-
return printType?.printAreas
|
|
61
|
+
return (printType?.printAreas || [])
|
|
62
62
|
.find(({ printSizes }) => {
|
|
63
63
|
return printSizes.map(size => size?._id || size).includes(sizeId);
|
|
64
|
-
}) || printType?.printAreas[0];
|
|
64
|
+
}) || (printType?.printAreas || [])[0];
|
|
65
65
|
}
|
|
@@ -21,7 +21,7 @@ export function getProductsForCalculatePricing(mainProduct, simpleProducts, laye
|
|
|
21
21
|
}];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function generateCartProducts(mainProduct, simpleProducts, layers, hasPrints,
|
|
24
|
+
export function generateCartProducts(mainProduct, simpleProducts, layers, hasPrints, printsThumbnails = {}, editorSize = null) {
|
|
25
25
|
return [{
|
|
26
26
|
guid: generateGUID(),
|
|
27
27
|
product: mainProduct,
|
|
@@ -30,7 +30,8 @@ export function generateCartProducts(mainProduct, simpleProducts, layers, hasPri
|
|
|
30
30
|
guid: generateGUID(),
|
|
31
31
|
amount: sp.amount || 0
|
|
32
32
|
})),
|
|
33
|
-
prints: hasPrints ? getPrintsFromLayers(layers, mainProduct) : []
|
|
33
|
+
prints: hasPrints ? getPrintsFromLayers(layers, mainProduct) : [],
|
|
34
|
+
printsThumbnails
|
|
34
35
|
}];
|
|
35
36
|
// const timestamp = Date.now();
|
|
36
37
|
// const productsWithPricing = getProductsForCalculatePricing(mainProduct, simpleProducts, layers, hasPrints);
|
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
export function groupProductsByColor(products = []
|
|
1
|
+
export function groupProductsByColor(products = []) {
|
|
2
2
|
const groupProducts = new Map();
|
|
3
|
-
products
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
products
|
|
4
|
+
.forEach(product => {
|
|
5
|
+
product.simpleProducts.forEach(sp => {
|
|
6
|
+
const defaultGroup = {
|
|
7
|
+
amount: 0,
|
|
8
|
+
products: []
|
|
9
|
+
};
|
|
10
|
+
const colorId = sp?.color._id || sp.color;
|
|
11
|
+
const group = groupProducts.get(colorId) || defaultGroup;
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
if (sp.amount > 0) {
|
|
14
|
+
group.amount += (sp.productCost || 0) * (sp.amount || 0);
|
|
15
|
+
group.product = {
|
|
16
|
+
...product.product,
|
|
17
|
+
printsThumbnails: (product.printsThumbnails || {})
|
|
18
|
+
};
|
|
19
|
+
group.color = sp.color;
|
|
20
|
+
group.products.push(sp);
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
groupProducts.set(product.color._id, group);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
+
groupProducts.set(colorId, group);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
22
26
|
return [...groupProducts];
|
|
23
27
|
}
|
|
@@ -11,25 +11,25 @@
|
|
|
11
11
|
<validation-provider
|
|
12
12
|
v-slot="{ errors }"
|
|
13
13
|
tag="div"
|
|
14
|
-
name="Name"
|
|
14
|
+
name="Full Name"
|
|
15
15
|
rules="required"
|
|
16
16
|
class="form-row">
|
|
17
17
|
<input
|
|
18
18
|
id="name"
|
|
19
19
|
ref="name"
|
|
20
|
-
v-model="form.
|
|
20
|
+
v-model="form.fullName"
|
|
21
21
|
name="name"
|
|
22
22
|
type="text"
|
|
23
23
|
class="form-field"
|
|
24
24
|
:class="{
|
|
25
25
|
'is-danger': errors.length,
|
|
26
|
-
filled: form.
|
|
26
|
+
filled: form.fullName
|
|
27
27
|
}"
|
|
28
28
|
@keyup.enter="$refs.email.focus()" />
|
|
29
29
|
<label
|
|
30
30
|
for="name"
|
|
31
31
|
class="form-label label-inner">
|
|
32
|
-
Name
|
|
32
|
+
Full Name
|
|
33
33
|
</label>
|
|
34
34
|
<span
|
|
35
35
|
v-if="errors.length"
|
|
@@ -104,12 +104,12 @@
|
|
|
104
104
|
<textarea
|
|
105
105
|
id="body"
|
|
106
106
|
ref="body"
|
|
107
|
-
v-model="form.
|
|
107
|
+
v-model="form.description"
|
|
108
108
|
name="body"
|
|
109
109
|
class="form-textarea--size3"
|
|
110
110
|
:class="{
|
|
111
111
|
'is-danger': errors.length,
|
|
112
|
-
filled: form.
|
|
112
|
+
filled: form.description
|
|
113
113
|
}">
|
|
114
114
|
</textarea>
|
|
115
115
|
<label
|
|
@@ -161,6 +161,7 @@ export default {
|
|
|
161
161
|
]),
|
|
162
162
|
...mapGetters('product', [
|
|
163
163
|
'product',
|
|
164
|
+
'template',
|
|
164
165
|
'layers',
|
|
165
166
|
'usedSimpleProducts',
|
|
166
167
|
'selectedPrintAreas',
|
|
@@ -174,19 +175,28 @@ export default {
|
|
|
174
175
|
try {
|
|
175
176
|
this.processing = true;
|
|
176
177
|
const recaptchaToken = await this.getRecaptcha('offer_screen_printing');
|
|
177
|
-
|
|
178
|
-
populateProductsFields(products);
|
|
178
|
+
let products = generateCartProducts(this.product, this.template.simpleProducts, this.template.layers, true, this.layerThumbnails);
|
|
179
|
+
products = populateProductsFields(products, this.productPricing);
|
|
180
|
+
products.forEach(p => {
|
|
181
|
+
p.prints.forEach(print => {
|
|
182
|
+
delete print.printType;
|
|
183
|
+
delete print.printCost;
|
|
184
|
+
delete print.setupCost;
|
|
185
|
+
delete print.pricing;
|
|
186
|
+
});
|
|
187
|
+
});
|
|
179
188
|
const body = {
|
|
180
189
|
recaptchaToken,
|
|
190
|
+
address: this.form,
|
|
181
191
|
...this.form,
|
|
182
|
-
products,
|
|
183
|
-
shop: this.shop._id
|
|
184
|
-
price: await api.calculateProductPrice({ products, screenPrint: true }, this.shop._id)
|
|
192
|
+
options: [{ products, index: 0 }],
|
|
193
|
+
shop: this.shop._id
|
|
185
194
|
};
|
|
186
|
-
await api.
|
|
195
|
+
await api.saveQuoteRequest(body, this.shop._id);
|
|
187
196
|
this.$toastr.s('We will review your design and advise if we can screen print you design and offer better pricing');
|
|
188
197
|
this.$emit('close');
|
|
189
198
|
} catch (error) {
|
|
199
|
+
console.log(error);
|
|
190
200
|
this.$toastr.e(error);
|
|
191
201
|
} finally {
|
|
192
202
|
this.processing = false;
|
|
@@ -61,10 +61,12 @@ export default {
|
|
|
61
61
|
this.$modal.show(
|
|
62
62
|
ImageViewer,
|
|
63
63
|
{
|
|
64
|
-
items: images.map(({ origin,
|
|
64
|
+
items: images.map(({ origin, types = [] }) => ({
|
|
65
65
|
src: staticLink(origin),
|
|
66
|
-
color: this.group.color.rgb
|
|
67
|
-
|
|
66
|
+
color: this.group.color.rgb,
|
|
67
|
+
print: Object.keys((this.entity.printsThumbnails || {}))
|
|
68
|
+
.filter(type => types.includes(type))
|
|
69
|
+
.map(type => this.entity.printsThumbnails[type])[0]
|
|
68
70
|
})),
|
|
69
71
|
index
|
|
70
72
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
|
-
:is="to ? 'nuxt-link' : 'div'"
|
|
3
|
+
:is="to ? 'nuxt-link' : (btnType || btnTag || 'div')"
|
|
4
|
+
:type="btnType"
|
|
4
5
|
class="Btn__wrapper"
|
|
5
6
|
:class="{
|
|
6
7
|
block: btnBlock,
|
|
@@ -56,6 +57,12 @@ export default {
|
|
|
56
57
|
to: {
|
|
57
58
|
type: String
|
|
58
59
|
},
|
|
60
|
+
btnTag: {
|
|
61
|
+
type: String
|
|
62
|
+
},
|
|
63
|
+
btnType: {
|
|
64
|
+
type: String
|
|
65
|
+
},
|
|
59
66
|
btnClass: {
|
|
60
67
|
type: String,
|
|
61
68
|
default: 'white'
|
|
@@ -9,19 +9,35 @@
|
|
|
9
9
|
class="form-label">
|
|
10
10
|
{{ labelText }}
|
|
11
11
|
</label>
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="col-7">
|
|
14
|
+
<input
|
|
15
|
+
id="coupon"
|
|
16
|
+
ref="coupon"
|
|
17
|
+
v-model="code"
|
|
18
|
+
placeholder="Coupon"
|
|
19
|
+
name="coupon"
|
|
20
|
+
type="text"
|
|
21
|
+
class="form-field labelless"
|
|
22
|
+
:class="{
|
|
23
|
+
'is-danger': code && notValidCoupon,
|
|
24
|
+
filled: code
|
|
25
|
+
}"
|
|
26
|
+
@keydown.enter="validateCoupon" />
|
|
27
|
+
</div>
|
|
28
|
+
<div class="col-5">
|
|
29
|
+
<btn
|
|
30
|
+
btn-class="green"
|
|
31
|
+
:btn-block="true"
|
|
32
|
+
:btn-disabled="isLoading"
|
|
33
|
+
btn-label="Apply"
|
|
34
|
+
@onclick="validateCoupon">
|
|
35
|
+
<i
|
|
36
|
+
slot="icon-after"
|
|
37
|
+
class="icon-arrow-right"></i>
|
|
38
|
+
</btn>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
25
41
|
<span
|
|
26
42
|
v-if="code && notValidCoupon"
|
|
27
43
|
class="form-help is-danger">
|
|
@@ -29,13 +45,15 @@
|
|
|
29
45
|
</span>
|
|
30
46
|
</validation-provider>
|
|
31
47
|
<div v-if="value">
|
|
32
|
-
<div
|
|
48
|
+
<div
|
|
49
|
+
v-if="isValidPricing"
|
|
50
|
+
class="lc_h4">
|
|
33
51
|
{{ value.value | price }} OFF
|
|
34
52
|
</div>
|
|
35
53
|
<div
|
|
36
|
-
v-
|
|
37
|
-
class="
|
|
38
|
-
Min Order
|
|
54
|
+
v-else
|
|
55
|
+
class="lc_caption form-help is-danger">
|
|
56
|
+
Invalid coupon: Min Order for Coupon: {{ value.minOrderValue | price }}
|
|
39
57
|
</div>
|
|
40
58
|
</div>
|
|
41
59
|
</div>
|
|
@@ -55,6 +73,9 @@ export default {
|
|
|
55
73
|
value: {
|
|
56
74
|
type: Object
|
|
57
75
|
},
|
|
76
|
+
pricing: {
|
|
77
|
+
type: Object
|
|
78
|
+
},
|
|
58
79
|
labelText: {
|
|
59
80
|
type: String,
|
|
60
81
|
default: 'Coupon'
|
|
@@ -69,6 +90,9 @@ export default {
|
|
|
69
90
|
},
|
|
70
91
|
computed: {
|
|
71
92
|
...mapGetters(['shop']),
|
|
93
|
+
isValidPricing() {
|
|
94
|
+
return !this.value.minOrderValue || this.pricing.coupon;
|
|
95
|
+
},
|
|
72
96
|
model: {
|
|
73
97
|
get() {
|
|
74
98
|
return this.value?.code;
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
type="text"
|
|
57
57
|
:value="value"
|
|
58
58
|
:class="{ 'filled': value || selected }"
|
|
59
|
-
class="form-hidden-validator form-field"
|
|
59
|
+
class="form-hidden-validator form-field"
|
|
60
|
+
style="display: none" />
|
|
60
61
|
<label
|
|
61
62
|
v-if="!labelless"
|
|
62
63
|
class="form-label label-inner"
|
|
@@ -126,7 +127,7 @@ export default {
|
|
|
126
127
|
set(option) {
|
|
127
128
|
this.selected = option;
|
|
128
129
|
this.$emit('input', option.value);
|
|
129
|
-
this.$emit('select', this.suburbs.find(({
|
|
130
|
+
this.$emit('select', this.suburbs.find(({ _id }) => _id === option._id));
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
},
|
|
@@ -148,10 +149,11 @@ export default {
|
|
|
148
149
|
this.options = [];
|
|
149
150
|
}
|
|
150
151
|
},
|
|
151
|
-
createOptionFromSuburb({ locality, state, postcode, city }) {
|
|
152
|
+
createOptionFromSuburb({ locality, state, postcode, city, _id }) {
|
|
152
153
|
return {
|
|
153
154
|
label: [locality || city, state, postcode].filter(i => !!i).join(', '),
|
|
154
|
-
value: postcode
|
|
155
|
+
value: postcode,
|
|
156
|
+
_id: _id
|
|
155
157
|
};
|
|
156
158
|
}
|
|
157
159
|
}
|
|
@@ -85,8 +85,8 @@ export default {
|
|
|
85
85
|
return getColorImage(this.product, this.size, this.side, color) || getProductCover(this.product, this.size, this.side, color);
|
|
86
86
|
},
|
|
87
87
|
print() {
|
|
88
|
-
const {
|
|
89
|
-
return
|
|
88
|
+
const { printsThumbnails = {} } = this.product || {};
|
|
89
|
+
return printsThumbnails[this.side];
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
methods: {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="form__wrapper SignInFrom__wrapper">
|
|
3
|
-
<validation-observer
|
|
3
|
+
<validation-observer
|
|
4
|
+
ref="form"
|
|
5
|
+
@keyup.enter="submit">
|
|
4
6
|
<div class="form__content mt-4">
|
|
5
7
|
<div class="row">
|
|
6
8
|
<div class="col-sm-6 col-12">
|
|
@@ -84,6 +86,7 @@
|
|
|
84
86
|
:btn-processing="processing"
|
|
85
87
|
:btn-disabled="processing"
|
|
86
88
|
:btn-block="true"
|
|
89
|
+
btn-type="button"
|
|
87
90
|
btn-label="Log in"
|
|
88
91
|
@onclick="submit()">
|
|
89
92
|
<i
|
|
@@ -121,7 +124,7 @@ export default {
|
|
|
121
124
|
this.error = null;
|
|
122
125
|
|
|
123
126
|
const isValid = await this.$refs.form.validate();
|
|
124
|
-
if (!isValid) {
|
|
127
|
+
if (!isValid || this.processing) {
|
|
125
128
|
return;
|
|
126
129
|
}
|
|
127
130
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<table class="lc_table bordered ApproveDesignTables__table">
|
|
4
4
|
<thead class="centered">
|
|
5
5
|
<tr class="stripped">
|
|
6
|
-
<th>
|
|
6
|
+
<th>Color</th>
|
|
7
7
|
<th>Design Front</th>
|
|
8
8
|
<th>Design Back</th>
|
|
9
9
|
<th>Products</th>
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
size="medium"
|
|
23
23
|
class="ApproveDesignTables__thumbnail" />
|
|
24
24
|
<div
|
|
25
|
-
v-if="!group.product.
|
|
25
|
+
v-if="!group.product.printsThumbnails.front"
|
|
26
26
|
class="ApproveDesignTables__no-design">
|
|
27
27
|
No design
|
|
28
28
|
</div>
|
|
29
|
-
<div v-if="group.product.
|
|
30
|
-
{{ group.product.
|
|
29
|
+
<div v-if="group.product.printsThumbnails.front">
|
|
30
|
+
{{ group.product.printsThumbnails.front | print }}
|
|
31
31
|
</div>
|
|
32
32
|
</td>
|
|
33
33
|
<td class="ApproveDesignTables__thumbnail-cell">
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
size="medium"
|
|
38
38
|
class="ApproveDesignTables__thumbnail" />
|
|
39
39
|
<div
|
|
40
|
-
v-if="!group.product.
|
|
40
|
+
v-if="!group.product.printsThumbnails.back"
|
|
41
41
|
class="ApproveDesignTables__no-design">
|
|
42
42
|
No design
|
|
43
43
|
</div>
|
|
44
|
-
<div v-if="group.product.
|
|
45
|
-
{{ group.product.
|
|
44
|
+
<div v-if="group.product.printsThumbnails.back">
|
|
45
|
+
{{ group.product.printsThumbnails.back | print }}
|
|
46
46
|
</div>
|
|
47
47
|
</td>
|
|
48
48
|
<td>
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
</thead>
|
|
76
76
|
<tbody class="centered">
|
|
77
77
|
<tr
|
|
78
|
-
v-for="product in
|
|
78
|
+
v-for="product in simpleProducts"
|
|
79
79
|
:key="product._id">
|
|
80
|
-
<td>{{ product.brand }}</td>
|
|
80
|
+
<td>{{ product.brand.name }}</td>
|
|
81
81
|
<td>{{ product.style }}</td>
|
|
82
82
|
<td>{{ product.color.name }}</td>
|
|
83
|
-
<td>{{ product.size.
|
|
83
|
+
<td>{{ product.size.shortName }}</td>
|
|
84
84
|
<td>{{ product.amount }}</td>
|
|
85
85
|
</tr>
|
|
86
86
|
<tr>
|
|
@@ -119,14 +119,25 @@ export default {
|
|
|
119
119
|
}
|
|
120
120
|
},
|
|
121
121
|
computed: {
|
|
122
|
+
simpleProducts() {
|
|
123
|
+
return this.products
|
|
124
|
+
.reduce((items, { product, simpleProducts }) => ([
|
|
125
|
+
...items,
|
|
126
|
+
...simpleProducts.map(sp => ({ ...product, ...sp }))
|
|
127
|
+
]), [])
|
|
128
|
+
.filter(({ amount }) => amount > 0);
|
|
129
|
+
},
|
|
122
130
|
totalAmount() {
|
|
123
|
-
return this.
|
|
124
|
-
return amount + product.amount;
|
|
131
|
+
return this.simpleProducts.reduce((amount, product) => {
|
|
132
|
+
return amount + (product.amount * product.productCost);
|
|
125
133
|
}, 0);
|
|
126
134
|
},
|
|
127
135
|
productsByColor() {
|
|
128
|
-
return groupProductsByColor(this.products
|
|
136
|
+
return groupProductsByColor(this.products);
|
|
129
137
|
}
|
|
138
|
+
},
|
|
139
|
+
mounted() {
|
|
140
|
+
groupProductsByColor(this.products);
|
|
130
141
|
}
|
|
131
142
|
};
|
|
132
143
|
</script>
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
</div>
|
|
8
8
|
<div class="ApproveDesignTees__container">
|
|
9
9
|
<product-side-with-print
|
|
10
|
-
:product="
|
|
10
|
+
:product="productWithPrint"
|
|
11
11
|
side="front"
|
|
12
12
|
size="large" />
|
|
13
13
|
<div
|
|
14
|
-
v-if="!
|
|
14
|
+
v-if="!hasFrontPrint"
|
|
15
15
|
class="ApproveDesignTees__no-design">
|
|
16
16
|
No design
|
|
17
17
|
</div>
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
<div class="ApproveDesignTees__container">
|
|
25
25
|
<product-side-with-print
|
|
26
|
-
:product="
|
|
26
|
+
:product="productWithPrint"
|
|
27
27
|
side="back"
|
|
28
28
|
size="large" />
|
|
29
29
|
<div
|
|
30
|
-
v-if="!
|
|
30
|
+
v-if="!hasBackPrint"
|
|
31
31
|
class="ApproveDesignTees__no-design">
|
|
32
32
|
No design
|
|
33
33
|
</div>
|
|
@@ -50,6 +50,20 @@ export default {
|
|
|
50
50
|
type: Object,
|
|
51
51
|
required: true
|
|
52
52
|
}
|
|
53
|
+
},
|
|
54
|
+
computed: {
|
|
55
|
+
hasFrontPrint() {
|
|
56
|
+
return this.product.prints.some(({ printArea }) => printArea.side === 'front');
|
|
57
|
+
},
|
|
58
|
+
hasBackPrint() {
|
|
59
|
+
return this.product.prints.some(({ printArea }) => printArea.side === 'back');
|
|
60
|
+
},
|
|
61
|
+
productWithPrint() {
|
|
62
|
+
return {
|
|
63
|
+
...this.product.product,
|
|
64
|
+
printsThumbnails: (this.product.printsThumbnails || {})
|
|
65
|
+
};
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
};
|
|
55
69
|
</script>
|
|
@@ -176,7 +176,7 @@ export default {
|
|
|
176
176
|
}
|
|
177
177
|
},
|
|
178
178
|
proceedToCard() {
|
|
179
|
-
const entities = generateCartProducts(this.product, this.template.simpleProducts, this.template.layers, this.isPrintPricing);
|
|
179
|
+
const entities = generateCartProducts(this.product, this.template.simpleProducts, this.template.layers, this.isPrintPricing, this.layerThumbnails);
|
|
180
180
|
this.addToCart({ entities, shop: this.shop });
|
|
181
181
|
this.showCartModal(async () => {
|
|
182
182
|
const message = 'Do you wish to continue with editing the current design or reset the editor?';
|
|
@@ -104,7 +104,6 @@ export default {
|
|
|
104
104
|
this.$emit('layer-added', { layer, toEditMode: true });
|
|
105
105
|
},
|
|
106
106
|
async handleUploaded({ url, size }) {
|
|
107
|
-
debugger
|
|
108
107
|
const layer = await this.createLayer({ type: 'art', url, size });
|
|
109
108
|
this.$emit('layer-added', { layer, toEditMode: false });
|
|
110
109
|
}
|
|
@@ -85,7 +85,7 @@ export default {
|
|
|
85
85
|
},
|
|
86
86
|
selectSuboption(option) {
|
|
87
87
|
this.closeSuboptions();
|
|
88
|
-
this.$emit('select', option);
|
|
88
|
+
this.$emit('select', { ...option, printArea: { ...option.printArea, _id: this.option.printArea._id } });
|
|
89
89
|
},
|
|
90
90
|
calcPrintPrice(option) {
|
|
91
91
|
const amount = this.usedSimpleProducts.reduce((amount, product) => product.amount + amount, 0);
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
placeholder="Search products"
|
|
9
9
|
type="text"
|
|
10
10
|
class="form-field no-label tiny-placeholder labelless"
|
|
11
|
+
autocomplete="off"
|
|
12
|
+
autocorrect="off"
|
|
13
|
+
autocapitalize="off"
|
|
14
|
+
spellcheck="false"
|
|
11
15
|
@input="searchWithDebounce"
|
|
12
16
|
@focus="showResults"
|
|
13
17
|
@blur="hideResults" />
|
|
@@ -40,14 +40,11 @@ export default {
|
|
|
40
40
|
name: 'ProductsFilters',
|
|
41
41
|
data() {
|
|
42
42
|
const sortByOptions = [{
|
|
43
|
-
value: '',
|
|
44
|
-
label: '
|
|
43
|
+
value: 'price-low-high',
|
|
44
|
+
label: 'low to high'
|
|
45
45
|
}, {
|
|
46
46
|
value: 'price-high-low',
|
|
47
47
|
label: ' high to low'
|
|
48
|
-
}, {
|
|
49
|
-
value: 'price-low-high',
|
|
50
|
-
label: 'low to high'
|
|
51
48
|
}];
|
|
52
49
|
return {
|
|
53
50
|
sortBy: sortByOptions.find(({ value }) => value === this.$route.query.sort),
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
height: $mobile_navbar_height + $mobile_notification_bar_height;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
p {
|
|
24
|
+
margin: 0 !important;
|
|
25
|
+
padding: 0 !important;
|
|
26
|
+
}
|
|
23
27
|
}
|
|
24
28
|
&__notification {
|
|
25
29
|
background-color: $black;
|
|
@@ -30,6 +34,7 @@
|
|
|
30
34
|
&-close {
|
|
31
35
|
position: absolute;
|
|
32
36
|
right: 20px;
|
|
37
|
+
top: 10px;
|
|
33
38
|
cursor: pointer;
|
|
34
39
|
color: $black;
|
|
35
40
|
}
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
v-if="notificationBar.enabled"
|
|
10
10
|
class="TheNavbar__notification lc_regular12"
|
|
11
11
|
:style="{
|
|
12
|
-
'background-color': notificationBar.backgroundColor
|
|
13
|
-
color: notificationBar.textColor
|
|
12
|
+
'background-color': notificationBar.backgroundColor
|
|
14
13
|
}">
|
|
15
14
|
<span v-html="notificationBar.text"></span>
|
|
16
15
|
<span
|
|
@@ -75,6 +75,12 @@ const productPreview = {
|
|
|
75
75
|
},
|
|
76
76
|
hasTags() {
|
|
77
77
|
return this.product?.tags.length > 0;
|
|
78
|
+
},
|
|
79
|
+
minPrice() {
|
|
80
|
+
return this.product.isClearance ? this.product.minPriceWithoutClearance : this.product.minPrice;
|
|
81
|
+
},
|
|
82
|
+
maxPrice() {
|
|
83
|
+
return this.product.isClearance ? this.product.maxPriceWithoutClearance : this.product.maxPrice;
|
|
78
84
|
}
|
|
79
85
|
},
|
|
80
86
|
mounted() {
|
package/package.json
CHANGED
package/plugins/vee-validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Vue from 'vue';
|
|
2
2
|
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate';
|
|
3
|
-
import { required, email, max, min_value as minValue, integer } from 'vee-validate/dist/rules';
|
|
3
|
+
import { required, email, max, min_value as minValue, max_value as maxValue, integer } from 'vee-validate/dist/rules';
|
|
4
4
|
import { phone, float } from '@lancom/shared/assets/js/utils/custom-validation-rules';
|
|
5
5
|
|
|
6
6
|
Vue.component('ValidationProvider', ValidationProvider);
|
|
@@ -16,5 +16,6 @@ extend('email', email);
|
|
|
16
16
|
extend('max', max);
|
|
17
17
|
extend('float', float);
|
|
18
18
|
extend('min_value', minValue);
|
|
19
|
+
extend('max_value', maxValue);
|
|
19
20
|
extend('phone', phone);
|
|
20
21
|
extend('integer', integer);
|
package/plugins/vue-recaptcha.js
CHANGED
|
@@ -4,10 +4,13 @@ export default () => {
|
|
|
4
4
|
Vue.mixin({
|
|
5
5
|
methods: {
|
|
6
6
|
async getRecaptcha(name) {
|
|
7
|
+
await this.preloadReCaptcha();
|
|
8
|
+
return process.env.IS_LOCAL === 'true' || await this.$recaptcha(name);
|
|
9
|
+
},
|
|
10
|
+
async preloadReCaptcha() {
|
|
7
11
|
if (!this.$recaptcha) {
|
|
8
12
|
await this.loadReCaptcha();
|
|
9
13
|
}
|
|
10
|
-
return process.env.IS_LOCAL === 'true' || await this.$recaptcha(name);
|
|
11
14
|
},
|
|
12
15
|
async loadReCaptcha() {
|
|
13
16
|
const { VueReCaptcha } = await import('vue-recaptcha-v3');
|
package/store/product.js
CHANGED
|
@@ -184,7 +184,6 @@ export const actions = {
|
|
|
184
184
|
}
|
|
185
185
|
) {
|
|
186
186
|
const { center: { top, left } } = getters.printArea;
|
|
187
|
-
const { selectedPrintArea } = getters;
|
|
188
187
|
const layer = await getLayerModel({
|
|
189
188
|
type,
|
|
190
189
|
colorId: editableColor?._id,
|
|
@@ -193,10 +192,11 @@ export const actions = {
|
|
|
193
192
|
isEditMode,
|
|
194
193
|
url,
|
|
195
194
|
originalSize: size,
|
|
195
|
+
sideId: getters.editableSide?.id,
|
|
196
196
|
properties: {
|
|
197
197
|
...(properties || {}),
|
|
198
198
|
printArea: editablePrintArea?._id,
|
|
199
|
-
printSize:
|
|
199
|
+
printSize: editablePrintArea?.printSize?._id,
|
|
200
200
|
printType: selectedPrintType?._id
|
|
201
201
|
}
|
|
202
202
|
});
|