chore: Update settings to match the new design (#11084)

This commit is contained in:
Sivin Varghese
2025-03-14 14:28:14 +05:30
committed by GitHub
parent 325dc4a741
commit ed970ee190
10 changed files with 224 additions and 217 deletions
@@ -19,12 +19,21 @@ const { t } = useI18n();
const showNewButton = computed(
() => props.newButtonRoutes.length && !props.showBackButton
);
const showSettingsHeader = computed(
() =>
props.headerTitle ||
props.icon ||
props.showBackButton ||
showNewButton.value
);
</script>
<template>
<div class="flex flex-1 flex-col m-0 bg-n-background overflow-auto">
<div class="max-w-6xl mx-auto w-full flex flex-col flex-1">
<SettingsHeader
v-if="showSettingsHeader"
button-route="new"
:icon="icon"
:header-title="t(headerTitle)"
@@ -1,59 +1,87 @@
<script>
import { mapGetters } from 'vuex';
<script setup>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useMapGetter, useStore } from 'dashboard/composables/store';
import { useAlert } from 'dashboard/composables';
import { frontendURL } from '../../../../helper/URLHelper';
import { useI18n } from 'vue-i18n';
import { frontendURL } from 'dashboard/helper/URLHelper';
import AgentBotRow from './components/AgentBotRow.vue';
export default {
components: { AgentBotRow },
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
agentBots: 'agentBots/getBots',
uiFlags: 'agentBots/getUIFlags',
}),
newAgentBotsURL() {
return frontendURL(
`accounts/${this.accountId}/settings/agent-bots/csml/new`
);
},
},
mounted() {
this.$store.dispatch('agentBots/get');
},
methods: {
async onDeleteAgentBot(bot) {
const ok = await this.$refs.confirmDialog.showConfirmation();
if (ok) {
try {
await this.$store.dispatch('agentBots/delete', bot.id);
useAlert(this.$t('AGENT_BOTS.DELETE.API.SUCCESS_MESSAGE'));
} catch (error) {
useAlert(this.$t('AGENT_BOTS.DELETE.API.ERROR_MESSAGE'));
}
}
},
onEditAgentBot(bot) {
this.$router.push(
frontendURL(
`accounts/${this.accountId}/settings/agent-bots/csml/${bot.id}`
)
);
},
},
import SettingsLayout from '../SettingsLayout.vue';
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
const router = useRouter();
const store = useStore();
const { t } = useI18n();
const accountId = useMapGetter('getCurrentAccountId');
const agentBots = useMapGetter('agentBots/getBots');
const uiFlags = useMapGetter('agentBots/getUIFlags');
const confirmDialog = ref(null);
const onConfigureNewBot = () => {
router.push({
name: 'agent_bots_csml_new',
});
};
onMounted(() => {
store.dispatch('agentBots/get');
});
const onDeleteAgentBot = async bot => {
const ok = await confirmDialog.value.showConfirmation();
if (ok) {
try {
await store.dispatch('agentBots/delete', bot.id);
useAlert(t('AGENT_BOTS.DELETE.API.SUCCESS_MESSAGE'));
} catch (error) {
useAlert(t('AGENT_BOTS.DELETE.API.ERROR_MESSAGE'));
}
}
};
const onEditAgentBot = bot => {
router.push(
frontendURL(
`accounts/${accountId.value}/settings/agent-bots/csml/${bot.id}`
)
);
};
</script>
<template>
<div class="flex-1 p-4 overflow-auto">
<div class="flex flex-row gap-4">
<div class="w-full lg:w-3/5">
<woot-loading-state
v-if="uiFlags.isFetching"
:message="$t('AGENT_BOTS.LIST.LOADING')"
/>
<table v-else-if="agentBots.length" class="woot-table">
<tbody>
<SettingsLayout
:is-loading="uiFlags.isFetching"
:loading-message="$t('AGENT_BOTS.LIST.LOADING')"
:no-records-found="!agentBots.length"
:no-records-message="$t('AGENT_BOTS.LIST.404')"
>
<template #header>
<BaseSettingsHeader
:title="$t('AGENT_BOTS.HEADER')"
:description="$t('AGENT_BOTS.DESCRIPTION')"
:link-text="$t('AGENT_BOTS.LEARN_MORE')"
feature-name="agent_bots"
>
<template #actions>
<woot-button
class="rounded-md button nice"
icon="add-circle"
@click="onConfigureNewBot"
>
{{ $t('AGENT_BOTS.ADD.TITLE') }}
</woot-button>
</template>
</BaseSettingsHeader>
</template>
<template #body>
<div class="flex-1 overflow-auto">
<table class="divide-y divide-slate-75 dark:divide-slate-700">
<tbody class="divide-y divide-n-weak text-n-slate-11">
<AgentBotRow
v-for="(agentBot, index) in agentBots"
:key="agentBot.id"
@@ -64,42 +92,12 @@ export default {
/>
</tbody>
</table>
<p v-else class="flex flex-col items-center justify-center h-full">
{{ $t('AGENT_BOTS.LIST.404') }}
</p>
<woot-confirm-modal
ref="confirmDialog"
:title="$t('AGENT_BOTS.DELETE.TITLE')"
:description="$t('AGENT_BOTS.DELETE.DESCRIPTION')"
/>
</div>
<div class="hidden w-1/3 lg:block">
<p v-dompurify-html="$t('AGENT_BOTS.SIDEBAR_TXT')" />
</div>
</div>
<woot-button
color-scheme="success"
class-names="button--fixed-top"
icon="add-circle"
>
<router-link :to="newAgentBotsURL" class="white-text">
{{ $t('AGENT_BOTS.ADD.TITLE') }}
</router-link>
</woot-button>
<woot-confirm-modal
ref="confirmDialog"
:title="$t('AGENT_BOTS.DELETE.TITLE')"
:description="$t('AGENT_BOTS.DELETE.DESCRIPTION')"
/>
</div>
</template>
</SettingsLayout>
</template>
<style scoped>
.bots-list {
list-style: none;
}
.nowrap {
white-space: nowrap;
}
.white-text {
color: white;
}
</style>
@@ -4,6 +4,7 @@ import CsmlEditBot from './csml/Edit.vue';
import CsmlNewBot from './csml/New.vue';
import { frontendURL } from '../../../../helper/URLHelper';
import SettingsContent from '../Wrapper.vue';
import SettingsWrapper from '../SettingsWrapper.vue';
export default {
routes: [
@@ -12,12 +13,7 @@ export default {
meta: {
permissions: ['administrator'],
},
component: SettingsContent,
props: {
headerTitle: 'AGENT_BOTS.HEADER',
icon: 'bot',
showNewButton: false,
},
component: SettingsWrapper,
children: [
{
path: '',
@@ -28,6 +24,19 @@ export default {
permissions: ['administrator'],
},
},
],
},
{
path: frontendURL('accounts/:accountId/settings/agent-bots'),
component: SettingsContent,
props: () => {
return {
headerTitle: 'AGENT_BOTS.HEADER',
icon: 'bot',
showBackButton: true,
};
},
children: [
{
path: 'csml/new',
name: 'agent_bots_csml_new',
@@ -1,81 +1,58 @@
<script>
<script setup>
import { computed } from 'vue';
import ShowMore from 'dashboard/components/widgets/ShowMore.vue';
import AgentBotType from './AgentBotType.vue';
export default {
components: { ShowMore, AgentBotType },
props: {
agentBot: {
type: Object,
required: true,
},
index: {
type: Number,
required: true,
},
const props = defineProps({
agentBot: {
type: Object,
required: true,
},
emits: ['edit', 'delete'],
computed: {
isACSMLTypeBot() {
const { bot_type: botType } = this.agentBot;
return botType === 'csml';
},
index: {
type: Number,
required: true,
},
};
});
const emit = defineEmits(['edit', 'delete']);
const isACSMLTypeBot = computed(() => {
const { bot_type: botType } = props.agentBot;
return botType === 'csml';
});
</script>
<template>
<tr class="space-x-2">
<td class="agent-bot--details">
<div class="agent-bot--link">
<td class="py-4 ltr:pl-0 ltr:pr-4 rtl:pl-4 rtl:pr-0">
<div class="flex items-center break-words font-medium">
{{ agentBot.name }}
(<AgentBotType :bot-type="agentBot.bot_type" />)
</div>
<div class="agent-bot--description">
<div class="text-sm">
<ShowMore :text="agentBot.description || ''" :limit="120" />
</div>
</td>
<td class="flex justify-end gap-1">
<woot-button
v-if="isACSMLTypeBot"
v-tooltip.top="$t('AGENT_BOTS.EDIT.BUTTON_TEXT')"
variant="smooth"
size="tiny"
color-scheme="secondary"
icon="edit"
@click="$emit('edit', agentBot)"
/>
<woot-button
v-tooltip.top="$t('AGENT_BOTS.DELETE.BUTTON_TEXT')"
variant="smooth"
color-scheme="alert"
size="tiny"
icon="dismiss-circle"
@click="$emit('delete', agentBot, index)"
/>
<td class="align-middle">
<div class="flex justify-end gap-1 h-full items-center">
<woot-button
v-if="isACSMLTypeBot"
v-tooltip.top="$t('AGENT_BOTS.EDIT.BUTTON_TEXT')"
variant="smooth"
size="tiny"
color-scheme="secondary"
icon="edit"
@click="emit('edit', agentBot)"
/>
<woot-button
v-tooltip.top="$t('AGENT_BOTS.DELETE.BUTTON_TEXT')"
variant="smooth"
color-scheme="alert"
size="tiny"
icon="dismiss-circle"
@click="emit('delete', agentBot, index)"
/>
</div>
</td>
</tr>
</template>
<style scoped lang="scss">
.agent-bot--link {
align-items: center;
display: flex;
font-weight: var(--font-weight-medium);
word-break: break-word;
}
.agent-bot--description {
font-size: var(--font-size-mini);
}
.agent-bot--type {
color: var(--s-600);
font-weight: var(--font-weight-medium);
margin-bottom: var(--space-small);
}
.agent-bot--details {
width: 90%;
}
</style>
@@ -1,43 +1,36 @@
<script>
export default {
props: {
botType: {
type: String,
default: 'webhook',
},
<script setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
defineProps({
botType: {
type: String,
default: 'webhook',
},
data() {
return {
botTypeConfig: {
csml: {
label: this.$t('AGENT_BOTS.TYPES.CSML'),
thumbnail: '/dashboard/images/agent-bots/csml.png',
},
webhook: {
label: this.$t('AGENT_BOTS.TYPES.WEBHOOK'),
thumbnail: '/dashboard/images/agent-bots/webhook.svg',
},
},
};
});
const { t } = useI18n();
const botTypeConfig = computed(() => ({
csml: {
label: t('AGENT_BOTS.TYPES.CSML'),
thumbnail: '/dashboard/images/agent-bots/csml.png',
},
};
webhook: {
label: t('AGENT_BOTS.TYPES.WEBHOOK'),
thumbnail: '/dashboard/images/agent-bots/webhook.svg',
},
}));
</script>
<template>
<span class="inline-flex items-center gap-1">
<img
v-tooltip="botTypeConfig[botType].label"
class="agent-bot-type--thumbnail"
class="h-3 w-auto"
:src="botTypeConfig[botType].thumbnail"
:alt="botTypeConfig[botType].label"
/>
<span>{{ botTypeConfig[botType].label }}</span>
</span>
</template>
<style scoped>
.agent-bot-type--thumbnail {
width: auto;
height: var(--space-slab);
}
</style>
@@ -108,22 +108,13 @@ export default {
</script>
<template>
<div
class="overflow-auto p-4 max-w-6xl mx-auto my-auto flex flex-wrap h-full"
>
<woot-button
v-if="showAddButton"
color-scheme="success"
class-names="button--fixed-top"
icon="add-circle"
@click="openAddHookModal"
>
{{ $t('INTEGRATION_APPS.ADD_BUTTON') }}
</woot-button>
<div class="overflow-auto p-4 w-full my-auto flex flex-wrap h-full">
<div v-if="showIntegrationHooks" class="w-full">
<div v-if="isIntegrationMultiple">
<MultipleIntegrationHooks
:integration-id="integrationId"
:show-add-button="showAddButton"
@add="openAddHookModal"
@delete="openDeletePopup"
/>
</div>
@@ -1,14 +1,23 @@
<script>
import { mapGetters } from 'vuex';
import { useIntegrationHook } from 'dashboard/composables/useIntegrationHook';
import BaseSettingsHeader from 'dashboard/routes/dashboard/settings/components/BaseSettingsHeader.vue';
export default {
components: {
BaseSettingsHeader,
},
props: {
integrationId: {
type: String,
required: true,
},
showAddButton: {
type: Boolean,
default: false,
},
},
emits: ['delete'],
emits: ['delete', 'add'],
setup(props) {
const { integration, isHookTypeInbox, hasConnectedHooks } =
useIntegrationHook(props.integrationId);
@@ -45,11 +54,37 @@ export default {
</script>
<template>
<div class="flex flex-row gap-4">
<div class="w-full lg:w-3/5">
<div class="flex flex-col flex-1 gap-8 overflow-auto">
<BaseSettingsHeader
:title="integration.name"
:description="
$t(
`INTEGRATION_APPS.SIDEBAR_DESCRIPTION.${integration.name.toUpperCase()}`,
{ installationName: globalConfig.installationName }
)
"
:feature-name="integrationId"
:back-button-label="$t('INTEGRATION_SETTINGS.HEADER')"
>
<template #actions>
<woot-button
v-if="showAddButton"
class="rounded-md button nice"
icon="add-circle"
@click="$emit('add')"
>
{{ $t('INTEGRATION_APPS.ADD_BUTTON') }}
</woot-button>
</template>
</BaseSettingsHeader>
<div class="w-full">
<table v-if="hasConnectedHooks" class="woot-table">
<thead>
<th v-for="hookHeader in hookHeaders" :key="hookHeader">
<th
v-for="hookHeader in hookHeaders"
:key="hookHeader"
class="ltr:!pl-0 rtl:!pr-0"
>
{{ hookHeader }}
</th>
<th v-if="isHookTypeInbox">
@@ -61,7 +96,7 @@ export default {
<td
v-for="property in hook.properties"
:key="property"
class="break-words"
class="ltr:!pl-0 rtl:!pr-0"
>
{{ property }}
</td>
@@ -90,18 +125,5 @@ export default {
}}
</p>
</div>
<div class="hidden w-1/3 lg:block">
<p>
<b>{{ integration.name }}</b>
</p>
<p
v-dompurify-html="
$t(
`INTEGRATION_APPS.SIDEBAR_DESCRIPTION.${integration.name.toUpperCase()}`,
{ installationName: globalConfig.installationName }
)
"
/>
</div>
</div>
</template>
@@ -48,6 +48,14 @@ export default {
path: frontendURL('accounts/:accountId/settings/integrations'),
component: SettingsContent,
props: params => {
const integrationId = params.params?.integration_id;
const hideHeader = ['dialogflow'].includes(integrationId);
// Don't show header
if (hideHeader) {
return {};
}
const showBackButton = params.name !== 'settings_integrations';
const backUrl =
params.name === 'settings_integrations_integration'