vue-intergrall-plugins 0.0.1085 → 1.0.0

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 (37) hide show
  1. package/README.md +13 -5
  2. package/dist/vue-intergrall-plugins.min.js +1 -1
  3. package/package.json +70 -62
  4. package/src/lib-components/Buttons/IconButton.vue +27 -0
  5. package/src/lib-components/Buttons/SimpleButton.vue +140 -0
  6. package/src/lib-components/Cards/Card.vue +429 -0
  7. package/src/lib-components/Cards/CardCheck.vue +35 -0
  8. package/src/lib-components/Cards/CardFile.vue +157 -0
  9. package/src/lib-components/Chat/AudioSpeedControl.vue +60 -0
  10. package/src/lib-components/Chat/BtnDownloadAllFiles.vue +36 -0
  11. package/src/lib-components/Chat/BtnEmojis.vue +51 -45
  12. package/src/lib-components/Chat/BtnFiles.vue +408 -131
  13. package/src/lib-components/Chat/BtnScreenShare.vue +36 -0
  14. package/src/lib-components/Chat/BtnStandardMessages.vue +17 -0
  15. package/src/lib-components/Chat/ExpandTextarea.vue +5 -6
  16. package/src/lib-components/Chat/MultipleFilePreview.vue +40 -22
  17. package/src/lib-components/Chat/Picker.vue +185 -149
  18. package/src/lib-components/Chat/SingleFilePreview.vue +28 -7
  19. package/src/lib-components/Chat/StandardMessages.vue +245 -0
  20. package/src/lib-components/Chat/TextFooter.vue +791 -451
  21. package/src/lib-components/Email/EmailFile.vue +126 -0
  22. package/src/lib-components/Email/EmailItem.vue +186 -0
  23. package/src/lib-components/Loader/Loader.vue +6 -1
  24. package/src/lib-components/Messages/AnexoMensagem.vue +442 -0
  25. package/src/lib-components/Messages/CardAttachment.vue +61 -0
  26. package/src/lib-components/Messages/CardMessages.vue +666 -0
  27. package/src/lib-components/Messages/ChatMessages.vue +1082 -0
  28. package/src/lib-components/Messages/InteratividadeBotoes.vue +165 -0
  29. package/src/lib-components/Messages/InteratividadeFormulario.vue +392 -0
  30. package/src/lib-components/Messages/InteratividadePopup.vue +89 -0
  31. package/src/lib-components/Messages/LinkPreview.vue +189 -0
  32. package/src/lib-components/Scroll/ScrollContent.vue +166 -0
  33. package/src/lib-components/Templates/TemplateGenerator.vue +187 -50
  34. package/src/lib-components/Templates/TemplateMessage.vue +12 -1
  35. package/src/lib-components/Templates/TemplateSingle.vue +232 -13
  36. package/dist/vue-intergrall-plugins.esm.js +0 -5693
  37. package/dist/vue-intergrall-plugins.ssr.js +0 -5033
@@ -1,9 +1,14 @@
1
1
  <template>
2
2
  <div class="ts-container">
3
3
  <div class="ts-content">
4
- <div v-for="(component, cIndex) in template.components" :key="cIndex">
4
+ <div
5
+ v-for="(component, cIndex) in template.components"
6
+ :key="cIndex"
7
+ :class="`${component.type == 'footer' ? 'order-1 custom-box-shadow-bottom' : ''}${template.components.length == 1 ? 'custom-box-shadow-bottom' : ''}`"
8
+ >
5
9
  <header
6
10
  v-if="component.type === 'header'"
11
+ :class="`${template.components.length == 1 ? 'border-radius-5 custom-border-gray' : ''}`"
7
12
  id="template_header"
8
13
  >
9
14
  <div v-for="(param, pIndex) in component.parameters" :key="pIndex">
@@ -19,11 +24,36 @@
19
24
  ></div>
20
25
  <p v-else v-html="param.text"></p>
21
26
  </template>
