vue-wiguet-chatweb 0.1.24 → 0.1.25

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-wiguet-chatweb",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -47,9 +47,11 @@
47
47
  "@vueuse/core": "^10.0.0",
48
48
  "axios": "^1.4.0",
49
49
  "luxon": "^3.0.0",
50
+ "socket.io-client": "^4.7.2",
50
51
  "uuid": "^9.0.1"
51
52
  },
52
53
  "devDependencies": {
54
+ "@primevue/auto-import-resolver": "^4.2.5",
53
55
  "@types/luxon": "^3.4.2",
54
56
  "@types/node": "^20.4.6",
55
57
  "@types/uuid": "^10.0.0",
@@ -17,6 +17,12 @@
17
17
  @loadMore="getMessages"
18
18
  @retry="retryMessage"
19
19
  @on-qualifying="(args) => onQualifying(args)"
20
+ @see="(message: Message) => {
21
+ currentDialogView = DIALOG_VIEWS.SEE
22
+ urlFileMessage = message.urlFile
23
+ dialog.title = 'Imagen'
24
+ dialog.modelValue = true
25
+ }"
20
26
  />
21
27
  <div class="fit" v-else>
22
28
  <span class="center">No tienes mensajes</span>
@@ -42,28 +48,29 @@
42
48
  @keyup.enter="saltoDeLineaOEnviar"
43
49
  />
44
50
 
45
- <FileUpload
46
- mode="basic"
47
- accept="image/png,image/jpeg,image/jpg,image/webp"
48
- :maxFileSize="2000000"
49
- @select="onFileSelect"
50
- title="Adjuntar imagen"
51
- :chooseButtonProps="{ label: '', outlined: true }"
52
- :auto="false"
53
- style="height: 39px"
51
+ <input
52
+ type="file"
53
+ ref="fileInputRef"
54
+ @change="onFileSelect"
55
+ accept="image/*"
56
+ style="display: none;"
57
+ key="fileInputKey"
58
+
59
+ />
60
+ <button
61
+ type="button"
62
+ class="pointer btn-primary"
63
+ title="Adjuntar archivo"
64
+ @click="
65
+ () => {
66
+ fileInputRef.value = '';
67
+ fileInputKey++;
68
+ fileInputRef?.click();
69
+ }
70
+ "
54
71
  >
55
- <template #filelabel>
56
- <span style="display:none"></span>
57
- </template>
58
-
59
- <template #chooseicon>
60
- <IconAttach style="width: 20px; height: 20px" />
61
- </template>
62
- </FileUpload>
63
-
64
- <!-- <button class="pointer btn-primary">
65
72
  <IconAttach style="width: 20px; height: 20px" />
66
- </button> -->
73
+ </button>
67
74
 
68
75
  <button type="submit" class="pointer btn-primary">
69
76
  <IconSend style="width: 20px; height: 20px" />
@@ -77,12 +84,11 @@
77
84
  </div>
78
85
  </div>
79
86
 
80
- <ODialog v-bind="dialog" modal>
81
- <div class="flex flex-col gap-3 justify-center items-center">
82
- <Image v-for="(urlFile, i) in urlFiles" :key="i" :src="urlFile" alt="Image" width="400" />
87
+ <ODialog v-bind="dialog">
88
+ <div v-if="currentDialogView === DIALOG_VIEWS.UPLOAD" class="flex flex-col gap-3 justify-center items-center">
89
+ <img v-for="(urlFile, i) in urlFiles" :key="i" :src="urlFile.toString()" alt="Image" width="400" />
83
90
 
84
- <div class="w-full">
85
- <form
91
+ <form
86
92
  class="message-send"
87
93
  @submit.prevent="
