diff --git a/package.json b/package.json
index 0538824e..4c45c0d4 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"@sentry/integrations": "^5.29.2",
"@vue-stripe/vue-stripe": "^4.2.9",
"@vue/web-component-wrapper": "^1.2.0",
- "@weni/unnnic-system": "^1.12.4",
+ "@weni/unnnic-system": "^1.16.6",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"dotenv": "^9.0.2",
diff --git a/src/api/projects.js b/src/api/projects.js
index 4f2404ed..efbf0e37 100644
--- a/src/api/projects.js
+++ b/src/api/projects.js
@@ -1,4 +1,7 @@
+import axios from 'axios';
import request from './request.js';
+import getEnv from '../utils/env.js';
+import KCService from '../services/Keycloak.js';
export default {
getProject({ uuid }) {
@@ -106,4 +109,17 @@ export default {
getTemplates() {
return request.$http().get('v2/projects/template-type/');
},
+
+ async apiFlowsGetSuccessOrg({ flowUuid }) {
+ const { data } = await axios.get(
+ `${getEnv('URL_FLOWS')}/api/v2/success_orgs/${flowUuid}`,
+ {
+ headers: {
+ Authorization: `Bearer ${KCService.keycloak.token}`,
+ },
+ },
+ );
+
+ return data;
+ },
};
diff --git a/src/app.vue b/src/app.vue
index d0902e09..430c2530 100644
--- a/src/app.vue
+++ b/src/app.vue
@@ -75,6 +75,7 @@
:routes="['chats']"
class="page"
dont-update-when-changes-language
+ name="chats"
/>
@@ -121,7 +122,6 @@ import sendAllIframes from './utils/plugins/sendAllIframes';
import iframessa from 'iframessa';
import KnowUserModal from './components/KnowUserModal/Index.vue';
import RightBar from './components/common/RightBar/Index.vue';
-import axios from 'axios';
import TrialPeriod from './modals/TrialPeriod.vue';
import { setUser } from '@sentry/browser';
@@ -271,22 +271,6 @@ export default {
}
} else if (event.data?.event === 'chats:update-unread-messages') {
this.unreadMessages = event.data.unreadMessages;
- } else if (event.data?.event === 'ia:get-flows-length') {
- this.$refs['system-ia'].$refs.iframe.contentWindow.postMessage(
- {
- event: 'connect:set-flows-length',
- flowsLength: this.$store.getters.currentProject?.flow_count || 0,
- },
- '*',
- );
- } else if (event.data?.event === 'flows:redirect') {
- this.$router.push({
- name: 'push',
- params: {
- projectUuid: this.$route.params.projectUuid,
- internal: event.data.path.split('/'),
- },
- });
}
if (content.startsWith(prefix)) {
@@ -314,8 +298,39 @@ export default {
}
});
- iframessa.getter('flowsLength', () => {
- return this.$store.getters.currentProject?.flow_count || 0;
+ iframessa.getter('hasFlows', async () => {
+ const { has_flows } = await this.$store.dispatch(
+ 'getSuccessOrgStatusByFlowUuid',
+ {
+ flowUuid: this.$store.getters.currentProject.flow_organization,
+ },
+ );
+
+ return has_flows;
+ });
+
+ iframessa.on('redirectToFlows', ({ data }) => {
+ this.$router.push({
+ name: 'push',
+ params: {
+ projectUuid: this.$route.params.projectUuid,
+ internal: data.path.split('/'),
+ },
+ });
+ });
+
+ iframessa.on('redirectToSettingsChats', ({ data }) => {
+ this.$router.push({
+ name: 'settingsChats',
+ params: {
+ projectUuid: this.$route.params.projectUuid,
+ internal: data.path.split('/'),
+ },
+ });
+ });
+
+ iframessa.getter('isOpenHowToIntegrateChatsModal', () => {
+ return true;
});
},
@@ -542,21 +557,36 @@ export default {
}
try {
const flowUuid = this.$store.getters.currentProject.flow_organization;
- const response = await axios.get(
- `${getEnv('URL_FLOWS')}/api/v2/success_orgs/${flowUuid}`,
- {
- headers: {
- Authorization: `Bearer ${this.$keycloak.token}`,
- },
- },
- );
- const { has_ia, has_flows, has_channel, has_msg } = response.data;
+
+ let oldValues = null;
+
+ if (this.$store.state.Project.championChatbots[flowUuid]) {
+ oldValues = this.$store.state.Project.championChatbots[flowUuid];
+ }
+
+ const { has_ia, has_flows, has_channel, has_msg } =
+ await this.$store.dispatch('getSuccessOrgStatusByFlowUuid', {
+ flowUuid: this.$store.getters.currentProject.flow_organization,
+ force: true,
+ });
+
+ iframessa.modules.ai?.emit('update:hasFlows', has_flows);
+
const level =
[has_flows, has_ia, has_channel, has_msg].lastIndexOf(true) + 1;
- if (this.championChatbotsByProject[projectUuid] === undefined) {
- this.$set(this.championChatbotsByProject, projectUuid, level);
- }
- if (this.championChatbotsByProject[projectUuid] <= 3 && level >= 4) {
+
+ if (
+ level >= 4 &&
+ oldValues &&
+ [
+ oldValues.has_flows,
+ oldValues.has_ia,
+ oldValues.has_channel,
+ oldValues.has_msg,
+ ].lastIndexOf(true) +
+ 1 <
+ 4
+ ) {
this.$store.dispatch('openModal', {
type: 'confirm',
showClose: true,
@@ -591,8 +621,7 @@ export default {
},
},
});
- this.championChatbotsByProject[projectUuid] = level;
- } else if (level <= 3) {
+ } else if (level < 4) {
setTimeout(() => {
this.verifyIfChampionChatbotStatusChanged({
projectUuid,
diff --git a/src/assets/tutorial/sidebar-chats.gif b/src/assets/tutorial/sidebar-chats.gif
new file mode 100644
index 00000000..1c33cf19
Binary files /dev/null and b/src/assets/tutorial/sidebar-chats.gif differ
diff --git a/src/assets/tutorial/sidebar-integrations.gif b/src/assets/tutorial/sidebar-integrations.gif
new file mode 100644
index 00000000..41e1526d
Binary files /dev/null and b/src/assets/tutorial/sidebar-integrations.gif differ
diff --git a/src/assets/tutorial/sidebar-intelligences.gif b/src/assets/tutorial/sidebar-intelligences.gif
new file mode 100644
index 00000000..b0905e47
Binary files /dev/null and b/src/assets/tutorial/sidebar-intelligences.gif differ
diff --git a/src/assets/tutorial/sidebar-studio.gif b/src/assets/tutorial/sidebar-studio.gif
new file mode 100644
index 00000000..6a2279b8
Binary files /dev/null and b/src/assets/tutorial/sidebar-studio.gif differ
diff --git a/src/components/SidebarModal.vue b/src/components/SidebarModal.vue
new file mode 100644
index 00000000..fe232e98
--- /dev/null
+++ b/src/components/SidebarModal.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
diff --git a/src/components/external/Sidebar.vue b/src/components/external/Sidebar.vue
index e2677689..d6c35304 100644
--- a/src/components/external/Sidebar.vue
+++ b/src/components/external/Sidebar.vue
@@ -18,6 +18,36 @@
+
+
+
+
+
+
+
+
+
+
@@ -26,9 +56,19 @@ import { mapGetters, mapActions } from 'vuex';
import { get } from 'lodash';
import getEnv from '@/utils/env';
import { PROJECT_ROLE_CHATUSER } from '../users/permissionsObjects';
+import SidebarModal from '../SidebarModal.vue';
+import gifStudio from '../../assets/tutorial/sidebar-studio.gif';
+import gifIntelligences from '../../assets/tutorial/sidebar-intelligences.gif';
+import gifChats from '../../assets/tutorial/sidebar-chats.gif';
+import gifIntegrations from '../../assets/tutorial/sidebar-integrations.gif';
export default {
name: 'Sidebar',
+
+ components: {
+ SidebarModal,
+ },
+
props: {
unreadMessages: Number,
},
@@ -38,6 +78,10 @@ export default {
open: true,
current: '',
notifyAgents: false,
+ gifStudio,
+ gifIntelligences,
+ gifChats,
+ gifIntegrations,
};
},
@@ -55,11 +99,15 @@ export default {
});
},
- mounted() {},
-
computed: {
...mapGetters(['currentProject']),
+ hasFlows() {
+ return this.$store.state.Project.championChatbots[
+ this.$store.getters.currentProject?.flow_organization
+ ]?.has_flows;
+ },
+
hideModulesButChats() {
if (
!this.$store.getters.currentProject.menu.chat.length &&
@@ -124,6 +172,7 @@ export default {
},
{
label: 'SIDEBAR.STUDIO',
+ id: 'studio',
icon: 'app-window-edit',
viewUrl: `/projects/${get(project, 'uuid')}/studio/init`,
show: () => {
@@ -132,6 +181,7 @@ export default {
},
{
label: 'SIDEBAR.BH',
+ id: 'intelligences',
icon: 'science-fiction-robot',
viewUrl: `/projects/${get(project, 'uuid')}/bothub/init`,
show: () => {
@@ -140,6 +190,7 @@ export default {
},
{
label: 'SIDEBAR.RC',
+ id: 'chats',
icon: 'messaging-we-chat',
viewUrl: `/projects/${get(project, 'uuid')}/rocketchat`,
show(project) {
@@ -149,6 +200,7 @@ export default {
},
{
label: 'SIDEBAR.chats',
+ id: 'chats',
icon: 'messaging-we-chat',
viewUrl: `/projects/${get(project, 'uuid')}/chats/init`,
show: (project) => {
@@ -169,6 +221,7 @@ export default {
items: [
{
label: 'SIDEBAR.INTEGRATIONS',
+ id: 'integrations',
icon: 'layout-dashboard',
viewUrl: `/projects/${get(project, 'uuid')}/integrations/init`,
show: (project) => {
diff --git a/src/locales/translations.json b/src/locales/translations.json
index 662416ef..a74596bf 100644
--- a/src/locales/translations.json
+++ b/src/locales/translations.json
@@ -74,6 +74,13 @@
"en": "Learn more",
"es": "Saber más"
},
+ "docs_urls": {
+ "how_to_create_a_flow": {
+ "pt-br": "https://docs.weni.ai/l/pt/fluxos/como-criar-um-fluxo",
+ "en": "https://docs.weni.ai/l/en/flows-category/untitled-article-ld-3",
+ "es": "https://docs.weni.ai/l/en/flows-category/untitled-article-ld-3"
+ }
+ },
"weni_community": {
"title": {
"pt-br": "Comunidade",
@@ -4914,6 +4921,56 @@
"pt-br": "Expandir",
"en": "Show",
"es": "Expandir"
+ },
+ "modules": {
+ "studio": {
+ "title": {
+ "pt-br": "Estúdio",
+ "en": "Studio",
+ "es": "Estudio"
+ },
+ "description": {
+ "pt-br": "Você pode criar grupos, ver seus contatos e programar o envio de mensagens. Para isso é importante ter um fluxo pronto.",
+ "en": "You can create groups, see your contacts and program the sending of messages. For this it is important to have a ready flow.",
+ "es": "Puede crear grupos, ver sus contactos y programar el envío de mensajes. Para esto es importante tener un flujo listo."
+ }
+ },
+ "intelligences": {
+ "title": {
+ "pt-br": "Inteligência Artificial",
+ "en": "Artificial Intelligence",
+ "es": "Inteligencia Artificial"
+ },
+ "description": {
+ "pt-br": "Treine ou conecte o seu chatbot a uma das nossas inteligências disponíveis. Para isso é importante ter um fluxo pronto.",
+ "en": "Train or connect your chatbot to one of our available intelligences. For this it is important to have a ready flow.",
+ "es": "Entrena o conecta tu chatbot a una de nuestras inteligencias disponibles. Para esto es importante tener un flujo listo."
+ }
+ },
+ "chats": {
+ "title": {
+ "pt-br": "Chats",
+ "en": "Chats",
+ "es": "Chats"
+ },
+ "description": {
+ "pt-br": "Realize atendimentos dentro da plataforma Weni para isso é importante ter um fluxo pronto para usar o Chats em seus projetos.",
+ "en": "Perform care within the Weni platform for this it is important to have a flow ready to use chats on your projects.",
+ "es": "Realice la atención dentro de la plataforma Weni para esto, es importante tener un flujo listo para usar los chats en sus proyectos."
+ }
+ },
+ "integrations": {
+ "title": {
+ "pt-br": "Integrações",
+ "en": "Integrations",
+ "es": "Integraciones"
+ },
+ "description": {
+ "pt-br": "Agilize seus atendimentos conectando seu chatbot a um canal de comunicação, para usar esta função você precisa de um fluxo.",
+ "en": "Agilize your calls connecting your chatbot to a communication channel to use this function you need a flow.",
+ "es": "Agilice sus llamadas conectando su chatbot a un canal de comunicación para usar esta función que necesita un flujo."
+ }
+ }
}
},
"NAVBAR": {
diff --git a/src/store/project/actions.js b/src/store/project/actions.js
index 4e2c8220..9af08ec0 100644
--- a/src/store/project/actions.js
+++ b/src/store/project/actions.js
@@ -164,4 +164,21 @@ export default {
projectUuid,
});
},
+
+ async getSuccessOrgStatusByFlowUuid({ state, commit }, { flowUuid, force }) {
+ if (!state.championChatbots[flowUuid] || force) {
+ const { has_ia, has_flows, has_channel, has_msg } =
+ await projects.apiFlowsGetSuccessOrg({ flowUuid });
+
+ commit('setChampionChatbot', {
+ flowUuid,
+ has_ia,
+ has_flows,
+ has_channel,
+ has_msg,
+ });
+ }
+
+ return state.championChatbots[flowUuid];
+ },
};
diff --git a/src/store/project/index.js b/src/store/project/index.js
index 72a52f71..0e9f7f76 100644
--- a/src/store/project/index.js
+++ b/src/store/project/index.js
@@ -9,6 +9,7 @@ const state = {
status: null,
data: [],
},
+ championChatbots: {},
};
const getters = {
diff --git a/src/store/project/mutations.js b/src/store/project/mutations.js
index 28707aa1..95e4252e 100644
--- a/src/store/project/mutations.js
+++ b/src/store/project/mutations.js
@@ -1,6 +1,15 @@
+import Vue from 'vue';
+
export default {
setCurrentProject: (state, project) => (state.currentProject = project),
+ setChampionChatbot: (state, value) => {
+ Vue.set(state, 'championChatbots', {
+ ...state.setChampionChatbots,
+ [value.flowUuid]: value,
+ });
+ },
+
PROJECT_CREATE_REQUEST: (state) => (state.loadingCreateProject = true),
PROJECT_CREATE_SUCCESS: (state, project) => {
state.currentProject = project;
diff --git a/src/views/ProjectHomeBlank/ChampionChatbot.vue b/src/views/ProjectHomeBlank/ChampionChatbot.vue
index c53abb3d..10618e36 100644
--- a/src/views/ProjectHomeBlank/ChampionChatbot.vue
+++ b/src/views/ProjectHomeBlank/ChampionChatbot.vue
@@ -99,18 +99,10 @@ export default {
try {
this.loading = true;
- const flowUuid = this.$store.getters.currentProject.flow_organization;
-
- const response = await axios.get(
- `${getEnv('URL_FLOWS')}/api/v2/success_orgs/${flowUuid}`,
- {
- headers: {
- Authorization: `Bearer ${this.$keycloak.token}`,
- },
- },
- );
-
- const { has_ia, has_flows, has_channel, has_msg } = response.data;
+ const { has_ia, has_flows, has_channel, has_msg } =
+ await this.$store.dispatch('getSuccessOrgStatusByFlowUuid', {
+ flowUuid: this.$store.getters.currentProject.flow_organization,
+ });
this.level =
[has_flows, has_ia, has_channel, has_msg].lastIndexOf(true) + 1;
diff --git a/src/views/settings.vue b/src/views/settings.vue
index 9d3a7971..b9fb8716 100644
--- a/src/views/settings.vue
+++ b/src/views/settings.vue
@@ -44,10 +44,6 @@ export default {
ExternalSystem,
},
- data() {
- return {};
- },
-
computed: {
...mapGetters(['currentProject']),
@@ -133,7 +129,7 @@ export default {
const { projectUuid } = this.$route.params;
if (!projectUuid) {
- return false;
+ return;
}
this.$refs['system-project'].reset();
diff --git a/tests/unit/views/settings.spec.js b/tests/unit/views/settings.spec.js
new file mode 100644
index 00000000..1d822cac
--- /dev/null
+++ b/tests/unit/views/settings.spec.js
@@ -0,0 +1,130 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import Settings from '@/views/settings.vue';
+import { PROJECT_ROLE_CHATUSER } from '@/components/users/permissionsObjects';
+
+window.configs = {
+ MODULE_CHATS: 'https://chats',
+};
+
+const localVue = createLocalVue();
+
+localVue.use(Vuex);
+
+const externalSystem = {
+ props: {
+ routes: Array,
+ },
+ render(h) {
+ return h('span');
+ },
+ methods: {
+ init() {
+ return this.routes;
+ },
+
+ reset() {},
+ },
+};
+
+const spyExternalSystemInit = jest.spyOn(externalSystem.methods, 'init');
+const spyExternalSystemReset = jest.spyOn(externalSystem.methods, 'reset');
+
+const $route = {
+ name: 'settingsProject',
+ fullPath: 'project',
+ params: {
+ projectUuid: '1',
+ },
+};
+
+const wrapper = shallowMount(Settings, {
+ store: new Vuex.Store({
+ state: {
+ currentProject: {
+ menu: { chat: [] },
+ authorization: { role: 1 },
+ },
+ },
+ getters: {
+ currentProject: (state) => state.currentProject,
+ },
+ mutations: {
+ setCurrentProject(state, value) {
+ state.currentProject = value;
+ },
+ },
+ }),
+
+ localVue,
+
+ mocks: {
+ $route,
+ $router: {
+ replace(href) {
+ wrapper.vm.$route.name = href.name;
+ },
+ },
+ $t: () => 'text',
+ },
+
+ stubs: {
+ 'unnnic-card': true,
+ 'external-system': externalSystem,
+ },
+});
+
+describe('settings.vue', () => {
+ it('it should call the project external system init method', async () => {
+ $route.name = 'settingsProject';
+ $route.fullPath = 'project';
+
+ await wrapper.vm.$nextTick();
+
+ expect(spyExternalSystemInit).lastReturnedWith(['settingsProject']);
+ });
+
+ it('it should call the chats external system init method', async () => {
+ $route.name = 'settingsChats';
+ $route.fullPath = 'chats';
+
+ await wrapper.vm.$nextTick();
+
+ expect(spyExternalSystemInit).lastReturnedWith(['settingsChats']);
+ });
+
+ it('it should not call system external reset method due projectUuid is not defined', async () => {
+ wrapper.vm.$route.params.projectUuid = '';
+
+ await wrapper.vm.$nextTick();
+
+ expect(spyExternalSystemReset).toHaveBeenCalledTimes(0);
+ });
+
+ it('it should call system external reset method two times, project and chats modules, due projectUuid is defined', async () => {
+ wrapper.vm.$route.params.projectUuid = '2';
+
+ await wrapper.vm.$nextTick();
+
+ expect(spyExternalSystemReset).toHaveBeenCalledTimes(2);
+ });
+
+ it('it should redirect user to the first valid tab if they try to enter in a non existent page', async () => {
+ wrapper.vm.$route.name = 'NonExistentPage';
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.$route.name).toBe(wrapper.vm.pages[0].href.name);
+ });
+
+ it('it should hide modules but chats if user authorization role is chat user', async () => {
+ wrapper.vm.$store.commit('setCurrentProject', {
+ menu: { chat: [] },
+ authorization: {
+ role: PROJECT_ROLE_CHATUSER,
+ },
+ });
+
+ expect(wrapper.vm.hideModulesButChats).toBeTruthy();
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index a5523d74..2a2fd855 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2005,10 +2005,10 @@
"@webassemblyjs/wast-parser" "1.9.0"
"@xtuc/long" "4.2.2"
-"@weni/unnnic-system@^1.12.4":
- version "1.12.4"
- resolved "https://registry.yarnpkg.com/@weni/unnnic-system/-/unnnic-system-1.12.4.tgz#d6d4c1d4049609694e892419cce4c331457cb0e4"
- integrity sha512-9HrrjjvMx92dDUEKgKC1VSNH/cN33f4foHYpm1PHTFEAgVjr8O/4pWTdh0RwrHcUnFSRXAY8p7+x46+6wynTSg==
+"@weni/unnnic-system@^1.16.6":
+ version "1.16.6"
+ resolved "https://registry.yarnpkg.com/@weni/unnnic-system/-/unnnic-system-1.16.6.tgz#ecc282b8436f76c9365449055f36cec27782e6bf"
+ integrity sha512-oMeO61rm+7h9UzV6TqIsUWSAWsjroCakcMVSnCUb2wRQxkTvT11p3LnYGYNGMkH6jTgNPuCqaZ5NdxyFgZBhRw==
dependencies:
core-js "^3.6.5"
moment "^2.29.1"