27
+ <div v-else-if="param.type == 'image'" class="ts-image-type">
28
+ <input class="d-none" type="file" :ref="`ts-input-header-${pIndex}`" :accept="acceptedExtensions" @change="fileUpload($event)" />
29
+ <template v-if="headerFiles[0]">
30
+ <fa-icon
31
+ :icon="['fas', headerFiles[0].current_icon]"
32
+ :class="`color-${headerFiles[0].current_color}`"
33
+ @click="triggerInputFile(`header-${pIndex}`)"
34
+ v-tippy="{placement: 'right'}"
35
+ :content="headerFiles[0].image_preview ?
36
+ `<div class='custom-tooltip-image'>
37
+ <img src='${headerFiles[0].image_preview}' alt='${headerFiles[0].name}' />
38
+ </div>`
39
+ : headerFiles[0].name
40
+ "
41
+ />
42
+ </template>
43
+ <fa-icon
44
+ v-else
45
+ class="select-image"
46
+ :icon="['fas', 'image']"
47
+ @click="triggerInputFile(`header-${pIndex}`)"
48
+ v-tippy="{placement: 'right'}"
49
+ :content="`Selecionar anexo`"
50
+ />
51
+ </div>
22
52
  </div>
23
53
  </header>
24
54
  <section
25
55
  v-if="component.type === 'body'"
26
- :class="{'margin-bottom' : template.components.length === 2 }"
56
+ :class="`${template.components.length == 1 ? 'border-radius-5 custom-border-gray' : ''} ${!hasButtonOrFooterComponent ? 'custom-footer-style custom-box-shadow-bottom custom-border-gray' : ''}`"
27
57
  id="template_body"
28
58
  >
29
59
  <div v-for="(param, pIndex) in component.parameters" :key="pIndex">
@@ -41,8 +71,18 @@
41
71
  </template>
42
72
  </div>
43
73
  </section>
74
+ <section
75
+ v-if="component.type == 'button'"
76
+ :class="`${template.components.length == 1 ? 'border-radius-5 custom-border-gray' : ''} ${!hasFooterComponent ? 'custom-footer-style custom-box-shadow-bottom' : ''}`"
77
+ id="template_buttons"
78
+ >
79
+ <div v-for="(param, pIndex) in component.parameters" :key="pIndex" class="ts-button">
80
+ <p v-if="param.text" v-html="param.text"></p>
81
+ </div>
82
+ </section>
44
83
  <footer
45
84
  v-if="component.type == 'footer'"
85
+ :class="`${template.components.length == 1 ? 'border-radius-5 custom-border-gray' : ''}`"
46
86
  id="template_footer"
47
87
  >
48
88
  <div v-for="(param, pIndex) in component.parameters" :key="pIndex">
@@ -51,15 +91,51 @@
51
91
  </footer>
52
92
  </div>
53
93
  </div>
54
- <div class="tg-btn" :class="{'small-btn' : iconButton}" v-if="hasButton">
55
- <button @click="$emit('click-trigger')">
56
- <template v-if="!iconButton"> {{ dictionary.btn_contatar_clientes }} </template>
57
- <fa-icon v-else :icon="['fas', 'paper-plane']" />
58
- </button>
94
+
95
+ <div class="container-btns">
96
+ <div class="tg-btn" :class="{'small-btn' : iconButton}" v-if="hasButton">
97
+ <button @click="$emit('click-trigger')" ref="template-single-button">
98
+ <template v-if="!iconButton">
99
+ <fa-icon :icon="['fas', 'paper-plane']" />
100
+ {{ dictionary.btn_contatar_clientes }}
101
+ </template>
102
+ <fa-icon v-else :icon="['fas', 'paper-plane']" />
103
+ </button>
104
+
105
+ </div>
106
+
107
+ <div class="tg-btn btn-red" :class="{'small-btn' : iconButton}" v-if="hasSecondaryButton">
108
+ <button v-if="hasSecondaryButton" @click="$emit('dispatch-clients-with-bot')" ref="template-single-button-secondary">
109
+ <template v-if="!iconButton">
110
+ <fa-icon :icon="['fas', 'paper-plane']" />
111
+ {{ dictionary.btn_contatar_clientes_com_bot }}
112
+ </template>
113
+ <fa-icon v-else :icon="['fas', 'paper-plane']" />
114
+ </button>
115
+ </div>
59
116
  </div>
