@lancom/shared 0.0.449 → 0.0.451

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 (24) hide show
  1. package/assets/js/api/index.js +4 -1
  2. package/assets/js/utils/export-print-to-svg.js +1 -1
  3. package/assets/js/utils/fabric/selection-style.js +145 -44
  4. package/assets/js/utils/fabric-helper.js +11 -34
  5. package/components/common/file_uploader.vue +5 -4
  6. package/components/common/postcode_select/postcode-select.vue +1 -1
  7. package/components/common/product_side_with_print/product-side-with-print.vue +6 -2
  8. package/components/editor/editor_colors/editor-colors.vue +1 -0
  9. package/components/editor/editor_layers/editor_layer_forms/editor_layer_form_text/editor-layer-form-text.vue +1 -2
  10. package/components/editor/editor_layers/editor_layers_layer/editor-layers-layer.scss +1 -0
  11. package/components/editor/editor_layers/editor_layers_layer/editor-layers-layer.vue +2 -2
  12. package/components/editor/editor_layers/editor_layers_toolbar/editor-layers-toolbar.vue +1 -0
  13. package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +9 -23
  14. package/components/modals/failed_conversion_modal/failed-conversion-modal.vue +1 -0
  15. package/components/order/order_view/order-view.mixin.js +4 -1
  16. package/components/order/order_view/order-view.vue +1 -1
  17. package/components/product/editor_pricing/editor-pricing.vue +0 -1
  18. package/components/product/product_check_delivery/product-check-delivery.vue +8 -1
  19. package/components/product/wizard/wizard_print_layers/wizard_print_layer/wizard-print-layer.vue +1 -0
  20. package/components/product/wizard/wizard_print_text_or_logo/wizard_text_or_logo_form/wizard-text-or-logo-form.vue +1 -0
  21. package/feeds/google-shopping.js +22 -13
  22. package/mixins/layouts/products.js +7 -2
  23. package/nuxt.config.js +1 -1
  24. package/package.json +1 -1
