@lancom/shared 0.0.307 → 0.0.309

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 (26) hide show
  1. package/assets/js/api/index.js +2 -2
  2. package/assets/js/models/print-area.js +7 -5
  3. package/assets/js/models/product-layers.js +2 -2
  4. package/assets/js/utils/layers.js +6 -5
  5. package/components/checkout/order/order-billing-information/order-billing-information.vue +16 -3
  6. package/components/checkout/order/order-payment-information/order-payment-information.vue +21 -13
  7. package/components/common/payment/payment_card/payment-card.vue +2 -2
  8. package/components/common/payment/payment_card/pinpayment/pinpayment.vue +8 -7
  9. package/components/common/payment/payment_card/stripe_payment/stripe-payment.vue +3 -2
  10. package/components/common/postcode_select/postcode-select.vue +12 -4
  11. package/components/editor/editor_product_details/editor-product-details.vue +67 -53
  12. package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +6 -0
  13. package/components/editor/mobile_editor_product_details/mobile-editor-product-details.vue +1 -1
  14. package/components/modals/payment_modal/payment-modal.vue +1 -1
  15. package/components/order/order_payment/order-payment.vue +1 -1
  16. package/components/order/order_view/order_product_prints/order_product_print/order-product-print.vue +4 -1
  17. package/components/order/order_view/order_view_product/order-view-product.vue +4 -1
  18. package/components/product/products_size_selector_color/product_size_selector_color/product-size-selector-color.vue +42 -18
  19. package/components/product/wizard/wizard.vue +6 -6
  20. package/components/products/product_list_product/product-list-product.vue +3 -3
  21. package/layouts/products.vue +10 -2
  22. package/mixins/product-preview.js +6 -2
  23. package/mixins/product-view.js +40 -16
  24. package/package.json +1 -1
  25. package/store/layers.js +0 -1
  26. package/store/product.js +20 -17
@@ -95,9 +95,9 @@ const api = {
95
95
  fetchOrderByToken(token, params) {
96
96
  return _get(`order/token/${token}`, params);
97
97
  },
98
- getPaymentIntent(shop, { amount, currency, payment, description }) {
98
+ getPaymentIntent(shop, { amount, currency, payment, description, order }) {
99
99
  const url = `shop/${shop}/payment-intent`;
100
- return _post(url, { amount, currency, payment, description });
100
+ return _post(url, { amount, currency, payment, description, order });
101
101
  },
102
102
  createOrderPayment(id, { card, shop, country, currency, invoice, payment, recaptchaToken }) {
103
103
  const url = invoice ? `shop/${shop}/order/${id}/invoice/${invoice}/payment` : `shop/${shop}/order/${id}/payment`;
@@ -17,9 +17,9 @@ export const getProductPrintAreas = product => {
17
17
  if (!product) {
18
18
  return {};
19
19
  }
20
- const getSizesFromAreas = (areas, map) => (areas || []).reduce((list, { printSize, printAreaOffsets, sizes, _id }) => {
20
+ const getSizesFromAreas = (areas, map, printAreaSide) => (areas || []).reduce((list, { printSize, side, printAreaOffsets, sizes, _id }) => {
21
21
  if (_id && printSize) {
22
- list[_id] = { printSize, printAreaOffsets };
22
+ list[_id] = { printSize, side: printAreaSide || side, printAreaOffsets };
23
23
  }
24
24
 
25
25
  if (sizes) {
@@ -46,8 +46,10 @@ export const getPrintAreaByName = (params, product) => {
46
46
  const { printArea, editorWidth, editorHeight, printSize, printAreaOffsets } = params;
47
47
  const printAreas = getProductPrintAreas(product);
48
48
  const pa = printAreas[printArea] || DEFAULT_PRINT_AREA;
49
- const productPrintArea = product.printAreasOffsets?.find(p => p.printAreas?.includes(printArea) && (!p.printSize || p.printSize === printSize?._id));
50
- const size = productPrintArea?.printAreaSize || printSize || pa.printSize;
49
+ const printSizeId = printSize?._id || printSize;
50
+ const productPrintArea = product.printAreasOffsets?.find(p => p.printAreas?.includes(printArea) && (!p.printSize || p.printSize === printSizeId));
51
+ const size = productPrintArea?.printAreaSize || (printSize?._id && printSize) || pa.printSize;
52
+ const sideId = productPrintArea?.side || pa.side;
51
53
  const offsets = productPrintArea?.printAreaOffsets || printAreaOffsets || pa.printAreaOffsets;
52
54
  const pxPerCm = (editorWidth * product.productToImageRatio) / (product.productWidthInCm || 60);
53
55
  const widthCm = size ? size.width : DEFAULT_PRINT_SIZE_CM;
@@ -74,7 +76,7 @@ export const getPrintAreaByName = (params, product) => {
74
76
  const widthInInches = widthCm * cmToInchesRatio;
75
77
  const heightInInches = heightCm * cmToInchesRatio;
76
78
 
77
- const info = { width, height, top, left, center, printArea, widthInInches, heightInInches };
79
+ const info = { width, sideId, height, top, left, center, printArea, widthInInches, heightInInches };
78
80
  // console.log('getPrintAreaByName:info ', offsets, info, params);
79
81
  return info;
80
82
  };
@@ -35,8 +35,8 @@ export class Layer {
35
35
  this.guid = generateGUID();
36
36
  this.createdAt = new Date().getTime();
37
37
  this.colorId = colorId;
38
- this.top = top;
39
- this.left = left;
38
+ this.top = top || 0;
39
+ this.left = left || 0;
40
40
  }
41
41
  };
42
42
 
@@ -7,13 +7,14 @@ export function generateLayersTemplate(template, printArea) {
7
7
  }));
8
8
  }
9
9
 
10
- function fitLayerToEditorSize(layer, { size }, { width, height, top, left }) {
11
- const scaleKoef = Math.min(width, height) / size.width;
12
- layer.top = top + layer.top * scaleKoef;
13
- layer.left = left + layer.left * scaleKoef;
10
+ export function fitLayerToEditorSize(layer, { size }, { width, height, top, left }) {
11
+ const scaleKoef = (width < height ? width / size.width : height / size.height) - 0.02;
12
+ layer.top = top + layer.top * scaleKoef + (Math.abs(size.height * scaleKoef - height) / 2) - 4;
13
+ layer.left = left + layer.left * scaleKoef + Math.abs((size.width * scaleKoef - width) / 2) - 3;
14
14
  ['fontSize', 'scaleX', 'scaleY'].forEach(property => {
15
15
  if (property in layer) {
16
- layer[property] = layer[property] * scaleKoef;
16
+ const originalSize = layer[property];
17
+ layer[property] = originalSize * scaleKoef;
17
18
  }
18
19
  });
19
20
  delete layer.sideId;
@@ -11,6 +11,19 @@
11
11
  <address-form
12
12
  :address="order.shippingAddress"
13
13
  :without-additional-info="true" />
14
+ <checkbox
15
+ v-model="copyToBillingAddress"
16
+ :dark="true">
17
+ <div class="ml-5">Use same address as the billing address</div>
18
+ </checkbox>
19
+ <div
20
+ v-if="!copyToBillingAddress"
21
+ class="mt-7">
22
+ <h5 class="lc_h4 mb-7">Billind Address</h5>
23
+ <address-form
24
+ :address="order.billingAddress"
25
+ :without-additional-info="true" />
26
+ </div>
14
27
  </div>
15
28
  <div class="Cart__quantity-errors">
16
29
  <cart-quantity-errors />
@@ -49,7 +62,7 @@ export default {
49
62
  },
50
63
  data() {
51
64
  return {
52
- copyToShippingAddress: true,
65
+ copyToBillingAddress: true,
53
66
  isSubmit: false
54
67
  };
55
68
  },
@@ -68,8 +81,8 @@ export default {
68
81
  const orderInfo = generateOrderData(this.order, this.entities, this.cartPricing);
69
82
  gtm.shippingInfo(orderInfo, this.currency);
70
83
 
71
- if (this.copyToShippingAddress) {
72
- this.order.shippingAddress = { ...this.order.shippingAddress };
84
+ if (this.copyToBillingAddress) {
85
+ this.order.billingAddress = { ...this.order.shippingAddress };
73
86
  this.$emit('next');
74
87
  // this.$emit('step', ORDER_STEPS.SHIPPING_METHOD);
75
88
  } else {
@@ -54,15 +54,15 @@
54
54
  {{ errorMessage }}
55
55
  </div>
56
56
  <div
57
- v-if="loadingCard"
57
+ v-if="loadingCard || !orderData"
58
58
  class="lc_modal__spinner">
59
59
  <spinner />
60
60
  </div>
61
61
  <payment-card
62
- v-if="cartPricing"
62
+ v-if="orderData"
63
63
  ref="paymentCart"
64
64
  :amount="cartPricing.totalPrice"
65
- :payment-data="order.shippingAddress"
65
+ :order="orderData"
66
66
  @inited="initedCard">
67
67
  </payment-card>
68
68
  <progress-steps-controls
@@ -121,12 +121,29 @@ export default {
121
121
  this.order.paymentMethod = this.isDepositPayment ? ORDER_PAYMENT_METHOD.DEPOSIT : ORDER_PAYMENT_METHOD.CART;
122
122
  }
123
123
  },
124
+ async mounted() {
125
+ await this.initOrderData();
126
+ },
124
127
  methods: {
125
128
  ...mapActions('order', ['createOrder', 'saveOrder']),
126
129
  ...mapMutations('order', ['setCard']),
127
130
  initedCard() {
128
131
  this.loadingCard = false;
129
132
  },
133
+ async initOrderData() {
134
+ if (!this.orderData) {
135
+ const recaptchaToken = await this.getRecaptcha('create_order');
136
+ await this.createOrder({
137
+ ...this.order,
138
+ recaptchaToken,
139
+ products: this.entities,
140
+ pricing: this.cartPricing,
141
+ shop: this.shop._id,
142
+ currency: this.currency?._id,
143
+ country: this.country?._id
144
+ });
145
+ }
146
+ },
130
147
  updatePaymentType(isDeposit) {
131
148
  this.isDepositPayment = isDeposit;
132
149
  this.errorMessage = null;
@@ -167,16 +184,7 @@ export default {
167
184
  try {
168
185
  this.creating = true;
169
186
  if (!this.orderData) {
170
- const recaptchaToken = await this.getRecaptcha('create_order');
171
- await this.createOrder({
172
- ...this.order,
173
- recaptchaToken,
174
- products: this.entities,
175
- pricing: this.cartPricing,
176
- shop: this.shop._id,
177
- currency: this.currency?._id,
178
- country: this.country?._id
179
- });
187
+ await this.initOrderData();
180
188
  } else {
181
189
  const data = {
182
190
  ...this.orderData,
@@ -4,7 +4,7 @@
4
4
  <component
5
5
  :is="cardComponent"
6
6
  ref="card"
7
- :paymentData="paymentData"
7
+ :order="order"
8
8
  :amount="amount"
9
9
  @inited="$emit('inited')" />
10
10
  </client-only>
@@ -22,7 +22,7 @@ export default {
22
22
  StripePayment: () => import('./stripe_payment/stripe-payment')
23
23
  },
24
24
  props: {
25
- paymentData: {
25
+ order: {
26
26
  type: Object,
27
27
  required: true
28
28
  },
@@ -91,7 +91,7 @@ let interval = null;
91
91
  export default {
92
92
  name: 'Payment',
93
93
  props: {
94
- paymentData: {
94
+ order: {
95
95
  type: Object,
96
96
  required: true
97
97
  },
@@ -174,15 +174,16 @@ export default {
174
174
  tokenize() {
175
175
  this.processing = true;
176
176
  return new Promise((resolve, reject) => {
177
+ const { shippingAddress } = this.order;
177
178
  this.fields.tokenize(
178
179
  {
179
180
  publishable_api_key: process.env.PINPAYMENT_PUBLISHABLE_API_KEY,
180
- address_line1: this.paymentData.addressLine1,
181
- address_line2: this.paymentData.addressLine2,
182
- address_city: this.paymentData.city,
183
- address_postcode: this.paymentData.postcode,
184
- address_state: this.paymentData.state,
185
- address_country: this.paymentData.country
181
+ address_line1: shippingAddress.addressLine1,
182
+ address_line2: shippingAddress.addressLine2,
183
+ address_city: shippingAddress.city,
184
+ address_postcode: shippingAddress.postcode,
185
+ address_state: shippingAddress.state,
186
+ address_country: shippingAddress.country
186
187
  },
187
188
  (err, response) => {
188
189
  if (!this.processing) {
@@ -21,7 +21,7 @@ export default {
21
21
  }
22
22
  },
23
23
  props: {
24
- paymentData: {
24
+ order: {
25
25
  type: Object,
26
26
  required: true
27
27
  },
@@ -87,7 +87,8 @@ export default {
87
87
  const params = {
88
88
  currency: this.currency?.isoCode,
89
89
  amount: this.amount,
90
- payment: this.payment
90
+ payment: this.payment,
91
+ order: this.order
91
92
  };
92
93
  const { clientSecret } = await api.getPaymentIntent(this.shop._id, params);
93
94
  this.stripe = window.Stripe(this.stripeClientKey);
@@ -141,6 +141,7 @@ export default {
141
141
  return this.selected?._id ? this.selected : null;
142
142
  },
143
143
  set(option) {
144
+
144
145
  this.updatePostcode(option);
145
146
  }
146
147
  }
@@ -157,7 +158,7 @@ export default {
157
158
  if (query.trim().length > 1) {
158
159
  this.isLoading = true;
159
160
  const country = this.codeCountry || this.country;
160
- const countryName = country ? (['GB','UK'].includes(country.isoCode) ? 'England' : (country.name || country)) : 'Australia';
161
+ const countryName = country ? (['GB','UK'].includes(country.isoCode) ? 'UK' : (country.name || country)) : 'Australia';
161
162
  const params = {
162
163
  query: query.trim(),
163
164
  country: countryName
@@ -182,9 +183,16 @@ export default {
182
183
  async updatePostcode(option) {
183
184
  this.selected = option;
184
185
  if (!option.value) {
185
- const suburb = await api.fetchAddressInfo(option._id, this.shop?._id);
186
- this.$emit('input', suburb.postcode);
187
- this.$emit('select', suburb);
186
+ try {
187
+ this.isLoading = true;
188
+ const suburb = await api.fetchAddressInfo(option._id, this.shop?._id);
189
+ this.$emit('input', suburb.postcode);
190
+ this.$emit('select', suburb);
191
+ option.label = this.createOptionFromSuburb(suburb).label;
192
+ } catch (e) {
193
+ } finally {
194
+ this.isLoading = false;
195
+ }
188
196
  } else {
189
197
  this.$emit('input', option.value);
190
198
  this.$emit('select', this.suburbs.find(({ _id }) => _id === option._id));
@@ -10,57 +10,6 @@
10
10
  <img :src="product.brand.logo | staticLink" />
11
11
  </div>
12
12
  <div class="EditorProductDetails__header-info">
13
- <div class="EditorProductDetails__header-row">
14
- <span
15
- v-if="defaultSimpleProductPricing"
16
- class="lc_regular16 lc_grey1">
17
- From {{ currency.isoCode }}
18
- <v-popover
19
- ref="popover"
20
- trigger="hover"
21
- :delay="{ show: 200, hide: 400 }"
22
- popover-class="tooltip popover white"
23
- class="EditorProductDetails__discounts-table">
24
- <price
25
- class="lc_semibold22 lc_black EditorProductDetails__dashed-underline"
26
- :price="defaultSimpleProductPricing[0].price"
27
- :with-gst="inclGST" />
28
- <template slot="popover">
29
- <pricing-discounts-table
30
- :prices="defaultSimpleProductPricing"
31
- :with-gst="inclGST" />
32
- </template>
33
- </v-popover>
34
- </span>
35
- <span class="lc_regular16 lc_grey1 ml-6">
36
- +
37
- <v-popover
38
- ref="popover"
39
- trigger="hover"
40
- :delay="{ show: 200, hide: 400 }"
41
- popover-class="tooltip popover white no-paddings"
42
- class="EditorProductDetails__discounts-table">
43
- <span class="EditorProductDetails__dashed-underline">
44
- Printing
45
- </span>
46
- <template slot="popover">
47
- <pricing-table
48
- :items="printTypePrices"
49
- :bordered="true"
50
- :striped="false"
51
- :with-gst="inclGST"
52
- class="EditorProductDetails__prints-table" />
53
- </template>
54
- </v-popover>
55
- </span>
56
- <span class="ml-10">
57
- <checkbox
58
- v-model="inclGST"
59
- :dark="true">
60
- <div class="ml-5">Inc. {{ taxName }}</div>
61
- </checkbox>
62
- </span>
63
- </div>
64
13
  <div class="EditorProductDetails__header-row">
65
14
  <span class="EditorProductDetails__product-link">
66
15
  SKU:
@@ -81,6 +30,68 @@
81
30
  </div>
82
31
  </div>
83
32
  </div>
33
+ <div class="EditorProductDetails__header-row">
34
+ <span
35
+ v-if="defaultSimpleProductPricing"
36
+ class="lc_regular16 lc_grey1">
37
+ From {{ currency.isoCode }}
38
+ <price
39
+ v-if="printsPrice"
40
+ class="lc_black lc_semibold22"
41
+ :price="printsPrice + defaultSimpleProductPricing[0].price"
42
+ :with-gst="inclGST" />
43
+ <v-popover
44
+ ref="popover"
45
+ trigger="hover"
46
+ :delay="{ show: 200, hide: 400 }"
47
+ popover-class="tooltip popover white"
48
+ class="EditorProductDetails__discounts-table">
49
+ <price
50
+ class="lc_black EditorProductDetails__dashed-underline"
51
+ :class="[printsPrice ? 'lc_semibold16' : 'lc_semibold22']"
52
+ :price="defaultSimpleProductPricing[0].price"
53
+ :with-gst="inclGST" />
54
+ <template slot="popover">
55
+ <pricing-discounts-table
56
+ :prices="defaultSimpleProductPricing"
57
+ :with-gst="inclGST" />
58
+ </template>
59
+ </v-popover>
60
+ </span>
61
+ <span class="lc_regular16 lc_grey1 ml-6">
62
+ +
63
+ <v-popover
64
+ ref="popover"
65
+ trigger="hover"
66
+ :delay="{ show: 200, hide: 400 }"
67
+ popover-class="tooltip popover white no-paddings"
68
+ class="EditorProductDetails__discounts-table">
69
+ <span class="EditorProductDetails__dashed-underline">
70
+ Printing
71
+ </span>
72
+ <price
73
+ v-if="printsPrice"
74
+ class="lc_black lc_semibold16 EditorProductDetails__dashed-underline"
75
+ :price="printsPrice"
76
+ :with-gst="inclGST" />
77
+ <template slot="popover">
78
+ <pricing-table
79
+ :items="printTypePrices"
80
+ :bordered="true"
81
+ :striped="false"
82
+ :with-gst="inclGST"
83
+ class="EditorProductDetails__prints-table" />
84
+ </template>
85
+ </v-popover>
86
+ </span>
87
+ <span class="ml-10">
88
+ <checkbox
89
+ v-model="inclGST"
90
+ :dark="true">
91
+ <div class="ml-5">Inc. {{ taxName }}</div>
92
+ </checkbox>
93
+ </span>
94
+ </div>
84
95
  <!-- <div
85
96
  v-if="!productAvailableInCurrentCountry"
86
97
  class="EditorProductDetails__available-warning">
@@ -126,7 +137,7 @@ import ProductColorsSelector from '@lancom/shared/components/product/product_col
126
137
  import EditorPricing from '@lancom/shared/components/product/editor_pricing/editor-pricing';
127
138
  import PricingDiscountsTable from '@lancom/shared/components/common/pricing_discounts_table/pricing-discounts-table';
128
139
  import modals from '@lancom/shared/mixins/modals';
129
- import { staticLink, priceWithTax } from '@lancom/shared/assets/js/utils/filters';
140
+ import { staticLink, priceWithTax, price, tax } from '@lancom/shared/assets/js/utils/filters';
130
141
  import PricingTable from '@lancom/shared/components/common/pricing_table/pricing-table';
131
142
  import { generateProductLink } from '@lancom/shared/assets/js/utils/product';
132
143
  import Price from '@lancom/shared/components/common/price';
@@ -146,6 +157,7 @@ export default {
146
157
  RichText
147
158
  },
148
159
  filters: {
160
+ price,
149
161
  priceWithTax,
150
162
  staticLink
151
163
  },
@@ -165,12 +177,14 @@ export default {
165
177
  'productDetailsLoaded',
166
178
  'defaultSimpleProduct',
167
179
  'selectedPrintType',
168
- 'isPrintPricing'
180
+ 'isPrintPricing',
181
+ 'printsPrice'
169
182
  ]),
170
183
  ...mapGetters('layers', [
171
184
  'layerThumbnails'
172
185
  ]),
173
186
  ...mapGetters([
187
+ 'gstTax',
174
188
  'pricingSettings',
175
189
  'country'
176
190
  ]),
@@ -132,6 +132,9 @@ export default {
132
132
  'editorSize',
133
133
  'editModeSelectedLayer'
134
134
  ]),
135
+ editableLayersCount() {
136
+ return this.editableLayers?.length;
137
+ },
135
138
  hasWireframeImage() {
136
139
  return !!this.wireframeImage;
137
140
  },
@@ -197,6 +200,9 @@ export default {
197
200
  layers() {
198
201
  this.redrawWithThrottle();
199
202
  },
203
+ editableLayersCount() {
204
+ this.redraw();
205
+ },
200
206
  selectedLayer() {
201
207
  this.redrawWithThrottle();
202
208
  },
@@ -10,7 +10,7 @@
10
10
  @click="show('details')">
11
11
  <i class="icon-product MobileEditorProductDetails__menu-action-icon"></i>
12
12
  <span class="MobileEditorProductDetails__menu-action-label">
13
- Editor
13
+ Designer
14
14
  </span>
15
15
  </div>
16
16
  <div
@@ -34,7 +34,7 @@
34
34
  </div>
35
35
  <payment-card
36
36
  ref="card"
37
- :paymentData="orderData"
37
+ :order="orderData"
38
38
  @inited="initedCard"/>
39
39
  <div class="form-row PaymentModal__terms">
40
40
  <label class="form-label PaymentModal__label-with-checkbox">
@@ -28,7 +28,7 @@
28
28
  ref="paymentCart"
29
29
  :amount="model.totalGST"
30
30
  :has-spinner="false"
31
- :payment-data="order.shippingAddress">
31
+ :order="order">
32
32
  </payment-card>
33
33
  <div
34
34
  v-if="errorMessage"
@@ -72,7 +72,10 @@ export default {
72
72
  return this.print.costType === 'setup only' ? 0 : (this.print.printCost || 0);
73
73
  },
74
74
  setupCost() {
75
- const isFreePricing = this.print.freeSetupOver >= 0 && this.amount > this.print.freeSetupOver;
75
+ let isFreePricing = this.print.freeSetupOver >= 0 && this.amount > this.print.freeSetupOver;
76
+ if ((this.print.products || []).indexOf(this.entity.guid) > 0) {
77
+ isFreePricing = true;
78
+ }
76
79
  return (this.print.costType === 'item cost only' || isFreePricing) ? 0 : (this.print.setupCost || 0);
77
80
  },
78
81
  total() {
@@ -128,7 +128,10 @@ export default {
128
128
  },
129
129
  totalPrice() {
130
130
  const printsTotal = (this.product.prints || []).reduce((total, print) => {
131
- const isFreePricing = print.freeSetupOver >= 0 && this.amount > print.freeSetupOver;
131
+ let isFreePricing = print.freeSetupOver >= 0 && this.amount > print.freeSetupOver;
132
+ if ((print.products || []).indexOf(this.product.guid) > 0) {
133
+ isFreePricing = true;
134
+ }
132
135
  const printCost = print.costType === 'setup only' ? 0 : print.printCost;
133
136
  const setupCost = (print.costType === 'item cost only' || isFreePricing) ? 0 : print.setupCost;
134
137
  return total + this.amount * printCost + setupCost;
@@ -18,23 +18,45 @@
18
18
  <div class="lc_regular14 hidden-md-and-up">
19
19
  {{ product.size.shortName }}
20
20
  </div>
21
- <v-popover
22
- trigger="hover"
23
- :delay="{ show: 200, hide: 400 }"
24
- popover-class="tooltip popover white"
25
- class="ProductSizeSelectorColor__price">
26
- <span>
27
- {{ productPrice | price(currency) }}
28
- </span>
29
- <template slot="popover">
30
- <div>
31
- <pricing-discounts-table
32
- :with-gst="withGst"
33
- :prices="pricing"
34
- :amount="usedSimpleProductsQuantity" />
35
- </div>
36
- </template>
37
- </v-popover>
21
+ <div>
22
+ <div v-if="printsPrice > 0">
23
+ <price
24
+ class="lc_semibold17 ProductSizeSelectorColor__price"
25
+ :price="printsPrice + productPrice"
26
+ :with-gst="withGst" />
27
+ </div>
28
+ <div>
29
+ <v-popover
30
+ trigger="hover"
31
+ :delay="{ show: 200, hide: 400 }"
32
+ popover-class="tooltip popover white"
33
+ class="ProductSizeSelectorColor__price"
34
+ :style="{
35
+ display: productPrice ? 'inline-block' : 'block'
36
+ }">
37
+ <span :class="[printsPrice ? 'lc_regular13' : 'lc_semibold17']">
38
+ {{ productPrice | price(currency) }}
39
+ </span>
40
+ <template slot="popover">
41
+ <div>
42
+ <pricing-discounts-table
43
+ :with-gst="withGst"
44
+ :prices="pricing"
45
+ :amount="usedSimpleProductsQuantity" />
46
+ </div>
47
+ </template>
48
+ </v-popover>
49
+ <span
50
+ v-if="printsPrice > 0"
51
+ class="lc_regular13">
52
+ +
53
+ <price
54
+ :price="printsPrice"
55
+ :with-gst="withGst" />
56
+ </span>
57
+ </div>
58
+
59
+ </div>
38
60
  <div class="hidden-md-and-up">
39
61
  <div
40
62
  v-if="product.quantityStock"
@@ -77,6 +99,7 @@ import PricingDiscountsTable from '@lancom/shared/components/common/pricing_disc
77
99
  import { price } from '@lancom/shared/assets/js/utils/filters';
78
100
  import ProductSizeSelectorColorCell from '@lancom/shared/components/product/product_size_selector/product_size_selector_color/product_size_selector_color_cell/product-size-selector-color-cell';
79
101
  import { tax } from '@lancom/shared/assets/js/utils/filters';
102
+ import Price from '@lancom/shared/components/common/price';
80
103
 
81
104
  export default {
82
105
  name: 'ProductSizeSelectorColor',
@@ -84,6 +107,7 @@ export default {
84
107
  price
85
108
  },
86
109
  components: {
110
+ Price,
87
111
  PricingDiscountsTable,
88
112
  ProductSizeSelectorColorCell
89
113
  },
@@ -102,7 +126,7 @@ export default {
102
126
  }
103
127
  },
104
128
  computed: {
105
- ...mapGetters('product', ['isPrintPricing', 'usedSimpleProductsQuantity']),
129
+ ...mapGetters('product', ['isPrintPricing', 'usedSimpleProductsQuantity', 'printsPrice']),
106
130
  ...mapGetters(['gstTax', 'currency']),
107
131
  currentQuantityStockLabel() {
108
132
  return this.currentQuantityStock > 100 ? '100+' : this.currentQuantityStock;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="Wizard__wrapper">
3
3
  <div
4
- v-if="preSetPrint"
4
+ v-if="preSetPrints"
5
5
  class="Wizard__header Wizard__header--active">
6
6
  <div class="Wizard__header-name">
7
7
  PRINT OPTIONS
@@ -43,7 +43,7 @@
43
43
  @edit="editLayer($event)"
44
44
  @delete="removeTemplateLayer($event)" />
45
45
  <div
46
- v-if="!preSetPrint"
46
+ v-if="!preSetPrints"
47
47
  class="Wizard__layers-info">
48
48
  <p>1. Note final print price will be calculated once garment quantity is finalised</p>
49
49
  <p>2. PDF mockup will be provided for approval prior to printing</p>
@@ -180,7 +180,7 @@ export default {
180
180
  },
181
181
  computed: {
182
182
  ...mapGetters(['shop']),
183
- ...mapGetters('product', ['product', 'layers', 'visibleSteps', 'hasLayers', 'isPrintPricing', 'availablePrintTypes', 'preSetPrint']),
183
+ ...mapGetters('product', ['product', 'layers', 'visibleSteps', 'hasLayers', 'isPrintPricing', 'availablePrintTypes', 'preSetPrints']),
184
184
  steps() {
185
185
  const isScreenPrint = (this.availablePrintTypes || []).includes('screen print');
186
186
  return isScreenPrint ? WIZARD_STEPS_LIST.filter(s => s !== WIZARD_STEPS.PRINT_SIZE) : WIZARD_STEPS_LIST.filter(s => s !== WIZARD_STEPS.PRINT_UNDERBASE);
@@ -193,7 +193,7 @@ export default {
193
193
  }
194
194
  },
195
195
  mounted() {
196
- if (this.product.printOnly || this.preSetPrint || this.layers.length > 0) {
196
+ if (this.product.printOnly || this.preSetPrints || this.layers.length > 0) {
197
197
  this.isVisible = true;
198
198
  this.setIsPrintPricing(this.isVisible);
199
199
  }
@@ -214,7 +214,7 @@ export default {
214
214
  'setVisibleSteps',
215
215
  'setIsPrintPricing',
216
216
  'clearTemplateLayers',
217
- 'setPreSetPrint'
217
+ 'setPreSetPrints'
218
218
  ]),
219
219
  ...mapMutations('layers', ['setEditableLayers']),
220
220
  goToNextStep() {
@@ -262,7 +262,7 @@ export default {
262
262
  }
263
263
  },
264
264
  removePreSetPrint() {
265
- this.setPreSetPrint(null);
265
+ this.setPreSetPrints(null);
266
266
  this.clearTemplateLayers();
267
267
  }
268
268
  }
@@ -82,14 +82,14 @@
82
82
  </a>
83
83
  </div>
84
84
  <div
85
- v-if="product.minPrice === product.maxPrice"
85
+ v-if="minPrice === maxPrice"
86
86
  class="ProductListProduct__info-item lc_h4">
87
- {{ product.minPrice | price(currency) }}
87
+ {{ minPrice | price(currency) }}
88
88
  </div>
89
89
  <div
90
90
  v-else
91
91
  class="ProductListProduct__info-item lc_h4">
92
- {{ product.minPrice | price(currency) }} - {{ product.maxPrice | price(currency) }}
92
+ {{ minPrice | price(currency) }} - {{ maxPrice | price(currency) }}
93
93
  </div>
94
94
  <div class="ProductListProduct__info-item mt-2 lc_caption">
95
95
  {{ product.colors.length }} {{ product.colors.length === 1 ? 'Colour' : 'Colours' }} | Size: {{ product.sizes | sizesRange }}
@@ -194,6 +194,14 @@
194
194
  numberOfItems: this.count,
195
195
  itemListOrder: 'ItemListUnordered',
196
196
  itemListElement: this.products.map(product => {
197
+ const minPrintsPrice = product.minPrintsPrice || 0;
198
+ const minProductPrice = product.isClearance ? product.minPriceWithoutClearance : product.minPrice;
199
+ const minPrice = minProductPrice + minPrintsPrice;
200
+
201
+ const maxPrintsPrice = product.maxPrintsPrice || 0;
202
+ const maxProductPrice = product.isClearance ? product.maxPriceWithoutClearance : product.maxPrice;
203
+ const maxPrice = maxProductPrice + maxPrintsPrice;
204
+
197
205
  const schema = {
198
206
  '@type': 'Product',
199
207
  name: product.name,
@@ -203,8 +211,8 @@
203
211
  '@type': 'AggregateOffer',
204
212
  offerCount: 1,
205
213
  name: product.name,
206
- highPrice: product.minPrice,
207
- lowPrice: product.maxPrice,
214
+ highPrice: maxPrice,
215
+ lowPrice: minPrice,
208
216
  priceCurrency: this.currency?.isoCode || 'AUD',
209
217
  availability: 'InStock'
210
218
  }
@@ -82,10 +82,14 @@ const productPreview = {
82
82
  return this.product?.tags.length > 0;
83
83
  },
84
84
  minPrice() {
85
- return this.product.isClearance ? this.product.minPriceWithoutClearance : this.product.minPrice;
85
+ const printsPrice = this.product.minPrintsPrice || 0;
86
+ const productPrice = this.product.isClearance ? this.product.minPriceWithoutClearance : this.product.minPrice;
87
+ return productPrice + printsPrice;
86
88
  },
87
89
  maxPrice() {
88
- return this.product.isClearance ? this.product.maxPriceWithoutClearance : this.product.maxPrice;
90
+ const printsPrice = this.product.maxPrintsPrice || 0;
91
+ const productPrice = this.product.isClearance ? this.product.maxPriceWithoutClearance : this.product.maxPrice;
92
+ return productPrice + printsPrice;
89
93
  }
90
94
  },
91
95
  mounted() {
@@ -1,5 +1,7 @@
1
1
  import { mapGetters, createNamespacedHelpers } from 'vuex';
2
2
  import gtm from '@lancom/shared/assets/js/utils/gtm';
3
+ import { fitLayerToEditorSize } from '@lancom/shared/assets/js/utils/layers';
4
+ import { getPrintAreaByName } from '@lancom/shared/assets/js/models/print-area';
3
5
  import { getLayerModel } from '@lancom/shared/assets/js/models/product-layers';
4
6
  import { tax, staticLink, inRange } from '@lancom/shared/assets/js/utils/filters';
5
7
  import { getProductLargeCover } from '@lancom/shared/assets/js/utils/colors';
@@ -63,7 +65,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
63
65
  },
64
66
  computed: {
65
67
  ...mapGetters(['shop', 'gstTax', 'country', 'currency']),
66
- ...mapGetters('product', ['product', 'simpleProducts', 'productDetails', 'editableColor', 'images', 'preSetPrint', 'preSetPrintPriceRange']),
68
+ ...mapGetters('product', ['product', 'simpleProducts', 'productDetails', 'editableColor', 'images', 'preSetPrints', 'editorSize', 'printsPrice']),
67
69
  pageItemImage() {
68
70
  return this.mainProductImageSrc;
69
71
  },
@@ -107,22 +109,44 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
107
109
  await this.fetchProductDetails(data);
108
110
  }
109
111
 
110
- if (this.preSetPrint) {
111
- const data = {
112
- colorId: this.editableColor._id,
113
- type: 'text',
114
- properties: {
115
- notes: '',
116
- copy: '',
117
- printArea: this.preSetPrint.printArea,
118
- printSize: this.preSetPrint.printSize,
119
- printType: this.preSetPrint.printType,
120
- required: true
112
+ try {
113
+ if (this.preSetPrints) {
114
+ for (const preSetPrint of this.preSetPrints) {
115
+ const layers = preSetPrint.printTemplate?.layers || [{ type: 'text' }];
116
+ for (const layer of layers) {
117
+ const properties = {
118
+ ...layer,
119
+ printArea: preSetPrint.printArea,
120
+ printSize: preSetPrint.printSize,
121
+ printType: preSetPrint.printType,
122
+ required: true
123
+ };
124
+ delete properties.guid;
125
+ if (preSetPrint.printTemplate) {
126
+ const printArea = getPrintAreaByName({
127
+ printArea: preSetPrint.printArea,
128
+ printSize: preSetPrint.printSize,
129
+ // printAreaOffsets: printArea?.printAreaOffsets,
130
+ editorWidth: this.editorSize.width,
131
+ editorHeight: this.editorSize.height
132
+ }, this.product);
133
+ fitLayerToEditorSize(properties, preSetPrint.printTemplate, printArea);
134
+ properties.sideId = printArea?.sideId;
135
+ }
136
+ const data = {
137
+ colorId: this.editableColor._id,
138
+ type: layer.type || 'text',
139
+ sideId: properties.sideId || 'front',
140
+ properties
141
+ };
142
+ const model = await getLayerModel(data);
143
+ this.addTemplateLayer(model);
144
+ }
121
145
  }
122
- };
123
- const layer = await getLayerModel(data);
124
- this.addTemplateLayer(layer);
146
+ }
147
+ } catch (e) {
125
148
  }
149
+
126
150
  this.fillBreadcrumbs();
127
151
 
128
152
  if (multipack) {
@@ -185,7 +209,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
185
209
  name,
186
210
  offers: this.productDetails?.simpleProducts.map(sp => {
187
211
  const spMaxPrice = (sp.pricing || []).reduce((price, pricing) => Math.max(price, pricing.price), 0);
188
- const maxPrice = this.preSetPrint ? spMaxPrice + this.preSetPrintPriceRange.max : spMaxPrice;
212
+ const maxPrice = this.printsPrice ? spMaxPrice + this.printsPrice : spMaxPrice;
189
213
  const availability = sp.quantityStock > 0 ? 'InStock' : 'OutOfStock';
190
214
 
191
215
  const offer = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.307",
3
+ "version": "0.0.309",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
package/store/layers.js CHANGED
@@ -19,7 +19,6 @@ export const actions = {
19
19
 
20
20
  export const mutations = {
21
21
  setLayersThumbnail(state, { side, value }) {
22
- console.log('setLayersThumbnail: ', side, value);
23
22
  Vue.set(state.layerThumbnails, side, value);
24
23
  },
25
24
  setEditableLayer(state, layer) {
package/store/product.js CHANGED
@@ -15,7 +15,7 @@ export const state = () => ({
15
15
  images: [],
16
16
  priceIncludeGST: false,
17
17
  product: null,
18
- preSetPrint: null,
18
+ preSetPrints: null,
19
19
  loadError: null,
20
20
  productDetails: null,
21
21
  editorSize: {
@@ -52,16 +52,7 @@ export const getters = {
52
52
  multipack: ({ multipack }) => multipack,
53
53
  calculatingPrice: ({ calculatingPrice }) => calculatingPrice,
54
54
  product: ({ product }) => product,
55
- preSetPrint: ({ preSetPrint }) => preSetPrint,
56
- preSetPrintPriceRange: ({ preSetPrint, product }) => {
57
- const printType = product.printTypes.find(pt => pt._id === preSetPrint?.printType);
58
- const { printCost = [] } = (printType?.printAreas || [])
59
- .find(({ printSizes }) => (printSizes || []).some(ps => ps?._id === preSetPrint?.printSize)) || {};
60
- return {
61
- min: Math.min(...printCost.map(({ price }) => price)),
62
- max: Math.max(...printCost.map(({ price }) => price))
63
- };
64
- },
55
+ preSetPrints: ({ preSetPrints }) => preSetPrints,
65
56
  loadError: ({ loadError }) => loadError,
66
57
  productDetails: ({ productDetails }) => productDetails,
67
58
  productDetailsLoaded: ({ productDetails }) => !!productDetails,
@@ -149,7 +140,13 @@ export const getters = {
149
140
  },
150
141
  editableSide: ({ editableSide }) => editableSide,
151
142
  priceIncludeGST: ({ priceIncludeGST }) => priceIncludeGST,
152
- hasUnprintedPrice: ({ product }) => product.minUnprintedPrice || product.maxUnprintedPrice
143
+ hasUnprintedPrice: ({ product }) => product.minUnprintedPrice || product.maxUnprintedPrice,
144
+ printsPrice: ({ productPricing, product }) => {
145
+ const maxPrintsPrice = +product.maxPrintsPrice || 0;
146
+ const pricing = (productPricing?.products || {})[product._id];
147
+ const price = pricing?.prints?.prints?.reduce((sum , print) => sum + (+print.priceWithoutTax || 0), 0) || maxPrintsPrice;
148
+ return price;
149
+ },
153
150
  };
154
151
 
155
152
  export const actions = {
@@ -159,7 +156,12 @@ export const actions = {
159
156
  const query = { country, currency };
160
157
  const product = await api.fetchProduct(shop, slug, query);
161
158
  commit('setProduct', product);
162
- commit('setPreSetPrint', (product.prints || []).find(({ _id }) => _id === print));
159
+
160
+ const prints = (product.prints || []).filter(({ _id, printTemplate }) => _id === print || (!print && !!printTemplate));
161
+ commit('setPreSetPrints', prints);
162
+ if (prints?.length > 0) {
163
+ commit('setIsPrintPricing', true);
164
+ }
163
165
  } catch (e) {
164
166
  console.log(e);
165
167
  const { status, data } = e?.response || {};
@@ -279,7 +281,8 @@ export const mutations = {
279
281
  state.loadError = error;
280
282
  },
281
283
  setProductDetails(state, simpleProducts) {
282
- const { preSetPrint } = state;
284
+ const { preSetPrints } = state;
285
+ const [preSetPrint] = preSetPrints || [];
283
286
  simpleProducts = simpleProducts.filter(sp => !preSetPrint || (preSetPrint.colors || []).includes(sp.color._id) )
284
287
  state.productDetails = { simpleProducts };
285
288
  const availableSizes = [];
@@ -364,7 +367,7 @@ export const mutations = {
364
367
  ...(state.template.layers || []).filter(l => l.guid !== layer.guid),
365
368
  layer
366
369
  ];
367
- Vue.set(state.template, 'layers', layers);
370
+ state.template = { ...state.template, layers };
368
371
  if (layers.length === 1 && !state.product.printOnly) {
369
372
  // const printType = state.product.printTypes[0];
370
373
  // state.selectedPrintType = printType;
@@ -492,8 +495,8 @@ export const mutations = {
492
495
  setVisibleSteps(state, visibleSteps) {
493
496
  state.template.visibleSteps = visibleSteps;
494
497
  },
495
- setPreSetPrint(state, preSetPrint) {
496
- state.preSetPrint = preSetPrint;
498
+ setPreSetPrints(state, preSetPrints) {
499
+ state.preSetPrints = preSetPrints;
497
500
  },
498
501
  setEditableSide(state, side) {
499
502
  state.editableSide = side;