mirror of
https://github.com/EvolutionAPI/evolution-manager.git
synced 2025-12-23 13:02:13 -06:00
muita coisa
This commit is contained in:
59
src/components/instance/InstanceBody.vue
Normal file
59
src/components/instance/InstanceBody.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<v-tabs>
|
||||
<v-tab v-for="tab in tabs" :key="tab.id" :value="tab.id">
|
||||
<v-icon start>{{ tab.icon }}</v-icon>
|
||||
{{ tab.title }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-window v-model="tab">
|
||||
<v-window-item v-for="tab in tabs" :key="tab.id" :value="tab.id" >
|
||||
<div class="d-flex flex-column gap-8">
|
||||
|
||||
<component
|
||||
v-for="component in tab.components"
|
||||
:key="component"
|
||||
:is="component"
|
||||
:instance="instance"
|
||||
/>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Webhook from "./settings/webhook.vue";
|
||||
import Websocket from "./settings/Websocket.vue";
|
||||
import Rabbitmq from "./settings/Rabbitmq.vue";
|
||||
import Chatwoot from "./settings/Chatwoot.vue";
|
||||
import Typebot from "./settings/Typebot.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Webhook,
|
||||
Websocket,
|
||||
Rabbitmq,
|
||||
Chatwoot,
|
||||
Typebot,
|
||||
},
|
||||
data: () => ({
|
||||
tab: "settings",
|
||||
tabs: [
|
||||
{
|
||||
id: "settings",
|
||||
icon: "mdi-cog",
|
||||
title: "Configurações",
|
||||
components: ["Webhook", "Websocket", "Rabbitmq", "Chatwoot", "Typebot"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
99
src/components/instance/InstanceHeader.vue
Normal file
99
src/components/instance/InstanceHeader.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<v-card
|
||||
variant="outlined"
|
||||
class="d-flex align-center gap-4 pa-2"
|
||||
rounded="xl"
|
||||
>
|
||||
<v-avatar size="100" rounded="xl">
|
||||
<v-icon
|
||||
v-if="
|
||||
instance.instance.status != 'open' &&
|
||||
statusMapper[instance.instance.status].icon
|
||||
"
|
||||
size="70"
|
||||
>
|
||||
{{ statusMapper[instance.instance.status].icon }}
|
||||
</v-icon>
|
||||
<v-img
|
||||
v-else
|
||||
:src="
|
||||
instance.instance.profilePictureUrl ||
|
||||
'https://cdn.vuetifyjs.com/images/lists/1.jpg'
|
||||
"
|
||||
/>
|
||||
</v-avatar>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="text-overline" style="line-height: 1em">
|
||||
{{ owner }}
|
||||
</span>
|
||||
<h2 class="mb-0">{{ instance.instance.instanceName }}</h2>
|
||||
<small>{{ instance.instance.profileStatus }}</small>
|
||||
</div>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
@click="disconnectInstance"
|
||||
:disabled="instance.instance.status === 'close'"
|
||||
:loading="disconnect.loading"
|
||||
variant="tonal"
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
<v-icon start>mdi-cellphone-nfc-off</v-icon>
|
||||
{{ disconnect.confirm ? "Tem Certeza?" : "Desconectar" }}
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useAppStore } from "@/store/app";
|
||||
import statusMapper from "@/helpers/mappers/status";
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceHeader",
|
||||
data: () => ({
|
||||
disconnect: {
|
||||
confirm: false,
|
||||
loading: false,
|
||||
},
|
||||
statusMapper: statusMapper,
|
||||
AppStore: useAppStore(),
|
||||
|
||||
}),
|
||||
methods: {
|
||||
async disconnectInstance() {
|
||||
if (!this.disconnect.confirm) return (this.disconnect.confirm = true);
|
||||
|
||||
this.disconnect.loading = true;
|
||||
try {
|
||||
this.disconnect.confirm = false;
|
||||
await instanceController.logout(this.instance.instance.instanceName);
|
||||
await this.AppStore.reconnect();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert(e.message || e.error || "Erro desconhecido");
|
||||
} finally {
|
||||
this.disconnect.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
owner() {
|
||||
if (!this.instance?.instance?.owner)
|
||||
return (
|
||||
this.statusMapper[this.instance.instance.status]?.text ||
|
||||
"Desconhecido"
|
||||
);
|
||||
return (this.instance?.instance?.owner || "").split("@")[0];
|
||||
},
|
||||
},
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
220
src/components/instance/settings/Chatwoot.vue
Normal file
220
src/components/instance/settings/Chatwoot.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<v-card variant="outlined" :loading="loading">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon start>mdi-chat</v-icon>
|
||||
Chatwoot
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
size="small"
|
||||
icon
|
||||
:disabled="loading"
|
||||
variant="tonal"
|
||||
@click="expanded = !expanded"
|
||||
:style="{ transform: expanded ? 'rotate(180deg)' : '' }"
|
||||
>
|
||||
<v-icon>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="expanded">
|
||||
<v-alert v-if="error" type="error" class="mb-3">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-form v-model="valid">
|
||||
<v-text-field
|
||||
v-model="chatwootData.url"
|
||||
label="URL"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(url) => {
|
||||
if (!url) return 'URL é obrigatório';
|
||||
if (!url.startsWith('http'))
|
||||
return 'URL deve começar com http ou https';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
||||
<div class="d-flex gap-4 flex-wrap">
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="chatwootData.account_id"
|
||||
label="ID da conta"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(account_id) => {
|
||||
if (!account_id) return 'ID da conta é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="chatwootData.token"
|
||||
label="Token da conta"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(token) => {
|
||||
if (!token) return 'Token é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-x-4 flex-wrap">
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="chatwootData.sign_msg"
|
||||
label="Assinar mensagens"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="chatwootData.reopen_conversation"
|
||||
label="Reabrir conversa"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="chatwootData.conversation_pending"
|
||||
label="Conversa pendente"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="expanded">
|
||||
<v-switch
|
||||
v-model="chatwootData.enabled"
|
||||
label="Habilitado"
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
JSON.stringify(chatwootData) === JSON.stringify(defaultChatwootData)
|
||||
"
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@click="saveChatwoot"
|
||||
variant="tonal"
|
||||
>
|
||||
Salvar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceChatwoot",
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
expanded: false,
|
||||
loading: false,
|
||||
error: false,
|
||||
valid: false,
|
||||
chatwootData: {
|
||||
enabled: false,
|
||||
url: "",
|
||||
account_id: "",
|
||||
token: "",
|
||||
sign_msg: true,
|
||||
reopen_conversation: true,
|
||||
conversation_pending: false,
|
||||
},
|
||||
defaultChatwootData: {
|
||||
enabled: false,
|
||||
url: "",
|
||||
account_id: "",
|
||||
token: "",
|
||||
sign_msg: true,
|
||||
reopen_conversation: true,
|
||||
conversation_pending: false,
|
||||
},
|
||||
}),
|
||||
|
||||
methods: {
|
||||
async saveChatwoot() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
await instanceController.chatwoot.set(
|
||||
this.instance.instance.instanceName,
|
||||
{
|
||||
...this.chatwootData,
|
||||
url: this.chatwootData.url.trim().replace(/\/$/, ""),
|
||||
}
|
||||
);
|
||||
this.defaultChatwootData = Object.assign({}, this.chatwootData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadChatwoot() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
const chatwootData = await instanceController.chatwoot.get(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
this.chatwootData = Object.assign({}, chatwootData);
|
||||
this.defaultChatwootData = Object.assign({}, chatwootData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
expanded(expanded) {
|
||||
if (expanded) this.loadChatwoot();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
154
src/components/instance/settings/Rabbitmq.vue
Normal file
154
src/components/instance/settings/Rabbitmq.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<v-card variant="outlined" :loading="loading">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon start>mdi-rabbit</v-icon>
|
||||
RabbitMQ
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
size="small"
|
||||
icon
|
||||
:disabled="loading"
|
||||
variant="tonal"
|
||||
@click="expanded = !expanded"
|
||||
:style="{ transform: expanded ? 'rotate(180deg)' : '' }"
|
||||
>
|
||||
<v-icon>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="expanded">
|
||||
<v-alert v-if="error" type="error" class="mb-3">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-form v-model="valid">
|
||||
<v-select
|
||||
:items="rabbitmqEventsType"
|
||||
v-model="rabbitmqData.events"
|
||||
:disabled="loading"
|
||||
label="Eventos"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
multiple
|
||||
outlined
|
||||
dense
|
||||
chips
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="expanded">
|
||||
<v-switch
|
||||
v-model="rabbitmqData.enabled"
|
||||
label="Habilitado"
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
JSON.stringify(rabbitmqData) === JSON.stringify(defaultRabbitmqData)
|
||||
"
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@click="saveRabbitmq"
|
||||
variant="tonal"
|
||||
>
|
||||
Salvar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceRabbitmq",
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
expanded: false,
|
||||
loading: false,
|
||||
error: false,
|
||||
valid: false,
|
||||
rabbitmqData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
},
|
||||
defaultRabbitmqData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
},
|
||||
rabbitmqEventsType: [
|
||||
"APPLICATION_STARTUP",
|
||||
"QRCODE_UPDATED",
|
||||
"MESSAGES_SET",
|
||||
"MESSAGES_UPSERT",
|
||||
"MESSAGES_UPDATE",
|
||||
"MESSAGES_DELETE",
|
||||
"SEND_MESSAGE",
|
||||
"CONTACTS_SET",
|
||||
"CONTACTS_UPSERT",
|
||||
"CONTACTS_UPDATE",
|
||||
"PRESENCE_UPDATE",
|
||||
"CHATS_SET",
|
||||
"CHATS_UPSERT",
|
||||
"CHATS_UPDATE",
|
||||
"CHATS_DELETE",
|
||||
"GROUPS_UPSERT",
|
||||
"GROUP_UPDATE",
|
||||
"GROUP_PARTICIPANTS_UPDATE",
|
||||
"CONNECTION_UPDATE",
|
||||
"CALL",
|
||||
"NEW_JWT_TOKEN",
|
||||
],
|
||||
}),
|
||||
|
||||
methods: {
|
||||
async saveRabbitmq() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
await instanceController.rabbitmq.set(
|
||||
this.instance.instance.instanceName,
|
||||
this.rabbitmqData
|
||||
);
|
||||
this.defaultRabbitmqData = Object.assign({}, this.rabbitmqData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadRabbitmq() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
const rabbitmqData = await instanceController.rabbitmq.get(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
this.rabbitmqData = Object.assign({}, rabbitmqData);
|
||||
this.defaultRabbitmqData = Object.assign({}, rabbitmqData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadRabbitmq();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
252
src/components/instance/settings/Typebot.vue
Normal file
252
src/components/instance/settings/Typebot.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<v-card variant="outlined" :loading="loading">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon start>mdi-robot-happy</v-icon>
|
||||
Typebot
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
size="small"
|
||||
icon
|
||||
:disabled="loading"
|
||||
variant="tonal"
|
||||
@click="expanded = !expanded"
|
||||
:style="{ transform: expanded ? 'rotate(180deg)' : '' }"
|
||||
>
|
||||
<v-icon>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="expanded">
|
||||
<v-alert v-if="error" type="error" class="mb-3">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-form v-model="valid">
|
||||
<v-text-field
|
||||
v-model="typebotData.url"
|
||||
label="URL"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(url) => {
|
||||
if (!url) return 'URL é obrigatório';
|
||||
if (!url.startsWith('http'))
|
||||
return 'URL deve começar com http ou https';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
||||
<div class="d-flex gap-4 flex-wrap">
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="typebotData.typebot"
|
||||
label="Typebot"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(account_id) => {
|
||||
if (!account_id) return 'Typebot é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="typebotData.keyword_finish"
|
||||
label="Palavra-chave de finalização"
|
||||
placeholder="#SAIR"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(token) => {
|
||||
if (!token)
|
||||
return 'Palavra-chave de finalização é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-4 flex-wrap">
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="typebotData.expire"
|
||||
label="Tempo de expiração (em minutos)"
|
||||
:disabled="loading"
|
||||
type="number"
|
||||
min="0"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(v) => {
|
||||
if (!v) return 'Tempo de expiração é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<v-text-field
|
||||
v-model="typebotData.delay_message"
|
||||
label="Tempo de atraso da mensagem (em milisegundos)"
|
||||
type="number"
|
||||
min="0"
|
||||
:hint="`${typebotData.delay_message}ms = ${(
|
||||
typebotData.delay_message / 1000
|
||||
)
|
||||
.toFixed(1)
|
||||
.replace('.0', '')}s`"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(token) => {
|
||||
if (token == null || token < 0)
|
||||
return 'Palavra-chave de finalização é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="typebotData.unknown_message"
|
||||
label="Mensagem de desconhecido"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(token) => {
|
||||
if (!token) return 'Mensagem de desconhecido é obrigatório';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="expanded">
|
||||
<v-switch
|
||||
v-model="typebotData.enabled"
|
||||
label="Habilitado"
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
JSON.stringify(typebotData) === JSON.stringify(defaultTypebotData)
|
||||
"
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@click="saveTypebot"
|
||||
variant="tonal"
|
||||
>
|
||||
Salvar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceTypebot",
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
expanded: false,
|
||||
loading: false,
|
||||
error: false,
|
||||
valid: false,
|
||||
typebotData: {
|
||||
enabled: false,
|
||||
expire: 0,
|
||||
sessions: [],
|
||||
typebot: "",
|
||||
url: "",
|
||||
keyword_finish: "",
|
||||
unknown_message: "",
|
||||
},
|
||||
defaultTypebotData: {
|
||||
enabled: false,
|
||||
expire: 0,
|
||||
sessions: [],
|
||||
typebot: "",
|
||||
url: "",
|
||||
keyword_finish: "",
|
||||
unknown_message: "",
|
||||
},
|
||||
}),
|
||||
|
||||
methods: {
|
||||
async saveTypebot() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
await instanceController.typebot.set(
|
||||
this.instance.instance.instanceName,
|
||||
{
|
||||
...this.typebotData,
|
||||
url: this.typebotData.url.trim().replace(/\/$/, ""),
|
||||
}
|
||||
);
|
||||
this.defaultTypebotData = Object.assign({}, this.typebotData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadTypebot() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
const typebotData = await instanceController.typebot.get(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
this.typebotData = Object.assign({}, typebotData);
|
||||
this.defaultTypebotData = Object.assign({}, typebotData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
expanded(expanded) {
|
||||
if (expanded) this.loadTypebot();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
204
src/components/instance/settings/Webhook.vue
Normal file
204
src/components/instance/settings/Webhook.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<v-card variant="outlined" :loading="loading">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon start>mdi-webhook</v-icon>
|
||||
Webhook
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
size="small"
|
||||
icon
|
||||
:disabled="loading"
|
||||
variant="tonal"
|
||||
@click="expanded = !expanded"
|
||||
:style="{ transform: expanded ? 'rotate(180deg)' : '' }"
|
||||
>
|
||||
<v-icon>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="expanded">
|
||||
<v-alert v-if="error" type="error" class="mb-3">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-form v-model="valid">
|
||||
<v-text-field
|
||||
v-model="webhookData.url"
|
||||
label="URL"
|
||||
:disabled="loading"
|
||||
outlined
|
||||
dense
|
||||
hide-details="auto"
|
||||
class="mb-3"
|
||||
:rules="[
|
||||
(url) => {
|
||||
if (!url) return 'URL é obrigatório';
|
||||
if (!url.startsWith('http'))
|
||||
return 'URL deve começar com http ou https';
|
||||
return true;
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
||||
<v-select
|
||||
:items="webhookEventsType"
|
||||
v-model="webhookData.events"
|
||||
:disabled="loading"
|
||||
label="Eventos"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
multiple
|
||||
outlined
|
||||
dense
|
||||
chips
|
||||
/>
|
||||
|
||||
<div class="d-flex gap-x-4 flex-wrap align-center">
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="webhookData.webhook_base64"
|
||||
:disabled="loading"
|
||||
label="Webhook base64"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="webhookData.webhook_by_events"
|
||||
:disabled="loading"
|
||||
label="Webhook por eventos"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="expanded">
|
||||
<v-switch
|
||||
v-model="webhookData.enabled"
|
||||
label="Habilitado"
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
JSON.stringify(webhookData) === JSON.stringify(defaultWebhookData)
|
||||
"
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@click="saveWebhook"
|
||||
variant="tonal"
|
||||
>
|
||||
Salvar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceWebhook",
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
expanded: false,
|
||||
loading: false,
|
||||
error: false,
|
||||
valid: false,
|
||||
webhookData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
url: "",
|
||||
webhook_base64: false,
|
||||
webhook_by_events: false,
|
||||
},
|
||||
defaultWebhookData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
url: "",
|
||||
webhook_base64: false,
|
||||
webhook_by_events: false,
|
||||
},
|
||||
webhookEventsType: [
|
||||
"APPLICATION_STARTUP",
|
||||
"QRCODE_UPDATED",
|
||||
"MESSAGES_SET",
|
||||
"MESSAGES_UPSERT",
|
||||
"MESSAGES_UPDATE",
|
||||
"MESSAGES_DELETE",
|
||||
"SEND_MESSAGE",
|
||||
"CONTACTS_SET",
|
||||
"CONTACTS_UPSERT",
|
||||
"CONTACTS_UPDATE",
|
||||
"PRESENCE_UPDATE",
|
||||
"CHATS_SET",
|
||||
"CHATS_UPSERT",
|
||||
"CHATS_UPDATE",
|
||||
"CHATS_DELETE",
|
||||
"GROUPS_UPSERT",
|
||||
"GROUP_UPDATE",
|
||||
"GROUP_PARTICIPANTS_UPDATE",
|
||||
"CONNECTION_UPDATE",
|
||||
"CALL",
|
||||
"NEW_JWT_TOKEN",
|
||||
],
|
||||
}),
|
||||
|
||||
methods: {
|
||||
async saveWebhook() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
await instanceController.webhook.set(
|
||||
this.instance.instance.instanceName,
|
||||
{
|
||||
...this.webhookData,
|
||||
url: this.webhookData.url.trim().replace(/\/$/, ""),
|
||||
}
|
||||
);
|
||||
this.defaultWebhookData = Object.assign({}, this.webhookData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadWebhook() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
const webhookData = await instanceController.webhook.get(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
this.webhookData = Object.assign({}, webhookData);
|
||||
this.defaultWebhookData = Object.assign({}, webhookData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
expanded(expanded) {
|
||||
if (expanded) this.loadWebhook();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
157
src/components/instance/settings/Websocket.vue
Normal file
157
src/components/instance/settings/Websocket.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<v-card variant="outlined" :loading="loading">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon start>mdi-email-fast</v-icon>
|
||||
Websocket
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
size="small"
|
||||
icon
|
||||
:disabled="loading"
|
||||
variant="tonal"
|
||||
@click="expanded = !expanded"
|
||||
:style="{ transform: expanded ? 'rotate(180deg)' : '' }"
|
||||
>
|
||||
<v-icon>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="expanded">
|
||||
<v-alert v-if="error" type="error" class="mb-3">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-form v-model="valid">
|
||||
<v-select
|
||||
:items="websocketEventsType"
|
||||
v-model="websocketData.events"
|
||||
:disabled="loading"
|
||||
label="Eventos"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
multiple
|
||||
outlined
|
||||
dense
|
||||
chips
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="expanded">
|
||||
<v-switch
|
||||
v-model="websocketData.enabled"
|
||||
label="Habilitado"
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
hide-details
|
||||
></v-switch>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
JSON.stringify(websocketData) === JSON.stringify(defaultWebsocketData)
|
||||
"
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
@click="saveWebsocket"
|
||||
variant="tonal"
|
||||
>
|
||||
Salvar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "InstanceWebsocket",
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
expanded: false,
|
||||
loading: false,
|
||||
error: false,
|
||||
valid: false,
|
||||
websocketData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
},
|
||||
defaultWebsocketData: {
|
||||
enabled: false,
|
||||
events: [],
|
||||
},
|
||||
websocketEventsType: [
|
||||
"APPLICATION_STARTUP",
|
||||
"QRCODE_UPDATED",
|
||||
"MESSAGES_SET",
|
||||
"MESSAGES_UPSERT",
|
||||
"MESSAGES_UPDATE",
|
||||
"MESSAGES_DELETE",
|
||||
"SEND_MESSAGE",
|
||||
"CONTACTS_SET",
|
||||
"CONTACTS_UPSERT",
|
||||
"CONTACTS_UPDATE",
|
||||
"PRESENCE_UPDATE",
|
||||
"CHATS_SET",
|
||||
"CHATS_UPSERT",
|
||||
"CHATS_UPDATE",
|
||||
"CHATS_DELETE",
|
||||
"GROUPS_UPSERT",
|
||||
"GROUP_UPDATE",
|
||||
"GROUP_PARTICIPANTS_UPDATE",
|
||||
"CONNECTION_UPDATE",
|
||||
"CALL",
|
||||
"NEW_JWT_TOKEN",
|
||||
],
|
||||
}),
|
||||
|
||||
methods: {
|
||||
async saveWebsocket() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
await instanceController.websocket.set(
|
||||
this.instance.instance.instanceName,
|
||||
this.websocketData
|
||||
);
|
||||
this.defaultWebsocketData = Object.assign({}, this.websocketData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadWebsocket() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
const websocketData = await instanceController.websocket.get(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
this.websocketData = Object.assign({}, websocketData);
|
||||
this.defaultWebsocketData = Object.assign({}, websocketData);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
expanded: {
|
||||
handler() {
|
||||
if (this.expanded) this.loadWebsocket();
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
99
src/components/modal/ConnectPhone.vue
Normal file
99
src/components/modal/ConnectPhone.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<v-alert
|
||||
icon="mdi-qrcode-scan"
|
||||
v-if="instance.instance?.status != 'open'"
|
||||
type="warning"
|
||||
>
|
||||
<div class="d-flex justify-space-between align-center flex-wrap">
|
||||
<span>Telefone não conectado</span>
|
||||
<v-btn @click="startConnect" size="small"> Conectar </v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
<v-dialog v-model="dialog" max-width="350px">
|
||||
<v-card :loading="loading && qrCode">
|
||||
<v-card-text>
|
||||
<v-img v-if="qrCode" :src="qrCode" width="300px" height="300px" />
|
||||
<v-card
|
||||
v-else
|
||||
width="300px"
|
||||
height="300px"
|
||||
variant="outlined"
|
||||
elevation="0"
|
||||
>
|
||||
<v-card-text class="d-flex justify-center align-center h-100">
|
||||
<v-progress-circular indeterminate color="primary" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-alert type="error" v-if="error">
|
||||
{{ Array.isArray(error) ? error.join(", ") : error }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="dialog = false" :disabled="loading"> Cancel </v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useAppStore } from "@/store/app";
|
||||
import instanceController from "@/services/instanceController";
|
||||
|
||||
export default {
|
||||
name: "SettingsModal",
|
||||
data: () => ({
|
||||
dialog: false,
|
||||
error: false,
|
||||
|
||||
loading: false,
|
||||
qrCode: null,
|
||||
success: false,
|
||||
|
||||
timeout: null,
|
||||
|
||||
AppStore: useAppStore(),
|
||||
}),
|
||||
methods: {
|
||||
async loadQr() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
|
||||
const response = await instanceController.connect(
|
||||
this.instance.instance.instanceName
|
||||
);
|
||||
|
||||
if (response.base64) this.qrCode = response.base64;
|
||||
else {
|
||||
this.dialog = false;
|
||||
return;
|
||||
}
|
||||
this.timeout = setTimeout(this.loadQr, 40000);
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async startConnect() {
|
||||
clearTimeout(this.timeout);
|
||||
this.dialog = true;
|
||||
this.error = false;
|
||||
|
||||
await this.loadQr();
|
||||
await this.AppStore.reconnect();
|
||||
},
|
||||
},
|
||||
props: {
|
||||
instance: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ["close"],
|
||||
};
|
||||
</script>
|
||||
@@ -5,7 +5,7 @@
|
||||
:persistent="!AppStore.validConnection"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-card-text class="d-flex flex-column gap-4">
|
||||
<v-form v-model="valid">
|
||||
<h3 class="mb-4">Criar instancia</h3>
|
||||
<v-text-field
|
||||
@@ -21,7 +21,7 @@
|
||||
]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="instance.apiKey"
|
||||
v-model="instance.token"
|
||||
label="API Key"
|
||||
required
|
||||
outlined
|
||||
@@ -41,7 +41,7 @@
|
||||
configurados após a criação da instância.
|
||||
</v-alert>
|
||||
|
||||
<v-alert type="error" v-if="error">
|
||||
<v-alert type="error" v-if="error" >
|
||||
{{ Array.isArray(error) ? error.join(", ") : error }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
@@ -62,7 +62,7 @@
|
||||
:disabled="!valid"
|
||||
:loading="loading"
|
||||
>
|
||||
Conectar
|
||||
Criar
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
valid: false,
|
||||
instance: {
|
||||
instanceName: "",
|
||||
apiKey: "",
|
||||
token: "",
|
||||
},
|
||||
loading: false,
|
||||
error: false,
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
}),
|
||||
methods: {
|
||||
generateApiKey() {
|
||||
this.instance.apiKey =
|
||||
this.instance.token =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
},
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
max-width="500px"
|
||||
:persistent="!AppStore.validConnection"
|
||||
>
|
||||
<v-dialog v-model="dialog" max-width="500px" :persistent="!AppStore.validConnection">
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-form v-model="valid">
|
||||
<h3 class="mb-4">Configurar conexão</h3>
|
||||
|
||||
<v-text-field
|
||||
v-model="apiKey"
|
||||
label="Global API Key"
|
||||
required
|
||||
outlined
|
||||
:type="revelPassword ? 'text' : 'password'"
|
||||
:append-inner-icon="revelPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
||||
@click:append-inner="revelPassword = !revelPassword"
|
||||
/>
|
||||
</v-form>
|
||||
|
||||
<v-alert type="error" v-if="error">
|
||||
{{ Array.isArray(error) ? error.join(", ") : error }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
v-if="AppStore.validConnection"
|
||||
text
|
||||
@click="dialog = false"
|
||||
:disabled="loading"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn text to="/" :disabled="loading">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="success"
|
||||
variant="tonal"
|
||||
@@ -35,13 +38,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import instanceController from "@/services/instanceController";
|
||||
import { useAppStore } from "@/store/app";
|
||||
|
||||
export default {
|
||||
name: "SettingsModal",
|
||||
data: () => ({
|
||||
dialog: false,
|
||||
valid: false,
|
||||
revelPassword: false,
|
||||
connection: {
|
||||
host: "",
|
||||
globalApiKey: "",
|
||||
},
|
||||
loading: false,
|
||||
error: false,
|
||||
AppStore: useAppStore(),
|
||||
@@ -52,13 +60,8 @@ export default {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
|
||||
const instance = await instanceController.create(this.instance);
|
||||
await this.AppStore.reconnect();
|
||||
|
||||
this.$router.push({
|
||||
name: "instance",
|
||||
params: { id: instance.instance.instanceName },
|
||||
});
|
||||
await this.AppStore.setConnection(this.connection);
|
||||
this.dialog = false;
|
||||
} catch (e) {
|
||||
this.error = e.message?.message || e.message || e;
|
||||
} finally {
|
||||
@@ -67,9 +70,6 @@ export default {
|
||||
},
|
||||
open() {
|
||||
this.dialog = true;
|
||||
this.error = false;
|
||||
this.instance.instanceName = "";
|
||||
this.generateApiKey();
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user