117
+
60
118
  </div>
61
119
  </template>
62
120
 
121
+ <style scoped>
122
+ .btn-red button {
123
+ background-color: red;
124
+ }
125
+
126
+ .btn-red svg {
127
+ color: rgb(182, 0, 0);
128
+ }
129
+
130
+ .container-btns {
131
+ display: flex;
132
+ flex-direction: column;
133
+ justify-content: center;
134
+ align-items: center;
135
+ gap: 10px;
136
+ }
137
+ </style>
138
+
63
139
  <script>
64
140
  export default {
65
141
  data() {
@@ -68,7 +144,9 @@ export default {
68
144
  regexVars: /{{var_\d}}/g,
69
145
  lastVar: 0,
70
146
  htmlInputString: `<input type='text' class='input-var input-var-${this.identifier}' autocomplete='off' />`,
71
- varListValues: ""
147
+ varListValues: "",
148
+ acceptedExtensions: "audio/aac, audio/mp4, audio/mpeg, audio/amr, audio/ogg, text/plain, application/pdf, application/vnd.ms-powerpoint, application/msword, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreasheetml.sheet, image/jpeg, image/png, video/mp4, video/3gp, image/webp",
149
+ headerFiles: [],
72
150
  };
73
151
  },
74
152
  props: {
@@ -81,6 +159,11 @@ export default {
81
159
  required: false,
82
160
  default: true,
83
161
  },
162
+ hasSecondaryButton: {
163
+ type: Boolean,
164
+ required: false,
165
+ default: false
166
+ },
84
167
  allVariables: {
85
168
  type: Boolean,
86
169
  required: false,
@@ -100,12 +183,29 @@ export default {
100
183
  required: true
101
184
  }
102
185
  },
186
+ computed: {
187
+ hasFooterComponent() {
188
+ if(!this.template.components || !this.template.components.length) return false
189
+ const types = []
190
+ this.template.components.forEach(({ type }) => types.push(type))
191
+ if(types.includes('footer')) return true
192
+ return false
193
+ },
194
+ hasButtonOrFooterComponent() {
195
+ if(!this.template.components || !this.template.components.length) return false
196
+ const types = []
197
+ this.template.components.forEach(({ type }) => types.push(type))
198
+ if(types.includes('button')) return true
199
+ return this.hasFooterComponent
200
+ },
201
+ },
103
202
  created() {
104
203
  this.$root.$refs[`template-single-${this.identifier}`] = this
105
204
  },
106
205
  mounted() {
107
- this.getListOpts()
206
+ if(this.allVariables) this.getListOpts()
108
207
  this.setInputs();
208
+ this.setImageVar(false)
109
209
  },
110
210
  updated() {
111
211
  this.lastVar = 0;
@@ -118,6 +218,11 @@ export default {
118
218
  })
119
219
  },
120
220
  methods: {
221
+ handleInitialFocus() {
222
+ const allInputs = document.querySelectorAll(`.input-var-${this.identifier}`);
223
+ if(allInputs.length) allInputs[0].focus()
224
+ else if(this.$refs["template-single-button"]) this.$refs["template-single-button"].focus()
225
+ },
121
226
  limparInputVar() {
122
227
  const allInputs = document.querySelectorAll(`.input-var-${this.identifier}`);
123
228
  allInputs.forEach((input) => {
@@ -126,6 +231,7 @@ export default {
126
231
  input.blur();
127
232
  this.setVar({ target: input }, input.id.replace(/[{}]+/g, ''));
128
233
  })
234
+ this.setImageVar(false)
129
235
  },
130
236
  getListOpts() {
131
237
  this.varListValues =
@@ -134,26 +240,112 @@ export default {
134
240
  ${this.allVariables ? '<li> telefone </li>' : ''}
135
241
  </ul>`
136
242
  },
243
+ fileUpload(event) {
244
+ try {
245
+ const allFiles = event.target.files ? event.target.files : event.dataTransfer.files ? event.dataTransfer.files : false
246
+ if(!allFiles || !allFiles.length) return this.setImageVar(false)
247
+ if(allFiles.length > 1) {
248
+ this.$toasted.global.defaultInfo({ msg: `Limite de 1 arquivo por vez` })
249
+ return this.setImageVar(false)
250
+ }
251
+ const file = allFiles[0]
252
+ const { type, name } = file
253
+ const validTypes = ['audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/amr', 'audio/ogg', 'text/plain', 'application/pdf', 'application/vnd.ms-powerpoint', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.spreasheetml.sheet', 'image/jpeg', 'image/png', 'video/mp4', 'video/3gp', 'image/webp']
254
+ if(!validTypes.includes(type)) {
255
+ this.$toasted.global.defaultInfo({ msg: `(${type}) ${this.dictionary.msg_arquivo_invalido}` })
256
+ return this.setImageVar(false)
257
+ }
258
+ const returnArrType = str => {
259
+ const arr = []
260
+ validTypes.forEach(currentType => {
261
+ if(currentType.indexOf(str) > -1) arr.push(currentType)
262
+ })
263
+ return arr
264
+ }
265
+
266
+ const imageTypes = returnArrType('image/')
267
+ const docTypes = returnArrType('application/')
268
+ const audioTypes = returnArrType('audio/')
269
+ const videoTypes = returnArrType('video/')
270
+ const isPdf = type.indexOf('application/pdf') > -1
271
+
272
+ let sizeInBytes = 0
273
+ Array.from(allFiles).forEach(file => sizeInBytes += file.size)
274
+ const sizeInMb = parseFloat((sizeInBytes / (1024*1024)).toFixed(2))
275
+ if(sizeInMb == 0 && sizeInBytes == 0) {
276
+ this.$toasted.global.defaultInfo({ msg: this.dictionary.msg_arquivo_invalido })
277
+ return this.setImageVar(false)
278
+ }
279
+ const max = imageTypes.includes(type) ? 5 : 15
280
+ if(sizeInMb >= max) {
281
+ this.$toasted.global.defaultInfo({ msg: `Limite de ${max} MB por arquivo` })
282
+ return this.setImageVar(false)
283
+ }
284
+
285
+ const fileReader = new FileReader()
286
+ let current_icon = 'file-alt', current_color = 'blue', setValues = true
287
+ if(imageTypes.includes(type)) {
288
+ setValues = false
289
+ fileReader.readAsDataURL(file)
290
+ fileReader.onload = () => {
291
+ const image_preview = fileReader.result
292
+ current_icon = 'image'
293
+ current_color = 'purple'
294
+ this.headerFiles[0] = { file, image_preview, current_icon, current_color, name }
295
+ this.$forceUpdate()
296
+ this.setImageVar(file)
297
+ }
298
+ }else if(docTypes.includes(type) && isPdf) {
299
+ current_icon = 'file-pdf'
300
+ current_color = 'red'
301
+ }else if(audioTypes.includes(type)) {
302
+ current_icon = 'microphone'
303
+ current_color = 'black'
304
+ }else if(videoTypes.includes(type)) {
305
+ current_icon = 'video',
306
+ current_color = 'black'
307
+ }
308
+
309
+ if(setValues) {
310
+ this.headerFiles[0] = { file, current_icon, current_color, name }
311
+ this.$forceUpdate()
312
+ this.setImageVar(file)
313
+ }
314
+ }catch(error) {
315
+ console.error("Erro file upload: ", error)
316
+ }
317
+ },
318
+ triggerInputFile(origin) {
319
+ if(this.$refs[`ts-input-${origin}`]) {
320
+ const elem = this.$refs[`ts-input-${origin}`][0] ? this.$refs[`ts-input-${origin}`][0] : this.$refs[`ts-input-${origin}`]
321
+ elem && elem.click()
322
+ }
323
+ },
137
324
  setInputs() {
138
325
  let qtdInputs = 0
139
326
  const header = document.querySelector("#template_header");
140
327
  const body = document.querySelector("#template_body");
141
328
  const footer = document.querySelector("#template_footer");
329
+
330
+ let hasText = false
142
331
  if (header !== null) {
143
332
  header.querySelectorAll(`.input-var-${this.identifier}`).forEach((input) => { this.setEvent(input) })
144
333
  qtdInputs += header.querySelectorAll(`.input-var-${this.identifier}`).length
334
+ if(qtdInputs > 0 && header.innerText) hasText = true
145
335
  }
146
336
  if (this.lastVar === 0) this.lastVar += 1
147
337
  if (body !== null) {
148
338
  body.querySelectorAll(`.input-var-${this.identifier}`).forEach((input) => { this.setEvent(input) });
149
339
  qtdInputs += body.querySelectorAll(`.input-var-${this.identifier}`).length
340
+ if(qtdInputs > 0 && body.innerText) hasText = true
150
341
  }
151
342
  if (footer !== null) {
152
343
  footer.querySelectorAll(`.input-var-${this.identifier}`).forEach((input) => { this.setEvent(input) });
153
344
  qtdInputs += footer.querySelectorAll(`.input-var-${this.identifier}`).length
345
+ if(qtdInputs > 0 && footer.innerText) hasText = true
154
346
  }
155
347
 
156
- if(qtdInputs === 1) {
348
+ if(qtdInputs === 1 && !hasText) {
157
349
  document.querySelector(`.input-var-${this.identifier}`).parentElement.style.width = "100%"
158
350
  document.querySelector(`.input-var-${this.identifier}`).style.width = "100%"
159
351
  }else if(qtdInputs === 0) {
@@ -163,21 +355,38 @@ export default {
163
355
  setEvent(input) {
164
356
  const varList = input.nextSibling
165
357
 
358
+ const currentInstance = this
359
+
166
360
  input.setAttribute("placeholder", `{{var_${this.lastVar}}}`);
167
361
  input.setAttribute("id", `{{var_${this.lastVar}}}`);
168
362
  input.addEventListener("input", (event) => {
169
363
  this.setVar(event, `var_${this.lastVar}`, true);
170
- varList.classList.remove("visible")
364
+ if(varList) varList.classList.remove("visible")
171
365
  });
172
366
  input.addEventListener("focus", () => {
173
367
  input.classList.remove("invalid");
174
368
  input.classList.add("active");
175
369
  if(varList) {
370
+ const handleNextFocus = () => {
371
+ const allInputs = document.querySelectorAll(`.input-var-${currentInstance.identifier}`)
372
+ let stop = false
373
+ if(allInputs.length) {
374
+ allInputs.forEach(elem => {
375
+ if(!elem.value && !stop) {
376
+ stop = true
377
+ elem.focus()
378
+ }
379
+ })
380
+ }
381
+ if(!stop && this.$refs["template-single-button"]) this.$refs["template-single-button"].focus()
382
+ }
383
+
176
384
  for(let i = 0; i < varList.children.length; i++) {
177
385
  varList.children[i].addEventListener("click", function() {
178
386
  input.value = `[${this.innerText}]`
179
387
  input.classList.add("active")
180
388
  input.dispatchEvent(new Event('input'))
389
+ handleNextFocus()
181
390
  })
182
391
  }
183
392
 
@@ -205,8 +414,7 @@ export default {
205
414
  if(varList) varList.classList.remove("visible")
206
415
  });
207
416
  input.addEventListener("keydown", (event) => {
208
- if (event.keyCode == 13 && this.hasButton)
209
- this.$emit("click-trigger");
417
+ if (event.keyCode == 13 && this.hasButton) this.$emit("click-trigger");
210
418
  });
211
419
  this.lastVar += 1;
212
420
  },
@@ -239,6 +447,17 @@ export default {
239
447
 
240
448
  return isValueValid
241
449
  },
450
+ setImageVar(file) {
451
+ this.$emit("set-file-var", file)
452
+ if(!file) {
453
+ this.headerFiles = []
454
+ return this.$forceUpdate()
455
+ }else {
456
+ if(this.lastVar == 0) return this.$emit("set-vars", this.template)
457
+ if(Object.keys(this.varValues).length) return this.$emit("set-vars", this.varValues)
458
+ this.$nextTick(() => this.handleInitialFocus())
459
+ }
460
+ },
242
461
  setVar(event, key, notificar) {
243
462
  if (event && event.target) {
244
463
  key = event.target.id.replace(/[{}]+/g, '')