@@ -161,7 +161,7 @@ const api = {
161
161
  subscribe(data, shop) {
162
162
  return _post(`shop/${shop}/subscribe`, data);
163
163
  },
164
- uploadImage(file, progressCallback = Function.prototype, requestUrl = 'image/upload') {
164
+ uploadImage(file, progressCallback = Function.prototype, requestUrl = 'image/upload', convert = false) {
165
165
  const config = {
166
166
  onUploadProgress: ({ loaded, total }) => {
167
167
  progressCallback(Math.round((loaded * 100) / total));
@@ -169,6 +169,9 @@ const api = {
169
169
  };
170
170
  const formData = new FormData();
171
171
  formData.set('file', file);
172
+ if (convert) {
173
+ requestUrl += (requestUrl.includes('?') ? '&' : '?') + 'convert=true';
174
+ }
172
175
  return _post(requestUrl, formData, config);
173
176
  },
174
177
  sendFailedConversionFile(data, shop) {
@@ -11,7 +11,7 @@ export function convertPrintToSVG(layers, printSize) {
11
11
  return new Promise(resolve => {
12
12
  const svgSize = getSVGSize(printSize);
13
13
  console.log('svgSize: ', svgSize);
14
- const canvas = new fabric.Canvas(document.createElement('canvas'), { ...svgSize, uniScaleTransform: false });
14
+ const canvas = new fabric.Canvas(document.createElement('canvas'), { ...svgSize });
15
15
  Promise.all(
16
16
  layers
17
17
  .map(layer => {
@@ -1,59 +1,160 @@
1
1
  import { fabric } from 'fabric';
2
2
 
3
- let rotate;
3
+ let rotateImg;
4
+ let centerImg;
4
5
 
5
6
  export function loadRotateImage(cb = Function.prototype) {
6
7
  const image = new Image();
7
- image.src = '/icons/rotate.png';
8
+ image.src = '/icons/rotate.png';
8
9
  image.onload = () => {
9
- rotate = image;
10
+ rotateImg = image;
10
11
  cb();
11
12
  };
12
13
  }
13
14
 
14
- fabric.Object.prototype.drawControls = function (ctx) {
15
- if (!this.hasControls) {
16
- return this;
17
- }
18
-
19
- const { x: width, y: height } = this._calculateCurrentDimensions();
20
- const scaleOffset = this.cornerSize;
21
- const left = -(width + scaleOffset) / 2;
22
- const top = -(height + scaleOffset) / 2;
23
- const methodName = this.transparentCorners ? 'stroke' : 'fill';
15
+ export function loadCenterImage(cb = Function.prototype) {
16
+ const image = new Image();
17
+ image.src = '/icons/center.png';
18
+ image.onload = () => {
19
+ centerImg = image;
20
+ cb();
21
+ };
22
+ }
24
23
 
24
+ function renderIcon(ctx, left, top, styleOverride, fabricObject, icon) {
25
+ const size = 20;
25
26
  ctx.save();
27
+ ctx.translate(left, top);
28
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
29
+ ctx.drawImage(icon, -size / 2, -size / 2, size, size);
30
+ ctx.restore();
31
+ }
26
32
 
27
- ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
28
- if (!this.transparentCorners) {
29
- ctx.strokeStyle = this.cornerStrokeColor;
30
- }
31
- this._setLineDash(ctx, this.cornerDashArray, null);
32
-
33
- this._drawControl('tl', ctx, methodName, left, top);
34
- this._drawControl('tr', ctx, methodName, left + width, top);
35
- this._drawControl('bl', ctx, methodName, left, top + height);
36
- this._drawControl('br', ctx, methodName, left + width, top + height);
37
-
38
- if (!this.get('lockUniScaling')) {
39
- this._drawControl('mt', ctx, methodName, left + width / 2, top);
40
- this._drawControl('mb', ctx, methodName, left + width / 2, top + height);
41
- this._drawControl('mr', ctx, methodName, left + width, top + height / 2);
42
- this._drawControl('ml', ctx, methodName, left, top + height / 2);
43
- }
44
-
45
- if (this.hasRotatingPoint) {
46
- const rotateLeft = left + width / 2 - 4;
47
- const rotateTop = top - this.rotatingPointOffset - 5;
48
- const drawRotateIcon = () => ctx.drawImage(rotate, rotateLeft, rotateTop, 20, 20);
49
- if (!rotate) {
50
- loadRotateImage(drawRotateIcon);
51
- } else {
52
- drawRotateIcon();
53
- }
54
- }
33
+ export function setupCustomControls(fabricHelper) {
34
+ fabric.Object.prototype.set({
35
+ padding: 20,
36
+ cornerSize: 8,
37
+ cornerStyle: 'circle',
38
+ borderColor: '#7D6AEF',
39
+ cornerColor: '#7D6AEF',
40
+ cornerStrokeColor: '#fff',
41
+ transparentCorners: false
42
+ });
55
43
 
56
- ctx.restore();
44
+ delete fabric.Object.prototype.controls.ml;
45
+ delete fabric.Object.prototype.controls.mr;
46
+ delete fabric.Object.prototype.controls.mt;
47
+ delete fabric.Object.prototype.controls.mb;
57
48
 
58
- return this;
59
- };
49
+ ['tl', 'bl', 'br'].forEach(key => {
50
+ fabric.Object.prototype.controls[key].sizeX = 7;
51
+ fabric.Object.prototype.controls[key].sizeY = 7;
52
+ });
53
+
54
+ fabric.Object.prototype.controls.tr = new fabric.Control({
55
+ x: 0.5,
56
+ y: -0.5,
57
+ cursorStyle: 'pointer',
58
+ mouseUpHandler: function(eventData, transform) {
59
+ const target = transform.target;
60
+ fabricHelper.dispatch('removeLayer', target.layer);
61
+ fabricHelper.editor.remove(target);
62
+ fabricHelper.editor.renderAll();
63
+ return true;
64
+ },
65
+ render: function(ctx, left, top, styleOverride, fabricObject) {
66
+ const size = 10;
67
+ ctx.save();
68
+ ctx.translate(left, top);
69
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
70
+ ctx.fillStyle = '#FF4444';
71
+ ctx.beginPath();
72
+ ctx.arc(0, 0, size / 2, 0, 2 * Math.PI, false);
73
+ ctx.fill();
74
+ ctx.strokeStyle = '#fff';
75
+ ctx.lineWidth = 2;
76
+ ctx.beginPath();
77
+ ctx.moveTo(-4, -4);
78
+ ctx.lineTo(4, 4);
79
+ ctx.moveTo(4, -4);
80
+ ctx.lineTo(-4, 4);
81
+ ctx.stroke();
82
+ ctx.restore();
83
+ },
84
+ cornerSize: 10
85
+ });
86
+
87
+ fabric.Object.prototype.controls.mtr = new fabric.Control({
88
+ x: 0,
89
+ y: -0.5,
90
+ offsetY: -30,
91
+ cursorStyle: 'pointer',
92
+ actionHandler: fabric.controlsUtils.rotationWithSnapping,
93
+ actionName: 'rotate',
94
+ render: function(ctx, left, top, styleOverride, fabricObject) {
95
+ if (rotateImg) {
96
+ renderIcon(ctx, left, top, styleOverride, fabricObject, rotateImg);
97
+ }
98
+ },
99
+ cornerSize: 20,
100
+ withConnection: true
101
+ });
102
+
103
+ fabric.Object.prototype.controls.centerControl = new fabric.Control({
104
+ x: 0,
105
+ y: -0.5,
106
+ offsetY: -30,
107
+ offsetX: 30,
108
+ cursorStyle: 'pointer',
109
+ mouseUpHandler: function(eventData, transform) {
110
+ const target = transform.target;
111
+ const helper = fabricHelper;
112
+
113
+ if (helper && helper.printAreaRect) {
114
+ const printArea = helper.printAreaRect;
115
+ const scaleX = printArea.width / target.width;
116
+ const scaleY = printArea.height / target.height;
117
+ const scale = Math.min(scaleX, scaleY) * 0.8;
118
+
119
+ target.set({
120
+ scaleX: scale,
121
+ scaleY: scale,
122
+ left: printArea.left + printArea.width / 2,
123
+ top: printArea.top + printArea.height / 2
124
+ });
125
+
126
+ target.setCoords();
127
+ helper.editor.renderAll();
128
+ helper.dispatch('setField', { field: 'scaleX', value: scale });
129
+ helper.dispatch('setField', { field: 'scaleY', value: scale });
130
+ helper.dispatch('setField', { field: 'left', value: target.left });
131
+ helper.dispatch('setField', { field: 'top', value: target.top });
132
+ helper.dispatch('setField', { field: 'boundingRect', value: target.getBoundingRect() });
133
+ }
134
+
135
+ return true;
136
+ },
137
+ render: function(ctx, left, top, styleOverride, fabricObject) {
138
+ if (centerImg) {
139
+ renderIcon(ctx, left, top, styleOverride, fabricObject, centerImg);
140
+ } else {
141
+ const size = 20;
142
+ ctx.save();
143
+ ctx.translate(left, top);
144
+ ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
145
+ ctx.fillStyle = '#7D6AEF';
146
+ ctx.beginPath();
147
+ ctx.arc(0, 0, size / 2, 0, 2 * Math.PI, false);
148
+ ctx.fill();
149
+ ctx.fillStyle = '#fff';
150
+ ctx.font = '14px Arial';
151
+ ctx.textAlign = 'center';
152
+ ctx.textBaseline = 'middle';
153
+ ctx.fillText('⊙', 0, 0);
154
+ ctx.restore();
155
+ }
156
+ },
157
+ cornerSize: 20,
158
+ withConnection: true
159
+ });
160
+ }
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { fabric } from 'fabric';
3
3
  import { getPrintAreaByName } from '../models/print-area';
4
- import { loadRotateImage } from './fabric/selection-style';
4
+ import { loadRotateImage, loadCenterImage, setupCustomControls } from './fabric/selection-style';
5
5
  import EventDispatcher from './event-dispatcher';
6
6
  import { createText, createArt } from './fabric/object-factory';
7
7
  import { buildWireframe } from './fabric/wireframe';
@@ -14,7 +14,7 @@ class Background {
14
14
  backgroundImage = null;
15
15
 
16
16
  constructor({ background, size }) {
17
- this.canvas = new fabric.Canvas(background, { ...size, uniScaleTransform: false });
17
+ this.canvas = new fabric.Canvas(background, { ...size });
18
18
  }
19
19
 
20
20
  clear() {
@@ -68,7 +68,7 @@ export default class FabricHelper {
68
68
 
69
69
  constructor({ editor, background, size }) {
70
70
  this.size = size;
71
- this.editor = new fabric.Canvas(editor, { ...size, uniScaleTransform: false });
71
+ this.editor = new fabric.Canvas(editor, { ...size });
72
72
  if (background) {
73
73
  this.background = new Background({ background, size });
74
74
  }
@@ -88,8 +88,10 @@ export default class FabricHelper {
88
88
  // return -this.height / 2 + this.getHeightOfLine(0) / this.lineHeight;
89
89
  // };
90
90
 
91
- this.setDeleteButtonListeners();
92
91
  loadRotateImage();
92
+ loadCenterImage();
93
+ setupCustomControls(this);
94
+ this.setDeleteButtonListeners();
93
95
  }
94
96
 
95
97
  clear() {
@@ -209,7 +211,6 @@ export default class FabricHelper {
209
211
  obj.hiddenTextarea.focus();
210
212
  }
211
213
  }
212
- obj.setControlsVisibility({ tr: false });
213
214
 
214
215
  if (initial) {
215
216
  this.dispatch('setField', { field: 'boundingRect', value: obj.getBoundingRect() });
@@ -264,28 +265,6 @@ export default class FabricHelper {
264
265
  }
265
266
 
266
267
  setDeleteButtonListeners() {
267
- this.editor.on('object:selected', ({ target }) => {
268
- this.dispatch('setDeleteButtonPosition', target.oCoords.tr);
269
- });
270
- this.editor.on('object:modified', ({ target }) => {
271
- this.dispatch('setDeleteButtonPosition', target.oCoords.tr);
272
- target.setControlsVisibility({ tr: false });
273
- });
274
- this.editor.on('object:moving', ({ target }) => {
275
- target.setControlsVisibility({ tr: true });
276
- this.dispatch('setDeleteButtonPosition', null);
277
- });
278
- this.editor.on('object:scaling', ({ target }) => {
279
- target.setControlsVisibility({ tr: true });
280
- this.dispatch('setDeleteButtonPosition', null);
281
- });
282
- this.editor.on('object:rotating', ({ target }) => {
283
- target.setControlsVisibility({ tr: true });
284
- this.dispatch('setDeleteButtonPosition', null);
285
- });
286
- this.editor.on('selection:cleared', () => {
287
- this.dispatch('setDeleteButtonPosition', null);
288
- });
289
268
  }
290
269
 
291
270
  handleListeners(object, type) {
@@ -293,13 +272,12 @@ export default class FabricHelper {
293
272
  ** COMMON EVENTS
294
273
  */
295
274
  object.on('selected', () => this.dispatch('selectLayer', object.layer));
296
- object.on('rotating', ({ target: { angle } }) => {
297
- this.dispatch('setField', { field: 'angle', value: parseInt(angle) });
275
+ object.on('rotating', () => {
276
+ this.dispatch('setField', { field: 'angle', value: parseInt(object.angle) });
298
277
  });
299
- object.on('moved', ({ target }) => {
300
- const { top, left } = target;
301
- this.dispatch('setField', { field: 'top', value: Math.round(top) });
302
- this.dispatch('setField', { field: 'left', value: Math.round(left) });
278
+ object.on('moved', () => {
279
+ this.dispatch('setField', { field: 'top', value: Math.round(object.top) });
280
+ this.dispatch('setField', { field: 'left', value: Math.round(object.left) });
303
281
  });
304
282
  object.on('moving', () => this.checkBoundingIntersection(object));
305
283
  object.on('scaling', () => {
@@ -339,7 +317,6 @@ export default class FabricHelper {
339
317
  object.on('changed', () => {
340
318
  this.dispatch('setField', { field: 'copy', value: object.text });
341
319
  this.dispatch('setField', { field: 'boundingRect', value: object.getBoundingRect() });
342
- this.dispatch('setDeleteButtonPosition', object.oCoords.tr);
343
320
  // if (/\n/g.test(object.text || '')) {
344
321
  // this.discardActiveObjects();
345
322
  // object.text = object.text.replace(/\n/g, '');
@@ -48,6 +48,7 @@ export default {
48
48
  name: 'FileUploader',
49
49
  props: {
50
50
  required: { type: Boolean, default: false },
51
+ convert: { type: Boolean, default: false },
51
52
  disabled: { type: Boolean, default: false },
52
53
  multiple: { type: Boolean, default: false },
53
54
  hasUploadLink: { type: Boolean, default: false },
@@ -113,18 +114,18 @@ export default {
113
114
  },
114
115
  async upload() {
115
116
  try {
116
- const image = await api.uploadImage(this.file, this.onProgress, this.url);
117
+ const image = await api.uploadImage(this.file, this.onProgress, this.url, this.convert);
117
118
  this.onProgress(0);
118
119
  const url = image && staticLink(image.thumb || image.origin);
119
120
  this.$emit('onuploaded', { ...image, url });
120
121
  } catch (e) {
121
122
  console.dir(e);
122
123
  this.onProgress(0);
123
- const { message, data } = e.response.data;
124
- if (message === 'Conversion failed' && this.hasConversionErrorModal) {
124
+ const { message, error, data } = e.response?.data || {};
125
+ if ([message, error].includes('Conversion failed') && this.hasConversionErrorModal) {
125
126
  this.showConversionFailedModal(data);
126
127
  } else if (this.showErrorMessage) {
127
- this.$toastr.e(e);
128
+ this.$toastr.e(error || message || e.message || e);
128
129
  } else {
129
130
  this.$emit('onerror', e);
130
131
  }
@@ -177,7 +177,7 @@ export default {
177
177
  if (this.suburb) {
178
178
  const option = this.createOptionFromSuburb(this.suburb);
179
179
  this.selected = option;
180
- await this.handleSearch(this.selected.value.trim())
180
+ // await this.handleSearch(this.selected.value.trim());
181
181
  }
182
182
  },
183
183
  methods: {
@@ -59,6 +59,10 @@ export default {
59
59
  defaultPreview: {
60
60
  type: Boolean,
61
61
  default: true
62
+ },
63
+ allowCoverImage: {
64
+ type: Boolean,
65
+ default: true
62
66
  }
63
67
  },
64
68
  computed: {
@@ -80,7 +84,7 @@ export default {
80
84
  },
81
85
  colorBackground() {
82
86
  const color = this.color || this.product.color;
83
- return this.fillBackground && getColorBackgroundStyle(color, false, this.originBackground);
87
+ return (this.fillBackground || (!this.allowCoverImage && !this.image)) && getColorBackgroundStyle(color, false, this.originBackground);
84
88
  },
85
89
  imageBackground() {
86
90
  return this.image && this.getImageBackground(this.image);
@@ -91,7 +95,7 @@ export default {
91
95
  image() {
92
96
  const color = this.defaultPreview ? null : (this.color || this.product.color || (this.product.colors || [])[0]);
93
97
  const product = this.product?.product || this.product;
94
- return getColorImage(product, this.size, this.side, color) || getProductCover(product, this.size, this.side, color);
98
+ return getColorImage(product, this.size, this.side, color) || (this.allowCoverImage ? getProductCover(product, this.size, this.side, color) : null);
95
99
  },
96
100
  print() {
97
101
  const { printsThumbnails = {} } = this.product || {};
@@ -14,6 +14,7 @@
14
14
  :color="color"
15
15
  :fill-background="false"
16
16
  :default-preview="false"
17
+ :allow-cover-image="false"
17
18
  class="EditorColors__color-image">
18
19
  </product-side-with-print>
19
20
  <div class="EditorColors__color-name">
@@ -310,8 +310,7 @@ export default {
310
310
  if (this.$refs.previewCanvas) {
311
311
  this.fabricCanvas = new fabric.Canvas(this.$refs.previewCanvas, {
312
312
  width: this.$refs.previewCanvas.parentElement.offsetWidth - 100,
313
- height: 200,
314
- uniScaleTransform: false
313
+ height: 200
315
314
  });
316
315
  this.fabricCanvas.renderOnAddRemove = false;
317
316
  this.setupCanvasListeners();
@@ -96,6 +96,7 @@
96
96
  white-space: nowrap;
97
97
  overflow: hidden;
98
98
  text-overflow: ellipsis;
99
+ max-width: 400px;
99
100
  }
100
101
  &__edit {
101
102
  width: 100%;
@@ -145,8 +145,8 @@ export default {
145
145
  break;
146
146
  }
147
147
  return {
148
- class: quality ? quality.replace(' ', '_') : null,
149
- value: isVector ? null : `${quality} / ${this.layer.dpi} DPI`
148
+ class: null, // quality ? quality.replace(' ', '_') : null,
149
+ value: isVector ? null : `${this.layer.dpi} DPI` // isVector ? null : `${quality} / ${this.layer.dpi} DPI`
150
150
  };
151
151
  }
152
152
  },
@@ -23,6 +23,7 @@
23
23
  <file-uploader
24
24
  class="EditorLayersToolbar__option"
25
25
  :multiple="false"
26
+ :convert="true"
26
27
  :url="`image/editor/${shop._id}/${product._id}`"
27
28
  @onuploaded="handleUploaded">
28
29
  <div slot="toggle">
@@ -34,13 +34,6 @@
34
34
  {{ visibleOnpress ? 'Real product photo' : 'Press for real product photo' }}
35
35
  </div>
36
36
  </div>
37
- <div
38
- v-if="deleteButtonPos"
39
- :style="deleteButtonPosition"
40
- class="EditorWorkspaceSide__delete-object"
41
- @click="removeSelected">
42
- <i class="icon-cancel"></i>
43
- </div>
44
37
  <div>
45
38
  <div class="EditorWorkspaceSide__background-container">
46
39
  <canvas ref="background"></canvas>
@@ -78,6 +71,7 @@
78
71
  }">
79
72
  <file-uploader
80
73
  :multiple="false"
74
+ :convert="true"
81
75
  :url="`image/editor/${shop._id}/${product._id}`"
82
76
  @onuploaded="handleUploaded">
83
77
  <template #toggle>
@@ -114,7 +108,7 @@
114
108
  class="EditorWorkspaceSide__overlay"
115
109
  @mousedown="$emit('zoom-in')"></div>
116
110
  <div class="EditorWorkspaceSide__alert-container">
117
- <div
111
+ <!-- <div
118
112
  v-if="showErrorAboutSmallImage"
119
113
  class="EditorWorkspaceSide__alert-error"
120
114
  @click="adjustSelectedArtDPI">
@@ -132,7 +126,7 @@
132
126
  class="EditorWorkspaceSide__alert-error">
133
127
  <i class="icon-attention-circled"></i>
134
128
  Part of your design layer is outside the print area.
135
- </div>
129
+ </div> -->
136
130
  </div>
137
131
  </div>
138
132
  </template>
@@ -190,7 +184,6 @@ export default {
190
184
  backgroundImage: null,
191
185
  backgroundImageUrl: null,
192
186
  backgroundImageLoaded: false,
193
- deleteButtonPos: null,
194
187
  drawingInProcess: false,
195
188
  breakpoints: new Breakpoints()
196
189
  };
@@ -239,12 +232,6 @@ export default {
239
232
  const layers = this.editableLayers.filter(l => !paId || (l.printArea === paId));
240
233
  return layers;
241
234
  },
242
- deleteButtonPosition() {
243
- return this.deleteButtonPos ? {
244
- top: `${this.deleteButtonPos.y}px`,
245
- left: `${this.deleteButtonPos.x}px`
246
- } : null;
247
- },
248
235
  positionPlaceholder() {
249
236
  const { center, left, top, width, height } = this.fabricHelper.printAreaRect;
250
237
  const ratio = this.calcWorkspaceSize() / this.editorSize.width;
@@ -339,7 +326,7 @@ export default {
339
326
  const layer = this.printAreaLayers.find(l => l.editableDetails);
340
327
  if (layer?.editableDetails) {
341
328
  this.setSelectedLayer(layer);
342
- this.setEditModeSelectedLayer(true);
329
+ this.$nextTick(() => this.setEditModeSelectedLayer(true));
343
330
  }
344
331
  },
345
332
  checkVisibleWireframe() {
@@ -393,7 +380,10 @@ export default {
393
380
  this.setSelectedLayerField(data);
394
381
  this.saveLayersAsImageWithDebounce();
395
382
  });
396
- this.fabricHelper.on('selectLayer', this.setSelectedLayer);
383
+ this.fabricHelper.on('selectLayer', (layer) => {
384
+ this.setSelectedLayer(layer);
385
+ this.$nextTick(() => this.setEditModeSelectedLayer(true));
386
+ });
397
387
  this.fabricHelper.on('removeLayer', (layer) => {
398
388
  setTimeout(() => {
399
389
  if (!this.editModeSelectedLayer) {
@@ -403,7 +393,6 @@ export default {
403
393
  }
404
394
  }, 100);
405
395
  });
406
- this.fabricHelper.on('setDeleteButtonPosition', this.setDeleteButtonPosition);
407
396
  this.fabricHelper.on('outOfPrintArea', this.setOffsetWarningVisibility);
408
397
 
409
398
  this.updateBackgroundImage();
@@ -478,7 +467,7 @@ export default {
478
467
  if (!this.isEditMode) {
479
468
  setTimeout(() => {
480
469
  this.setSelectedLayer(layer);
481
- this.setEditModeSelectedLayer(true);
470
+ this.$nextTick(() => this.setEditModeSelectedLayer(true));
482
471
  this.toogleBoundBox(true);
483
472
  });
484
473
  }
@@ -488,9 +477,6 @@ export default {
488
477
  await this.createLayer({ type: 'art', url, size, fileName });
489
478
  this.visibleWireframe = true;
490
479
  },
491
- setDeleteButtonPosition(pos) {
492
- this.deleteButtonPos = pos;
493
- },
494
480
  setOffsetWarningVisibility(visible) {
495
481
  this.setOffsetWarningVisible(visible);
496
482
  },
@@ -53,6 +53,7 @@
53
53
  </validation-provider>
54
54
  <phone-input
55
55
  :item="form"
56
+ :labelless="false"
56
57
  class="form-col col-half" />
57
58
  </div>
58
59
  <validation-provider
@@ -100,13 +100,16 @@ export default {
100
100
  taxName() {
101
101
  return this.settings?.pricing?.taxName || 'GST';
102
102
  },
103
+ isPayedViaLink() {
104
+ return this.model.charge?.payment_method_details?.type === 'link';
105
+ },
103
106
  paymentCard() {
104
107
  if (this.model.charge?.card) {
105
108
  return {
106
109
  number: this.model.charge.card.display_number,
107
110
  scheme: this.model.charge.card.scheme
108
111
  };
109
- } else if (this.model.charge) {
112
+ } else if (this.model.charge && !this.isPayedViaLink) {
110
113
  return {
111
114
  number: `XXXX-XXXX-XXXX-${this.model.charge.payment_method_details?.card?.last4 || 'XXXX'}`,
112
115
  scheme: this.model.charge.payment_method_details?.card?.brand
@@ -125,7 +125,7 @@
125
125
  v-if="model.paid"
126
126
  class="w-33">
127
127
  <div class="lc_regular16">
128
- <b>PAID VIA CREDIT CARD</b>
128
+ <b>{{ isPayedViaLink ? 'PAID VIA Stripe(Link)' : 'PAID VIA CREDIT CARD' }}</b>
129
129
  </div>
130
130
  <div v-if="paymentCard">
131
131
  <div class="lc_regular16">
@@ -192,7 +192,6 @@ export default {
192
192
  return this.availableColors?.filter(color => this.product.colorsPricing?.some(c => c.colors?.includes(color._id) && c.clearance)) || [];
193
193
  },
194
194
  clearanceColorsWithQty() {
195
- console.log('clearanceColors: ', this.clearanceColors);
196
195
  return this.clearanceColors.map(color => {
197
196
  const qty = this.usedSimpleProducts.reduce((sum, sp) => sp.color._id === color._id ? sum + (sp.amount || 0) : sum, 0);
198
197
  return qty > 0 ? { ...color, qty } : null;
@@ -9,7 +9,7 @@
9
9
  <span>Detected:</span>
10
10
  </div>
11
11
  <div class="ProductCheckDelivery__detected-position">
12
- London, Greater London
12
+ {{ suburbInfo }}
13
13
  </div>
14
14
  </div>
15
15
  <div class="ProductCheckDelivery__postcode">
@@ -119,6 +119,13 @@ export default {
119
119
  },
120
120
  suppliersWithRates() {
121
121
  return this.pricing?.shipping?.suppliersWithRates || [];
122
+ },
123
+ suburbInfo() {
124
+ const { locality, state } = this.suburb || {};
125
+ if (locality && state) {
126
+ return `${locality}, ${state}`;
127
+ }
128
+ return 'London, Greater London';
122
129
  }
123
130
  },
124
131
  watch: {
@@ -74,6 +74,7 @@
74
74
  v-else-if="layer.type === 'art'">
75
75
  <div class="WizardPrintLayer__info-art">
76
76
  <a
77
+ v-if="layer.file"
77
78
  :href="layer.file.origin"
78
79
  target="_blank">
79
80
  <!-- <img
@@ -30,6 +30,7 @@
30
30
  <div class="WizardTextOrLogoForm__type-input">
31
31
  <file-uploader
32
32
  :multiple="false"
33
+ :convert="true"
33
34
  :url="`image/editor/${shop._id}/${product._id}`"
34
35
  :has-conversion-error-modal="false"
35
36
  :show-error-message="false"
@@ -20,6 +20,13 @@ const COUNTRIES_SIZE_SYSTEMS = {
20
20
  GB: 'UK'
21
21
  };
22
22
 
23
+ function replaceVariables(text, product, sp) {
24
+ return (text || '')
25
+ .replace(/{colour}/g, sp.color?.name)
26
+ .replace(/{size}/g, sp.size?.name)
27
+ .replace(/{brand}/g, product.brand?.name);
28
+ }
29
+
23
30
  async function googleShoppingFeed(axios, config, availableStores, country, isEditor, params) {
24
31
  const { allowSameDayDelivery } = params || {};
25
32
  const { data } = await axios.get(`${config.LOCAL_API_URL}/feed/products?host=${config.HOST_NAME}&country=${country || ''}`);
@@ -38,23 +45,22 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
38
45
  return !availableStores || availableStores.includes(sp.storeCode);
39
46
  })
40
47
  .map(sp => {
41
- const filterImagesByType = imgType => (product.images || []).filter(i => (i.types || []).includes(imgType) && sp.color._id === i.color).map(i => i.image);
48
+ const filterImagesByType = (imgType, skipColor) => (product.images || []).filter(i => (i.types || []).includes(imgType) && (skipColor || sp.color._id === i.color)).map(i => i.image);
42
49
  const feedImages = filterImagesByType('feed_primary');
43
50
  const frontImages = filterImagesByType('front');
44
51
  const backImages = filterImagesByType('back');
45
52
  const additionalImages = filterImagesByType('additional_image_link');
46
- const catalogFrontImages = (product.images || []).filter(i => (i.types || []).includes('catalog_front')).map(i => i.image);
53
+ const catalogFrontImages = filterImagesByType('catalog_front');
54
+ const catalogBackImages = filterImagesByType('catalog_back');
47
55
  const feedImage = spliceFirstImage(feedImages);
48
- const image = feedImage || spliceFirstImage(frontImages) || spliceFirstImage(catalogFrontImages) || spliceFirstImage(backImages) || null;
56
+ const image = feedImage || spliceFirstImage(catalogFrontImages) || spliceFirstImage(frontImages) || spliceFirstImage(backImages) || null;
49
57
  const images = [
58
+ ...catalogBackImages,
50
59
  ...additionalImages,
51
60
  ...((!backImages?.includes(image) && getImages(backImages)) || (!frontImages?.includes(image) && getImages(frontImages)) || [])
52
- .filter(image => !additionalImages.includes(image))
61
+ .filter(image => !additionalImages.includes(image) && !catalogBackImages.includes(image))
53
62
  ];
54
- const feedTitle = (product.feedTitle || '')
55
- .replace(/{colour}/g, sp.color.name)
56
- .replace(/{size}/g, sp.size.name)
57
- .replace(/{brand}/g, product.brand.name)
63
+ const feedTitle = product.feedTitle;
58
64
  const title = feedTitle || `${product.name} ${sp.color.name}`;
59
65
  const description = `${product.description || product.fabricInfoShort || product.name || ''}`
60
66
  .replace(/&nbsp;/g, ' ')
@@ -66,11 +72,10 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
66
72
  if (sp.multipackQty) {
67
73
  link = link.includes('?') ? `${link}&multipack=${sp.SKU}` : `${link}?multipack=${sp.SKU}`;
68
74
  }
69
-
70
75
  const productWeight = +((product.weight || 0) * (sp.multipackQty || 1)).toFixed(3);
71
76
  const info = {
72
- 'g:title': { _text: sp.title || title },
73
- 'g:description': { _text: sp.description || description },
77
+ 'g:title': { _text: replaceVariables(sp.title || title, product, sp) },
78
+ 'g:description': { _text: replaceVariables(sp.description || description, product, sp) },
74
79
  link: { _text: link },
75
80
  'g:canonical_link': { _text: link.split('?')[0] },
76
81
  'g:id': { _text: sp.SKU },
@@ -143,7 +148,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
143
148
  info['g:local_shipping_label'] = { _text: 'sameday' };
144
149
  }
145
150
 
146
- const productHighlight = `${product.feedProductHighlight || ''}`
151
+ const productHighlight = replaceVariables(`${product.feedProductHighlight || ''}`, product, sp)
147
152
  .trim()
148
153
  .split(/\n+/g)
149
154
  .map(highlight => `${highlight || ''}`.trim())
@@ -183,7 +188,11 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
183
188
  }
184
189
  }
185
190
 
186
- const feedLifestyleImage = product.feedLifestyleImage || filterImagesByType('model')[0];
191
+ if (isClearance) {
192
+ info['g:custom_label_4'] = { _text: 'clearance' };
193
+ }
194
+
195
+ const feedLifestyleImage = filterImagesByType('model')[0] || product.feedLifestyleImage || filterImagesByType('model', true)[0];
187
196
  if (feedLifestyleImage) {
188
197
  info['g:lifestyle_image_link'] = { _text: staticLink(feedLifestyleImage, config) };
189
198
  }
@@ -60,7 +60,6 @@ export default {
60
60
  if (this.currentCategory) {
61
61
  const categoriesBreadcrumbs = [];
62
62
  let category = this.currentCategory;
63
- console.log('category: ', category);
64
63
  while (category) {
65
64
  categoriesBreadcrumbs.unshift({
66
65
  to: generateProductsLink(this.$route, {
@@ -120,7 +119,13 @@ export default {
120
119
 
121
120
  const items = ['Products'];
122
121
  if (this.currentCategory) {
123
- items.push(this.currentCategory.name);
122
+ const categoryNames = [];
123
+ let category = this.currentCategory;
124
+ while (category) {
125
+ categoryNames.unshift(category.name);
126
+ category = category.parent;
127
+ }
128
+ items.push(...categoryNames);
124
129
  }
125
130
  if (this.currentProductType) {
126
131
  items.push(this.currentProductType.name);
package/nuxt.config.js CHANGED
@@ -49,7 +49,7 @@ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor
49
49
  },
50
50
  plugins: [
51
51
  '@/node_modules/@lancom/shared/plugins/jsonld',
52
- '@/node_modules/@lancom/shared/plugins/vue-fragment',
52
+ { src: '@/node_modules/@lancom/shared/plugins/vue-fragment', ssr: false },
53
53
  '@/node_modules/@lancom/shared/plugins/global-components',
54
54
  '@/node_modules/@lancom/shared/plugins/directives',
55
55
  '@/node_modules/@lancom/shared/plugins/vue-js-modal',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.449",
3
+ "version": "0.0.451",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {