@lancom/shared 0.0.467 → 0.0.469
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/api/admin.js +2 -2
- package/assets/js/models/product-layers.js +6 -3
- package/assets/js/utils/fabric/object-factory.js +1 -1
- package/assets/js/utils/fabric-helper.js +11 -1
- package/components/editor/editor_layers/editor_layers_toolbar/editor-layers-toolbar.vue +3 -2
- package/components/editor/editor_workspace/editor-workspace.vue +5 -0
- package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +9 -3
- package/components/product/related_products/related-products.vue +1 -0
- package/components/product/wizard/wizard_print_text_or_logo/wizard_text_or_logo_form/wizard-text-or-logo-form.scss +3 -3
- package/components/product/wizard/wizard_print_text_or_logo/wizard_text_or_logo_form/wizard-text-or-logo-form.vue +59 -3
- package/components/product/wizard/wizard_print_text_or_logo/wizard_text_or_logo_form/wizard_print_template_select/wizard-print-template-select.vue +33 -0
- package/mixins/product-view.js +4 -0
- package/package.json +1 -1
- package/store/cart.js +8 -4
- package/store/product.js +12 -7
package/assets/js/api/admin.js
CHANGED
|
@@ -152,8 +152,8 @@ export default {
|
|
|
152
152
|
returnShipments(body) {
|
|
153
153
|
return _post('admin/shipments/return', body);
|
|
154
154
|
},
|
|
155
|
-
markShipmentAsDispatched(order, shipment) {
|
|
156
|
-
return _post(`admin/shop/${order.shop?._id || order.shop}/order/${order._id}/shipment/${shipment._id || shipment.guid}/dispatched`, shipment);
|
|
155
|
+
markShipmentAsDispatched(order, shipment, options) {
|
|
156
|
+
return _post(`admin/shop/${order.shop?._id || order.shop}/order/${order._id}/shipment/${shipment._id || shipment.guid}/dispatched`, { ...shipment, ...(options || {}) });
|
|
157
157
|
},
|
|
158
158
|
markLabelsPrinted(order) {
|
|
159
159
|
return _post(`admin/order/${order._id}/mark-labels-printed`);
|
|
@@ -72,16 +72,19 @@ export class ArtLayer extends Layer {
|
|
|
72
72
|
width = null;
|
|
73
73
|
height = null;
|
|
74
74
|
|
|
75
|
-
constructor(type, colorId, top, left, url, originalSize, fileName) {
|
|
75
|
+
constructor(type, colorId, top, left, url, originalSize, fileName, file) {
|
|
76
76
|
super(colorId, top, left);
|
|
77
77
|
this.type = type;
|
|
78
78
|
this.url = url;
|
|
79
79
|
this.originalSize = originalSize;
|
|
80
80
|
this.fileName = fileName;
|
|
81
|
+
this.file = file;
|
|
81
82
|
}
|
|
82
83
|
};
|
|
83
84
|
|
|
84
|
-
export const getLayerModel = async
|
|
85
|
+
export const getLayerModel = async options => {
|
|
86
|
+
const { type, colorId, top, left, isEditMode, url, originalSize, sideId, fileName, file, properties = {} } = options;
|
|
87
|
+
|
|
85
88
|
if (!type) {
|
|
86
89
|
throw new Error('When creating a new layer, you must specify its type');
|
|
87
90
|
}
|
|
@@ -97,7 +100,7 @@ export const getLayerModel = async ({ type, colorId, top, left, isEditMode, url,
|
|
|
97
100
|
}
|
|
98
101
|
break;
|
|
99
102
|
case 'art':
|
|
100
|
-
layer = new ArtLayer(type, colorId, top, left, url, originalSize, fileName);
|
|
103
|
+
layer = new ArtLayer(type, colorId, top, left, url, originalSize, fileName, file);
|
|
101
104
|
break;
|
|
102
105
|
default:
|
|
103
106
|
throw new Error(`Layer type '${type}' not found or not supported.`);
|
|
@@ -20,7 +20,7 @@ export function createText(layer) {
|
|
|
20
20
|
.then(() => {
|
|
21
21
|
const params = { ...layer, layer };
|
|
22
22
|
delete params.copy;
|
|
23
|
-
const copy = layer.copy
|
|
23
|
+
const copy = layer.copy?.length ? layer.copy : '';
|
|
24
24
|
if (!layer.enableOutline) {
|
|
25
25
|
params.strokeWidth = 0;
|
|
26
26
|
}
|
|
@@ -201,7 +201,7 @@ export default class FabricHelper {
|
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
createObject({ layer, active }) {
|
|
204
|
+
createObject({ layer, active, selectable = true }) {
|
|
205
205
|
const initial = !layer.modifiedAt;
|
|
206
206
|
return new Promise(resolve => {
|
|
207
207
|
const methods = {
|
|
@@ -211,6 +211,16 @@ export default class FabricHelper {
|
|
|
211
211
|
this[methods[layer.type]]({ layer, initial }).then(obj => {
|
|
212
212
|
// obj.clipPath = this.boundingBox;
|
|
213
213
|
// console.log('obj: ', obj.top, obj.left);
|
|
214
|
+
if (!selectable) {
|
|
215
|
+
obj.set({
|
|
216
|
+
selectable: false,
|
|
217
|
+
evented: false,
|
|
218
|
+
hasControls: false,
|
|
219
|
+
hasBorders: false,
|
|
220
|
+
lockMovementX: true,
|
|
221
|
+
lockMovementY: true
|
|
222
|
+
});
|
|
223
|
+
}
|
|
214
224
|
this.handleListeners(obj, layer.type);
|
|
215
225
|
this.editor.add(obj);
|
|
216
226
|
if (active) {
|
|
@@ -120,8 +120,9 @@ export default {
|
|
|
120
120
|
const layer = await this.createLayer({ type: 'text' });
|
|
121
121
|
this.$emit('layer-added', { layer, toEditMode: true });
|
|
122
122
|
},
|
|
123
|
-
async handleUploaded(
|
|
124
|
-
const
|
|
123
|
+
async handleUploaded(file) {
|
|
124
|
+
const { url, size, fileName } = file;
|
|
125
|
+
const layer = await this.createLayer({ type: 'art', url, size, fileName, file });
|
|
125
126
|
this.$emit('layer-added', { layer, toEditMode: false });
|
|
126
127
|
}
|
|
127
128
|
}
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
ref="editor"
|
|
31
31
|
:key="side"
|
|
32
32
|
:is-edit-mode="isEditMode"
|
|
33
|
+
:is-selectable="isSelectable"
|
|
33
34
|
:side="side"
|
|
34
35
|
:print-area="editablePrintArea"
|
|
35
36
|
:zoom-size="sideZoomSize"
|
|
@@ -160,6 +161,10 @@ export default {
|
|
|
160
161
|
isEditMode: {
|
|
161
162
|
type: Boolean,
|
|
162
163
|
default: true
|
|
164
|
+
},
|
|
165
|
+
isSelectable: {
|
|
166
|
+
type: Boolean,
|
|
167
|
+
default: true
|
|
163
168
|
}
|
|
164
169
|
},
|
|
165
170
|
data() {
|
|
@@ -171,6 +171,10 @@ export default {
|
|
|
171
171
|
isEditMode: {
|
|
172
172
|
type: Boolean,
|
|
173
173
|
default: true
|
|
174
|
+
},
|
|
175
|
+
isSelectable: {
|
|
176
|
+
type: Boolean,
|
|
177
|
+
default: true
|
|
174
178
|
}
|
|
175
179
|
},
|
|
176
180
|
data() {
|
|
@@ -435,7 +439,8 @@ export default {
|
|
|
435
439
|
for (const layer of this.sideEditableLayers) {
|
|
436
440
|
const params = {
|
|
437
441
|
layer,
|
|
438
|
-
active: this.isLayerSelected(layer)
|
|
442
|
+
active: this.isLayerSelected(layer),
|
|
443
|
+
selectable: this.isSelectable
|
|
439
444
|
};
|
|
440
445
|
await this.fabricHelper.createObject(params);
|
|
441
446
|
}
|
|
@@ -493,9 +498,10 @@ export default {
|
|
|
493
498
|
});
|
|
494
499
|
}
|
|
495
500
|
},
|
|
496
|
-
async handleUploaded(
|
|
501
|
+
async handleUploaded(file) {
|
|
497
502
|
window.scrollTo(0, 0);
|
|
498
|
-
|
|
503
|
+
const { url, size, fileName } = file;
|
|
504
|
+
await this.createLayer({ type: 'art', url, size, fileName, file });
|
|
499
505
|
this.visibleWireframe = true;
|
|
500
506
|
},
|
|
501
507
|
setOffsetWarningVisibility(visible) {
|
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
:class="{
|
|
7
7
|
'WizardTextOrLogoForm__type--selected': layerType === 'text'
|
|
8
8
|
}"
|
|
9
|
+
style="width: 45%"
|
|
9
10
|
@click="selectLayerType('text')">
|
|
10
11
|
<div class="WizardTextOrLogoForm__type-name">
|
|
11
12
|
Add text
|
|
12
13
|
</div>
|
|
13
14
|
<div class="WizardTextOrLogoForm__type-input">
|
|
15
|
+
<wizard-print-template-select
|
|
16
|
+
v-if="hasPrintTemplateOptions"
|
|
17
|
+
v-model="layer.printTemplate"
|
|
18
|
+
:options="layer.printTemplates"
|
|
19
|
+
@input="onPrintTemplateChange" />
|
|
14
20
|
<input
|
|
21
|
+
v-else
|
|
15
22
|
v-model="layer.copy"
|
|
16
23
|
placeholder="Enter your text"
|
|
17
24
|
type="text"
|
|
@@ -83,6 +90,7 @@
|
|
|
83
90
|
</a>
|
|
84
91
|
</div>
|
|
85
92
|
</div>
|
|
93
|
+
|
|
86
94
|
</div>
|
|
87
95
|
<div class="WizardTextOrLogoForm__description">
|
|
88
96
|
<textarea
|
|
@@ -95,13 +103,18 @@
|
|
|
95
103
|
</template>
|
|
96
104
|
|
|
97
105
|
<script>
|
|
98
|
-
import { mapGetters } from 'vuex';
|
|
106
|
+
import { mapGetters, mapMutations } from 'vuex';
|
|
107
|
+
import { getLayerModel } from '@lancom/shared/assets/js/models/product-layers';
|
|
108
|
+
import { fitLayerToEditorSize } from '@lancom/shared/assets/js/utils/layers';
|
|
109
|
+
import { getPrintAreaByName } from '@lancom/shared/assets/js/models/print-area';
|
|
99
110
|
import FileUploader from '@lancom/shared/components/common/file_uploader';
|
|
111
|
+
import WizardPrintTemplateSelect from './wizard_print_template_select/wizard-print-template-select';
|
|
100
112
|
|
|
101
113
|
export default {
|
|
102
114
|
name: 'WizardTextOrLogoForm',
|
|
103
115
|
components: {
|
|
104
|
-
FileUploader
|
|
116
|
+
FileUploader,
|
|
117
|
+
WizardPrintTemplateSelect
|
|
105
118
|
},
|
|
106
119
|
props: {
|
|
107
120
|
layer: {
|
|
@@ -117,9 +130,52 @@ export default {
|
|
|
117
130
|
},
|
|
118
131
|
computed: {
|
|
119
132
|
...mapGetters(['shop']),
|
|
120
|
-
...mapGetters('product', ['product'])
|
|
133
|
+
...mapGetters('product', ['product', 'editorSize', 'editableColor']),
|
|
134
|
+
hasPrintTemplateOptions() {
|
|
135
|
+
return (this.layer.printTemplates || []).length > 1;
|
|
136
|
+
}
|
|
121
137
|
},
|
|
122
138
|
methods: {
|
|
139
|
+
...mapMutations('product', ['addTemplateLayer', 'removeTemplateLayer']),
|
|
140
|
+
async onPrintTemplateChange(template) {
|
|
141
|
+
if (!template) return;
|
|
142
|
+
|
|
143
|
+
this.removeTemplateLayer(this.layer);
|
|
144
|
+
|
|
145
|
+
const layers = template.layers || [{ type: 'text' }];
|
|
146
|
+
for (const templateLayer of layers) {
|
|
147
|
+
const properties = {
|
|
148
|
+
...templateLayer,
|
|
149
|
+
printArea: this.layer.printArea,
|
|
150
|
+
printSize: this.layer.printSize,
|
|
151
|
+
printType: this.layer.printType,
|
|
152
|
+
printTemplates: this.layer.printTemplates,
|
|
153
|
+
printTemplate: template,
|
|
154
|
+
copy: templateLayer.copy || templateLayer.text || '',
|
|
155
|
+
required: true
|
|
156
|
+
};
|
|
157
|
+
delete properties.guid;
|
|
158
|
+
|
|
159
|
+
const printArea = getPrintAreaByName({
|
|
160
|
+
printArea: this.layer.printArea,
|
|
161
|
+
printSize: this.layer.printSize,
|
|
162
|
+
editorWidth: this.editorSize.width,
|
|
163
|
+
editorHeight: this.editorSize.height
|
|
164
|
+
}, this.product, true);
|
|
165
|
+
fitLayerToEditorSize(properties, template, printArea);
|
|
166
|
+
properties.sideId = printArea?.sideId;
|
|
167
|
+
|
|
168
|
+
const data = {
|
|
169
|
+
colorId: this.editableColor._id,
|
|
170
|
+
type: templateLayer.type || 'text',
|
|
171
|
+
sideId: properties.sideId || 'front',
|
|
172
|
+
properties
|
|
173
|
+
};
|
|
174
|
+
const model = await getLayerModel(data);
|
|
175
|
+
model.createdAt = Date.now();
|
|
176
|
+
this.addTemplateLayer(model);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
123
179
|
selectLayerType(type) {
|
|
124
180
|
if (this.layerType !== type) {
|
|
125
181
|
this.layerType = type;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<multiselect
|
|
3
|
+
class="WizardPrintTemplateSelect"
|
|
4
|
+
:value="value"
|
|
5
|
+
:options="options"
|
|
6
|
+
label="name"
|
|
7
|
+
track-by="_id"
|
|
8
|
+
:searchable="false"
|
|
9
|
+
:show-labels="false"
|
|
10
|
+
placeholder="Select template"
|
|
11
|
+
@input="$emit('input', $event)" />
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
import Multiselect from 'vue-multiselect';
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
name: 'WizardPrintTemplateSelect',
|
|
19
|
+
components: {
|
|
20
|
+
Multiselect
|
|
21
|
+
},
|
|
22
|
+
props: {
|
|
23
|
+
value: {
|
|
24
|
+
type: Object,
|
|
25
|
+
default: null
|
|
26
|
+
},
|
|
27
|
+
options: {
|
|
28
|
+
type: Array,
|
|
29
|
+
default: () => []
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
</script>
|
package/mixins/product-view.js
CHANGED
|
@@ -153,12 +153,16 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
|
|
|
153
153
|
if (this.preSetPrints?.length) {
|
|
154
154
|
for (const preSetPrint of this.preSetPrints) {
|
|
155
155
|
const layers = preSetPrint.printTemplate?.layers || [{ type: 'text' }];
|
|
156
|
+
const printTemplates = [preSetPrint.printTemplate, ...(preSetPrint.additionalPrintTemplates || [])]
|
|
156
157
|
for (const layer of layers) {
|
|
157
158
|
const properties = {
|
|
158
159
|
...layer,
|
|
159
160
|
printArea: preSetPrint.printArea,
|
|
160
161
|
printSize: preSetPrint.printSize,
|
|
161
162
|
printType: preSetPrint.printType,
|
|
163
|
+
printTemplates,
|
|
164
|
+
printTemplate: preSetPrint.printTemplate,
|
|
165
|
+
minimumOrderQuantity: preSetPrint.minimumOrderQuantity,
|
|
162
166
|
required: true
|
|
163
167
|
};
|
|
164
168
|
delete properties.guid;
|
package/package.json
CHANGED
package/store/cart.js
CHANGED
|
@@ -65,12 +65,13 @@ const getInvalidStockQuantities = entities => {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
const getPrintsQuantities = entities => {
|
|
68
|
-
const printTypes = entities.reduce((types, e) => [...types, ...e.prints.map(({ printType }) => ({ ...printType, guid: `${e.guid}-${printType?._id}` }))], []);
|
|
68
|
+
const printTypes = entities.reduce((types, e) => [...types, ...e.prints.map(({ printType, layers }) => ({ ...printType, layers, guid: `${e.guid}-${printType?._id}` }))], []);
|
|
69
69
|
const grouped = groupBy(printTypes, 'guid');
|
|
70
70
|
const quantities = Object.keys(grouped).map(guid => {
|
|
71
|
-
const [{ name, minQuantity }] = grouped[guid];
|
|
71
|
+
const [{ name, minQuantity, layers }] = grouped[guid];
|
|
72
72
|
const printTypeEntities = entities.filter(e => (e.prints || []).some(({ printType }) => `${e.guid}-${printType?._id}` === guid));
|
|
73
|
-
|
|
73
|
+
const layersMinOrderQty = Math.max(0, ...(layers?.map(l => l?.params?.minimumOrderQuantity || 0) || []));
|
|
74
|
+
return { name, minQuantity: layersMinOrderQty || minQuantity, quantity: getEntitiesQuantity(printTypeEntities), layers };
|
|
74
75
|
});
|
|
75
76
|
return quantities;
|
|
76
77
|
};
|
|
@@ -147,7 +148,10 @@ export const getters = {
|
|
|
147
148
|
return (quantities?.stock || []).filter(({ maxQuantity, quantity }) => quantity > maxQuantity);
|
|
148
149
|
},
|
|
149
150
|
notValidPrintsQuantities(state, { quantities }) {
|
|
150
|
-
return (quantities?.prints || []).filter(({ minQuantity, quantity }) =>
|
|
151
|
+
return (quantities?.prints || []).filter(({ minQuantity, quantity, layers }) => {
|
|
152
|
+
const printMinQty = quantity || 0;
|
|
153
|
+
return printMinQty < minQuantity;
|
|
154
|
+
});
|
|
151
155
|
},
|
|
152
156
|
cartPricingError: ({ cartPricingError }) => cartPricingError
|
|
153
157
|
};
|
package/store/product.js
CHANGED
|
@@ -17,7 +17,7 @@ export const state = () => ({
|
|
|
17
17
|
loadingProductDetails: false,
|
|
18
18
|
productDetailsKey: Date.now(),
|
|
19
19
|
images: [],
|
|
20
|
-
priceIncludeGST:
|
|
20
|
+
priceIncludeGST: false,
|
|
21
21
|
product: null,
|
|
22
22
|
preSetPrints: null,
|
|
23
23
|
loadError: null,
|
|
@@ -117,7 +117,7 @@ export const getters = {
|
|
|
117
117
|
editModeSelectedLayer: ({ editModeSelectedLayer }) => editModeSelectedLayer,
|
|
118
118
|
selectedPrintAreas: ({ selectedPrintAreas }) => selectedPrintAreas,
|
|
119
119
|
selectedPrintArea: ({ selectedPrintAreas, editablePrintArea }) =>
|
|
120
|
-
(
|
|
120
|
+
(editablePrintArea ? selectedPrintAreas[editablePrintArea._id] : null),
|
|
121
121
|
productPricing: ({ productPricing }) => productPricing,
|
|
122
122
|
mainProductPricing: ({ productPricing, product }) => (productPricing?.products || {})[product._id],
|
|
123
123
|
selectedColors: ({ template: { colors = [] }, availableColors }) => {
|
|
@@ -148,13 +148,16 @@ export const getters = {
|
|
|
148
148
|
availablePrintTypes: ({ availablePrintTypes }) => availablePrintTypes,
|
|
149
149
|
isPrintPricing: ({ isPrintPricing }) => isPrintPricing,
|
|
150
150
|
minimumOrderQuantity: ({ product, template }) => {
|
|
151
|
-
const layersPrintTypes = (template.layers || []).map(({ printType }) => printType);
|
|
151
|
+
const layersPrintTypes = (template.layers || []).map(({ printType, minimumOrderQuantity }) => minimumOrderQuantity > 0 ? null : printType);
|
|
152
152
|
const printTypesMinQuantity = (product.printTypes || [])
|
|
153
153
|
.filter(printType => layersPrintTypes.includes(printType._id || printType))
|
|
154
154
|
.map(printType => printType.minQuantity);
|
|
155
|
+
const layersWithTemplate = (template.layers || []).map(({ minimumOrderQuantity }) => minimumOrderQuantity).filter(mo => mo > 0);
|
|
156
|
+
|
|
155
157
|
return Math.max(
|
|
156
|
-
|
|
157
|
-
...printTypesMinQuantity
|
|
158
|
+
template.layers?.length > 0 ? product.minimumPrintOrderQuantity : product.minimumOrderQuantity,
|
|
159
|
+
...printTypesMinQuantity,
|
|
160
|
+
...layersWithTemplate
|
|
158
161
|
);
|
|
159
162
|
},
|
|
160
163
|
images: ({ images, product }) => {
|
|
@@ -332,6 +335,7 @@ export const actions = {
|
|
|
332
335
|
preselect = true,
|
|
333
336
|
isEditMode,
|
|
334
337
|
size,
|
|
338
|
+
file,
|
|
335
339
|
properties,
|
|
336
340
|
fileName
|
|
337
341
|
}
|
|
@@ -344,6 +348,7 @@ export const actions = {
|
|
|
344
348
|
left,
|
|
345
349
|
isEditMode,
|
|
346
350
|
fileName,
|
|
351
|
+
file,
|
|
347
352
|
url,
|
|
348
353
|
originalSize: size,
|
|
349
354
|
sideId: getters.editableSide?.id,
|
|
@@ -410,8 +415,8 @@ export const mutations = {
|
|
|
410
415
|
},
|
|
411
416
|
setProductDetails(state, simpleProducts) {
|
|
412
417
|
const { preSetPrints } = state;
|
|
413
|
-
const
|
|
414
|
-
simpleProducts = simpleProducts.filter(sp => !
|
|
418
|
+
const allPreSetColors = (preSetPrints || []).reduce((colors, print) => [...colors, ...(print.colors || [])], []);
|
|
419
|
+
simpleProducts = simpleProducts.filter(sp => !allPreSetColors.length || allPreSetColors.includes(sp.color._id));
|
|
415
420
|
state.productDetails = { simpleProducts };
|
|
416
421
|
const availableSizes = [];
|
|
417
422
|
const sizesPerColor = simpleProducts.reduce((map, { color, size }) => {
|