@subiz/widget-v4 4.1.121 → 4.1.122

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 (39) hide show
  1. package/assets/less/avatar.less +0 -29
  2. package/assets/less/chatbox.less +7 -10
  3. package/assets/less/form.less +26 -9
  4. package/assets/less/message.less +7 -7
  5. package/assets/less/message_input.less +2 -3
  6. package/assets/less/widget.less +5 -11
  7. package/assets/less/widget_header.less +18 -16
  8. package/compiled_src/chatbox/AgentsAvatar.js +2 -2
  9. package/compiled_src/chatbox/Events.js +5 -2
  10. package/compiled_src/chatbox/Header.js +2 -7
  11. package/compiled_src/chatbox/Widget.js +55 -42
  12. package/compiled_src/lib.js +0 -5
  13. package/compiled_src/util/common.js +2 -2
  14. package/dist/{19642b47dfe.app.js → 19643345ab6.app.js} +1 -1
  15. package/dist/{19642b47dfe.index.html → 19643345ab6.index.html} +1 -1
  16. package/dist/{19642b47dfe.library.html → 19643345ab6.library.html} +1 -1
  17. package/dist/{19642b47dfe.library.js → 19643345ab6.library.js} +1 -1
  18. package/dist/{19642b47dfe.preview.html → 19643345ab6.preview.html} +1 -1
  19. package/dist/{19642b47dfe.preview.js → 19643345ab6.preview.js} +1 -1
  20. package/dist/{19642b47dfe.webview.js → 19643345ab6.webview.js} +1 -1
  21. package/dist/HASH +1 -1
  22. package/dist/screenshoot.js +4 -3
  23. package/package.json +2 -2
  24. package/src/chatbox/AgentsAvatar.js +3 -2
  25. package/src/chatbox/Events.js +5 -2
  26. package/src/chatbox/Header.js +2 -3
  27. package/src/chatbox/Widget.js +52 -43
  28. package/src/lib.js +2 -0
  29. package/src/util/common.js +2 -2
  30. package/up +3 -6
  31. /package/dist/{19642b47dfe.127.js → 19643345ab6.127.js} +0 -0
  32. /package/dist/{19642b47dfe.132.js → 19643345ab6.132.js} +0 -0
  33. /package/dist/{19642b47dfe.185.js → 19643345ab6.185.js} +0 -0
  34. /package/dist/{19642b47dfe.27.js → 19643345ab6.27.js} +0 -0
  35. /package/dist/{19642b47dfe.278.js → 19643345ab6.278.js} +0 -0
  36. /package/dist/{19642b47dfe.459.js → 19643345ab6.459.js} +0 -0
  37. /package/dist/{19642b47dfe.490.js → 19643345ab6.490.js} +0 -0
  38. /package/dist/{19642b47dfe.74.js → 19643345ab6.74.js} +0 -0
  39. /package/dist/{19642b47dfe.997.js → 19643345ab6.997.js} +0 -0
package/dist/HASH CHANGED
@@ -1 +1 @@
1
- 19642b47dfe
1
+ 19643345ab6
@@ -8021,10 +8021,11 @@ var chatbox_time = __webpack_require__(1620);
8021
8021
 
