@subiz/widget-v4 4.0.50 → 4.0.55

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 (61) hide show
  1. package/assets/img/default.jpg +0 -0
  2. package/assets/less/message.less +101 -10
  3. package/compiled_src/chatbox/Event.js +6 -0
  4. package/compiled_src/chatbox/OrderAttachments.js +109 -0
  5. package/compiled_src/chatbox/ProductAttachments.js +9 -7
  6. package/compiled_src/notif/notif.js +6 -2
  7. package/compiled_src/plugin.js +1 -1
  8. package/compiled_src/store/api.js +1 -0
  9. package/compiled_src/util/campaign.js +6 -1
  10. package/compiled_src/util/common.js +7 -0
  11. package/dist/{17cbc0a3e5f.178.js → 17d7ef81b7a.178.js} +1 -1
  12. package/dist/{17cbc0a3e5f.213.js → 17d7ef81b7a.213.js} +0 -0
  13. package/dist/{17cbc0a3e5f.362.js → 17d7ef81b7a.362.js} +1 -1
  14. package/dist/17d7ef81b7a.441.js +1 -0
  15. package/dist/17d7ef81b7a.605.js +1 -0
  16. package/dist/{17cbc0a3e5f.619.js → 17d7ef81b7a.619.js} +1 -1
  17. package/dist/{17cbc0a3e5f.678.js → 17d7ef81b7a.678.js} +0 -0
  18. package/dist/17d7ef81b7a.74.js +1 -0
  19. package/dist/{17cbc0a3e5f.782.js → 17d7ef81b7a.782.js} +1 -1
  20. package/dist/17d7ef81b7a.app.js +1 -0
  21. package/dist/{17cbc0a3e5f.index.html → 17d7ef81b7a.index.html} +1 -1
  22. package/dist/{17cbc0a3e5f.library.html → 17d7ef81b7a.library.html} +1 -1
  23. package/dist/17d7ef81b7a.library.js +1 -0
  24. package/dist/{17cbc0a3e5f.preview.html → 17d7ef81b7a.preview.html} +1 -1
  25. package/dist/17d7ef81b7a.preview.js +1 -0
  26. package/dist/17d7ef81b7a.screenshoot.js +1 -0
  27. package/dist/17d7ef84382.178.js +406 -0
  28. package/dist/17d7ef84382.213.js +3223 -0
  29. package/dist/17d7ef84382.362.js +107 -0
  30. package/dist/17d7ef84382.441.js +2706 -0
  31. package/dist/17d7ef84382.605.js +391 -0
  32. package/dist/17d7ef84382.619.js +777 -0
  33. package/dist/17d7ef84382.678.js +107 -0
  34. package/dist/17d7ef84382.74.js +4732 -0
  35. package/dist/17d7ef84382.782.js +107 -0
  36. package/dist/17d7ef84382.screenshoot.js +5719 -0
  37. package/dist/HASH +1 -1
  38. package/dist/assets/img/default.06e6ee0.jpg +0 -0
  39. package/dist/screenshoot.html +7 -0
  40. package/package.json +12 -12
  41. package/screenshoot.html +15 -0
  42. package/screenshoot.js +28 -0
  43. package/src/chatbox/Event.js +2 -0
  44. package/src/chatbox/OrderAttachments.js +107 -0
  45. package/src/chatbox/ProductAttachments.js +14 -8
  46. package/src/notif/notif.js +36 -27
  47. package/src/plugin.js +1 -1
  48. package/src/store/api.js +2 -5
  49. package/src/store/store.js +9 -8
  50. package/src/util/campaign.js +14 -11
  51. package/src/util/common.js +6 -0
  52. package/take.js +36 -0
  53. package/webpack.dev.js +7 -0
  54. package/webpack.prod.js +12 -2
  55. package/webpack.screenshoot.js +28 -0
  56. package/dist/17cbc0a3e5f.605.js +0 -1
  57. package/dist/17cbc0a3e5f.712.js +0 -1
  58. package/dist/17cbc0a3e5f.84.js +0 -1
  59. package/dist/17cbc0a3e5f.app.js +0 -1
  60. package/dist/17cbc0a3e5f.library.js +0 -1
  61. package/dist/17cbc0a3e5f.preview.js +0 -1
