@subiz/widget-v4 4.1.71 → 4.1.73

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 (92) hide show
  1. package/app.js +0 -42
  2. package/assets/less/widget.less +6 -5
  3. package/assets/less/widget_header.less +5 -3
  4. package/compiled_src/chatbox/AudioAttachments.js +25 -32
  5. package/compiled_src/chatbox/Event.js +19 -3
  6. package/compiled_src/chatbox/InfoMessage.js +2 -1
  7. package/compiled_src/chatbox/OrderAttachments.js +102 -6
  8. package/compiled_src/chatbox/PoweredBy.js +20 -27
  9. package/compiled_src/chatbox/TextMessage.js +0 -14
  10. package/compiled_src/chatbox/VideoAttachments.js +1 -0
  11. package/compiled_src/chatbox/Widget.js +21 -21
  12. package/compiled_src/chatbox/time.js +46 -28
  13. package/compiled_src/lib.js +2 -2
  14. package/compiled_src/order/icon.js +2 -2
  15. package/compiled_src/order/index.js +2 -2
  16. package/compiled_src/plugin.js +2 -4
  17. package/compiled_src/store/api.js +1 -16
  18. package/compiled_src/store/store.js +2 -5
  19. package/compiled_src/util/common.js +12 -494
  20. package/compiled_src/util/index.js +0 -2
  21. package/dist/127.js +273 -0
  22. package/dist/132.js +2 -2
  23. package/dist/185.js +1885 -0
  24. package/dist/18eaf7ad0c6.127.js +1 -0
  25. package/dist/{18e74ca84ff.132.js → 18eaf7ad0c6.132.js} +1 -1
  26. package/dist/18eaf7ad0c6.185.js +1 -0
  27. package/dist/18eaf7ad0c6.278.js +1 -0
  28. package/dist/{18e74ca84ff.490.js → 18eaf7ad0c6.490.js} +1 -1
  29. package/dist/{18e74ca84ff.74.js → 18eaf7ad0c6.74.js} +1 -1
  30. package/dist/18eaf7ad0c6.app.js +1 -0
  31. package/dist/{18e74ca84ff.index.html → 18eaf7ad0c6.index.html} +1 -1
  32. package/dist/{18e74ca84ff.library.html → 18eaf7ad0c6.library.html} +1 -1
  33. package/dist/18eaf7ad0c6.library.js +1 -0
  34. package/dist/{18e74ca84ff.preview.html → 18eaf7ad0c6.preview.html} +1 -1
  35. package/dist/18eaf7ad0c6.preview.js +1 -0
  36. package/dist/18eaf7ad0c6.screenshoot.js +1 -0
  37. package/dist/18eaf7ad0c6.webview.js +1 -0
  38. package/dist/18ed1c3d33e.127.js +1 -0
  39. package/dist/18ed1c3d33e.132.js +1 -0
  40. package/dist/18ed1c3d33e.185.js +1 -0
  41. package/dist/18ed1c3d33e.27.js +1 -0
  42. package/dist/18ed1c3d33e.278.js +1 -0
  43. package/dist/18ed1c3d33e.459.js +1 -0
  44. package/dist/18ed1c3d33e.490.js +1 -0
  45. package/dist/18ed1c3d33e.74.js +1 -0
  46. package/dist/18ed1c3d33e.997.js +1 -0
  47. package/dist/18ed1c3d33e.app.js +1 -0
  48. package/dist/18ed1c3d33e.index.html +33 -0
  49. package/dist/18ed1c3d33e.library.html +33 -0
  50. package/dist/18ed1c3d33e.library.js +1 -0
  51. package/dist/18ed1c3d33e.preview.html +759 -0
  52. package/dist/18ed1c3d33e.preview.js +1 -0
  53. package/dist/18ed1c3d33e.screenshoot.js +1 -0
  54. package/dist/18ed1c3d33e.webview.js +1 -0
  55. package/dist/278.js +1 -1
  56. package/dist/490.js +1 -1
  57. package/dist/74.js +1 -1
  58. package/dist/HASH +1 -1
  59. package/dist/screenshoot.html +1 -1
  60. package/dist/screenshoot.js +4740 -627
  61. package/inject.js +3 -3
  62. package/package.json +6 -6
  63. package/src/chatbox/AudioAttachments.js +19 -26
  64. package/src/chatbox/Event.js +22 -3
  65. package/src/chatbox/InfoMessage.js +11 -11
  66. package/src/chatbox/OrderAttachments.js +118 -5
  67. package/src/chatbox/PoweredBy.js +14 -22
  68. package/src/chatbox/TextMessage.js +0 -14
  69. package/src/chatbox/VideoAttachments.js +1 -1
  70. package/src/chatbox/Widget.js +16 -20
  71. package/src/chatbox/time.js +54 -24
  72. package/src/lib.js +2 -2
  73. package/src/order/icon.js +2 -2
  74. package/src/order/index.js +2 -2
  75. package/src/plugin.js +4 -3
  76. package/src/store/api.js +1 -14
  77. package/src/store/store.js +3 -11
  78. package/src/util/common.js +13 -215
  79. package/src/util/index.js +0 -3
  80. package/compiled_src/util/order.js +0 -112
  81. package/dist/18e74ca84ff.278.js +0 -1
  82. package/dist/18e74ca84ff.497.js +0 -1
  83. package/dist/18e74ca84ff.app.js +0 -1
  84. package/dist/18e74ca84ff.library.js +0 -1
  85. package/dist/18e74ca84ff.preview.js +0 -1
  86. package/dist/18e74ca84ff.screenshoot.js +0 -1
  87. package/dist/18e74ca84ff.webview.js +0 -1
  88. package/dist/497.js +0 -6721
  89. package/src/util/order.js +0 -120
  90. /package/dist/{18e74ca84ff.27.js → 18eaf7ad0c6.27.js} +0 -0
  91. /package/dist/{18e74ca84ff.459.js → 18eaf7ad0c6.459.js} +0 -0
  92. /package/dist/{18e74ca84ff.997.js → 18eaf7ad0c6.997.js} +0 -0