8022
8022
  class TextMessage extends common/* Com */.Mw {
8023
8023
  renderTime() {
8024
+ return null;
8024
8025
  if (!this.props.showTime && !this.props.isLast && !this.props.isFirst) return null;
8025
- return (0,preact_module.h)("div", null, (0,preact_module.h)(chatbox_time/* default */.A, {
8026
+ return h("div", null, h(Time, {
8026
8027
  class: "inline-time",
8027
- time: util/* default */.A.get(this.props.ev, 'created', util/* default */.A.Date.now()),
8028
+ time: sb.get(this.props.ev, 'created', sb.Date.now()),
8028
8029
  t: this.props.t
8029
8030
  }));
8030
8031
  }
@@ -8447,7 +8448,7 @@ class Event extends common/* Com */.Mw {
8447
8448
  }
8448
8449
  let $sender = null;
8449
8450
  if (this.props.event.mode === 'lead') $sender = (0,preact_module.h)("div", {
8450
- class: "pt-2"
8451
+ class: "pt-3"
8451
8452
  });
8452
8453
  let $error = null;
8453
8454
  if (ev._error && ev.type === 'message_sent') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.121",
3
+ "version": "4.1.122",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -31,7 +31,7 @@
31
31
  "babel-plugin-syntax-jsx": "^6.18.0",
32
32
  "html-webpack-plugin": "^5.6.3",
33
33
  "puppeteer": "^24.6.1",
34
- "webpack": "^5.99.5",
34
+ "webpack": "^5.99.6",
35
35
  "webpack-bundle-analyzer": "^4.10.2",
36
36
  "webpack-cli": "^6.0.1",
37
37
  "webpack-dev-server": "^5.2.1"
@@ -27,7 +27,8 @@ export default function AgentsAvatar(props) {
27
27
 
28
28
  let agentM = props.store.matchAgent('*')
29
29
  var agents = sb.map(props.agent_ids, (id) => agentM[id])
30
- if (props.agent_ids === undefined || sb.size(props.agent_ids) === 0) {
30
+
31
+ if (props.agent_ids !== undefined && sb.size(props.agent_ids) == 0) {
31
32
  var agids = sb.get(accountSetting, 'header.agent_ids', [])
32
33
  agids = sb.filter(agids, (id) => !!agentM[id])
33
34
  if (agids.length !== 0) {
@@ -38,7 +39,7 @@ export default function AgentsAvatar(props) {
38
39
  }
39
40
 
40
41
  agents = sb.filter(agents, (ag) => !!ag)
41
- if (agents.length === 0 || props.agent_ids === undefined || sb.size(props.agent_ids) === 0) {
42
+ if (sb.size(agents) === 0) {
42
43
  // use account avatar
43
44
  return (
44
45
  <div class={`group-avatar group-avatar--${mode}`}>
@@ -306,7 +306,7 @@ function processEvents(convo, events) {
306
306
 
307
307
  if (visibleEvent(curEvent.ev)) {
308
308
  let elapsedSec = sb.unixSec(curEvent.ev.created) - sb.unixSec(lastEvent.ev.created)
309
- if (elapsedSec > 120) curEvent.show_time = true // 2 mins
309
+ if (elapsedSec > 300) curEvent.show_time = true // 5 mins
310
310
  }
311
311
  }
312
312
 
@@ -340,7 +340,10 @@ function processEvents(convo, events) {
340
340
 
341
341
  function visibleEvent(ev) {
342
342
  let type = sb.get(ev, 'type')
343
- return type === 'message_sent' || type === 'conversation_state_updated'
343
+ if (type === 'message_sent') return true
344
+ if (type === 'conversation_state_updated') {
345
+ return sb.get(ev, 'data.conversation.state') == 'ended'
346
+ }
344
347
  }
345
348
 
346
349
  function isSummited(form) {
@@ -28,6 +28,7 @@ export default class Header extends Com {
28
28
  renderSocial = layout === 'social'
29
29
  renderTeam = !(renderSocial || renderClassic)
30
30
  }
31
+
31
32
  return (
32
33
  <div class={cls}>
33
34
  {this.renderCollapsedHeader(renderCollapsed || renderClassic, renderClassic)}
@@ -54,8 +55,6 @@ export default class Header extends Com {
54
55
  return (
55
56
  <div class={cls} style={style}>
56
57
  {$back}
57
- <div class="mr-2"></div>
58
- <AgentsAvatar plugin={this.props.plugin} store={this.props.store} mode='secondary' />
59
58
  <div class='widget-header--title-container'>
60
59
  <div class='widget-header--title'>{this.getTitle()}</div>
61
60
  <div class='widget-header--description' title={desc}>
@@ -149,7 +148,7 @@ Vui lòng hỏi chúng tôi bất cứ điều gì hoặc chia sẻ phản hồi
149
148
  </div>
150
149
  <div class='widget_header__body'>
151
150
  <div class='widget-header--avatar-wrapper'>
152
- <AgentsAvatar plugin={this.props.plugin} store={this.props.store} mode='primary' />
151
+ <AgentsAvatar plugin={this.props.plugin} store={this.props.store} mode='primary' agent_ids={[]} />
153
152
  </div>
154
153
  <div class='widget-header--title-container'>
155
154
  <div class='widget-header--description'>{tags}</div>
@@ -159,31 +159,30 @@ export default class Widget extends Com {
159
159
  )
160
160
  }
161
161
 
162
- async submitPrechatForm(form, formName) {
163
- if (this.state.submiting) return
164
- // check error first
165
- var hasError = false
166
- sb.map(this.state.formData, (field, key) => {
162
+ validateForm(formData) {
163
+ let newformdata = {}
164
+ sb.map(formData, (field, key) => {
165
+ field = Object.assign({}, field)
166
+ field.is_empty_error = false
167
+
167
168
  if (
168
169
  field.is_required &&
169
170
  (field.value === undefined || field.value === '' || (sb.size(field.value) === 0 && Array.isArray(field.value)))
170
171
  ) {
172
+ field.is_empty_error = true
171
173
  field.error = this.props.t('error_empty')
172
- hasError = true
173
174
  } else field.error = ''
174
175
 
175
176
  if (key.substr(5) === 'emails') {
176
177
  if (field.is_required) {
177
178
  if (!sb.isEmail(field.value)) {
178
179
  field.error = this.props.t('error_email_invalid') // 'The email is not valid'
179
- hasError = true
180
180
  }
181
181
  } else {
182
182
  let value = field.value || ''
183
183
  if (typeof value !== 'string') value = value + '' // try to makesure use ''.trim doesnt errror
184
184
  if (value.trim() && !sb.isEmail(field.value)) {
185
185
  field.error = this.props.t('error_email_invalid') // 'The email is not valid'
186
- hasError = true
187
186
  }
188
187
  }
189
188
  }
@@ -192,19 +191,30 @@ export default class Widget extends Com {
192
191
  if (field.is_required) {
193
192
  if (!sb.isPhone(field.value)) {
194
193
  field.error = this.props.t('error_phone_invalid') // 'The email is not valid'
195
- hasError = true
196
194
  }
197
195
  } else {
198
196
  let value = field.value || ''
199
197
  if (typeof value !== 'string') value = value + '' // try to makesure use ''.trim doesnt errror
200
198
  if (value.trim() && !sb.isPhone(field.value)) {
201
199
  field.error = this.props.t('error_phone_invalid') // 'The email is not valid'
202
- hasError = true
203
200
  }
204
201
  }
205
202
  }
203
+ newformdata[key] = field
206
204
  })
207
- if (hasError) return this.forceUpdate()
205
+ return newformdata
206
+ }
207
+
208
+ async submitPrechatForm(form, formName) {
209
+ if (this.state.submiting) return
210
+ // check error first
211
+ let newData = this.validateForm(this.state.formData)
212
+ let hasError = sb.find(newData, (field) => !!field.error)
213
+ if (hasError) {
214
+ this.setState({formData: newData})
215
+ this.focusInput()
216
+ return
217
+ }
208
218
 
209
219
  // user must already send the message or it would create be a loop
210
220
  let data = sb.cloneDeep(this.state.formData)
@@ -325,6 +335,7 @@ export default class Widget extends Com {
325
335
  let field = sb.find(formData, (_, fkkey) => fkkey === key)
326
336
  if (!field) return
327
337
  field.value = val
338
+ field.error = undefined
328
339
  this.setState({formData})
329
340
  }
330
341
 
@@ -476,7 +487,7 @@ export default class Widget extends Com {
476
487
  if (key == 'question' || sb.get(field, 'def.multiple_line')) {
477
488
  $field = (
478
489
  <textarea
479
- class='form-control'
490
+ class={`form-control ${errclass}`}
480
491
  ref={(ref) => {
481
492
  this['form_' + encode(key)] = ref
482
493
  }}
@@ -485,7 +496,6 @@ export default class Widget extends Com {
485
496
  onInput={(e) => this.onInputChange(key, e.target.value)}
486
497
  onChange={(e) => this.onInputChange(key, e.target.value)}
487
498
  row='4'
488
- class='form-control'
489
499
  ></textarea>
490
500
  )
491
501
  } else if (sb.get(field, 'def.select') === 'checkbox') {
@@ -601,8 +611,8 @@ export default class Widget extends Com {
601
611
  {label}
602
612
  {field.is_required ? <span class='ml-1 red'>*</span> : ''}
603
613
  </label>
604
- {field.error ? <div class='form-control--valid-input--error'>{field.error}</div> : ''}
605
614
  {$field}
615
+ {field.error ? <div class='form-control--valid-input--error'>{field.error}</div> : ''}
606
616
  </div>
607
617
  )
608
618
  })
@@ -614,30 +624,28 @@ export default class Widget extends Com {
614
624
  if (!title) title = sb.get(form, 'title', this.props.t('capture_lead_title'))
615
625
  // replace ↵ character
616
626
  title = title.replace('↵', '')
617
-
627
+ let btncls = 'btn--start-convo '
628
+ let hasError = sb.find(this.state.formData, (field) => !!field.error)
629
+ let newData = this.validateForm(this.state.formData)
630
+ let hasEmptyError = sb.find(newData, (field) => !!field.is_empty_error)
618
631
  return (
619
632
  <div class='widget-body'>
620
633
  <FormMounter key={form._updated} form={form} onMount={this.formMounted} />
621
634
  <div class='widget-body--content flex-grow-1 pl-3 pr-3 d-flex flex-column'>
622
- <div class='flex-grow-1'>
623
- <div class='form mt-3 pl-3 pr-3 pt-3 pb-3 d-flex flex-column'>
624
- <div>{title}</div>
625
- {this.renderFields(form, formName)}
626
- </div>
627
- </div>
628
- <div class="mt-2"></div>
629
- <PoweredBy store={this.props.store} t={this.props.t} />
630
- </div>
631
-
632
- <div class='message-input message-input--preform'>
635
+ <div class='mt-3 d-flex flex-column'>
636
+ <div>{title}</div>
637
+ {this.renderFields(form, formName)}
638
+ </div>
639
+ <div style='min-height: 15px !important; flex: 1 !important; flex-shrink: 0 !important'></div>
633
640
  <button
634
- class='btn--start-convo'
641
+ class={btncls}
635
642
  onClick={(_) => this.submitPrechatForm(form, formName)}
636
- disabled={this.state.creating}
643
+ disabled={!!hasEmptyError || !!hasError || this.state.creating}
637
644
  >
638
645
  {submitLabel}
639
646
  </button>
640
647
  </div>
648
+ <div class='mt-2'></div>
641
649
  </div>
642
650
  )
643
651
  }
@@ -666,10 +674,10 @@ export default class Widget extends Com {
666
674
  <AgentsAvatar
667
675
  plugin={this.props.plugin}
668
676
  store={this.props.store}
669
- mode='member'
677
+ mode='secondary'
670
678
  class='d-flex flex-row'
671
679
  tooltipPos='right'
672
- agent_ids={agentids}
680
+ agent_ids={[agentids[0]]}
673
681
  />
674
682
  {this.renderConvoSummary(convo)}
675
683
  </div>
@@ -694,12 +702,12 @@ export default class Widget extends Com {
694
702
  {$body}
695
703
  </div>
696
704
  <div class='convo-list--footer'>
705
+ <PoweredBy store={this.props.store} />
697
706
  <button class='btn--start-convo' onClick={(_) => this.goToConvo('')}>
698
707
  <span>{this.props.t('start_conversation')}</span>
699
708
  <img src={send} alt='send button' width='26' height='26' />
700
709
  </button>
701
- <div class="mt-2"></div>
702
- <PoweredBy store={this.props.store} />
710
+ <div class='mt-2'></div>
703
711
  </div>
704
712
  </div>
705
713
  )
@@ -717,20 +725,21 @@ export default class Widget extends Com {
717
725
  if (this.props.store.isPreview) return
718
726
  if (this.shouldShowForm()) {
719
727
  // focus on the element
720
- let firstemptykey = ''
728
+ let focusfieldkey = ''
721
729
  sb.map(this.state.formData, (field, key) => {
722
- if (field.type !== 'text') return
723
- if (firstemptykey) return
724
-
725
- if (!field.value) firstemptykey = key
730
+ if (focusfieldkey || field.type !== 'text') return
731
+ if (!field.value || field.error) focusfieldkey = key
726
732
  })
727
-
733
+ var $input = this['form_' + encode(focusfieldkey)]
734
+ if ($input) $input.focus()
728
735
  sb.setTimeout(() => {
729
- var $input = this['form_' + encode(firstemptykey)]
730
- if (!$input) return
731
-
732
- $input.focus()
733
- }, 300)
736
+ sb.map(this.state.formData, (field, key) => {
737
+ if (focusfieldkey || field.type !== 'text') return
738
+ if (!field.value || field.error) focusfieldkey = key
739
+ })
740
+ var $input = this['form_' + encode(focusfieldkey)]
741
+ if ($input) $input.focus()
742
+ }, 200)
734
743
  return
735
744
  }
736
745
 
package/src/lib.js CHANGED
@@ -2,12 +2,14 @@ import Store from './store/store.js'
2
2
  import sb from './util/index.js'
3
3
  import PluginMgr from './plugin.js'
4
4
 
5
+ /*
5
6
  var $link = document.createElement('link')
6
7
  $link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap'
7
8
  $link.rel = 'stylesheet'
8
9
  document.head.append($link)
9
10
 
10
11
  sb.preloadWebfonts(['Inter'], [400, 500])
12
+ */
11
13
 
12
14
  class Widget {
13
15
  constructor(ApiURL, FileURL, RealtimeURL, inmem, fake) {
@@ -599,7 +599,7 @@ export function replaceCssVariablePlugin(css, setting) {
599
599
  let parseColor = parseCssColor(primary_color)
600
600
  if (!parseColor) parseColor = {red: 238, blue: 238, green: 238}
601
601
  let hue = rgbToHsl(parseColor.red, parseColor.green, parseColor.blue)[0]
602
- let [r, g, b] = hslToRgb(hue, 0.5, 0.95)
602
+ let [r, g, b] = hslToRgb(hue, 0.25, 0.95)
603
603
  DEFVAL.primary_color_faded = `rgb(${r},${g},${b})`
604
604
 
605
605
  var ret = []
@@ -659,7 +659,7 @@ export function replaceCssVariable(css, setting) {
659
659
  let parseColor = parseCssColor(primary_color)
660
660
  if (!parseColor) parseColor = {red: 238, blue: 238, green: 238}
661
661
  let hue = rgbToHsl(parseColor.red, parseColor.green, parseColor.blue)[0]
662
- let [r, g, b] = hslToRgb(hue, 0.5, 0.95)
662
+ let [r, g, b] = hslToRgb(hue, 0.25, 0.95)
663
663
  DEFVAL.primary_color_faded = `rgb(${r},${g},${b})`
664
664
 
665
665
  var ret = []
package/up CHANGED
@@ -23,7 +23,6 @@ mv dist/.HASH dist/HASH
23
23
  if [ "$1" = 'prod' ]; then
24
24
  gsutil -h "Cache-Control:public, max-age=10" -h "Content-Type: text/plain" cp dist/HASH gs://public-gcs.subiz-cdn.com/widget-v4/public/
25
25
 
26
- exit
27
26
  echo "Deploy to fallback server: google cloud storage"
28
27
  # support mallicious version
29
28
  # for https://widget.subiz.xyz/js/app.js
@@ -38,17 +37,15 @@ if [ "$1" = 'prod' ]; then
38
37
  cat cloudflare_worker.js | sed -e "s/XXXXXXXX/$(cat dist/HASH)/" > cfw.js
39
38
  curl -X PUT "https://api.cloudflare.com/client/v4/accounts/cc22eff135a40705ebe59955b450217d/workers/scripts/app" -H "X-Auth-Email: thanhpham@vietnambiz.com" -H "X-Auth-Key: $(cat ~/.cloudflare)" -H "Content-Type: application/javascript" --data "$(cat cfw.js)"
40
39
  rm cfw.js
41
-
42
- echo "Waiting for widget server (5sec)"
43
- sleep 5
44
-
45
- echo "Remove cloudfare cache"
46
40
  else
47
41
  cp dist/HASH dist/DEVHASH
48
42
  gsutil -h "Cache-Control:public, max-age=10" -h "Content-Type: text/plain" cp dist/DEVHASH gs://public-gcs.subiz-cdn.com/widget-v4/public/
49
43
  rm dist/DEVHASH
50
44
  fi
51
45
 
46
+ echo "Waiting for widget server (5sec) to fetch new hash"
47
+ sleep 5
48
+ echo "Remove cloudfare cache"
52
49
  # PURGE CLOUD FLARE CACHE
53
50
  curl -X POST "https://api.cloudflare.com/client/v4/zones/e8f5c860cbbc130dd389313324a6c35e/purge_cache" -H "X-Auth-Email: thanhpham@vietnambiz.com" -H "X-Auth-Key: $(cat ~/.cloudflare)" -H "Content-Type: application/json" --data '{"purge_everything":true}'
54
51
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes