feat: Configure Chatwoot & Analytics for SaaS app (#2975)
* feat: Add Chatwoot support inside Chatwoot SaaS * Fix identity issues with Chatwoot
This commit is contained in:
parent
8de4ce0037
commit
4759730022
12 changed files with 125 additions and 39 deletions
|
@ -23,7 +23,9 @@ class DashboardController < ActionController::Base
|
||||||
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
|
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
|
||||||
'CHATWOOT_INBOX_TOKEN',
|
'CHATWOOT_INBOX_TOKEN',
|
||||||
'API_CHANNEL_NAME',
|
'API_CHANNEL_NAME',
|
||||||
'API_CHANNEL_THUMBNAIL'
|
'API_CHANNEL_THUMBNAIL',
|
||||||
|
'ANALYTICS_TOKEN',
|
||||||
|
'ANALYTICS_HOST'
|
||||||
).merge(
|
).merge(
|
||||||
APP_VERSION: Chatwoot.config[:version]
|
APP_VERSION: Chatwoot.config[:version]
|
||||||
)
|
)
|
||||||
|
|
|
@ -255,19 +255,11 @@ export default {
|
||||||
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
currentUser(newUserInfo, oldUserInfo) {
|
|
||||||
if (!oldUserInfo.email && newUserInfo.email) {
|
|
||||||
this.setChatwootUser();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('labels/get');
|
this.$store.dispatch('labels/get');
|
||||||
this.$store.dispatch('inboxes/get');
|
this.$store.dispatch('inboxes/get');
|
||||||
this.$store.dispatch('notifications/unReadCount');
|
this.$store.dispatch('notifications/unReadCount');
|
||||||
this.$store.dispatch('teams/get');
|
this.$store.dispatch('teams/get');
|
||||||
this.setChatwootUser();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -288,17 +280,6 @@ export default {
|
||||||
toggleSupportChatWindow() {
|
toggleSupportChatWindow() {
|
||||||
window.$chatwoot.toggle();
|
window.$chatwoot.toggle();
|
||||||
},
|
},
|
||||||
setChatwootUser() {
|
|
||||||
if (!this.currentUser.email || !this.globalConfig.chatwootInboxToken) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.$chatwoot.setUser(this.currentUser.email, {
|
|
||||||
name: this.currentUser.name,
|
|
||||||
email: this.currentUser.email,
|
|
||||||
avatar_url: this.currentUser.avatar_url,
|
|
||||||
identifier_hash: this.currentUser.hmac_identifier,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
filterMenuItemsByRole(menuItems) {
|
filterMenuItemsByRole(menuItems) {
|
||||||
if (!this.currentRole) {
|
if (!this.currentRole) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -94,10 +94,6 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
logout() {
|
logout() {
|
||||||
Auth.logout();
|
Auth.logout();
|
||||||
|
|
||||||
if (this.globalConfig.chatwootInboxToken) {
|
|
||||||
window.$chatwoot.reset();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
43
app/javascript/dashboard/helper/scriptHelpers.js
Normal file
43
app/javascript/dashboard/helper/scriptHelpers.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import posthog from 'posthog-js';
|
||||||
|
|
||||||
|
export const CHATWOOT_SET_USER = 'CHATWOOT_SET_USER';
|
||||||
|
export const CHATWOOT_RESET = 'CHATWOOT_RESET';
|
||||||
|
|
||||||
|
export const ANALYTICS_IDENTITY = 'ANALYTICS_IDENTITY';
|
||||||
|
export const ANALYTICS_RESET = 'ANALYTICS_RESET';
|
||||||
|
|
||||||
|
export const initializeAnalyticsEvents = () => {
|
||||||
|
window.bus.$on(ANALYTICS_IDENTITY, ({ user }) => {
|
||||||
|
if (window.analyticsConfig) {
|
||||||
|
posthog.identify(user.id, { name: user.name, email: user.email });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.bus.$on(ANALYTICS_RESET, () => {
|
||||||
|
if (window.analyticsConfig) {
|
||||||
|
posthog.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initializeChatwootEvents = () => {
|
||||||
|
window.bus.$on(CHATWOOT_RESET, () => {
|
||||||
|
if (window.$chatwoot) {
|
||||||
|
window.$chatwoot.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.bus.$on(CHATWOOT_SET_USER, ({ user }) => {
|
||||||
|
if (window.$chatwoot) {
|
||||||
|
window.$chatwoot.setUser(user.email, {
|
||||||
|
avatar_url: user.avatar_url,
|
||||||
|
email: user.email,
|
||||||
|
identifier_hash: user.hmac_identifier,
|
||||||
|
name: user.name,
|
||||||
|
});
|
||||||
|
window.$chatwoot.setCustomAttributes({
|
||||||
|
signedUpAt: user.created_at,
|
||||||
|
cloudCustomer: 'true',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -81,7 +81,9 @@ export const actions = {
|
||||||
async validityCheck(context) {
|
async validityCheck(context) {
|
||||||
try {
|
try {
|
||||||
const response = await authAPI.validityCheck();
|
const response = await authAPI.validityCheck();
|
||||||
setUser(response.data.payload.data, getHeaderExpiry(response));
|
setUser(response.data.payload.data, getHeaderExpiry(response), {
|
||||||
|
setUserInSDK: true,
|
||||||
|
});
|
||||||
context.commit(types.default.SET_CURRENT_USER);
|
context.commit(types.default.SET_CURRENT_USER);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error?.response?.status === 401) {
|
if (error?.response?.status === 401) {
|
||||||
|
|
|
@ -3,6 +3,12 @@ import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
import differenceInDays from 'date-fns/differenceInDays';
|
import differenceInDays from 'date-fns/differenceInDays';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { frontendURL } from '../../helper/URLHelper';
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
import {
|
||||||
|
ANALYTICS_IDENTITY,
|
||||||
|
ANALYTICS_RESET,
|
||||||
|
CHATWOOT_RESET,
|
||||||
|
CHATWOOT_SET_USER,
|
||||||
|
} from '../../helper/scriptHelpers';
|
||||||
|
|
||||||
Cookies.defaults = { sameSite: 'Lax' };
|
Cookies.defaults = { sameSite: 'Lax' };
|
||||||
|
|
||||||
|
@ -11,10 +17,15 @@ export const setLoadingStatus = (state, status) => {
|
||||||
state.fetchAPIloadingStatus = status;
|
state.fetchAPIloadingStatus = status;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setUser = (userData, expiryDate) =>
|
export const setUser = (user, expiryDate, options = {}) => {
|
||||||
Cookies.set('user', userData, {
|
if (options && options.setUserInSDK) {
|
||||||
|
window.bus.$emit(CHATWOOT_SET_USER, { user });
|
||||||
|
window.bus.$emit(ANALYTICS_IDENTITY, { user });
|
||||||
|
}
|
||||||
|
Cookies.set('user', user, {
|
||||||
expires: differenceInDays(expiryDate, new Date()),
|
expires: differenceInDays(expiryDate, new Date()),
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getHeaderExpiry = response =>
|
export const getHeaderExpiry = response =>
|
||||||
fromUnixTime(response.headers.expiry);
|
fromUnixTime(response.headers.expiry);
|
||||||
|
@ -28,6 +39,9 @@ export const setAuthCredentials = response => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearCookiesOnLogout = () => {
|
export const clearCookiesOnLogout = () => {
|
||||||
|
window.bus.$emit(CHATWOOT_RESET);
|
||||||
|
window.bus.$emit(ANALYTICS_RESET);
|
||||||
|
|
||||||
Cookies.remove('auth_data');
|
Cookies.remove('auth_data');
|
||||||
Cookies.remove('user');
|
Cookies.remove('user');
|
||||||
window.location = frontendURL('login');
|
window.location = frontendURL('login');
|
||||||
|
|
|
@ -34,6 +34,11 @@ import {
|
||||||
import * as Sentry from '@sentry/vue';
|
import * as Sentry from '@sentry/vue';
|
||||||
import 'vue-easytable/libs/theme-default/index.css';
|
import 'vue-easytable/libs/theme-default/index.css';
|
||||||
import { Integrations } from '@sentry/tracing';
|
import { Integrations } from '@sentry/tracing';
|
||||||
|
import posthog from 'posthog-js';
|
||||||
|
import {
|
||||||
|
initializeAnalyticsEvents,
|
||||||
|
initializeChatwootEvents,
|
||||||
|
} from '../dashboard/helper/scriptHelpers';
|
||||||
|
|
||||||
Vue.config.env = process.env;
|
Vue.config.env = process.env;
|
||||||
|
|
||||||
|
@ -45,6 +50,12 @@ if (window.errorLoggingConfig) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.analyticsConfig) {
|
||||||
|
posthog.init(window.analyticsConfig.token, {
|
||||||
|
api_host: window.analyticsConfig.host,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
Vue.use(WootUiKit);
|
Vue.use(WootUiKit);
|
||||||
|
@ -75,6 +86,9 @@ commonHelpers();
|
||||||
window.WootConstants = constants;
|
window.WootConstants = constants;
|
||||||
window.axios = createAxios(axios);
|
window.axios = createAxios(axios);
|
||||||
window.bus = new Vue();
|
window.bus = new Vue();
|
||||||
|
initializeChatwootEvents();
|
||||||
|
initializeAnalyticsEvents();
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
window.WOOT = new Vue({
|
window.WOOT = new Vue({
|
||||||
router,
|
router,
|
||||||
|
@ -85,6 +99,7 @@ window.onload = () => {
|
||||||
}).$mount('#app');
|
}).$mount('#app');
|
||||||
vueActionCable.init();
|
vueActionCable.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
verifyServiceWorkerExistence(registration =>
|
verifyServiceWorkerExistence(registration =>
|
||||||
registration.pushManager.getSubscription().then(subscription => {
|
registration.pushManager.getSubscription().then(subscription => {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
json.payload do
|
json.payload do
|
||||||
json.success true
|
json.success true
|
||||||
json.partial! 'auth.json.jbuilder', resource: @resource
|
json.partial! 'auth.json.jbuilder', resource: @resource
|
||||||
|
json.data do
|
||||||
|
json.created_at @resource.created_at
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,6 +48,14 @@
|
||||||
}
|
}
|
||||||
window.errorLoggingConfig = '<%= ENV.fetch('SENTRY_DSN', '')%>'
|
window.errorLoggingConfig = '<%= ENV.fetch('SENTRY_DSN', '')%>'
|
||||||
</script>
|
</script>
|
||||||
|
<% if @global_config['ANALYTICS_TOKEN'].present? && @global_config['ANALYTICS_HOST'].present? %>
|
||||||
|
<script>
|
||||||
|
window.analyticsConfig = {
|
||||||
|
token: '<%= @global_config['ANALYTICS_TOKEN'] %>',
|
||||||
|
host: '<%= @global_config['ANALYTICS_HOST'] %>',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% end %>
|
||||||
<%= javascript_pack_tag 'application' %>
|
<%= javascript_pack_tag 'application' %>
|
||||||
<%= stylesheet_pack_tag 'application' %>
|
<%= stylesheet_pack_tag 'application' %>
|
||||||
</head>
|
</head>
|
||||||
|
@ -55,21 +63,14 @@
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<noscript id="noscript">This app works best with JavaScript enabled.</noscript>
|
<noscript id="noscript">This app works best with JavaScript enabled.</noscript>
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
<% @global_config['CHATWOOT_INBOX_TOKEN'] %>
|
<% if @global_config['CHATWOOT_INBOX_TOKEN'].present? %>
|
||||||
<% if @global_config['CHATWOOT_INBOX_TOKEN'] %>
|
|
||||||
<script>
|
<script>
|
||||||
window.chatwootSettings = {
|
window.chatwootSettings = { hideMessageBubble: true, position: 'left' };
|
||||||
hideMessageBubble: true,
|
|
||||||
position: 'left',
|
|
||||||
};
|
|
||||||
(function(d,t) {
|
(function(d,t) {
|
||||||
var BASE_URL="";
|
var BASE_URL="https://app.chatwoot.com";
|
||||||
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||||
g.src=BASE_URL+"/packs/js/sdk.js";
|
g.src=BASE_URL+"/packs/js/sdk.js"; g.defer=true; g.async=true;
|
||||||
g.defer = true;
|
|
||||||
g.async = true;
|
|
||||||
s.parentNode.insertBefore(g,s);
|
s.parentNode.insertBefore(g,s);
|
||||||
s.async=!0;
|
|
||||||
g.onload=function(){
|
g.onload=function(){
|
||||||
window.chatwootSDK.run({
|
window.chatwootSDK.run({
|
||||||
websiteToken: '<%= @global_config['CHATWOOT_INBOX_TOKEN'] %>',
|
websiteToken: '<%= @global_config['CHATWOOT_INBOX_TOKEN'] %>',
|
||||||
|
|
|
@ -40,3 +40,7 @@
|
||||||
value:
|
value:
|
||||||
- name: API_CHANNEL_THUMBNAIL
|
- name: API_CHANNEL_THUMBNAIL
|
||||||
value:
|
value:
|
||||||
|
- name: ANALYTICS_TOKEN
|
||||||
|
value:
|
||||||
|
- name: ANALYTICS_HOST
|
||||||
|
value:
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
"marked": "2.0.3",
|
"marked": "2.0.3",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
|
"posthog-js": "^1.13.7",
|
||||||
"prosemirror-markdown": "1.5.1",
|
"prosemirror-markdown": "1.5.1",
|
||||||
"prosemirror-state": "1.3.4",
|
"prosemirror-state": "1.3.4",
|
||||||
"prosemirror-view": "1.18.4",
|
"prosemirror-view": "1.18.4",
|
||||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -1723,6 +1723,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.4.1.tgz#7c0a4355a1d04321b901197723a8f55c263226e9"
|
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.4.1.tgz#7c0a4355a1d04321b901197723a8f55c263226e9"
|
||||||
integrity sha512-sTu/GaLsLYk1AkAqpkMT4+4q665LtZjhV0hkgiTD4N3zPl5uSf1pCIzxPRYjOpe7NEANmWv8U4PaGKGtc2eMfA==
|
integrity sha512-sTu/GaLsLYk1AkAqpkMT4+4q665LtZjhV0hkgiTD4N3zPl5uSf1pCIzxPRYjOpe7NEANmWv8U4PaGKGtc2eMfA==
|
||||||
|
|
||||||
|
"@sentry/types@^6.11.0":
|
||||||
|
version "6.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.12.0.tgz#b7395688a79403c6df8d8bb8d81deb8222519853"
|
||||||
|
integrity sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==
|
||||||
|
|
||||||
"@sentry/utils@6.4.1":
|
"@sentry/utils@6.4.1":
|
||||||
version "6.4.1"
|
version "6.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.4.1.tgz#55fa7da58898773cbd538e4895fc2e4ec695ecab"
|
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.4.1.tgz#55fa7da58898773cbd538e4895fc2e4ec695ecab"
|
||||||
|
@ -6686,6 +6691,11 @@ fd-slicer@~1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pend "~1.2.0"
|
pend "~1.2.0"
|
||||||
|
|
||||||
|
fflate@^0.4.1:
|
||||||
|
version "0.4.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
|
||||||
|
integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
|
||||||
|
|
||||||
figgy-pudding@^3.5.1:
|
figgy-pudding@^3.5.1:
|
||||||
version "3.5.2"
|
version "3.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
|
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
|
||||||
|
@ -11793,6 +11803,15 @@ postcss@^8.1.10:
|
||||||
nanoid "^3.1.22"
|
nanoid "^3.1.22"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
posthog-js@^1.13.7:
|
||||||
|
version "1.13.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.13.7.tgz#bfb429cda7b5fe497f1bc849ab8ee962ec620311"
|
||||||
|
integrity sha512-t96F3KeqtTaTiaCFtKQFE+pWxvAeWcGyvgH985TMIOZhmeBNDYQHw5oKu4j3gAoLkICzxxbemtpGNY/w6Auu/A==
|
||||||
|
dependencies:
|
||||||
|
"@sentry/types" "^6.11.0"
|
||||||
|
fflate "^0.4.1"
|
||||||
|
rrweb-snapshot "^1.1.7"
|
||||||
|
|
||||||
prelude-ls@~1.1.2:
|
prelude-ls@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||||
|
@ -13041,6 +13060,11 @@ rope-sequence@^1.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b"
|
resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b"
|
||||||
integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==
|
integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==
|
||||||
|
|
||||||
|
rrweb-snapshot@^1.1.7:
|
||||||
|
version "1.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.8.tgz#57c3a8003a6ebbe4a2e2f5be29e30a47a8b1eb03"
|
||||||
|
integrity sha512-wi8T9IVobEwlns7U2m8cbPfoihsNAMpTI+UBe4KUjclU2N+vtowD2U1Rq8PleiFTDvcseHvkQDmEIZoZLXFzwg==
|
||||||
|
|
||||||
rsvp@^4.8.4:
|
rsvp@^4.8.4:
|
||||||
version "4.8.5"
|
version "4.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue