feat: Update the slack integration-flow to allow users to select the channel (#7637)

This commit is contained in:
Pranav Raj S
2023-07-28 14:50:30 -07:00
committed by GitHub
parent 4d8ba0148c
commit 9ddd428935
28 changed files with 593 additions and 131 deletions

View File

@@ -1,23 +1,23 @@
<template>
<div class="flex">
<div class="flex h-[6.25rem] w-[6.25rem]">
<div class="flex flex-col md:flex-row items-start md:items-center">
<div class="flex items-center justify-center m-0 mx-4 flex-1">
<img
:src="'/dashboard/images/integrations/' + integrationLogo"
class="max-w-full p-6"
class="p-2 h-16 w-16 mr-4"
/>
</div>
<div class="flex flex-col justify-center m-0 mx-4 flex-1">
<h3 class="text-xl text-slate-800 dark:text-slate-100">
{{ integrationName }}
</h3>
<p>
{{
useInstallationName(
integrationDescription,
globalConfig.installationName
)
}}
</p>
<div>
<h3 class="text-xl text-slate-800 dark:text-slate-100">
{{ integrationName }}
</h3>
<p>
{{
useInstallationName(
integrationDescription,
globalConfig.installationName
)
}}
</p>
</div>
</div>
<div class="flex justify-center items-center mb-0 w-[15%]">
<router-link
@@ -29,13 +29,13 @@
>
<div v-if="integrationEnabled">
<div v-if="integrationAction === 'disconnect'">
<div @click="openDeletePopup()">
<div @click="openDeletePopup">
<woot-submit-button
:button-text="
$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.BUTTON_TEXT')
actionButtonText ||
$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.BUTTON_TEXT')
"
icon-class="dismiss-circle"
button-class="nice alert"
button-class="smooth alert"
/>
</div>
</div>
@@ -56,8 +56,14 @@
:show.sync="showDeleteConfirmationPopup"
:on-close="closeDeletePopup"
:on-confirm="confirmDeletion"
:title="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.TITLE')"
:message="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.MESSAGE')"
:title="
deleteConfirmationText.title ||
$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.TITLE')
"
:message="
deleteConfirmationText.message ||
$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.MESSAGE')
"
:confirm-text="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.YES')"
:reject-text="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.NO')"
/>
@@ -81,6 +87,8 @@ export default {
integrationDescription: { type: String, default: '' },
integrationEnabled: { type: Boolean, default: false },
integrationAction: { type: String, default: '' },
actionButtonText: { type: String, default: '' },
deleteConfirmationText: { type: Object, default: () => ({}) },
},
data() {
return {

View File

@@ -65,7 +65,7 @@ export default {
}),
},
mounted() {
this.intializeSlackIntegration();
this.fetchIntegrations();
},
methods: {
integrationAction() {
@@ -74,13 +74,8 @@ export default {
}
return this.integration.action;
},
async intializeSlackIntegration() {
async fetchIntegrations() {
await this.$store.dispatch('integrations/get', this.integrationId);
if (this.code) {
await this.$store.dispatch('integrations/connectSlack', this.code);
// we are clearing code from the path as subsequent request would throw error
this.$router.replace(this.$route.path);
}
this.integrationLoaded = true;
},
},

View File

@@ -0,0 +1,108 @@
<template>
<div
v-if="integrationLoaded && !uiFlags.isCreatingSlack"
class="flex flex-col flex-1 overflow-auto"
>
<div
class="bg-white dark:bg-slate-800 border-b border-solid border-slate-75 dark:border-slate-700/50 rounded-sm p-4"
>
<integration
:integration-id="integration.id"
:integration-logo="integration.logo"
:integration-name="integration.name"
:integration-description="integration.description"
:integration-enabled="integration.enabled"
:integration-action="integrationAction"
:action-button-text="$t('INTEGRATION_SETTINGS.SLACK.DELETE')"
:delete-confirmation-text="{
title: $t('INTEGRATION_SETTINGS.SLACK.DELETE_CONFIRMATION.TITLE'),
message: $t('INTEGRATION_SETTINGS.SLACK.DELETE_CONFIRMATION.MESSAGE'),
}"
/>
</div>
<div v-if="areHooksAvailable" class="p-6 flex-1">
<select-channel-warning v-if="!isIntegrationHookEnabled" />
<slack-integration-help-text
:selected-channel-name="selectedChannelName"
/>
</div>
</div>
<div v-else class="flex flex-1 items-center justify-center">
<spinner size="" color-scheme="primary" />
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import Integration from './Integration.vue';
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
import SelectChannelWarning from './Slack/SelectChannelWarning.vue';
import SlackIntegrationHelpText from './Slack/SlackIntegrationHelpText.vue';
import Spinner from 'shared/components/Spinner';
export default {
components: {
Spinner,
Integration,
SelectChannelWarning,
SlackIntegrationHelpText,
},
mixins: [globalConfigMixin, messageFormatterMixin],
props: {
code: { type: String, default: '' },
},
data() {
return { integrationLoaded: false };
},
computed: {
integration() {
return this.$store.getters['integrations/getIntegration']('slack');
},
areHooksAvailable() {
const { hooks = [] } = this.integration || {};
return !!hooks.length;
},
isIntegrationHookEnabled() {
const { hooks = [] } = this.integration || {};
const [hook = {}] = hooks;
return hook.status || false;
},
selectedChannelName() {
const { hooks = [] } = this.integration || {};
const [hook = {}] = hooks;
if (hook.status) {
const { settings: { channel_name: channelName = '' } = {} } = hook;
return channelName || 'customer-conversations';
}
return this.$t('INTEGRATION_SETTINGS.SLACK.HELP_TEXT.SELECTED');
},
...mapGetters({
currentUser: 'getCurrentUser',
globalConfig: 'globalConfig/get',
accountId: 'getCurrentAccountId',
uiFlags: 'integrations/getUIFlags',
}),
integrationAction() {
if (this.integration.enabled) {
return 'disconnect';
}
return this.integration.action;
},
},
mounted() {
this.intializeSlackIntegration();
},
methods: {
async intializeSlackIntegration() {
await this.$store.dispatch('integrations/get', 'slack');
if (this.code) {
await this.$store.dispatch('integrations/connectSlack', this.code);
// Clear the query param `code` from the URL as the
// subsequent reloads would result in an error
this.$router.replace(this.$route.path);
}
this.integrationLoaded = true;
},
},
};
</script>

View File

@@ -0,0 +1,117 @@
<template>
<div
class="rounded-md bg-yellow-50 border border-yellow-200 dark:border-slate-700 dark:bg-slate-800 px-6 py-4 mb-4"
>
<div class="flex">
<div class="flex-shrink-0 mt-0.5">
<fluent-icon
icon="alert"
class="text-yellow-500 dark:text-yellow-400"
size="24"
/>
</div>
<div class="ml-3">
<p
class="text-base font-semibold text-yellow-900 dark:text-yellow-500 mb-1"
>
{{
$t('INTEGRATION_SETTINGS.SLACK.SELECT_CHANNEL.ATTENTION_REQUIRED')
}}
</p>
<div class="text-sm text-yellow-800 dark:text-yellow-600 mt-2">
<p
v-dompurify-html="
formatMessage(
useInstallationName(
$t('INTEGRATION_SETTINGS.SLACK.SELECT_CHANNEL.DESCRIPTION'),
globalConfig.installationName
),
false
)
"
/>
</div>
</div>
</div>
<div class="ml-8 mt-2">
<woot-submit-button
v-if="!availableChannels.length"
button-class="smooth small warning"
:loading="uiFlags.isFetchingSlackChannels"
:button-text="
$t('INTEGRATION_SETTINGS.SLACK.SELECT_CHANNEL.BUTTON_TEXT')
"
spinner-class="warning"
@click="fetchChannels"
/>
<div v-else class="inline-flex">
<select
v-model="selectedChannelId"
class="h-8 border-yellow-300 border mr-4 text-xs leading-4 py-1"
>
<option value="">
{{ $t('INTEGRATION_SETTINGS.SLACK.SELECT_CHANNEL.OPTION_LABEL') }}
</option>
<option
v-for="channel in availableChannels"
:key="channel.id"
:value="channel.id"
>
#{{ channel.name }}
</option>
</select>
<woot-submit-button
button-class="smooth small success"
:button-text="$t('INTEGRATION_SETTINGS.SLACK.SELECT_CHANNEL.UPDATE')"
spinner-class="success"
:loading="uiFlags.isUpdatingSlack"
@click="updateIntegration"
/>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
import alertMixin from 'shared/mixins/alertMixin';
export default {
mixins: [alertMixin, globalConfigMixin, messageFormatterMixin],
data() {
return { selectedChannelId: '', availableChannels: [] };
},
computed: {
...mapGetters({
globalConfig: 'globalConfig/get',
uiFlags: 'integrations/getUIFlags',
}),
},
methods: {
async fetchChannels() {
try {
this.availableChannels = await this.$store.dispatch(
'integrations/listAllSlackChannels'
);
this.availableChannels.sort((c1, c2) => c1.name - c2.name);
} catch {
this.$t('INTEGRATION_SETTINGS.SLACK.FAILED_TO_FETCH_CHANNELS');
this.availableChannels = [];
}
},
async updateIntegration() {
try {
await this.$store.dispatch('integrations/updateSlack', {
referenceId: this.selectedChannelId,
});
this.showAlert(this.$t('INTEGRATION_SETTINGS.SLACK.UPDATE_SUCCESS'));
} catch (error) {
this.showAlert(
error.message || 'INTEGRATION_SETTINGS.SLACK.UPDATE_ERROR'
);
}
},
},
};
</script>

View File

@@ -0,0 +1,35 @@
<template>
<div
class="flex-1 w-full p-6 bg-white rounded-md border border-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 "
>
<div class="prose-lg max-w-5xl">
<h5 class="dark:text-slate-100">
{{ $t('INTEGRATION_SETTINGS.SLACK.HELP_TEXT.TITLE') }}
</h5>
<p>
<span
v-dompurify-html="
formatMessage(
$t('INTEGRATION_SETTINGS.SLACK.HELP_TEXT.BODY', {
selectedChannelName: selectedChannelName,
}),
false
)
"
/>
</p>
</div>
</div>
</template>
<script>
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
export default {
mixins: [messageFormatterMixin],
props: {
selectedChannelName: {
type: String,
required: true,
},
},
};
</script>

View File

@@ -3,6 +3,7 @@ import SettingsContent from '../Wrapper';
import Webhook from './Webhooks/Index';
import DashboardApps from './DashboardApps/Index';
import ShowIntegration from './ShowIntegration';
import Slack from './Slack';
import { frontendURL } from '../../../../helper/URLHelper';
export default {
@@ -42,6 +43,13 @@ export default {
name: 'settings_integrations_dashboard_apps',
roles: ['administrator'],
},
{
path: 'slack',
name: 'settings_integrations_slack',
component: Slack,
roles: ['administrator'],
props: route => ({ code: route.query.code }),
},
{
path: ':integration_id',
name: 'settings_integrations_integration',