feat: Update the design for the webhook management page (#10050)

This commit is contained in:
Pranav
2024-08-29 20:29:27 +05:30
committed by GitHub
parent 3a47b7e3d1
commit f087461abc
12 changed files with 157 additions and 201 deletions

View File

@@ -1,38 +1,34 @@
<script>
export default {
props: {
text: {
type: String,
default: '',
},
limit: {
type: Number,
default: 120,
},
},
data() {
return {
showMore: false,
};
},
computed: {
textToBeDisplayed() {
if (this.showMore || this.text.length <= this.limit) {
return this.text;
}
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'dashboard/composables/useI18n';
return this.text.slice(0, this.limit) + '...';
},
buttonLabel() {
const i18nKey = !this.showMore ? 'SHOW_MORE' : 'SHOW_LESS';
return this.$t(`COMPONENTS.SHOW_MORE_BLOCK.${i18nKey}`);
},
const props = defineProps({
text: {
type: String,
default: '',
},
methods: {
toggleShowMore() {
this.showMore = !this.showMore;
},
limit: {
type: Number,
default: 120,
},
});
const { t } = useI18n();
const showMore = ref(false);
const textToBeDisplayed = computed(() => {
if (showMore.value || props.text.length <= props.limit) {
return props.text;
}
return props.text.slice(0, props.limit) + '...';
});
const buttonLabel = computed(() => {
const i18nKey = !showMore.value ? 'SHOW_MORE' : 'SHOW_LESS';
return t(`COMPONENTS.SHOW_MORE_BLOCK.${i18nKey}`);
});
const toggleShowMore = () => {
showMore.value = !showMore.value;
};
</script>
@@ -41,16 +37,10 @@ export default {
{{ textToBeDisplayed }}
<button
v-if="text.length > limit"
class="show-more--button"
class="text-woot-500 !p-0 !border-0 align-top"
@click="toggleShowMore"
>
{{ buttonLabel }}
</button>
</span>
</template>
<style scoped>
.show-more--button {
color: var(--w-500);
}
</style>

View File

@@ -17,6 +17,7 @@ const FEATURE_HELP_URLS = {
reports: 'https://chwt.app/hc/reports',
sla: 'https://chwt.app/hc/sla',
team_management: 'https://chwt.app/hc/teams',
webhook: 'https://chwt.app/hc/webhooks',
};
export function getHelpUrlForFeature(featureName) {

View File

@@ -12,6 +12,7 @@
},
"WEBHOOK": {
"SUBSCRIBED_EVENTS": "Subscribed Events",
"LEARN_MORE": "Learn more about webhooks",
"FORM": {
"CANCEL": "Cancel",
"DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.",

View File

@@ -2,7 +2,7 @@
import { useAlert } from 'dashboard/composables';
import EditAttribute from './EditAttribute.vue';
import { useStoreGetters, useStore } from 'dashboard/composables/store';
import { computed, onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'dashboard/composables/useI18n';
const props = defineProps({
attributeModel: {

View File

@@ -3,16 +3,18 @@ import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import NewWebhook from './NewWebHook.vue';
import EditWebhook from './EditWebHook.vue';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import WebhookRow from './WebhookRow.vue';
import BaseSettingsHeader from '../../components/BaseSettingsHeader.vue';
import SettingsLayout from '../../SettingsLayout.vue';
export default {
components: {
SettingsLayout,
BaseSettingsHeader,
NewWebhook,
EditWebhook,
WebhookRow,
},
mixins: [globalConfigMixin],
data() {
return {
loading: {},
@@ -26,8 +28,10 @@ export default {
...mapGetters({
records: 'webhooks/getWebhooks',
uiFlags: 'webhooks/getUIFlags',
globalConfig: 'globalConfig/get',
}),
integration() {
return this.$store.getters['integrations/getIntegration']('webhook');
},
},
mounted() {
this.$store.dispatch('webhooks/get');
@@ -75,69 +79,59 @@ export default {
</script>
<template>
<div class="flex-1 p-4 overflow-auto">
<woot-button
color-scheme="success"
class-names="button--fixed-top"
icon="add-circle"
@click="openAddPopup"
>
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.HEADER_BTN_TXT') }}
</woot-button>
<div class="flex flex-row gap-4">
<div class="w-full lg:w-3/5">
<p
v-if="!uiFlags.fetchingList && !records.length"
class="flex flex-col items-center justify-center h-full"
<SettingsLayout
:is-loading="uiFlags.fetchingList"
:loading-message="$t('INTEGRATION_SETTINGS.WEBHOOK.LOADING')"
:no-records-message="$t('INTEGRATION_SETTINGS.WEBHOOK.LIST.404')"
:no-records-found="!records.length"
>
<template #header>
<BaseSettingsHeader
v-if="integration.name"
:title="integration.name"
:description="integration.description"
:link-text="$t('INTEGRATION_SETTINGS.WEBHOOK.LEARN_MORE')"
feature-name="webhook"
:back-button-label="$t('INTEGRATION_SETTINGS.HEADER')"
>
<template #actions>
<woot-button
class="button nice rounded-md"
icon="add-circle"
@click="openAddPopup"
>
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.HEADER_BTN_TXT') }}
</woot-button>
</template>
</BaseSettingsHeader>
</template>
<template #body>
<table class="min-w-full divide-y divide-slate-75 dark:divide-slate-700">
<thead>
<th
v-for="thHeader in $t(
'INTEGRATION_SETTINGS.WEBHOOK.LIST.TABLE_HEADER'
)"
:key="thHeader"
class="py-4 pr-4 text-left font-semibold text-slate-700 dark:text-slate-300 last:text-right last:pr-4"
>
{{ thHeader }}
</th>
</thead>
<tbody
class="divide-y divide-slate-25 dark:divide-slate-800 flex-1 text-slate-700 dark:text-slate-100"
>
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.LIST.404') }}
</p>
<woot-loading-state
v-if="uiFlags.fetchingList"
:message="$t('INTEGRATION_SETTINGS.WEBHOOK.LOADING')"
/>
<table
v-if="!uiFlags.fetchingList && records.length"
class="woot-table"
>
<thead>
<th
v-for="thHeader in $t(
'INTEGRATION_SETTINGS.WEBHOOK.LIST.TABLE_HEADER'
)"
:key="thHeader"
class="last:text-right"
>
{{ thHeader }}
</th>
</thead>
<tbody>
<WebhookRow
v-for="(webHookItem, index) in records"
:key="webHookItem.id"
:index="index"
:webhook="webHookItem"
@edit="openEditPopup"
@delete="openDeletePopup"
/>
</tbody>
</table>
</div>
<div class="hidden w-1/3 lg:block">
<span
v-dompurify-html="
useInstallationName(
$t('INTEGRATION_SETTINGS.WEBHOOK.SIDEBAR_TXT'),
globalConfig.installationName
)
"
/>
</div>
</div>
<WebhookRow
v-for="(webHookItem, index) in records"
:key="webHookItem.id"
:index="index"
:webhook="webHookItem"
@edit="openEditPopup"
@delete="openDeletePopup"
/>
</tbody>
</table>
</template>
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
<NewWebhook v-if="showAddPopup" :on-close="hideAddPopup" />
</woot-modal>
@@ -163,5 +157,5 @@ export default {
:confirm-text="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.YES')"
:reject-text="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.CONFIRM.NO')"
/>
</div>
</SettingsLayout>
</template>

View File

@@ -1,8 +1,8 @@
<script>
import { useVuelidate } from '@vuelidate/core';
import { required, url, minLength } from '@vuelidate/validators';
import webhookMixin from './webhookMixin';
import wootConstants from 'dashboard/constants/globals';
import { getEventNamei18n } from './webhookHelper';
const { EXAMPLE_WEBHOOK_URL } = wootConstants;
@@ -18,7 +18,6 @@ const SUPPORTED_WEBHOOK_EVENTS = [
];
export default {
mixins: [webhookMixin],
props: {
value: {
type: Object,
@@ -70,6 +69,7 @@ export default {
subscriptions: this.subscriptions,
});
},
getEventNamei18n,
},
};
</script>
@@ -105,10 +105,10 @@ export default {
type="checkbox"
:value="event"
name="subscriptions"
class="checkbox"
class="mr-2"
/>
<label :for="event" class="text-sm">
{{ `${getEventLabel(event)} (${event})` }}
{{ `${$t(getEventNamei18n(event))} (${event})` }}
</label>
</div>
</div>
@@ -129,9 +129,3 @@ export default {
</div>
</form>
</template>
<style lang="scss" scoped>
.checkbox {
@apply mr-2;
}
</style>

View File

@@ -1,59 +1,58 @@
<script>
import webhookMixin from './webhookMixin';
<script setup>
import { computed } from 'vue';
import { getEventNamei18n } from './webhookHelper';
import ShowMore from 'dashboard/components/widgets/ShowMore.vue';
import { useI18n } from 'dashboard/composables/useI18n';
export default {
components: { ShowMore },
mixins: [webhookMixin],
props: {
webhook: {
type: Object,
required: true,
},
index: {
type: Number,
required: true,
},
const props = defineProps({
webhook: {
type: Object,
required: true,
},
computed: {
subscribedEvents() {
const { subscriptions } = this.webhook;
return subscriptions.map(event => this.getEventLabel(event)).join(', ');
},
index: {
type: Number,
required: true,
},
};
});
const { t } = useI18n();
const subscribedEvents = computed(() => {
const { subscriptions } = props.webhook;
return subscriptions.map(event => t(getEventNamei18n(event))).join(', ');
});
</script>
<template>
<tr class="space-x-2">
<td class="max-w-2xl">
<tr>
<td class="py-4 ltr:pr-4 rtl:pl-4">
<div class="font-medium break-words text-slate-700 dark:text-slate-100">
{{ webhook.url }}
</div>
<span class="text-xs text-slate-500 dark:text-slate-400">
<div class="text-sm text-slate-500 dark:text-slate-400 block mt-1">
<span class="font-medium">
{{ $t('INTEGRATION_SETTINGS.WEBHOOK.SUBSCRIBED_EVENTS') }}:
</span>
<ShowMore :text="subscribedEvents" :limit="60" />
</span>
</div>
</td>
<td class="min-w-[7rem] flex gap-1 justify-end flex-shrink-0">
<woot-button
v-tooltip.top="$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.BUTTON_TEXT')"
variant="smooth"
size="tiny"
color-scheme="secondary"
icon="edit"
@click="$emit('edit', webhook)"
/>
<woot-button
v-tooltip.top="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.BUTTON_TEXT')"
variant="smooth"
color-scheme="alert"
size="tiny"
icon="dismiss-circle"
@click="$emit('delete', webhook, index)"
/>
<td class="py-4 min-w-xs">
<div class="flex gap-1 justify-end">
<woot-button
v-tooltip.top="$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.BUTTON_TEXT')"
variant="smooth"
size="tiny"
color-scheme="secondary"
icon="edit"
@click="$emit('edit', webhook)"
/>
<woot-button
v-tooltip.top="$t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.BUTTON_TEXT')"
variant="smooth"
color-scheme="alert"
size="tiny"
icon="dismiss-circle"
@click="$emit('delete', webhook, index)"
/>
</div>
</td>
</tr>
</template>

View File

@@ -0,0 +1,9 @@
import { getEventNamei18n } from '../webhookHelper';
describe('#getEventNamei18n', () => {
it('returns correct i18n translation text', () => {
expect(getEventNamei18n('message_created')).toEqual(
`INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.MESSAGE_CREATED`
);
});
});

View File

@@ -1,26 +0,0 @@
import { createWrapper } from '@vue/test-utils';
import webhookMixin from '../webhookMixin';
import Vue from 'vue';
describe('webhookMixin', () => {
describe('#getEventLabel', () => {
it('returns correct i18n translation:', () => {
const Component = {
render() {},
title: 'WebhookComponent',
mixins: [webhookMixin],
methods: {
$t(text) {
return text;
},
},
};
const Constructor = Vue.extend(Component);
const vm = new Constructor().$mount();
const wrapper = createWrapper(vm);
expect(wrapper.vm.getEventLabel('message_created')).toEqual(
`INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.MESSAGE_CREATED`
);
});
});
});

View File

@@ -0,0 +1,4 @@
export const getEventNamei18n = event => {
const eventName = event.toUpperCase();
return `INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.${eventName}`;
};

View File

@@ -1,10 +0,0 @@
export default {
methods: {
getEventLabel(event) {
const eventName = event.toUpperCase();
return this.$t(
`INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.${eventName}`
);
},
},
};

View File

@@ -30,6 +30,14 @@ export default {
permissions: ['administrator'],
},
},
{
path: 'webhook',
component: Webhook,
name: 'settings_integrations_webhook',
meta: {
permissions: ['administrator'],
},
},
],
},
{
@@ -49,14 +57,6 @@ export default {
};
},
children: [
{
path: 'webhook',
component: Webhook,
name: 'settings_integrations_webhook',
meta: {
permissions: ['administrator'],
},
},
{
path: 'slack',
name: 'settings_integrations_slack',