feat: Add the UI flow for Microsoft Oauth (#6243)

This commit is contained in:
Pranav Raj S
2023-01-17 04:18:14 -08:00
committed by GitHub
parent 66cb0ee865
commit f6a56edf86
11 changed files with 273 additions and 105 deletions

View File

@@ -55,7 +55,8 @@ class DashboardController < ActionController::Base
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'), ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''), FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
FACEBOOK_API_VERSION: 'v14.0', FACEBOOK_API_VERSION: 'v14.0',
IS_ENTERPRISE: ChatwootApp.enterprise? IS_ENTERPRISE: ChatwootApp.enterprise?,
AZURE_APP_ID: ENV.fetch('AZURE_APP_ID', '')
} }
end end
end end

View File

@@ -0,0 +1,14 @@
/* global axios */
import ApiClient from '../ApiClient';
class MicrosoftClient extends ApiClient {
constructor() {
super('microsoft', { accountScoped: true });
}
generateAuthorization(payload) {
return axios.post(`${this.url}/authorization`, payload);
}
}
export default new MicrosoftClient();

View File

@@ -36,7 +36,7 @@ export default {
margin: -1px; margin: -1px;
padding: var(--space-normal) 0; padding: var(--space-normal) 0;
transition: all 0.2s ease-in; transition: all 0.2s ease-in;
align-items: center;
&:hover { &:hover {
border: 1px solid var(--w-500); border: 1px solid var(--w-500);
box-shadow: var(--shadow-medium); box-shadow: var(--shadow-medium);

View File

@@ -53,10 +53,6 @@
"ENABLE": "Create conversations from mentioned Tweets" "ENABLE": "Create conversations from mentioned Tweets"
} }
}, },
"MICROSOFT": {
"HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ",
"ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again"
},
"WEBSITE_CHANNEL": { "WEBSITE_CHANNEL": {
"TITLE": "Website channel", "TITLE": "Website channel",
"DESC": "Create a channel for your website and start supporting your customers via our website widget.", "DESC": "Create a channel for your website and start supporting your customers via our website widget.",
@@ -218,7 +214,7 @@
"PHONE_NUMBER": { "PHONE_NUMBER": {
"LABEL": "Phone number", "LABEL": "Phone number",
"PLACEHOLDER": "Please enter the phone number from which message will be sent.", "PLACEHOLDER": "Please enter the phone number from which message will be sent.",
"ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces."
}, },
"PHONE_NUMBER_ID": { "PHONE_NUMBER_ID": {
"LABEL": "Phone number ID", "LABEL": "Phone number ID",
@@ -348,6 +344,17 @@
"FINISH": { "FINISH": {
"TITLE": "Nailed It!", "TITLE": "Nailed It!",
"DESC": "You have successfully finished integrating your Facebook Page with Chatwoot. Next time a customer messages your Page, the conversation will automatically appear on your inbox.<br>We are also providing you with a widget script that you can easily add to your website. Once this is live on your website, customers can message you right from your website without the help of any external tool and the conversation will appear right here, on Chatwoot.<br>Cool, huh? Well, we sure try to be :)" "DESC": "You have successfully finished integrating your Facebook Page with Chatwoot. Next time a customer messages your Page, the conversation will automatically appear on your inbox.<br>We are also providing you with a widget script that you can easily add to your website. Once this is live on your website, customers can message you right from your website without the help of any external tool and the conversation will appear right here, on Chatwoot.<br>Cool, huh? Well, we sure try to be :)"
},
"EMAIL_PROVIDER": {
"TITLE": "Select your email provider",
"DESCRIPTION": "Select an email provider from the list below. If you don't see your email provider in the list, you can select the other provider option and provide the IMAP and SMTP Credentials."
},
"MICROSOFT": {
"TITLE": "Microsoft Email",
"DESCRIPTION": "Click on the Sign in with Microsoft button to get started. You will redirected to the email sign in page. Once you accept the requested permissions, you would be redirected back to the inbox creation step.",
"EMAIL_PLACEHOLDER": "Enter email address",
"HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ",
"ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again"
} }
}, },
"DETAILS": { "DETAILS": {
@@ -674,6 +681,10 @@
}, },
"BRANDING_TEXT": "Powered by Chatwoot", "BRANDING_TEXT": "Powered by Chatwoot",
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};" "SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
"MICROSOFT": "Microsoft",
"OTHER_PROVIDERS": "Other Providers"
} }
} }
} }

View File

@@ -50,12 +50,11 @@
:script="currentInbox.callback_webhook_url" :script="currentInbox.callback_webhook_url"
/> />
</div> </div>
<div class="medium-6 small-offset-3"> <div
<woot-code v-if="isAEmailInbox && !currentInbox.provider"
v-if="isAEmailInbox" class="medium-6 small-offset-3"
lang="html" >
:script="currentInbox.forward_to_email" <woot-code lang="html" :script="currentInbox.forward_to_email" />
/>
</div> </div>
<div class="footer"> <div class="footer">
<router-link <router-link
@@ -140,7 +139,7 @@ export default {
)}`; )}`;
} }
if (this.isAEmailInbox) { if (this.isAEmailInbox && !this.currentInbox.provider) {
return this.$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE'); return this.$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE');
} }

