vue-wiguet-chatweb 0.0.8 → 0.0.10

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.0.8",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -30,15 +30,19 @@
30
30
  "preview": "vite preview"
31
31
  },
32
32
  "dependencies": {
33
+ "@vueuse/core": "^10.9.0",
33
34
  "axios": "^1.4.0",
34
- "moment": "^2.29.4",
35
+ "luxon": "^3.4.4",
35
36
  "socket.io-client": "^4.7.2",
37
+ "uuid": "^9.0.1",
36
38
  "vite-plugin-dts": "^3.4.0",
37
39
  "vue": "^3.3.4",
38
40
  "vue-rabbit-frontend": "^0.0.15"
39
41
  },
40
42
  "devDependencies": {
43
+ "@types/luxon": "^3.4.2",
41
44
  "@types/node": "^20.4.6",
45
+ "@types/uuid": "^9.0.8",
42
46
  "@vitejs/plugin-vue": "^4.2.3",
43
47
  "typescript": "^5.0.2",
44
48
  "vite": "^4.4.5",
@@ -1,153 +1,269 @@
1
1
  <template>
2
- <div class="widget">
3
- <div class="header-widget">
4
- <h4 class="title-chat">{{ titlePrincipal }}</h4>
5
- <IconClose class="pointer" @click="toggleChat()"/>
6
- </div>
7
- <div class="messages-container" id="messages-container" ref="messagesContainer">
8
- <chat-message class="message" v-for="(message, index) in messages" :key="index" :message="message">
9
- </chat-message>
10
- </div>
11
- <div class="input-message w-full">
12
- <form class="message-send" @submit.prevent="submitMessage">
13
- <div class="form-message">
14
- <button type="submit">
15
- <IconUser/>
16
- </button>
17
- <div class="jl-inputgroup-chat">
18
- <input v-model="message" class="jl2-input-chat" required/>
19
- <button type="submit" class="pointer">
20
- <IconSend/>
21
- </button>
22
- </div>
23
- </div>
24
- </form>
2
+ <div class="widget">
3
+ <div class="header-widget">
4
+ <h4 class="title-chat">{{ titlePrincipal }}</h4>
5
+ <button @click="() => toggleChat()" class="btn-close">
6
+ <IconClose class="pointer" />
7
+ </button>
8
+ </div>
9
+ <div class="messages-container" ref="messageContainerRef">
10
+ <div class="loader" v-if="isLoading">
11
+ <Loader />
12
+ </div>
13
+ <MessageList
14
+ :messages="messagesData.data"
15
+ :canLoadMoreMessages="messagesData.canLoadMoreMessages"
16
+ @loadMore="getMessages"
17
+ @retry="retryMessage"
18
+ />
19
+ </div>
20
+
21
+ <div class="input-message w-full">
22
+ <form class="message-send" @submit.prevent="submitMessage">
23
+ <div class="form-message">
24
+ <!-- <button type="submit">
25
+ <IconUser />
26
+ </button> -->
27
+ <div class="jl-inputgroup-chat">
28
+ <input v-model="message" class="jl2-input-chat" required />
29
+ <button type="submit" class="pointer btn-primary">
30
+ <IconSend style="width: 20px; height: 20px" />
31
+ </button>
32
+ </div>
25
33
  </div>
34
+ </form>
26
35
  </div>
36
+ </div>
27
37
  </template>
28
38
 
29
39
  <script setup lang="ts">
30
- import { ref, onMounted, computed } from 'vue';
31
- import ChatMessage from './ChatMessage.vue';
32
- //import { useStore } from 'vuex';
33
- import { RabbitMQService } from 'vue-rabbit-frontend'
34
- import IconClose from './IconClose.vue'
35
- import IconSend from './IconSend.vue'
36
- import IconUser from './IconUser.vue'
37
- import {syncVirtualHostCentral,sendMessageByAppAndPhone,messagesByAppAndPhone} from '../store'
38
- ///STORE
39
- //const store = useStore()
40
+ import { ref, onMounted, nextTick, PropType, watch } from "vue";
41
+ import { v4 as uuidv4 } from "uuid";
42
+
43
+ import { RabbitMQService } from "vue-rabbit-frontend";
44
+ import {
45
+ type SendMessageBody,
46
+ ListMessageBody,
47
+ Message,
48
+ RABBIT_EVENTS,
49
+ } from "../dto/app.dto";
50
+ import IconClose from "./IconClose.vue";
51
+ import IconSend from "./IconSend.vue";
52
+ // import IconUser from "./IconUser.vue";
53
+ import { getMessagesApi, sendMessageApi } from "../store/index";
54
+ import { getInformationApi } from "../store";
55
+ import MessageList from "./MessageList.vue";
56
+ import Loader from "./Loader.vue";
57
+ import { searchFromLast } from "../resources/functions.helpers";
58
+
40
59
  //DATA
41
- const app = ref('webchat')
42
- // const deviceSelect = ref('76177719')
43
- const rabbitMQServiceListen:any = ref(null);
44
- const message:any = ref(null);
45
- const messages:any = ref([]);
46
- const virtualHost:any = ref({virtualhost:''});
60
+ const rabbitMQServiceListen: any = ref(null);
61
+ const message = ref("");
62
+
63
+ const messagesData = ref<{ data: Message[]; canLoadMoreMessages: boolean }>({
64
+ data: [],
65
+ canLoadMoreMessages: false,
66
+ });
67
+ const virtualHost: any = ref({ virtualhost: "" });
68
+ const appChatId = ref("");
69
+ const isLoading = ref(false);
70
+
71
+ const emit = defineEmits([
72
+ "show-toast",
73
+ "show-confirm",
74
+ "new-message",
75
+ "clear-new-messages",
76
+ ]);
47
77
 
48
78
  const props = defineProps({
49
- titlePrincipal:{ type: String,default:'Comunicación en linea para consultas' },
50
- toggleChat:{ type: Function, required: true },
51
- tokenAuth:{ type: String, required: true },
52
- phoneUser:{ type: String, required: true }
79
+ titlePrincipal: {
80
+ type: String,
81
+ default: "Comunicación en linea para consultas",
82
+ },
83
+ toggleChat: { type: Function, required: true },
84
+ tokenAuth: { type: String, required: true },
85
+ user: {
86
+ type: Object as PropType<{
87
+ nombreCompleto: string;
88
+ ci: string;
89
+ msPersonaId: number;
90
+ }>,
91
+ required: true,
92
+ },
93
+ visible: { type: Boolean, required: true },
53
94
  });
54
95
 
55
- const removeNumPrefix = (num:string) => {
56
- if (num.startsWith("591") && num.length === 11) {
57
- return num.substring(3);
58
- }
59
- return num;
60
- }
96
+ const messageContainerRef = ref<HTMLElement | null>(null);
61
97
 
62
- const messagesContainer = ref<HTMLElement | null>(null);
98
+ watch(
99
+ () => props.visible,
100
+ async (current) => {
101
+ if (current) {
102
+ emit("clear-new-messages");
103
+ if (!virtualHost.value || !appChatId.value) {
104
+ const resp = await getInformationApi(props.tokenAuth);
105
+ const data = resp.response.data;
106
+ virtualHost.value = data.virtualHost;
107
+ appChatId.value = data.appChat.id;
108
+ connectMsRabbit();
109
+ }
110
+ if (messagesData.value.data.length === 0) {
111
+ getMessages();
112
+ }
113
+ }
114
+ }
115
+ );
116
+ onMounted(async () => {
117
+ if (!virtualHost.value || !appChatId.value) {
118
+ const resp = await getInformationApi(props.tokenAuth);
119
+ const data = resp.response.data;
120
+ virtualHost.value = data.virtualHost;
121
+ appChatId.value = data.appChat.id;
122
+ connectMsRabbit();
123
+ }
124
+ });
63
125
 
64
- const scrollToBottom = () => {
65
- if (messagesContainer.value) {
66
- messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
126
+ const submitMessage = async (event: any) => {
127
+ event.preventDefault();
128
+ if (message.value?.length > 300) {
129
+ emit("show-toast", {
130
+ severity: "warn",
131
+ summary: "Error",
132
+ detail: "El mensaje no puede superar los 300 caracteres",
133
+ life: 5000,
134
+ });
135
+ return;
136
+ }
137
+ if (!message.value.trim()) {
138
+ emit("show-toast", {
139
+ severity: "warn",
140
+ summary: "Error",
141
+ detail: "Por favor ingrese un mensaje",
142
+ life: 5000,
143
+ });
144
+ return;
145
+ }
146
+ const newMessage = {
147
+ id: uuidv4(),
148
+ message: message.value,
149
+ visto: true,
150
+ multimedia: false,
151
+ esCliente: true,
152
+ appChatId: appChatId.value,
153
+ createdAt: new Date().toISOString(),
154
+ updatedAt: new Date().toISOString(),
155
+ sender: {
156
+ nombreCompleto: props.user.nombreCompleto,
157
+ ci: props.user.ci,
158
+ msPersonaId: props.user.msPersonaId,
159
+ },
160
+ };
161
+ const idx = messagesData.value.data.push(newMessage) - 1;
162
+ try {
163
+ const newMsg = await sendApi(message.value, appChatId.value);
164
+ messagesData.value.data[idx] = newMsg.response.data;
165
+ } catch (error) {
166
+ messagesData.value.data[idx].error = {
167
+ error: true,
168
+ id: newMessage.id,
169
+ };
170
+ emit("show-toast", {
171
+ severity: "error",
172
+ summary: "Error",
173
+ detail: "Ocurrio un error al enviar el mensaje, intente nuevamente",
174
+ life: 5000,
175
+ });
176
+ } finally {
177
+ message.value = "";
178
+ scrollToBottom();
67
179
  }
68
180
  };
69
181
 
70
- const submitMessage = async (event:any) => {
71
- event.preventDefault();
72
- //const {virtualHost} = store.state
73
- const {virtualhost} = virtualHost.value
74
- // await store.dispatch('sendMessageByAppAndPhone',{
75
- // token:props.tokenAuth,
76
- // data:{
77
- // virtualHost:virtualHost.value,
78
- // queue:`widget_chat_${sessionStorage.getItem('tabBrowser')}`,//`${selectDetailtChat.value.app}_${queueUniqueUser.value}`,
79
- // device:'00000000',
80
- // phone:removeNumPrefix(deviceSelect.value),
81
- // app:app.value,
82
- // message: message.value,
83
- // widget:true
84
- // }
85
- // })
86
- await sendMessageByAppAndPhone({
87
- token:props.tokenAuth,
88
- data:{
89
- virtualHost:virtualhost,
90
- queue:`widget_chat_${sessionStorage.getItem('tabBrowser')}`,//`${selectDetailtChat.value.app}_${queueUniqueUser.value}`,
91
- device:'00000000',
92
- phone:removeNumPrefix(props.phoneUser),
93
- app:app.value,
94
- message: message.value,
95
- widget:true
96
- }
97
- })
98
- message.value = null
99
- scrollToBottom()
100
- }
182
+ const sendApi = async (message: string, appChatId: string) => {
183
+ const body: SendMessageBody = {
184
+ esCliente: true,
185
+ message,
186
+ appChatId,
187
+ };
188
+ return sendMessageApi(body, props.tokenAuth);
189
+ };
101
190
 
191
+ const getMessages = async () => {
192
+ try {
193
+ isLoading.value = true;
194
+ const lastMessagesId = messagesData.value.data[0]?.id;
195
+ const body: ListMessageBody = {
196
+ limit: 10,
197
+ lastMessagesId,
198
+ appChatId: appChatId.value,
199
+ };
102
200
 
103
- const getMessagesByApp = async (param:{resetConnection:boolean}) => {
104
- if(param.resetConnection)
105
- disconnectMsRabbit();
106
- connectMsRabbit();
107
- // const messagesParse = [];
108
- // await store.dispatch('messagesByAppAndPhone', {
109
- // token:props.tokenAuth,
110
- // data:{
111
- // phone:props.phoneUser,
112
- // app:app.value,
113
- // device:null
114
- // }
115
- // });
116
- const getMessages = await messagesByAppAndPhone({
117
- token:props.tokenAuth,
118
- data:{
119
- phone:props.phoneUser,
120
- app:app.value,
121
- device:null
201
+ const resp = await getMessagesApi({ body, token: props.tokenAuth });
202
+ messagesData.value.data.unshift(
203
+ ...resp.data.sort((a, b) => -b.createdAt.localeCompare(a.createdAt))
204
+ );
205
+ messagesData.value.canLoadMoreMessages =
206
+ resp.pagination.total > resp.pagination.limit;
207
+ if (lastMessagesId && messageContainerRef.value?.scrollHeight) {
208
+ mantainElementsOnViewport(messageContainerRef.value?.scrollHeight);
122
209
  }
123
- });
124
- //const messagesData = store.getters.get_messages_chat;
125
- const messagesData = getMessages
126
- if (messagesData) {
127
-
128
- messages.value = messagesData;
129
-
130
- // for (const msg of messagesData) {
131
- // messagesParse.push(...msg.messages);
132
- // }
133
- // messages.value = messagesParse;
210
+ if (!lastMessagesId) scrollToBottom();
211
+ } catch (error) {
212
+ isLoading.value = false;
213
+ throw error;
214
+ } finally {
215
+ isLoading.value = false;
134
216
  }
135
217
  };
136
218
 
137
- const connectMsRabbit = (app:any = 'webchat') => {
138
- //const {virtualHost} = store.state
139
- const {virtualhost} = virtualHost.value
140
- let data = sessionStorage.getItem('tabBrowser');
141
- if(!data){
142
- let tab = Date.now();
143
- sessionStorage.setItem('tabBrowser', `${tab}`)
219
+ const retryMessage = async (message: Message) => {
220
+ emit("show-confirm", async () => {
221
+ try {
222
+ if (!message.error?.id) return;
223
+ const msg = await sendApi(message.message, appChatId.value);
224
+ const idx = searchFromLast<Message>(
225
+ messagesData.value.data,
226
+ "id",
227
+ message.error.id
228
+ );
229
+ messagesData.value.data[idx] = { ...msg.response.data, error: undefined };
230
+ } catch (error) {
231
+ emit("show-toast", {
232
+ severity: "error",
233
+ summary: "Error",
234
+ detail: "Ocurrio un error al enviar el mensaje, intente nuevamente",
235
+ life: 5000,
236
+ });
237
+ } finally {
238
+ scrollToBottom();
239
+ }
240
+ });
241
+ };
242
+
243
+ const connectMsRabbit = (app: any = "webchat") => {
244
+ const { virtualhost } = virtualHost.value;
245
+ let data = sessionStorage.getItem("tabBrowser");
246
+ if (!data) {
247
+ let tab = Date.now();
248
+ sessionStorage.setItem("tabBrowser", `${tab}`);
144
249
  }
145
- rabbitMQServiceListen.value = new RabbitMQService(`${virtualhost}`,`widget_chat_${sessionStorage.getItem('tabBrowser')}`);
250
+ rabbitMQServiceListen.value = new RabbitMQService(
251
+ `${virtualhost}`,
252
+ `widget_chat_${sessionStorage.getItem("tabBrowser")}`
253
+ );
146
254
  rabbitMQServiceListen.value.connect();
147
- rabbitMQServiceListen.value.subscribe(async () => {
148
- await getMessagesByApp({resetConnection:false});
149
- const objDiv = document.getElementById("messages-container") as HTMLElement;
150
- objDiv.scrollTop = objDiv.scrollHeight;
255
+
256
+ rabbitMQServiceListen.value.subscribe(async (val: any) => {
257
+ if (
258
+ val.event &&
259
+ val.event === RABBIT_EVENTS.NEW_MESSAGE &&
260
+ !val.message.esCliente
261
+ ) {
262
+ messagesData.value.data.push(val.message);
263
+ if (props.visible === false) {
264
+ emit("new-message");
265
+ }
266
+ }
151
267
  });
152
268
  };
153
269
 
@@ -155,26 +271,54 @@ const disconnectMsRabbit = () => {
155
271
  rabbitMQServiceListen.value?.disconnectClient();
156
272
  };
157
273
 
158
- onMounted(async () => {
159
- await getMessagesByApp({resetConnection:false})
160
- const virtualHostUniqueUser = `widget_chat_${props.phoneUser}`
161
- // await store.dispatch('syncVirtualHostCentral',{
162
- // token:props.tokenAuth,
163
- // data:{
164
- // virtualHost:virtualHostUniqueUser
165
- // }
166
- // })
167
- virtualHost.value = await syncVirtualHostCentral({
168
- token:props.tokenAuth,
169
- data:{
170
- virtualHost:virtualHostUniqueUser
274
+ const scrollToBottom = () => {
275
+ nextTick(() => {
276
+ if (messageContainerRef.value) {
277
+ messageContainerRef.value.scrollTop =
278
+ messageContainerRef.value.scrollHeight;
171
279
  }
172
- })
173
- connectMsRabbit()
174
- scrollToBottom()
175
- });
280
+ });
281
+ };
282
+
283
+ const mantainElementsOnViewport = (scrollHeightBeforeAdd: number) => {
284
+ nextTick(() => {
285
+ const objDiv = messageContainerRef.value;
286
+ if (objDiv) {
287
+ objDiv.scrollTop = objDiv.scrollHeight - scrollHeightBeforeAdd;
288
+ }
289
+ });
290
+ };
176
291
  </script>
177
292
 
178
293
  <style scoped>
179
- @import url(../style.css);
294
+ @import url(../style.css);
295
+ .btn-primary {
296
+ padding: 10px 12px;
297
+ &:hover {
298
+ background-color: rgb(242, 139, 12, 0.1);
299
+ }
300
+ }
301
+
302
+ .btn-close {
303
+ padding: 0;
304
+ background-color: transparent;
305
+ border: none;
306
+ display: flex;
307
+ align-items: center;
308
+ border-radius: 50%;
309
+ &:hover {
310
+ background-color: rgba(202, 202, 202, 0.534);
311
+ }
312
+ }
313
+
314
+ .messages-container {
315
+ position: relative;
316
+ }
317
+ .loader {
318
+ position: absolute;
319
+ top: 18px;
320
+ z-index: 5;
321
+ left: 50%;
322
+ transform: translate(-50%, -50%);
323
+ }
180
324
  </style>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
3
+ <path
4
+ fill="rgb(248 113 113)"
5
+ d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"
6
+ />
7
+ </svg>
8
+ </template>
9
+
10
+ <script setup lang="ts"></script>
11
+
12
+ <style scoped></style>
@@ -1,6 +1,6 @@
1
- <template>
2
- <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M16 31C24.2843 31 31 24.2843 31 16C31 7.71573 24.2843 1 16 1C7.71573 1 1 7.71573 1 16C1 24.2843 7.71573 31 16 31Z" stroke="#B3B3B3" stroke-miterlimit="10"/>
4
- <path d="M19.78 22.3L15.98 16.63H15.9601L12.2001 22.3H11.0701L15.37 15.8899L11.25 9.69995H12.38L15.98 15.08H16L19.67 9.69995H20.8L16.55 15.82L20.92 22.3H19.77H19.78Z" fill="#B3B3B3"/>
5
- </svg>
1
+ <template>
2
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M16 31C24.2843 31 31 24.2843 31 16C31 7.71573 24.2843 1 16 1C7.71573 1 1 7.71573 1 16C1 24.2843 7.71573 31 16 31Z" stroke="#B3B3B3" stroke-miterlimit="10"/>
4
+ <path d="M19.78 22.3L15.98 16.63H15.9601L12.2001 22.3H11.0701L15.37 15.8899L11.25 9.69995H12.38L15.98 15.08H16L19.67 9.69995H20.8L16.55 15.82L20.92 22.3H19.77H19.78Z" fill="#B3B3B3"/>
5
+ </svg>
6
6
  </template>
@@ -1,6 +1,8 @@
1
- <template>
2
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M11.9301 23.25C5.65005 23.25 0.550049 18.14 0.550049 11.87C0.550049 5.6 5.66005 0.48999 11.9301 0.48999C18.2001 0.48999 23.3101 5.6 23.3101 11.87C23.3101 18.14 18.2001 23.25 11.9301 23.25ZM11.9301 1.10999C6.00005 1.10999 1.17004 5.94 1.17004 11.87C1.17004 17.8 6.00005 22.63 11.9301 22.63C17.8601 22.63 22.6899 17.8 22.6899 11.87C22.6899 5.94 17.8601 1.10999 11.9301 1.10999Z" fill="#F28B0C"/>
4
- <path d="M8.68994 17.0601C8.63994 17.0601 8.58004 17.0501 8.54004 17.0201C8.44004 16.9701 8.38 16.8601 8.38 16.7501V6.99007C8.38 6.88007 8.44004 6.78005 8.54004 6.72005C8.64004 6.66005 8.74998 6.67005 8.84998 6.72005L17.1899 11.6001C17.2799 11.6601 17.34 11.7601 17.34 11.8701C17.34 11.9801 17.2799 12.0801 17.1899 12.1401L8.84998 17.0201C8.84998 17.0201 8.74994 17.0601 8.68994 17.0601ZM9 7.5301V16.21L16.42 11.8701L9 7.5301Z" fill="#F28B0C"/>
5
- </svg>
6
- </template>
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
3
+ <path
4
+ fill="#F28B0C"
5
+ d="M16.1 260.2c-22.6 12.9-20.5 47.3 3.6 57.3L160 376V479.3c0 18.1 14.6 32.7 32.7 32.7c9.7 0 18.9-4.3 25.1-11.8l62-74.3 123.9 51.6c18.9 7.9 40.8-4.5 43.9-24.7l64-416c1.9-12.1-3.4-24.3-13.5-31.2s-23.3-7.5-34-1.4l-448 256zm52.1 25.5L409.7 90.6 190.1 336l1.2 1L68.2 285.7zM403.3 425.4L236.7 355.9 450.8 116.6 403.3 425.4z"
6
+ />
7
+ </svg>
8
+ </template>
@@ -1,6 +1,6 @@
1
- <template>
2
- <svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M21.04 40.9399C32.0857 40.9399 41.04 31.9856 41.04 20.9399C41.04 9.89419 32.0857 0.939941 21.04 0.939941C9.99428 0.939941 1.03998 9.89419 1.03998 20.9399C1.03998 31.9856 9.99428 40.9399 21.04 40.9399Z" stroke="#F28B0C" stroke-width="1.5" stroke-miterlimit="10"/>
4
- <path d="M22.87 20.36C24.92 19.57 26.38 17.5801 26.38 15.2601C26.38 12.2401 23.93 9.79004 20.91 9.79004C17.89 9.79004 15.44 12.2401 15.44 15.2601C15.44 17.6301 16.95 19.6401 19.06 20.4001C16.15 21.2601 14.02 23.9501 14.02 27.1301V31.7701C14.02 31.9401 14.16 32.08 14.33 32.08H27.76C27.93 32.08 28.07 31.9401 28.07 31.7701V27.1301C28.07 23.8901 25.86 21.16 22.87 20.36Z" fill="#F28B0C"/>
5
- </svg>
1
+ <template>
2
+ <svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M21.04 40.9399C32.0857 40.9399 41.04 31.9856 41.04 20.9399C41.04 9.89419 32.0857 0.939941 21.04 0.939941C9.99428 0.939941 1.03998 9.89419 1.03998 20.9399C1.03998 31.9856 9.99428 40.9399 21.04 40.9399Z" stroke="#F28B0C" stroke-width="1.5" stroke-miterlimit="10"/>
4
+ <path d="M22.87 20.36C24.92 19.57 26.38 17.5801 26.38 15.2601C26.38 12.2401 23.93 9.79004 20.91 9.79004C17.89 9.79004 15.44 12.2401 15.44 15.2601C15.44 17.6301 16.95 19.6401 19.06 20.4001C16.15 21.2601 14.02 23.9501 14.02 27.1301V31.7701C14.02 31.9401 14.16 32.08 14.33 32.08H27.76C27.93 32.08 28.07 31.9401 28.07 31.7701V27.1301C28.07 23.8901 25.86 21.16 22.87 20.36Z" fill="#F28B0C"/>
5
+ </svg>
6
6
  </template>