package/inject.js CHANGED
@@ -38,7 +38,7 @@ function runChatbox(subiz) {
38
38
  show_close_button: true,
39
39
  overlay: true,
40
40
  text_color: 'rgba(0,0,0,1)',
41
- font_family: 'Roboto',
41
+ font_family: 'Inter',
42
42
  font_size: '14px',
43
43
  background: 'white',
44
44
  spacer: '10',
@@ -410,7 +410,7 @@ function runPopup(subiz) {
410
410
  overlay: true,
411
411
  primary_color: '#FDAD15',
412
412
  text_color: '#333',
413
- font_family: 'Roboto',
413
+ font_family: 'Inter',
414
414
  font_size: '14px',
415
415
  background: 'white',
416
416
  spacer: '10',
@@ -636,7 +636,7 @@ function runPopup2(subiz) {
636
636
  overlay: true,
637
637
  primary_color: '#E36539',
638
638
  text_color: '#333',
639
- font_family: 'Roboto',
639
+ font_family: 'Inter',
640
640
  font_size: '14px',
641
641
  background: 'white',
642
642
  spacer: '10',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.71",
3
+ "version": "4.1.73",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -9,10 +9,10 @@
9
9
  "lib": "rm -rf compiled_src && npx babel --out-dir=compiled_src src"
10
10
  },
11
11
  "dependencies": {
12
- "@subiz/ajax": "^3.0.12",
12
+ "@subiz/ajax": "^3.0.14",
13
13
  "@subiz/flow": "^1.1.0",
14
14
  "@subiz/storage": "0.0.8",
15
- "@subiz/wsclient": "^4.0.21",
15
+ "@subiz/wsclient": "^4.0.24",
16
16
  "cookie_js": "1.4.2",
17
17
  "core-js": "^3.36.1",
18
18
  "css-loader": "5.2.7",
@@ -23,14 +23,14 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "@babel/cli": "^7.24.1",
26
- "@babel/core": "^7.24.3",
26
+ "@babel/core": "^7.24.4",
27
27
  "@babel/plugin-proposal-class-properties": "^7.18.6",
28
28
  "@babel/plugin-transform-react-jsx": "^7.23.4",
29
- "@babel/preset-env": "^7.24.3",
29
+ "@babel/preset-env": "^7.24.4",
30
30
  "babel-loader": "^9.1.3",
31
31
  "babel-plugin-syntax-jsx": "^6.18.0",
32
32
  "html-webpack-plugin": "^5.6.0",
33
- "puppeteer": "^22.6.0",
33
+ "puppeteer": "^22.6.3",
34
34
  "webpack": "^5.91.0",
35
35
  "webpack-bundle-analyzer": "^4.10.1",
36
36
  "webpack-cli": "^5.1.4",
@@ -1,31 +1,24 @@
1
1
  import sb from '../util/index.js'
2
- import {Com} from '../common.js'
3
2
  import {h} from 'preact'
4
3
 
5
- export default class AudioAttachments extends Com {
6
- constructor(props) {
7
- super(props)
8
- }
4
+ export default function AudioAttachments(props) {
5
+ let byType = sb.get(props.ev, 'by.type')
6
+ var attachments = sb.get(props.ev, 'data.message.attachments', [])
7
+ var audios = []
8
+ sb.each(attachments, (att, i) => {
9
+ if (!att) return
10
+ if (sb.includes(att.mimetype, 'audio')) audios.push(att)
11
+ })
12
+ if (audios.length === 0) return null
9
13
 
10
- render() {
11
- let byType = sb.get(this.props.ev, 'by.type')
12
- var attachments = sb.get(this.props.ev, 'data.message.attachments', [])
13
- var audios = []
14
- sb.each(attachments, (att, i) => {
15
- if (!att) return
16
- if (sb.includes(att.mimetype, 'audio')) audios.push(att)
17
- })
18
- if (audios.length === 0) return null
19
-
20
- var $audios = sb.map(audios, (audio, index) => {
21
- return (
22
- <div key={index} class={`audio-attachment audio-attachment--${byType}`}>
23
- <audio class='audio-attachment__audio' controls>
24
- <source src={this.props.store.replaceFileUrl(audio.url)} type={audio.mimetype} />
25
- </audio>
26
- </div>
27
- )
28
- })
29
- return <div class={`message-${byType} message-${byType}-audio`}>{$audios}</div>
30
- }
14
+ var $audios = sb.map(audios, (audio, index) => {
15
+ return (
16
+ <div key={index} class={`audio-attachment audio-attachment--${byType}`}>
17
+ <audio class='audio-attachment__audio' controls>
18
+ <source src={props.store.replaceFileUrl(audio.url)} type={audio.mimetype} />
19
+ </audio>
20
+ </div>
21
+ )
22
+ })
23
+ return <div class={`message-${byType} message-${byType}-audio`}>{$audios}</div>
31
24
  }
@@ -7,7 +7,10 @@ import FormAttachments from './FormAttachments.js'
7
7
  import FormSubmitAttachments from './FormSubmitAttachments.js'
8
8
  import GenericTemplateAttachments from './GenericTemplateAttachments.js'
9
9
  import ProductAttachments from './ProductAttachments.js'
10
- import OrderAttachments from './OrderAttachments.js'
10
+ let OrderAttachments = false
11
+
12
+ const LoadOrderAttachments = () => import(/*webpackChunkName: "OrderAttachment" */ './OrderAttachments.js')
13
+
11
14
  //import FormSubmitAttachment from './formSubmitAttachment.js'
12
15
  import VideoAttachments from './VideoAttachments.js'
13
16
  import TextMessage from './TextMessage.js'
@@ -113,14 +116,30 @@ export default class Event extends Com {
113
116
  )
114
117
  }
115
118
 
119
+ tryLoadOrder(ev) {
120
+ let byType = sb.get(ev, 'by.type')
121
+ if (byType === 'user') return
122
+ // filter out all generic
123
+
124
+ let acc = this.props.store.matchAccount()
125
+ var attachments = sb.get(ev, 'data.message.attachments', [])
126
+ let orders = sb.filter(attachments, (att) => att.type === 'order' && att.order).map((att) => att.order)
127
+
128
+ if (orders.length === 0) return
129
+ if (!OrderAttachments)
130
+ sb.setTimeout(async () => {
131
+ OrderAttachments = (await LoadOrderAttachments()).default
132
+ })
133
+ }
134
+
116
135
  renderMessageSent() {
117
136
  let ev = this.props.event.ev
118
-
137
+ this.tryLoadOrder(ev)
119
138
  var $time = null
120
139
  if (this.props.event.show_time) {
121
140
  $time = (
122
141
  <div class='convo-event-time'>
123
- <Time time={ev.created} />
142
+ <Time time={ev.created} t={this.props.t} />
124
143
  </div>
125
144
  )
126
145
  }
@@ -1,6 +1,6 @@
1
1
  import sb from '../util/index.js'
2
2
 
3
- function getRateLang (rating) {
3
+ function getRateLang(rating) {
4
4
  if (!rating) return 'poor'
5
5
  if (rating < 2) return 'poor'
6
6
  if (rating < 4) return 'average'
@@ -8,29 +8,29 @@ function getRateLang (rating) {
8
8
  }
9
9
 
10
10
  import Time from './time.js'
11
- import { Com } from '../common.js'
12
- import { h } from 'preact'
11
+ import {Com} from '../common.js'
12
+ import {h} from 'preact'
13
13
  // [ 'ev'],
14
14
  export default class InfoMessage extends Com {
15
- render () {
15
+ render() {
16
16
  let ev = this.props.ev.ev
17
17
  var $time = null
18
18
  if (this.props.ev.show_time) {
19
19
  $time = (
20
- <div class="convo-event-time">
21
- <Time time={ev.created} />
20
+ <div class='convo-event-time'>
21
+ <Time time={ev.created} t={this.props.t} />
22
22
  </div>
23
23
  )
24
24
  }
25
25
 
26
26
  // if (this.ev.type === 'conversation_rating_requested') return <div class="info-message"></div>
27
27
  if (ev.type === 'conversation_rated') {
28
- var rating = sb.get(ev, 'data.conversation.ratings.0', { no_rating: true, rating: 0 })
28
+ var rating = sb.get(ev, 'data.conversation.ratings.0', {no_rating: true, rating: 0})
29
29
  if (rating.no_rating) return null
30
30
  return (
31
- <div class="convo-event">
31
+ <div class='convo-event'>
32
32
  {$time}
33
- <div class="info-message">
33
+ <div class='info-message'>
34
34
  {this.props.t('rated_as') + ' ' + this.props.t(getRateLang(rating.rating)).toLowerCase()}
35
35
  </div>
36
36
  </div>
@@ -38,9 +38,9 @@ export default class InfoMessage extends Com {
38
38
  }
39
39
  if (ev.type === 'conversation_state_updated' && sb.get(ev, 'data.conversation.state') === 'ended') {
40
40
  return (
41
- <div class="convo-event">
41
+ <div class='convo-event'>
42
42
  {$time}
43
- <div class="info-message">{this.props.t('conversation_ended')}</div>
43
+ <div class='info-message'>{this.props.t('conversation_ended')}</div>
44
44
  </div>
45
45
  )
46
46
  }
@@ -15,7 +15,7 @@ export default class ProductAttachments extends Com {
15
15
  if (orders.length === 0) return null
16
16
 
17
17
  let $orders = sb.map(orders, (order, index) => {
18
- sb.calcOrderTotal(order)
18
+ calcOrderTotal(order)
19
19
  let $note = null
20
20
  if (order.note) {
21
21
  $note = (
@@ -50,7 +50,7 @@ export default class ProductAttachments extends Com {
50
50
  >
51
51
  {this.props.t.i18n(product.i18n_name) || product.name}
52
52
  </div>
53
- <div class='order_attachment__product_desc'>{sb.getProductPropName(product, ' / ')}</div>
53
+ <div class='order_attachment__product_desc'>{getProductPropName(product, ' / ')}</div>
54
54
  <div class='order_attachment__product_desc'>
55
55
  {this.props.t('qty')} {item.quantity}&nbsp;·&nbsp;{sb.formatCurrency(product.price)}
56
56
  </div>
@@ -82,11 +82,11 @@ export default class ProductAttachments extends Com {
82
82
  if (phone) phone = this.props.t('phn') + ': ' + phone
83
83
 
84
84
  let $shipto = null
85
- if (sb.displayOrderShippingAddress(order) || phone) {
85
+ if (displayOrderShippingAddress(order) || phone) {
86
86
  $shipto = (
87
87
  <div>
88
88
  <div class='order_attachment__label'>{this.props.t('ship_to')}</div>
89
- <div>{sb.displayOrderShippingAddress(order)}</div>
89
+ <div>{displayOrderShippingAddress(order)}</div>
90
90
  <div class='order_attachment__text_truncate'>{phone}</div>
91
91
  </div>
92
92
  )
@@ -185,7 +185,7 @@ export default class ProductAttachments extends Com {
185
185
  <div class={`order_attachment order_attachment__${byType}`}>
186
186
  <div class='order_attachment__header'>
187
187
  {this.props.t('order')}
188
- <span class='order_attachment__text_muted order_attachment__text_sm'>{sb.getOrderDisplayName(order)}</span>
188
+ <span class='order_attachment__text_muted order_attachment__text_sm'>{getOrderDisplayName(order)}</span>
189
189
  </div>
190
190
  {$products}
191
191
  {$shipto}
@@ -199,3 +199,116 @@ export default class ProductAttachments extends Com {
199
199
  return $orders
200
200
  }
201
201
  }
202
+
203
+ function getProductPropName(product, seperator = ' - ') {
204
+ let options = op.get(product, 'options', [])
205
+ let props = op.get(product, 'props', [])
206
+ let multiPropsOptions = op.filter(options, (option) => op.size(option.values) > 1)
207
+ let names = []
208
+ op.each(multiPropsOptions, (option) => {
209
+ let prop = op.find(props, (prop) => prop.key === option.key) || {}
210
+ if (prop.value) names.push(prop.value)
211
+ })
212
+ return names.join(seperator)
213
+ }
214
+
215
+ function getOrderDisplayName(order = {}) {
216
+ return order.number || order.id || ''
217
+ }
218
+
219
+ function displayOrderShippingAddress(order, isShorten) {
220
+ let addr = op.get(order, 'shipping.address', {})
221
+ let fulladdr = [addr.address, addr.ward, addr.district, addr.province]
222
+ .filter((a) => a)
223
+ .join(', ')
224
+ .trim()
225
+ if (isShorten) fulladdr = shortenAddr(fulladdr)
226
+ return fulladdr
227
+ }
228
+
229
+ function shortenAddr(str) {
230
+ str = str || ''
231
+ str = str.replace(/Hà Nội/gi, 'HN')
232
+ str = str.replace(/Hồ Chí Minh/gi, 'HCM')
233
+ str = str.replace(/Thành Phố/gi, 'TP')
234
+
235
+ return str
236
+ }
237
+
238
+ function calcOrderTotal(order) {
239
+ // compute subtotal first, since it doesn't depend on tax or gloal discount
240
+ let subtotal = 0
241
+ let beforeTaxDiscount = 0
242
+ let ordervalue = 0
243
+ op.map(order.items, (item) => {
244
+ if (!item.product) return 0
245
+ let price = op.get(item, 'product.price', 0) * (item.quantity || 0)
246
+ ordervalue += price
247
+
248
+ // some products that are discounted before tax
249
+ // the discount amount is calculated in product price for
250
+ // simpler calucation in future
251
+ if (item.discount_type == 'percentage') {
252
+ if (item.discount_percentage > 0) {
253
+ beforeTaxDiscount += (price * item.discount_percentage) / 10000
254
+ price = price * (1 - (item.discount_percentage || 0) / 10000)
255
+ }
256
+ } else if (item.discount_type == 'amount') {
257
+ if (item.discount_amount > 0) {
258
+ beforeTaxDiscount += item.discount_amount
259
+ price = price - (item.discount_amount || 0)
260
+ if (price <= 0) price = 0
261
+ }
262
+ }
263
+ item.total = price
264
+ subtotal += price
265
+ })
266
+
267
+ // TAX
268
+ let taxM = {}
269
+ op.map(order.items, (item, i) => {
270
+ if (!item.product) return
271
+ let tax = item.product.tax
272
+ if (!tax) return
273
+ if (!tax.id) return
274
+ let taxprice = (item.total * tax.percentage) / 10000
275
+ item.total_tax = taxprice
276
+ taxM[i] = {tax: tax, taxprice: taxprice}
277
+ })
278
+
279
+ let totaltax = 0
280
+ op.map(taxM, (t) => (totaltax += t.taxprice))
281
+
282
+ let computed_discount = 0
283
+ if (order.discount_type == 'percentage' && order.discount_percentage > 0) {
284
+ computed_discount = ((subtotal + totaltax) * order.discount_percentage) / 10000
285
+ } else if (order.discount_type == 'amount') {
286
+ computed_discount = order.discount_amount || 0
287
+ }
288
+
289
+ // SHIP
290
+ let shippingfee = op.get(order, 'shipping.nominal_fee', 0)
291
+ if (shippingfee > 0) {
292
+ if (op.get(order, 'shipping.tax.id')) {
293
+ let tax = order.shipping.tax
294
+ let taxprice = shippingfee * (tax.percentage / 10000)
295
+ taxM['ship'] = {tax: tax, taxprice: taxprice}
296
+ order.shipping.total_tax = taxprice
297
+ }
298
+ }
299
+
300
+ totaltax = 0
301
+ op.map(taxM, (t) => (totaltax += t.taxprice))
302
+ let total = subtotal + totaltax - computed_discount + shippingfee
303
+
304
+ // add adjustment
305
+ total += order.adjustment || 0
306
+ if (total < 0) total = 0
307
+ order.subtotal = subtotal
308
+ order._computed_discount = computed_discount
309
+ order._taxM = taxM
310
+ order.total = total
311
+ order.total_tax = totaltax
312
+ order._discount_before_tax = beforeTaxDiscount
313
+ order._order_value = ordervalue
314
+ }
@@ -1,26 +1,18 @@
1
1
  var logo = require('../../assets/img/subiz-mono.svg')
2
-
3
- import {Com} from '../common.js'
4
2
  import {h} from 'preact'
5
- export default class PoweredBy extends Com {
6
- constructor(props) {
7
- super(props)
8
- }
9
-
10
- render() {
11
- // dont render onmobile
12
- if (this.props.store.isMobile()) return null
13
- let user = this.props.store.matchUser() || {}
14
- var link = `https://subiz.com.vn/?utm_source=${
15
- window.location.host || `id-${user.account_id}`
16
- }&utm_medium=widget&utm_campaign=widget_referral`
3
+ export default function PoweredBy(props) {
4
+ // dont render on mobile
5
+ if (props.store.isMobile()) return null
6
+ let user = props.store.matchUser() || {}
7
+ var link = `https://subiz.com.vn/?utm_source=${
8
+ window.location.host || `id-${user.account_id}`
9
+ }&utm_medium=widget&utm_campaign=widget_referral`
17
10
 
18
- return (
19
- <div class='poweredby'>
20
- <a href={link} class='poweredby--text' target='_blank' title='Subiz'>
21
- <img width='14' height='10' class='poweredby--subiz-logo' src={logo} alt='power by subiz' /> Subiz
22
- </a>
23
- </div>
24
- )
25
- }
11
+ return (
12
+ <div class='poweredby'>
13
+ <a href={link} class='poweredby--text' target='_blank' title='Subiz'>
14
+ <img width='14' height='10' class='poweredby--subiz-logo' src={logo} alt='powerred by subiz' /> Subiz
15
+ </a>
16
+ </div>
17
+ )
26
18
  }
@@ -42,7 +42,6 @@ export default class TextMessage extends Com {
42
42
  })
43
43
 
44
44
  if (sb.size(tags) === 0) return null
45
-
46
45
  if (byType === 'agent') {
47
46
  return <div class='message-agent message-text--agent'>{tags}</div>
48
47
  }
@@ -63,19 +62,6 @@ export default class TextMessage extends Com {
63
62
  if (insert.dynamicField) {
64
63
  return sb.get(insert, 'dynamicField.text', '')
65
64
  }
66
- if (insert.mention) {
67
- if (insert.mention.id == '*' || insert.mention.id == 'all') {
68
- return <span class='mention'>@{this.$t('all')}</span>
69
- }
70
-
71
- let ag = store.matchAgent(insert.mention.id)
72
- let name = sb.getAgentDisplayName(ag, insert.mention.fullname)
73
- return (
74
- <span class='mention' data-agent-id={ag.id} data-agent-fullname={name}>
75
- @{name}
76
- </span>
77
- )
78
- }
79
65
  if (insert.emoji) return <Emoji emojiCode={`:${insert.emoji}:`} />
80
66
  if (typeof insert === 'string') {
81
67
  let $tags = sb.extractText(insert).map((tag, i) => {
@@ -20,7 +20,7 @@ export default class VideoAttachments extends Com {
20
20
 
21
21
  var $videos = sb.map(videos, (video, index) => {
22
22
  return (
23
- <div class={`video-attachment video-attachment--${byType}`}>
23
+ <div key={index} class={`video-attachment video-attachment--${byType}`}>
24
24
  <video
25
25
  class='video-attachment__video'
26
26
  src={this.props.store.replaceFileUrl(video.url)}
@@ -10,7 +10,9 @@ import MessageInput from './MessageInput.js'
10
10
  import Events from './Events.js'
11
11
  import Typing from './Typing.js'
12
12
  import Rating from './Rating.js'
13
- import Order from './Order.js'
13
+ // import Order from './Order.js'
14
+ let Order = false
15
+ const LoadOrder = () => import(/*webpackChunkName: "Order" */ './Order.js')
14
16
  import ListField from './ListField.js'
15
17
 
16
18
  const defaultAvatar = 'https://file-subiz.com/fiqoahedjbgwclpouoml-avatar_default_baa8a1d7.png'
@@ -426,15 +428,17 @@ export default class Widget extends Com {
426
428
  let $field = null
427
429
  var errclass = field.error ? 'input--error' : ''
428
430
  if (field.type === 'text') {
429
- if (key === 'question') {
431
+ if (key == 'question' || sb.get(field, 'def.multiple_line')) {
430
432
  $field = (
431
433
  <textarea
432
434
  class='form-control'
433
435
  ref={(ref) => {
434
436
  this['form_' + encode(key)] = ref
435
437
  }}
438
+ id={'form_' + encode(key)}
436
439
  value={field.value || ''}
437
440
  onInput={(e) => this.onInputChange(key, e.target.value)}
441
+ onChange={(e) => this.onInputChange(key, e.target.value)}
438
442
  row='4'
439
443
  class='form-control'
440
444
  ></textarea>
@@ -445,23 +449,10 @@ export default class Widget extends Com {
445
449
  $field = this.renderRadioField(field)
446
450
  } else if (sb.get(field, 'def.select') === 'dropdown') {
447
451
  $field = this.renderDropdownField(field)
448
- } else if (sb.get(field, 'def.multiple_line')) {
449
- $field = (
450
- <textarea
451
- class='form-control'
452
- ref={(ref) => {
453
- this['form_' + encode(key)] = ref
454
- }}
455
- value={field.value || ''}
456
- onInput={(e) => this.onInputChange(key, e.target.value)}
457
- onChange={(e) => this.onInputChange(key, e.target.value)}
458
- row='4'
459
- class='form-control'
460
- ></textarea>
461
- )
462
452
  } else {
463
453
  $field = (
464
454
  <input
455
+ id={'form_' + encode(key)}
465
456
  value={field.value || ''}
466
457
  onInput={(e) => this.onInputChange(key, e.target.value)}
467
458
  onChange={(e) => this.onInputChange(key, e.target.value)}
@@ -476,6 +467,7 @@ export default class Widget extends Com {
476
467
  } else if (field.type === 'number') {
477
468
  $field = (
478
469
  <input
470
+ id={'form_' + encode(key)}
479
471
  value={field.value}
480
472
  onInput={(e) => this.onInputChange(key, e.target.value)}
481
473
  ref={(ref) => {
@@ -537,6 +529,7 @@ export default class Widget extends Com {
537
529
  } else if (field.type === 'datetime') {
538
530
  $field = (
539
531
  <input
532
+ id={'form_' + encode(key)}
540
533
  value={field.value}
541
534
  onInput={(e) => this.onInputChange(key, e.target.value)}
542
535
  class={`form-control ${errclass}`}
@@ -558,10 +551,10 @@ export default class Widget extends Com {
558
551
  if (field.label) label = field.label
559
552
  return (
560
553
  <div key={'form_' + encode(key)}>
561
- <div class='label'>
554
+ <label class='label' for={'form_' + encode(key)}>
562
555
  {label}
563
556
  {field.is_required ? <span class='ml-1 red'>*</span> : ''}
564
- </div>
557
+ </label>
565
558
  {field.error ? <div class='form-control--valid-input--error'>{field.error}</div> : ''}
566
559
  {$field}
567
560
  </div>
@@ -824,7 +817,10 @@ export default class Widget extends Com {
824
817
  if (noschemeurl.startsWith('widget.subiz.com.vn/orders/')) {
825
818
  let orderid = noschemeurl.substr('widget.subiz.com.vn/orders/'.length)
826
819
  orderid = orderid.split('/')[0]
827
- this.setState({showOrder: true, order_id: orderid})
820
+ sb.setTimeout(async () => {
821
+ if (!Order) Order = (await LoadOrder()).default
822
+ this.setState({showOrder: true, order_id: orderid})
823
+ })
828
824
  // display order instead
829
825
  e.preventDefault()
830
826
  return
@@ -935,7 +931,7 @@ export default class Widget extends Com {
935
931
  <div class='d-flex flex-row'>
936
932
  <div class='convo-list--convo-name'>{name}</div>
937
933
  <div class='convo-list--convo-time'>
938
- <Time ago time={time} key={time} short />
934
+ <Time ago time={time} t={this.props.t} short />
939
935
  </div>
940
936
  </div>
941
937
 
@@ -18,21 +18,28 @@ export default class Time extends Com {
18
18
 
19
19
  updateTime() {
20
20
  var t = ''
21
+ let forceLocale = ''
22
+ if (this.props.t) {
23
+ if (this.props.t.locale && typeof this.props.t.locale === 'function') {
24
+ forceLocale = this.props.t.locale()
25
+ }
26
+ }
21
27
  if (this.props.ago) {
22
28
  if (this.props.short) {
23
29
  // more than 10 days
24
- if (Math.abs(this.props.time - new sb.Date().getTime()) > 86400000 * 10) {
25
- let tim = new sb.Date(this.props.time)
26
- let tzyear = tim.getFullYear()
27
- let locale = sb.get(navigator, 'languages.0', 'en-US')
28
- let tzmonthname = tim.toLocaleDateString(locale, {month: 'short'})
29
- let tzdate = tim.getDate()
30
- t = tzdate + ' ' + tzmonthname + ' ' + tzyear
31
- }
30
+ //if (Math.abs(this.props.time - new sb.Date().getTime()) > 86400000 * 10) {
31
+ //let tim = new sb.Date(this.props.time)
32
+ //let tzyear = tim.getFullYear()
33
+ //let locale = sb.get(navigator, 'languages.0', 'en-US')
34
+ //if (forceLocale) locale = forceLocale
35
+ //let tzmonthname = tim.toLocaleDateString(locale, {month: 'short'})
36
+ //let tzdate = tim.getDate()
37
+ //t = tzdate + ' ' + tzmonthname + ' ' + tzyear
38
+ //}
32
39
  }
33
40
 
34
- if (!t && this.props.time > 0) t = ago(this.props.time, new sb.Date().getTime())
35
- } else t = sb.shortTime(this.props.time, this.props.tzoffset)
41
+ if (!t && this.props.time > 0) t = ago(this.props.time, new sb.Date().getTime(), this.props.t)
42
+ } else t = sb.shortTime(this.props.time, this.props.tzoffset, Date.now(), forceLocale)
36
43
 
37
44
  if (this.props.uppercase) t = t.toUpperCase()
38
45
 
@@ -47,24 +54,47 @@ export default class Time extends Com {
47
54
  }
48
55
 
49
56
  render() {
50
- return <span style={this.props.style}>{this.state.t}</span>
57
+ let locale = sb.get(navigator, 'languages.0', 'en-US')
58
+ if (this.props.t) {
59
+ if (this.props.t.locale && typeof this.props.t.locale === 'function') {
60
+ locale = this.props.t.locale()
61
+ }
62
+ }
63
+ let title = new Date(this.props.time).toLocaleString(locale)
64
+ return <span style={this.props.style} title={title}>{this.state.t}</span>
51
65
  }
52
66
  }
53
67
 
54
- function ago(timeA, timeB) {
55
- const relObj = {
56
- s: '%d s',
57
- m: '1m',
58
- mm: '%dm',
59
- h: '1h',
60
- hh: '%dh',
61
- d: '1d',
62
- dd: '%dd',
63
- M: '1M',
64
- MM: '%M',
65
- y: '1y',
66
- yy: '%dy',
68
+ function ago(timeA, timeB, t) {
69
+ let relObj = {
70
+ s: '%d sec',
71
+ m: '1 min',
72
+ mm: '%d mins',
73
+ h: '1 hour',
74
+ hh: '%d hours',
75
+ d: '1 day',
76
+ dd: '%d days',
77
+ M: '1 month',
78
+ MM: '%d months',
79
+ y: '1 year',
80
+ yy: '%d years',
67
81
  }
82
+ if (t) {
83
+ relObj = {
84
+ s: `%d ${t('second_short')}`,
85
+ m: `1 ${t('minute_short')}`,
86
+ mm: `%d ${t('minutes_short')}`,
87
+ h: `1 ${t('hour_short')}`,
88
+ hh: `%d ${t('hours_short')}`,
89
+ d: `1 ${t('day_short')}`,
90
+ dd: `%d ${t('days_short')}`,
91
+ M: `1 ${t('month_short')}`,
92
+ MM: `%d ${t('months_short')}`,
93
+ y: `1 ${t('year_short')}`,
94
+ yy: `%d ${t('years_short')}`,
95
+ }
96
+ }
97
+
68
98
  const loc = relObj
69
99
  const T = [
70
100
  {l: 's', r: 44, d: 1000},