88
94
  (event) => {
@@ -116,8 +122,10 @@
116
122
  </button>
117
123
  </div>
118
124
  </div>
119
- </form>
120
- </div>
125
+ </form>
126
+ </div>
127
+ <div v-else>
128
+ <img v-if="urlFileMessage" :src="urlFileMessage" alt="Image" style="width: 55vw;" />
121
129
  </div>
122
130
  </ODialog>
123
131
  </template>
@@ -156,16 +164,22 @@ import { io, Socket } from "socket.io-client";
156
164
  import { APP_TYPE } from "../dto/chat.dto";
157
165
  import { MESSAGE_TYPE_CODES, TypeMessageTypeCodes } from "../resources/constants/message-type.constant";
158
166
  import { useMobile } from "../hooks/useMobile";
159
- import { IPropsDialog } from "./ODialog/IPropsDialog";
160
167
  import IconAttach from "./IconAttach.vue";
161
168
  import ODialog from "./ODialog/ODialog.vue";
162
- import { type FileUploadSelectEvent } from "primevue";
163
- import FileUpload from "primevue/fileupload";
164
- import Image from "primevue/image";
169
+ import { IPropsDialog } from "./ODialog/IPropsDialog";
170
+
171
+ const enum DIALOG_VIEWS {
172
+ UPLOAD,
173
+ SEE
174
+ }
165
175
 
166
176
  //DATA
167
177
  const message = ref("");
168
178
  const notViewed = ref(0);
179
+ const fileInputRef = ref();
180
+ const fileInputKey = ref(0);
181
+ const currentDialogView = ref(DIALOG_VIEWS.SEE)
182
+ const urlFileMessage = ref<string>()
169
183
 
170
184
  const messagesData = ref<{ data: Message[]; canLoadMoreMessages: boolean }>({
171
185
  data: [],
@@ -267,10 +281,7 @@ function createMessage(message: string, codigoTipoMensaje?: TypeMessageTypeCodes
267
281
  ? {
268
282
  code: codigoTipoMensaje,
269
283
  }
270
- : undefined;
271
-
272
- console.warn(inputFiles.value);
273
-
284
+ : undefined;
274
285
 
275
286
  const newMessage: Message = {
276
287
  id: uuidv4(),
@@ -283,7 +294,7 @@ function createMessage(message: string, codigoTipoMensaje?: TypeMessageTypeCodes
283
294
  createdAt: new Date().toISOString(),
284
295
  updatedAt: new Date().toISOString(),
285
296
  file: inputFiles.value?.[0],
286
- urlFile: inputFiles.value?.[0]?.objectURL,
297
+ urlFile: urlFiles.value?.[0].toString(),
287
298
  messageType,
288
299
  sender: {
289
300
  nombreCompleto: props.user.nombreCompleto,
@@ -570,29 +581,29 @@ function onQualifying({ message: messageParam, emoji }: { message: Message, emoj
570
581
  const isDisabledBoxMessage = ref(false);
571
582
 
572
583
  // Adjuntar
573
- function onFileSelect(event: FileUploadSelectEvent) {
584
+ function onFileSelect() {
574
585
  inputFiles.value = [];
575
586
  urlFiles.value = [];
576
587
 
577
- const file = event.files[0]; // Obtiene el primer archivo seleccionado
588
+ const filesParam = fileInputRef.value?.files ?? []
589
+
590
+ const file = filesParam?.[0];
578
591
 
579
- inputFiles.value = event.files as (File & { objectURL: string })[];
592
+ inputFiles.value = filesParam;
580
593
 
581
594
  if (!file) return;
582
595
 
583
- const reader = new FileReader();
584
- reader.onload = (e) => {
585
- e.target?.result && urlFiles.value.push(e.target?.result);
586
- };
587
- reader.readAsDataURL(file);
596
+ urlFiles.value.push(URL.createObjectURL(file))
588
597
 
598
+ currentDialogView.value = DIALOG_VIEWS.UPLOAD;
599
+ dialog.title = 'Preparar imagen';
589
600
  dialog.modelValue = true;
590
601
  }
591
602
 
592
603
  // Dialog
593
604
 
594
605
  const inputFiles = ref<(File & { objectURL: string })[]>([]);
595
- const urlFiles = ref<Array<string | ArrayBuffer>>([]);
606
+ const urlFiles = ref<Array<string>>([]);
596
607
 
597
608
  const dialog = reactive<IPropsDialog>({
598
609
  modelValue: false,
@@ -604,8 +615,7 @@ const dialog = reactive<IPropsDialog>({
604
615
  urlFiles.value = [];
605
616
  inputFiles.value = [];
606
617
  },
607
- hideActions: true,
608
- title: 'SIN TITULO',
618
+ title: 'Preparar imagen'
609
619
  });
610
620
 
611
621
  //
@@ -50,18 +50,10 @@
50
50
  </div>
51
51
 
52
52
  <div v-else>
53
- <div v-if="message.messageType?.code === MESSAGE_TYPE_CODES.IMAGEN" class="text-center">
54
- <Image
55
- alt="Image"
56
- preview
57
- >
58
- <template #image>
59
- <img :src="message.thumbnailFile ?? message.urlFile" alt="image" width="150" />
60
- </template>
61
- <template #original="slotProps">
62
- <img :src="message.urlFile" alt="preview" :style="slotProps.style" width="800" @click="slotProps.previewCallback" />
63
- </template>
64
- </Image>
53
+ <div v-if="message.messageType?.code === MESSAGE_TYPE_CODES.IMAGEN" style="text-align: center;">
54
+ <a href="javascript:" class="btn-icon" @click="emit('see', message)">
55
+ <img :src="message.thumbnailFile ?? message.urlFile" alt="image" width="150" />
56
+ </a>
65
57
  </div>
66
58
 
67
59
  <div v-if="emoji[message.message ?? '']">
@@ -108,9 +100,8 @@ import HappiestIcon from '../assets/emojis/HappiestIcon.svg'
108
100
  import NeutralIcon from '../assets/emojis/NeutralIcon.svg'
109
101
  import HappyIcon from '../assets/emojis/HappyIcon.svg'
110
102
  import { MESSAGE_TYPE_CODES } from '../resources/constants/message-type.constant';
111
- import Image from 'primevue/image';
112
103
 
113
- const emit = defineEmits(["loadMore", "retry", "onQualifying"]);
104
+ const emit = defineEmits(["loadMore", "retry", "onQualifying", "see"]);
114
105
  const props = defineProps({
115
106
  messages: {
116
107
  type: Array as PropType<Message[]>,
@@ -357,35 +348,10 @@ const emoji: { [key in string]: any } = {
357
348
  font-size: 2rem;
358
349
  }
359
350
 
360
- .flex {
361
- display: flex;
362
- flex-wrap: wrap;
363
- }
364
- .flex-col {
365
- flex-direction: column;
366
- }
367
- .items-center {
368
- align-items: center;
369
- }
370
- .gap-2 {
371
- gap: 0.5rem;
372
- }
373
-
374
- .mb-2 {
375
- margin-bottom: 8px;
376
- }
377
-
378
- .mt-2 {
379
- margin-top: 8px;
380
- }
381
-
382
351
  .content-disabled {
383
352
  -webkit-filter: grayscale(1); /* Google Chrome, Safari 6+ & Opera 15+ */
384
353
  filter: grayscale(1); /* Microsoft Edge and Firefox 35+ */
385
354
  opacity: 0.5;
386
355
  }
387
- .justify-between {
388
- justify-content: space-between;
389
- }
390
356
 
391
357
  </style>
@@ -1,10 +1,4 @@
1
1
  import { IPropsSidebar } from './IPropsSidebar';
2
- import { type DialogProps } from "primevue/dialog";
3
2
 
4
3
 
5
- export interface IPropsDialog extends Omit<IPropsSidebar, 'position'> {
6
- position?: DialogProps['position'];
7
-
8
- // solo para v-bind
9
- 'onUpdate:modelValue'?: (args: IPropsDialog['modelValue'])=> void
10
- }
4
+ export interface IPropsDialog extends IPropsSidebar {}
@@ -1,16 +1,12 @@
1
- import type { DrawerProps } from "primevue/drawer";
2
-
3
1
  export interface IPropsSidebar {
4
- modelValue: boolean;
5
- dismissable?: boolean;
6
- position?: DrawerProps['position'];
2
+ modelValue?: boolean;
7
3
  title?: string;
8
- isLoading?: boolean;
9
- hideActions?: boolean;
10
- hideHeader?: boolean;
11
- acceptFn?: () => Promise<boolean>;
12
- beforeAcceptFn?: () => Promise<boolean>;
13
- disable?: boolean;
4
+ // isLoading?: boolean;
5
+ // hideActions?: boolean;
6
+ // hideHeader?: boolean;
7
+ // acceptFn?: () => Promise<boolean>;
8
+ // beforeAcceptFn?: () => Promise<boolean>;
9
+ // disable?: boolean;
14
10
 
15
11
  // solo para v-bind
16
12
  'onUpdate:modelValue'?: (args: IPropsSidebar['modelValue'])=> void
@@ -1,82 +1,85 @@
1
1
  <template>
2
- <Dialog
3
- :visible="propsComponent.modelValue"
4
- :dismissable
5
- :position
6
- @update:visible="(val: boolean) => emit('update:modelValue', val)"
7
- class="!w-auto min-w-[20rem] dark:!bg-[var(--theme-background-secondary)]"
8
- >
9
- <template #container>
10
- <LoadingComponent v-if="propsComponent.isLoading || isWorking" />
11
- <SidebarHeader v-if="!propsComponent.hideHeader" :title :closeFn="() => emit('update:modelValue', false)" />
12
-
13
- <!-- <Divider class="!m-0 !p-0" /> -->
2
+ <div v-if="props.modelValue" class="dialog-overlay" @click="closeDialog">
3
+ <div class="dialog-content" @click.stop>
4
+ <div class="header-widget">
5
+ <h4 class="title-chat">{{ title }}</h4>
6
+ <button @click="() => closeDialog()" class="btn-close">
7
+ <IconClose class="pointer" />
8
+ </button>
9
+ </div>
14
10
 
15
- <div class="overflow-y-auto m-4 h-full max-h-full">
11
+ <div>
16
12
  <slot></slot>
17
13
  </div>
18
-
19
- <footer v-if="!hideActions" class="flex sm:justify-between gap-16 items-center mx-5 py-5">
20
- <Button
21
- label="Cancelar"
22
- severity="secondary"
23
- class="w-full h-[40px] !rounded-2xl pr-5 sm:max-w-48"
24
- icon="pi pi-times"
25
- outlined
26
- @click="() => emit('update:modelValue', false)"
27
- ></Button>
28
- <Button
29
- label="Aceptar"
30
- :disabled="disable || isLoading"
31
- icon="pi pi-check"
32
- aria-label="save"
33
- class="w-full h-[40px] !rounded-2xl pr-5 sm:max-w-48"
34
- @click="onAccept"
35
- ></Button>
36
- </footer>
37
- </template>
38
- </Dialog>
14
+ </div>
15
+ </div>
39
16
  </template>
40
17
 
41
- <script setup lang="ts">
42
- import Dialog from 'primevue/dialog';
18
+ <script lang="ts" setup>
19
+ import IconClose from '../IconClose.vue'
43
20
  import { IPropsDialog } from './IPropsDialog';
44
- import LoadingComponent from '../LoadingComponent.vue';
45
- import SidebarHeader from '../sidebar/SidebarHeader.vue';
46
- import { computed, ref } from 'vue';
47
- import Button from 'primevue/button';
48
21
 
49
- const propsComponent = withDefaults(defineProps<IPropsDialog>(), {
50
- beforeAcceptFn: async () => true
51
- });
22
+ const props = defineProps<IPropsDialog>()
52
23
 
53
- const emit = defineEmits<{
54
- 'update:modelValue': [visible: boolean];
55
- accept: [void];
56
- }>();
24
+ const emit = defineEmits(['update:modelValue']);
57
25
 
58
- const isWorking = ref(false);
59
- const isLoading = computed(() => {
60
- return isWorking.value || propsComponent.disable || propsComponent.isLoading;
61
- });
26
+ const closeDialog = () => {
27
+ emit("update:modelValue", false);
28
+ };
29
+ </script>
62
30
 
63
- async function onAccept() {
64
- if (!propsComponent.acceptFn) {
65
- emit('accept');
66
- return;
67
- }
31
+ <style scoped>
32
+ .dialog-overlay {
33
+ position: fixed;
34
+ top: 0;
35
+ left: 0;
36
+ width: 100%;
37
+ height: 100%;
38
+ background-color: rgba(0, 0, 0, 0.5);
39
+ display: flex;
40
+ justify-content: center;
41
+ align-items: center;
42
+ opacity: 0;
43
+ animation: fadeIn 0.3s forwards;
44
+ }
68
45
 
69
- const isPassed = await propsComponent.beforeAcceptFn();
46
+ .dialog-content {
47
+ background-color: white;
48
+ padding: 15px;
49
+ border-radius: 8px;
50
+ text-align: center;
51
+ animation: slideIn 0.3s ease-out forwards;
52
+ }
70
53
 
71
- if(!isPassed) return;
54
+ @keyframes fadeIn {
55
+ to {
56
+ opacity: 1;
57
+ }
58
+ }
72
59
 
73
- isWorking.value = true;
74
- const result = await propsComponent.acceptFn?.();
75
- isWorking.value = false;
60
+ @keyframes slideIn {
61
+ from {
62
+ transform: translateY(-50px);
63
+ }
64
+ to {
65
+ transform: translateY(0);
66
+ }
67
+ }
76
68
 
77
- if(!result) return
78
-
79
- emit('accept')
80
- emit('update:modelValue', false);
69
+ .dialog-overlay.v-enter-active,
70
+ .dialog-overlay.v-leave-active {
71
+ transition: opacity 0.3s;
81
72
  }
82
- </script>
73
+
74
+ .btn-close {
75
+ padding: 0;
76
+ background-color: transparent;
77
+ border: none;
78
+ display: flex;
79
+ align-items: center;
80
+ border-radius: 50%;
81
+ &:hover {
82
+ background-color: rgba(202, 202, 202, 0.534);
83
+ }
84
+ }
85
+ </style>
@@ -53,6 +53,24 @@
53
53
  @on-qualifying="(args)=> emit('onQualifying', args)"
54
54
  />
55
55
  </div>
56
+
57
+ <Button label="Show" @click="visible = true" />
58
+
59
+ <Dialog v-model:visible="visible" modal header="Edit Profile" :style="{ width: '25rem' }">
60
+ <span class="text-surface-500 dark:text-surface-400 block mb-8">Update your information.</span>
61
+ <div class="flex items-center gap-4 mb-4">
62
+ <label for="username" class="font-semibold w-24">Username</label>
63
+ <InputText id="username" class="flex-auto" autocomplete="off" />
64
+ </div>
65
+ <div class="flex items-center gap-4 mb-8">
66
+ <label for="email" class="font-semibold w-24">Email</label>
67
+ <InputText id="email" class="flex-auto" autocomplete="off" />
68
+ </div>
69
+ <div class="flex justify-end gap-2">
70
+ <Button type="button" label="Cancel" severity="secondary" @click="visible = false"></Button>
71
+ <Button type="button" label="Save" @click="visible = false"></Button>
72
+ </div>
73
+ </Dialog>
56
74
  </div>
57
75
  </template>
58
76
 
@@ -65,7 +83,7 @@ import IconWhatsApp from "./IconWhatsApp.vue";
65
83
  import IconChat from "./IconChat.vue";
66
84
 
67
85
  const emit = defineEmits(["show-toast", "show-confirm", "onQualifying"]);
68
-
86
+ const visible = ref(true);
69
87
  const enum MeansCommunication{
70
88
  WHATSAPP,
71
89
  TELEGRAM
@@ -1,21 +0,0 @@
1
- import { PropType } from 'vue';
2
- declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
3
- size: {
4
- type: PropType<"small" | "normal">;
5
- default: string;
6
- };
7
- footer_text: {
8
- type: StringConstructor;
9
- };
10
- }>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
11
- size: {
12
- type: PropType<"small" | "normal">;
13
- default: string;
14
- };
15
- footer_text: {
16
- type: StringConstructor;
17
- };
18
- }>> & Readonly<{}>, {
19
- size: "small" | "normal";
20
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
21
- export default _default;
@@ -1,36 +0,0 @@
1
- import { PropType } from 'vue';
2
- declare function __VLS_template(): {
3
- attrs: Partial<{}>;
4
- slots: {
5
- content?(_: {}): any;
6
- };
7
- refs: {};
8
- rootEl: HTMLElement;
9
- };
10
- type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
11
- declare const __VLS_component: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
12
- closeFn: {
13
- type: PropType<(event: Event) => void>;
14
- required: true;
15
- };
16
- title: {
17
- type: StringConstructor;
18
- required: false;
19
- };
20
- }>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
21
- closeFn: {
22
- type: PropType<(event: Event) => void>;
23
- required: true;
24
- };
25
- title: {
26
- type: StringConstructor;
27
- required: false;
28
- };
29
- }>> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLElement>;
30
- declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
31
- export default _default;
32
- type __VLS_WithTemplateSlots<T, S> = T & {
33
- new (): {
34
- $slots: S;
35
- };
36
- };
@@ -1,40 +0,0 @@
1
- <template>
2
- <header class="flex justify-between items-center border-b-2 p-2 pl-4 pb-0">
3
- <slot name="content">
4
- <span class="text-gray-500 dark:text-gray-200 text-xl font-roboto font-medium">
5
- {{ props.title }}
6
- </span>
7
- </slot>
8
- <Button
9
- icon="pi pi-times"
10
- severity="secondary"
11
- text
12
- rounded
13
- aria-label="Cancel"
14
- @click="props.closeFn"
15
- >
16
- <template #icon>
17
- <IconClose />
18
- </template>
19
- </Button>
20
- </header>
21
- </template>
22
-
23
- <script setup lang="ts">
24
- import Button from 'primevue/button';
25
- import IconClose from '../IconClose.vue';
26
- import { PropType } from 'vue';
27
-
28
- const props = defineProps({
29
- closeFn: {
30
- type: Function as PropType<(event: Event) => void>,
31
- required: true,
32
- },
33
- title: {
34
- type: String,
35
- required: false,
36
- },
37
- });
38
- </script>
39
-
40
- <style scoped></style>