View File

@@ -470,7 +470,7 @@ export default {
this.isATwilioChannel || this.isATwilioChannel ||
this.isALineChannel || this.isALineChannel ||
this.isAPIInbox || this.isAPIInbox ||
this.isAnEmailChannel || (this.isAnEmailChannel && !this.inbox.provider) ||
this.isAWhatsAppChannel || this.isAWhatsAppChannel ||
this.isAWebWidgetInbox this.isAWebWidgetInbox
) { ) {

View File

@@ -1,111 +1,71 @@
<template> <template>
<div class="wizard-body small-9 columns"> <div
v-if="!provider"
class="wizard-body small-12 medium-9 columns height-auto"
>
<page-header <page-header
:header-title="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.DESCRIPTION')"
/> />
<form class="row" @submit.prevent="createChannel()"> <div class="row channel-list">
<div class="medium-8 columns"> <channel-selector
<label :class="{ error: $v.channelName.$error }"> v-for="emailProvider in emailProviderList"
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.LABEL') }} :key="emailProvider.key"
<input :title="emailProvider.title"
v-model.trim="channelName" :src="emailProvider.src"
type="text" @click="() => onClick(emailProvider.key)"
:placeholder=" />
$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.PLACEHOLDER') </div>
"
@blur="$v.channelName.$touch"
/>
<span v-if="$v.channelName.$error" class="message">{{
$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.ERROR')
}}</span>
</label>
</div>
<div class="medium-8 columns">
<label :class="{ error: $v.email.$error }">
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.LABEL') }}
<input
v-model.trim="email"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.PLACEHOLDER')"
@blur="$v.email.$touch"
/>
</label>
<p class="help-text">
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.SUBTITLE') }}
</p>
</div>
<div class="medium-12 columns">
<woot-submit-button
:loading="uiFlags.isCreating"
:button-text="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.SUBMIT_BUTTON')"
/>
</div>
</form>
</div> </div>
<microsoft v-else-if="provider === 'microsoft'" />
<forward-to-option v-else-if="provider === 'other_provider'" />
</template> </template>
<script> <script>
import ForwardToOption from './emailChannels/ForwardToOption';
import Microsoft from './emailChannels/Microsoft';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin'; import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
import { required, email } from 'vuelidate/lib/validators';
import router from '../../../../index';
import PageHeader from '../../SettingsSubPageHeader'; import PageHeader from '../../SettingsSubPageHeader';
export default { export default {
components: { components: {
ChannelSelector,
ForwardToOption,
Microsoft,
PageHeader, PageHeader,
}, },
mixins: [alertMixin],
data() { data() {
return { return { provider: '' };
channelName: '',
email: '',
};
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({ globalConfig: 'globalConfig/get' }),
uiFlags: 'inboxes/getUIFlags', emailProviderList() {
}), return [
}, {
validations: { title: this.$t('INBOX_MGMT.EMAIL_PROVIDERS.MICROSOFT'),
channelName: { required }, isEnabled: !!this.globalConfig.azureAppId,
email: { required, email }, key: 'microsoft',
src: '/assets/images/dashboard/channels/microsoft.png',
},
{
title: this.$t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS'),
isEnabled: true,
key: 'other_provider',
src: '/assets/images/dashboard/channels/email.png',
},
].filter(provider => provider.isEnabled);
},
}, },
methods: { methods: {
async createChannel() { onClick(provider) {
this.$v.$touch(); this.provider = provider;
if (this.$v.$invalid) {
return;
}
try {
const emailChannel = await this.$store.dispatch(
'inboxes/createChannel',
{
name: this.channelName,
channel: {
type: 'email',
email: this.email,
},
}
);
router.replace({
name: 'settings_inboxes_add_agents',
params: {
page: 'new',
inbox_id: emailChannel.id,
},
});
} catch (error) {
this.showAlert(
this.$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.API.ERROR_MESSAGE')
);
}
}, },
}, },
}; };
</script> </script>
<style scoped>
.channel-list {
margin-top: var(--space-medium);
}
</style>

View File

@@ -0,0 +1,111 @@
<template>
<div class="wizard-body small-9 columns">
<page-header
:header-title="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')"
/>
<form class="row" @submit.prevent="createChannel()">
<div class="medium-8 columns">
<label :class="{ error: $v.channelName.$error }">
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.LABEL') }}
<input
v-model.trim="channelName"
type="text"
:placeholder="
$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.PLACEHOLDER')
"
@blur="$v.channelName.$touch"
/>
<span v-if="$v.channelName.$error" class="message">{{
$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.ERROR')
}}</span>
</label>
</div>
<div class="medium-8 columns">
<label :class="{ error: $v.email.$error }">
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.LABEL') }}
<input
v-model.trim="email"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.PLACEHOLDER')"
@blur="$v.email.$touch"
/>
</label>
<p class="help-text">
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.SUBTITLE') }}
</p>
</div>
<div class="medium-12 columns">
<woot-submit-button
:loading="uiFlags.isCreating"
:button-text="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.SUBMIT_BUTTON')"
/>
</div>
</form>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import { required, email } from 'vuelidate/lib/validators';
import router from '../../../../../index';
import PageHeader from '../../../SettingsSubPageHeader';
export default {
components: {
PageHeader,
},
mixins: [alertMixin],
data() {
return {
channelName: '',
email: '',
};
},
computed: {
...mapGetters({
uiFlags: 'inboxes/getUIFlags',
}),
},
validations: {
channelName: { required },
email: { required, email },
},
methods: {
async createChannel() {
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
try {
const emailChannel = await this.$store.dispatch(
'inboxes/createChannel',
{
name: this.channelName,
channel: {
type: 'email',
email: this.email,
},
}
);
router.replace({
name: 'settings_inboxes_add_agents',
params: {
page: 'new',
inbox_id: emailChannel.id,
},
});
} catch (error) {
this.showAlert(
this.$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.API.ERROR_MESSAGE')
);
}
},
},
};
</script>

View File

@@ -0,0 +1,69 @@
<template>
<div class="wizard-body columns content-box small-9">
<settings-sub-page-header
:header-title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')"
/>
<form
class="microsoft--sign-in-form"
@submit.prevent="requestAuthorization"
>
<woot-input
v-model.trim="email"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.MICROSOFT.EMAIL_PLACEHOLDER')"
@blur="$v.email.$touch"
/>
<woot-submit-button
icon="brand-twitter"
button-text="Sign in with Microsoft"
type="submit"
:loading="isRequestingAuthorization"
/>
</form>
</div>
</template>
<script>
import alertMixin from 'shared/mixins/alertMixin';
import microsoftClient from 'dashboard/api/channel/microsoftClient';
import SettingsSubPageHeader from '../../../SettingsSubPageHeader.vue';
import { required, email } from 'vuelidate/lib/validators';
export default {
components: { SettingsSubPageHeader },
mixins: [alertMixin],
data() {
return { email: '', isRequestingAuthorization: false };
},
validations: {
email: { required, email },
},
methods: {
async requestAuthorization() {
try {
this.$v.$touch();
if (this.$v.$invalid) return;
this.isRequestingAuthorization = true;
const response = await microsoftClient.generateAuthorization({
email: this.email,
});
const {
data: { url },
} = response;
window.location.href = url;
} catch (error) {
this.showAlert(this.$t('INBOX_MGMT.ADD.MICROSOFT.ERROR_MESSAGE'));
} finally {
this.isRequestingAuthorization = false;
}
},
},
};
</script>
<style scoped>
.microsoft--sign-in-form {
margin-top: var(--space-medium);
}
</style>

View File

@@ -2,6 +2,7 @@ const {
API_CHANNEL_NAME: apiChannelName, API_CHANNEL_NAME: apiChannelName,
API_CHANNEL_THUMBNAIL: apiChannelThumbnail, API_CHANNEL_THUMBNAIL: apiChannelThumbnail,
APP_VERSION: appVersion, APP_VERSION: appVersion,
AZURE_APP_ID: azureAppId,
BRAND_NAME: brandName, BRAND_NAME: brandName,
CHATWOOT_INBOX_TOKEN: chatwootInboxToken, CHATWOOT_INBOX_TOKEN: chatwootInboxToken,
CSML_EDITOR_HOST: csmlEditorHost, CSML_EDITOR_HOST: csmlEditorHost,
@@ -23,6 +24,7 @@ const state = {
apiChannelName, apiChannelName,
apiChannelThumbnail, apiChannelThumbnail,
appVersion, appVersion,
azureAppId,
brandName, brandName,
chatwootInboxToken, chatwootInboxToken,
csmlEditorHost, csmlEditorHost,

View File

@@ -89,9 +89,10 @@ if resource.api?
json.additional_attributes resource.channel.try(:additional_attributes) json.additional_attributes resource.channel.try(:additional_attributes)
end end
json.provider resource.channel.try(:provider)
### WhatsApp Channel ### WhatsApp Channel
if resource.whatsapp? if resource.whatsapp?
json.provider resource.channel.try(:provider)
json.message_templates resource.channel.try(:message_templates) json.message_templates resource.channel.try(:message_templates)
json.provider_config resource.channel.try(:provider_config) if Current.account_user&.administrator? json.provider_config resource.channel.try(:provider_config) if Current.account_user&.administrator?
end end