package/dist/HASH CHANGED
@@ -1 +1 @@
1
- 17d029eeb07
1
+ 17d7ef84382
@@ -0,0 +1,7 @@
1
+ <html><head><meta name="viewport" content="width=device-width,intial-scale=1"/><style>/* make the environment look like widget environment */
2
+ #sbz-pop * {
3
+ all: unset;
4
+ }
5
+ body {
6
+ background: #ddd;
7
+ }</style></head><body><script defer="defer" src="./17d7ef84382.screenshoot.js"></script></body></html>
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.0.50",
3
+ "version": "4.0.55",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
7
- "build": "npx webpack --config webpack.prod.js",
7
+ "build": "rm -rf dist && npx webpack --config webpack.prod.js && npx webpack --config webpack.screenshoot.js",
8
8
  "report": "npx webpack --config webpack.report.js",
9
9
  "lib": "rm -rf compiled_src && npx babel --out-dir=compiled_src src"
10
10
  },
11
11
  "dependencies": {
12
- "@babel/core": "^7.16.0",
13
- "@googlemaps/js-api-loader": "^1.11.4",
12
+ "@googlemaps/js-api-loader": "^1.12.11",
14
13
  "@subiz/ajax": "^3.0.12",
15
14
  "@subiz/flow": "^1.0.9",
16
15
  "@subiz/storage": "0.0.8",
17
16
  "@subiz/wsclient": "^3.1.7",
18
- "babel-loader": "^8.2.3",
19
17
  "cookie_js": "1.4.2",
20
- "core-js": "^3.19.1",
18
+ "core-js": "^3.19.2",
21
19
  "css-loader": "^5.2.6",
22
20
  "file-loader": "^6.2.0",
23
21
  "less": "^4.1.2",
24
22
  "less-loader": "^10.2.0",
25
- "marked": "^4.0.0",
26
- "preact": "^10.5.15"
23
+ "marked": "^4.0.6",
24
+ "preact": "^10.6.2"
27
25
  },
28
26
  "devDependencies": {
27
+ "babel-loader": "^8.2.3",
28
+ "@babel/core": "^7.16.0",
29
29
  "@babel/cli": "^7.16.0",
30
30
  "@babel/plugin-proposal-class-properties": "^7.16.0",
31
31
  "@babel/plugin-transform-react-jsx": "^7.16.0",
32
- "@babel/preset-env": "^7.16.0",
32
+ "@babel/preset-env": "^7.16.4",
33
33
  "babel-plugin-syntax-jsx": "^6.18.0",
34
34
  "html-webpack-plugin": "^5.5.0",
35
- "prettier": "^2.4.1",
36
- "webpack": "^5.62.1",
35
+ "puppeteer": "^12.0.1",
36
+ "webpack": "^5.64.4",
37
37
  "webpack-bundle-analyzer": "^4.5.0",
38
38
  "webpack-cli": "^4.9.1",
39
- "webpack-dev-server": "^4.4.0"
39
+ "webpack-dev-server": "^4.6.0"
40
40
  }
41
41
  }
@@ -0,0 +1,15 @@
1
+ <html>
2
+ <head>
3
+ <meta name="viewport" content="width=device-width, intial-scale=1.0" />
4
+ <style>
5
+ /* make the environment look like widget environment */
6
+ #sbz-pop * {
7
+ all: unset;
8
+ }
9
+ body {
10
+ background: #ddd;
11
+ }
12
+ </style>
13
+ </head>
14
+ <body></body>
15
+ </html>
package/screenshoot.js ADDED
@@ -0,0 +1,28 @@
1
+ import Widget from './src/lib.js'
2
+
3
+ let accid = 'acqsulrowbxiugvginhw'
4
+ let widget = new Widget(
5
+ 'https://api.subiz.com.vn/4.0/',
6
+ 'https://file-subiz.com/',
7
+ 'https://rt-0.subiz.com.vn/',
8
+ true,
9
+ true,
10
+ )
11
+
12
+ function main() {
13
+ var $div = document.createElement('div')
14
+ document.body.appendChild($div)
15
+ let hash = document.location.hash.substr(1)
16
+ let plugin = JSON.parse(decodeURIComponent(atob(hash)))
17
+ plugin.triggers = [{type: 'after_pageview', after_pageview_sec: 0, enabled: true}]
18
+ if (plugin.popup) plugin.popup.overlay = false
19
+ plugin._dont_close = true
20
+ plugin._page_index = 0
21
+
22
+ plugin.account_id = accid
23
+ widget._overridePlugin([plugin])
24
+ // widget._overrideDeviceType('mobile')
25
+ widget.setAccount(accid, $div)
26
+ }
27
+
28
+ main()
@@ -7,6 +7,7 @@ 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
11
  //import FormSubmitAttachment from './formSubmitAttachment.js'
11
12
  import VideoAttachments from './VideoAttachments.js'
12
13
  import TextMessage from './TextMessage.js'
@@ -140,6 +141,7 @@ export default class Event extends Com {
140
141
  <FormSubmitAttachments store={this.props.store} ev={ev} t={this.props.t} />
141
142
  <GenericTemplateAttachments store={this.props.store} ev={ev} onSelect={(e) => this.choosePostbackBtn(e)} />
142
143
  <ProductAttachments t={this.props.t} store={this.props.store} ev={ev} />
144
+ <OrderAttachments t={this.props.t} store={this.props.store} ev={ev} />
143
145
  <QuickReplies store={this.props.store} ev={ev} onSelect={(e) => this.chooseQuickReply(e)} />
144
146
  </div>
145
147
  )
@@ -0,0 +1,107 @@
1
+ import sb from '../util/index.js'
2
+ import {Com} from '../common.js'
3
+ import {h, createRef, Fragment} from 'preact'
4
+
5
+ export default class ProductAttachments extends Com {
6
+ render() {
7
+ let byType = sb.get(this.props.ev, 'by.type')
8
+ if (byType === 'user') return null
9
+ // filter out all generic
10
+
11
+ let acc = this.props.store.matchAccount()
12
+ var attachments = sb.get(this.props.ev, 'data.message.attachments', [])
13
+ let orders = sb.filter(attachments, (att) => att.type === 'order' && att.order).map((att) => att.order)
14
+
15
+ if (orders.length === 0) return null
16
+
17
+ let $orders = sb.map(orders, (order, index) => {
18
+ let $note = null
19
+ if (order.note) {
20
+ $note = (
21
+ <div>
22
+ <div class='order_attachment__label'>{this.props.t('note')}</div>
23
+ <div class='order_attachment__text_truncate'>{order.note}</div>
24
+ </div>
25
+ )
26
+ }
27
+
28
+ let $products = null
29
+ if (sb.size(order.items) > 0) {
30
+ $products = (
31
+ <div>
32
+ <div class='order_attachment__label'>{this.props.t('product')}</div>
33
+ {sb.map(order.items, (item) => {
34
+ let product = item.product || {}
35
+
36
+ return (
37
+ <div class='order_attachment__product'>
38
+ <img
39
+ class='order_attachment__product_img'
40
+ src={(sb.get(product, 'images.0'), require('../../assets/img/default.jpg'))}
41
+ />
42
+ <div style='overflow: hidden !important;'>
43
+ <div class='order_attachment__product_name'>{product.name}</div>
44
+ <div class='order_attachment__product_desc'>{product.description || product.long_description}</div>
45
+ <div class='order_attachment__product_desc'>
46
+ {this.props.t('qty')} {item.quantity}&nbsp;·&nbsp;{sb.formatCurrency(product.price)}
47
+ </div>
48
+ </div>
49
+ </div>
50
+ )
51
+ })}
52
+ </div>
53
+ )
54
+ }
55
+
56
+ let $total = (
57
+ <div class='order_attachment__footer'>
58
+ <div>{this.props.t('total')}</div>{' '}
59
+ <div>
60
+ <b>{sb.formatCurrency(order.total)}</b>
61
+ </div>
62
+ </div>
63
+ )
64
+
65
+ let addr = sb.get(order, 'shipping.address', {})
66
+ let fulladdr = [addr.street, addr.ward, addr.district, addr.region, addr.country].filter((a) => a).join(', ')
67
+ let address = fulladdr
68
+
69
+ let phoneattr = sb.find(sb.get(order, 'user.attributes', []), (attr) => attr.key === 'phones') || {
70
+ key: 'phones',
71
+ text: '',
72
+ }
73
+ let phone = sb.get(order, 'shipping.address.phone', '').trim()
74
+ phone = phone || sb.trim(phoneattr.text)
75
+ if (phone) phone = this.props.t('phn') + ': ' + phone
76
+
77
+ let $shipto = null
78
+ if (address || phone) {
79
+ $shipto = (
80
+ <div>
81
+ <div class='order_attachment__label'>{this.props.t('ship_to')}</div>
82
+ <div class='order_attachment__text_truncate'>{address}</div>
83
+ <div class='order_attachment__text_truncate'>{phone}</div>
84
+ </div>
85
+ )
86
+ }
87
+
88
+ return (
89
+ <div class={`order_attachment order_attachment__${byType}`}>
90
+ <div class='order_attachment__header'>
91
+ {this.props.t('order')}
92
+ <span class='order_attachment__text_muted order_attachment__text_sm'>#{order.id}</span>
93
+ </div>
94
+ {$products}
95
+ {$shipto}
96
+ <div>
97
+ <div class='order_attachment__label'>{this.props.t('payment')}</div>
98
+ <div>COD</div>
99
+ </div>
100
+ {$note}
101
+ {$total}
102
+ </div>
103
+ )
104
+ })
105
+ return $orders
106
+ }
107
+ }
@@ -16,6 +16,9 @@ export default class ProductAttachments extends Com {
16
16
  products = products.filter((product) => product.product && product.product.name)
17
17
 
18
18
  if (products.length === 0) return null
19
+
20
+ let acc = this.props.store.matchAccount()
21
+
19
22
  let $products = sb.map(products, (att, index) => {
20
23
  let product = att.product
21
24
 
@@ -32,23 +35,26 @@ export default class ProductAttachments extends Com {
32
35
 
33
36
  let $title = (
34
37
  <a href={url} class='product_attachment__title' target='_blank'>
35
- {this.props.t.i18n(product.name)}
38
+ {product.name}
36
39
  </a>
37
40
  )
38
41
  let $subtitle = null
39
- if (product.short_description)
40
- $subtitle = <div class='product_attachment__subtitle'>{product.short_description}</div>
42
+ let desc = product.description || product.long_description
43
+ if (desc) $subtitle = <div class='product_attachment__subtitle'>{desc}</div>
41
44
 
42
45
  let $price = null
43
- if (product.sale_price) {
44
- $price = <div class='product_attachment__price'>{product.sale_price} VND</div>
46
+ if (product.price) {
47
+ $price = (
48
+ <div class='product_attachment__price'>
49
+ {product.currency || sb.get(acc, 'currency', '')} {sb.formatCurrency(product.price)}
50
+ </div>
51
+ )
45
52
  }
46
-
47
53
  return (
48
54
  <div class={'product_attachment product_attachment__' + byType}>
49
55
  <div>
50
- <a href={this.props.store.replaceFileUrl(product.images[0])} target='_blank'>
51
- <img class='product_attachment__img' src={this.props.store.replaceFileUrl(product.images[0])} />
56
+ <a href={this.props.store.replaceFileUrl(product.image)} target='_blank'>
57
+ <img class='product_attachment__img' src={this.props.store.replaceFileUrl(product.image)} />
52
58
  </a>
53
59
  </div>
54
60
  {$title}
@@ -68,17 +68,17 @@ export default class Notif extends Com {
68
68
 
69
69
  renderCloseButton() {
70
70
  return (
71
- <div class="btn_close" onClick={e => this.onCloseButtonClick(e)}>
71
+ <div class='btn_close' onClick={(e) => this.onCloseButtonClick(e)}>
72
72
  <svg
73
- class="btn_close__icon"
74
- xmlns="http://www.w3.org/2000/svg"
75
- width="24"
76
- height="24"
77
- viewBox="0 0 24 24"
78
- stroke-linecap="round"
79
- stroke-linejoin="round">
80
- <line x1="18" y1="6" x2="6" y2="18" style="stroke-width: 3 !important;"></line>
81
- <line x1="6" y1="6" x2="18" y2="18" style="stroke-width: 3 !important;"></line>
73
+ class='btn_close__icon'
74
+ xmlns='http://www.w3.org/2000/svg'
75
+ width='24'
76
+ height='24'
77
+ viewBox='0 0 24 24'
78
+ stroke-linecap='round'
79
+ stroke-linejoin='round'>
80
+ <line x1='18' y1='6' x2='6' y2='18' style='stroke-width: 3 !important;'></line>
81
+ <line x1='6' y1='6' x2='18' y2='18' style='stroke-width: 3 !important;'></line>
82
82
  </svg>
83
83
  </div>
84
84
  )
@@ -106,7 +106,7 @@ export default class Notif extends Com {
106
106
  await sb.preloadImg(user.avatar_url)
107
107
  }
108
108
 
109
- sb.map(sb.get(this.props.plugin, 'notification.custom_avatar_urls'), url => {
109
+ sb.map(sb.get(this.props.plugin, 'notification.custom_avatar_urls'), (url) => {
110
110
  profiles.push({
111
111
  type: 'person',
112
112
  fullname: randomName(),
@@ -119,7 +119,12 @@ export default class Notif extends Com {
119
119
  })
120
120
 
121
121
  let i18n_messages = sb.get(this.props, 'plugin.notification.i18n_message_quill_deltas', [])
122
+
122
123
  let i18n_message = i18n_messages[Math.floor(Math.random() * sb.size(i18n_messages))]
124
+
125
+ let fallbackmsgs = sb.get(this.props, 'plugin.notification.message_quill_deltas', [])
126
+ let fallbackmessage = fallbackmsgs[Math.floor(Math.random() * sb.size(fallbackmsgs))]
127
+
123
128
  let i18n_title = sb.get(this.props, 'plugin.notification.i18n_title_quill_delta')
124
129
 
125
130
  return this.setState({
@@ -127,8 +132,8 @@ export default class Notif extends Com {
127
132
  last_show: sb.Date.now(),
128
133
  timestamp: timestamp,
129
134
  user: user,
130
- title: this.props.t.i18n(i18n_title),
131
- message: this.props.t.i18n(i18n_message),
135
+ title: this.props.t.i18n(i18n_title) || sb.get(this.props, 'plugin.notification.title_quill_delta'),
136
+ message: this.props.t.i18n(i18n_message) || fallbackmessage,
132
137
  })
133
138
  }
134
139
  this.setState({closeAnimation: true})
@@ -140,7 +145,7 @@ export default class Notif extends Com {
140
145
  async refreshCSS() {
141
146
  // make sure you are still alive
142
147
  let plugins = this.props.store.matchPlugin()
143
- let found = sb.find(plugins, plugin => plugin.id === this.props.plugin.id)
148
+ let found = sb.find(plugins, (plugin) => plugin.id === this.props.plugin.id)
144
149
  if (!found) return
145
150
  let css = (await notifCSS).default.default.toString()
146
151
  // var outcss = sb.replaceCssVariablePlugin(css, this.props.plugin.popup)
@@ -191,7 +196,7 @@ export default class Notif extends Com {
191
196
  renderTextFromQuillDelta(quilldelta, profile) {
192
197
  let qd = sb.parseJSON(quilldelta)
193
198
  if (!qd || !qd.ops) return null
194
- return sb.map(qd.ops, op => {
199
+ return sb.map(qd.ops, (op) => {
195
200
  let out = null
196
201
  if (!op.insert) return null
197
202
 
@@ -215,7 +220,6 @@ export default class Notif extends Com {
215
220
  if (!this.state.imageReady) return null
216
221
  if (this.state.close) return null
217
222
  if (!this.state.message) return null
218
-
219
223
  let notif = sb.get(this.props.plugin, 'notification', {})
220
224
  if (!notif) return null
221
225
  let animation = ''
@@ -244,24 +248,29 @@ export default class Notif extends Com {
244
248
  return (
245
249
  <div
246
250
  class={animation + ' notif' + ' notif__' + pos}
247
- onClick={e => this.onClick(e)}
248
- onmouseover={e => this.setOver(true)}
251
+ onClick={(e) => this.onClick(e)}
252
+ onmouseover={(e) => this.setOver(true)}
249
253
  style={st}
250
- onmouseleave={e => this.setOver(false)}>
251
- <div style="position: relative !important; display: flex !important; align-items: center !important;">
252
- <img alt="notification background" class="notif_bg" src={notif.background_url} style={sb.js2css(notif.background_style)} />
254
+ onmouseleave={(e) => this.setOver(false)}>
255
+ <div style='position: relative !important; display: flex !important; align-items: center !important;'>
256
+ <img
257
+ alt='notification background'
258
+ class='notif_bg'
259
+ src={notif.background_url}
260
+ style={sb.js2css(notif.background_style)}
261
+ />
253
262
  {this.renderCloseButton()}
254
- <div class="notif_avatar" style={sb.js2css(notif.avatar_style)}>
255
- <img alt="notification avatar" class="notif_avatar__img" src={user.avatar_url} />
263
+ <div class='notif_avatar' style={sb.js2css(notif.avatar_style)}>
264
+ <img alt='notification avatar' class='notif_avatar__img' src={user.avatar_url} />
256
265
  </div>
257
- <div class="notif_body">
258
- <div class="notif_title" style={sb.js2css(notif.title_style)}>
266
+ <div class='notif_body'>
267
+ <div class='notif_title' style={sb.js2css(notif.title_style)}>
259
268
  {this.renderTextFromQuillDelta(this.state.title, user)}
260
269
  </div>
261
- <div class="notif_message" style={sb.js2css(notif.message_style)}>
270
+ <div class='notif_message' style={sb.js2css(notif.message_style)}>
262
271
  {this.renderTextFromQuillDelta(this.state.message, user)}
263
272
  </div>
264
- <div class="notif_time" style={sb.js2css(notif.subtext_style)}>
273
+ <div class='notif_time' style={sb.js2css(notif.subtext_style)}>
265
274
  {this.state.timestamp}
266
275
  </div>
267
276
  </div>
package/src/plugin.js CHANGED
@@ -372,7 +372,7 @@ function downloadLanguage(store, locale, force) {
372
372
  const getLocale = (plugin, loc) => {
373
373
  if (!loc) loc = ''
374
374
  let found = sb.find(plugin.locales, (l) => l.toLowerCase().trim() === loc.toLowerCase().trim())
375
- if (found) return loc
375
+ if (found) return found
376
376
 
377
377
  found = sb.find(plugin.locales, (l) => {
378
378
  l = l.replace('-', '_').split('_')[0].trim().toLowerCase()
package/src/store/api.js CHANGED
@@ -47,10 +47,7 @@ export default class {
47
47
  .get('/conversations')
48
48
 
49
49
  listMessages = (cid, limit, start_id) =>
50
- this.apireq
51
- .addQuery('limit', limit)
52
- .addQuery('start_id', start_id)
53
- .get(`/conversations/${cid}/messages`)
50
+ this.apireq.addQuery('limit', limit).addQuery('start_id', start_id).get(`/conversations/${cid}/messages`)
54
51
 
55
52
  ajax = ajax
56
53
 
@@ -85,7 +82,7 @@ export default class {
85
82
  return this.sendEvent({
86
83
  by: {id: this.uid, device: device},
87
84
  type: 'content_viewed',
88
- data: {product: {url: pageUrl, title: pageTitle}},
85
+ data: {product: {url: pageUrl, name: pageTitle, title: pageTitle}},
89
86
  })
90
87
  }
91
88
 
@@ -149,7 +149,7 @@ export default class Store {
149
149
  overridePlugin(plugins) {
150
150
  if (!Array.isArray(plugins)) throw 'plugins must be be an array'
151
151
  let pluginM = {}
152
- sb.map(plugins, function(plugin) {
152
+ sb.map(plugins, function (plugin) {
153
153
  pluginM[plugin.id] = plugin
154
154
  })
155
155
  this.forcePlugins = pluginM
@@ -1042,7 +1042,7 @@ function getGaClientId() {
1042
1042
  if (!hasGA()) return clientId
1043
1043
 
1044
1044
  var allTrackers = window.ga.getAll() || []
1045
- allTrackers.forEach(function(tracker) {
1045
+ allTrackers.forEach(function (tracker) {
1046
1046
  clientId = tracker.get('clientId')
1047
1047
  })
1048
1048
 
@@ -1059,7 +1059,7 @@ function getGaTrackingIds() {
1059
1059
  if (!hasGA()) return trackingIds
1060
1060
 
1061
1061
  const allTrackers = window.ga.getAll() || []
1062
- allTrackers.forEach(function(tracker) {
1062
+ allTrackers.forEach(function (tracker) {
1063
1063
  var trackingId = tracker.get('trackingId')
1064
1064
  if (trackingId) trackingIds.push(trackingId)
1065
1065
  })
@@ -1077,7 +1077,7 @@ function sendToGA(category, action, label) {
1077
1077
  if (!hasGA()) return
1078
1078
 
1079
1079
  const allTrackers = window.ga.getAll() || []
1080
- allTrackers.forEach(function(tracker) {
1080
+ allTrackers.forEach(function (tracker) {
1081
1081
  const name = tracker.get('name')
1082
1082
  window.ga(name + '.send', 'event', category, action, label)
1083
1083
  })
@@ -1090,7 +1090,7 @@ function sendEventToThirdParty(category, action, label) {
1090
1090
  }
1091
1091
 
1092
1092
  // from http://detectmobilebrowsers.com/
1093
- let mobileCheck = (function(a) {
1093
+ let mobileCheck = (function (a) {
1094
1094
  return (
1095
1095
  /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
1096
1096
  a,
@@ -1101,6 +1101,7 @@ let mobileCheck = (function(a) {
1101
1101
  )
1102
1102
  })(navigator.userAgent || navigator.vendor || window.opera)
1103
1103
 
1104
- let tabletCheck = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(
1105
- navigator.userAgent,
1106
- )
1104
+ let tabletCheck =
1105
+ /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(
1106
+ navigator.userAgent,
1107
+ )
@@ -1,8 +1,8 @@
1
1
  var common = require('./common.js')
2
2
 
3
3
  // callback when user is about to leave site
4
- export function trackExitIntent (cb) {
5
- let handler = e => {
4
+ export function trackExitIntent(cb) {
5
+ let handler = (e) => {
6
6
  if (e.clientY > 10) return // not move to top
7
7
  if (cb) cb()
8
8
  document.removeEventListener('mouseout', handler)
@@ -11,8 +11,8 @@ export function trackExitIntent (cb) {
11
11
  }
12
12
 
13
13
  // callback after user has scrolled X% down on the current page
14
- export function trackScrollingPercentage (percent, cb) {
15
- let handleScroll = e => {
14
+ export function trackScrollingPercentage(percent, cb) {
15
+ let handleScroll = (e) => {
16
16
  let scrollTop = e.srcElement.scrollingElement ? e.srcElement.scrollingElement.scrollTop : e.srcElement.scrollTop
17
17
  let scrollHeight = e.srcElement.scrollingElement
18
18
  ? e.srcElement.scrollingElement.scrollHeight
@@ -28,14 +28,17 @@ export function trackScrollingPercentage (percent, cb) {
28
28
  }
29
29
 
30
30
  // callback when user clicks on a specific part of page
31
- export function trackAreaClicked (selector, cb) {
31
+ export function trackAreaClicked(selector, cb) {
32
32
  if (!selector) return
33
33
  selector = selector || ''
34
- common.map(selector.split('\n'), selector => {
34
+ common.map(selector.split('\n'), (selector) => {
35
35
  selector = common.trim(selector)
36
36
  if (!selector) return
37
- let doms = document.querySelectorAll(selector)
38
- common.map(doms, dom => {
37
+ let doms = []
38
+ try {
39
+ doms = document.querySelectorAll(selector)
40
+ } catch (e) {}
41
+ common.map(doms, (dom) => {
39
42
  if (!dom) return
40
43
  dom.addEventListener('click', cb, false)
41
44
  })
@@ -43,7 +46,7 @@ export function trackAreaClicked (selector, cb) {
43
46
  }
44
47
 
45
48
  // callback when user has stopped all activity (clicking, scrolling, ...)
46
- export function trackInactivity (seconds, cb) {
49
+ export function trackInactivity(seconds, cb) {
47
50
  let events = [
48
51
  'scroll',
49
52
  'keyup',
@@ -80,9 +83,9 @@ let timeOnPage = 0 // seconds
80
83
  setInterval(() => timeOnPage++, 1000)
81
84
 
82
85
  // when user has been on the page for at least X seconds
83
- export function trackTimeOnPage (seconds, cb) {
86
+ export function trackTimeOnPage(seconds, cb) {
84
87
  if (seconds < 1) {
85
- return setTimeout(function () {
88
+ return setTimeout(function() {
86
89
  cb(seconds)
87
90
  }, 100)
88
91
  }
@@ -1071,6 +1071,12 @@ export function getUrlParameter() {
1071
1071
 
1072
1072
  export var sleep = (ms) => new Promise((res) => setTimeout(res, ms))
1073
1073
 
1074
+ export function formatCurrency(val) {
1075
+ if (!val) return 0
1076
+ val = Math.round(val)
1077
+ return val.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
1078
+ }
1079
+
1074
1080
  function replaceFileUrl(src) {
1075
1081
  let fileurl = 'https://vcdn.subiz-cdn.com/file/'
1076
1082
  if (!src) return ''
package/take.js ADDED
@@ -0,0 +1,36 @@
1
+ const puppeteer = require('puppeteer')
2
+
3
+ async function take() {
4
+ const browser = await puppeteer.launch({args: ['--no-sandbox']})
5
+ const page = await browser.newPage()
6
+ let templateBase64 = Buffer.from(encodeURIComponent(process.argv[2])).toString('base64')
7
+
8
+ await page.setViewport({width: 940, height: 700})
9
+ await page.goto('file://' + __dirname + '/dist/screenshoot.html#' + templateBase64, {
10
+ waitUntil: 'networkidle2',
11
+ })
12
+ await timeout(3000)
13
+ let out = makeid(20) + '.jpg'
14
+ await page.screenshot({path: out})
15
+ console.log(out)
16
+ browser.close()
17
+ }
18
+
19
+ function timeout(ms) {
20
+ return new Promise((resolve) => setTimeout(resolve, ms))
21
+ }
22
+
23
+ function makeid(length) {
24
+ var result = []
25
+ var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
26
+ var charactersLength = characters.length
27
+ for (var i = 0; i < length; i++) {
28
+ result.push(characters.charAt(Math.floor(Math.random() * charactersLength)))
29
+ }
30
+ return result.join('')
31
+ }
32
+
33
+ take()
34
+
35
+ // try (remember to run 'npm run build' before running following command)
36
+ // node take.js '{"account_id":"acpxkgumifuoofoosble","id":"cpqzpnooosdhodmkjlixs","name":"Đặt hàng trước","type":"notification","created":1620813049308,"created_by":"agqmwfyuehpuzpehmv","updated":1620816899738,"updated_by":"agqmwfyuehpuzpehmv","triggers":[{"type":"after_pageview"}],"initiative_frequency":{"once_per_session":true},"notification":{"profile_tags":["nam thanh niên","nữ thanh niên"],"profiles":[{"avatar_url":"https://public-gcs.subiz-cdn.com/widget_assets/profile_images/VNM02.png","fullname":"Đoàn Hải Nam","country":"vn","gender":"Male","age":26,"tags":["nam thanh niên","vui tươi"],"email":"hainamxx@gmail.com","phone":"0823588xxx","address":"Hàng Da, Hoàn Kiếm, Hà Nội"},{"avatar_url":"https://public-gcs.subiz-cdn.com/widget_assets/profile_images/VNF03.png","fullname":"Nguyễn Hoàng Lan","country":"vn","gender":"Female","age":36,"tags":["nữ trung niên","lịch sự","vui tươi"],"email":"lannhoanggxx@gmail.com","phone":"0326789xxx","address":"Lũng Cú, Đồng Văn, Hà Giang"},{"avatar_url":"https://public-gcs.subiz-cdn.com/widget_assets/profile_images/VNM04.png","fullname":"Nguyễn Quý Tiến","country":"vn","gender":"Male","age":40,"tags":["nam trung niên","lịch sự"],"email":"tiendzxxx@gmail.com","phone":"056189xxxx","address":"Thụy Khuê, Tây Hồ, Hà Nội"},{"avatar_url":"https://public-gcs.subiz-cdn.com/widget_assets/profile_images/VNF05.png","fullname":"Phạm Hồng Thắm","country":"vn","gender":"Female","age":36,"tags":["nữ trung niên","lịch sự"],"email":"thampham1506xxx@gmail.com","phone":"033820xxxx","address":"Ngọc Đào, Hà Quang, Cao Bằng"},{"avatar_url":"https://public-gcs.subiz-cdn.com/widget_assets/profile_images/VNF06.png","fullname":"Ngô Thị Như Hòa","country":"vn","gender":"Female","age":29,"tags":["nữ thanh niên","lịch sự"],"email":"alice29xxx@gmail.com","phone":"058104xxxx","address":"Xuân Đỉnh, Bắc Từ Liêm, Hà Nội"}],"title_quill_delta":"{\"ops\":[{\"insert\":{\"dynamicField\":{\"id\":\"hesivydbbhoybwyk\",\"value\":\"Họ tên\",\"key\":\"user.fullname\"}}},{\"insert\":\" Ba \\n\"}]}","title_style":{"color":"rgba(255,255,255,1)"},"message_quill_deltas":["{\"ops\":[{\"insert\":\"Đặt trước Note 20 thành công\\n\"}]}"],"message_style":{"color":"rgba(216,207,207,1)"},"url":"https://subiz.com.vn","new_tab":true,"background_url":"https://public-gcs.subiz-cdn.com/widget_assets/notification_background/Frame%20129.png","subtext_style":{},"avatar_style":{"border_radius":"50%"},"position":"bottom-left","interval_sec":60000},"conversion_notification":{"all_agents":true},"template_title":"Đặt hàng trước","thumbnail_url":"https://file-subiz.com/fiqzpohaoyckhgxylulxcpqzpnooosdhodmkjlixs.jpg"}'
package/webpack.dev.js CHANGED
@@ -7,6 +7,7 @@ module.exports = {
7
7
  inject: './inject.js',
8
8
  preview: './preview.js',
9
9
  library: './library.js',
10
+ screenshoot: './screenshoot.js',
10
11
  },
11
12
  output: {publicPath: '/'},
12
13
  devServer: {port: 8082, host: '0.0.0.0', hot: false, liveReload: false},
@@ -16,6 +17,12 @@ module.exports = {
16
17
  new HtmlWebpackPlugin({filename: './preview.html', template: './preview.html', chunks: ['preview']}),
17
18
  new HtmlWebpackPlugin({filename: './inject.html', template: './inject.html', inject: 'body', chunks: ['inject']}),
18
19
  new HtmlWebpackPlugin({filename: './library.html', template: './index.html', inject: 'body', chunks: ['library']}),
20
+ new HtmlWebpackPlugin({
21
+ filename: './screenshoot.html',
22
+ template: './screenshoot.html',
23
+ inject: 'body',
24
+ chunks: ['screenshoot'],
25
+ }),
19
26
  ],
20
27
  module: {
21
28
  rules: [