feat: Update the design for dashboard_apps (#9840)
This PR migrates the dashboard apps page to the new layout and includes the following updates: - Create a compact design for the back button - Add a back button to the settings header - Reduce letter-spacing on the description - Fix mobile styles - Migrate the layout of dashboard apps/index to new layouts Note: I've moved all feature help URLs from features.yml to the frontend. This change prevents features.yml from becoming bloated due to frontend modifications. --------- Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
table {
|
||||
@apply border-spacing-0 text-sm w-full;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.woot-table {
|
||||
thead {
|
||||
th {
|
||||
@apply font-semibold tracking-[1px] text-left px-2.5 uppercase text-slate-900 dark:text-slate-200;
|
||||
@@ -16,9 +20,7 @@ table {
|
||||
@apply p-2.5 text-slate-700 dark:text-slate-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woot-table {
|
||||
tr {
|
||||
.show-if-hover {
|
||||
transition: opacity 0.2s $swift-ease-out-function;
|
||||
|
||||
@@ -1,34 +1,40 @@
|
||||
<script setup>
|
||||
import router from '../../routes/index';
|
||||
const props = defineProps({
|
||||
backUrl: {
|
||||
type: [String, Object],
|
||||
default: '',
|
||||
},
|
||||
buttonLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
if (props.backUrl !== '') {
|
||||
router.push(props.backUrl);
|
||||
} else {
|
||||
router.go(-1);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonStyleClass = props.compact
|
||||
? 'text-sm text-slate-600 dark:text-slate-300'
|
||||
: 'text-base text-woot-500 dark:text-woot-500';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="header-section flex items-center text-base font-normal mr-4 ml-2 p-0 cursor-pointer text-woot-500 dark:text-woot-500"
|
||||
class="flex items-center font-normal p-0 cursor-pointer"
|
||||
:class="buttonStyleClass"
|
||||
@click.capture="goBack"
|
||||
>
|
||||
<fluent-icon icon="chevron-left" />
|
||||
<fluent-icon icon="chevron-left" class="-ml-1" />
|
||||
{{ buttonLabel || $t('GENERAL_SETTINGS.BACK') }}
|
||||
</button>
|
||||
</template>
|
||||
<script>
|
||||
import router from '../../routes/index';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
backUrl: {
|
||||
type: [String, Object],
|
||||
default: '',
|
||||
},
|
||||
buttonLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
if (this.backUrl !== '') {
|
||||
router.push(this.backUrl);
|
||||
} else {
|
||||
router.go(-1);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
const FEATURE_HELP_URLS = {
|
||||
channel_email: 'https://chwt.app/hc/email',
|
||||
channel_facebook: 'https://chwt.app/hc/fb',
|
||||
help_center: 'https://chwt.app/hc/help-center',
|
||||
agent_bots: 'https://chwt.app/hc/agent-bots',
|
||||
team_management: 'https://chwt.app/hc/teams',
|
||||
labels: 'https://chwt.app/hc/labels',
|
||||
custom_attributes: 'https://chwt.app/hc/custom-attributes',
|
||||
canned_responses: 'https://chwt.app/hc/canned',
|
||||
integrations: 'https://chwt.app/hc/integrations',
|
||||
campaigns: 'https://chwt.app/hc/campaigns',
|
||||
reports: 'https://chwt.app/hc/reports',
|
||||
message_reply_to: 'https://chwt.app/hc/reply-to',
|
||||
sla: 'https://chwt.app/hc/sla',
|
||||
dashboard_apps: 'https://chwt.app/hc/dashboard-apps',
|
||||
};
|
||||
|
||||
export function getHelpUrlForFeature(featureName) {
|
||||
const { helpUrls } = window.chatwootConfig;
|
||||
return helpUrls[featureName];
|
||||
return FEATURE_HELP_URLS[featureName];
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
"HEADER_BTN_TXT": "Add a new dashboard app",
|
||||
"SIDEBAR_TXT": "<p><b>Dashboard Apps</b></p><p>Dashboard Apps allow organizations to embed an application inside the Chatwoot dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that inside the dashboard to provide user information, their orders, or their previous payment history.</p><p>When you embed your application using the dashboard in Chatwoot, your application will get the context of the conversation and contact as a window event. Implement a listener for the message event on your page to receive the context.</p><p>To add a new dashboard app, click on the button 'Add a new dashboard app'.</p>",
|
||||
"DESCRIPTION": "Dashboard Apps allow organizations to embed an application inside the dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that to provide user information, their orders, or their previous payment history.",
|
||||
"LEARN_MORE": "Learn more about Dashboard Apps",
|
||||
"LIST": {
|
||||
"404": "There are no dashboard apps configured on this account yet",
|
||||
"LOADING": "Fetching dashboard apps...",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
v-if="showBackButton"
|
||||
:button-label="backButtonLabel"
|
||||
:back-url="backUrl"
|
||||
class="ml-2 mr-4"
|
||||
/>
|
||||
<fluent-icon
|
||||
v-if="icon"
|
||||
|
||||
@@ -9,7 +9,7 @@ defineProps({
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col w-full h-full m-0 px-8 lg:px-16 py-8 overflow-auto bg-white dark:bg-slate-900"
|
||||
class="flex flex-col w-full h-full m-0 p-6 sm:py-8 lg:px-16 overflow-auto bg-white dark:bg-slate-900 font-inter"
|
||||
>
|
||||
<div class="flex items-start w-full max-w-6xl mx-auto">
|
||||
<keep-alive v-if="keepAlive">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import CustomBrandPolicyWrapper from 'dashboard/components/CustomBrandPolicyWrapper.vue';
|
||||
import { getHelpUrlForFeature } from '../../../../helper/featureHelper';
|
||||
import BackButton from '../../../../components/widgets/BackButton.vue';
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
@@ -22,6 +23,10 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
backButtonLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const helpURL = getHelpUrlForFeature(props.featureName);
|
||||
@@ -33,7 +38,12 @@ const openInNewTab = url => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col items-start w-full gap-3 pt-4">
|
||||
<div class="flex flex-col items-start w-full gap-2 pt-4">
|
||||
<BackButton
|
||||
v-if="backButtonLabel"
|
||||
compact
|
||||
:button-label="backButtonLabel"
|
||||
/>
|
||||
<div class="flex items-center justify-between w-full gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
@@ -64,7 +74,7 @@ const openInNewTab = url => {
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 text-slate-600 dark:text-slate-300 w-full">
|
||||
<p
|
||||
class="mb-0 text-base font-normal line-clamp-5 sm:line-clamp-none max-w-3xl"
|
||||
class="mb-0 text-base font-normal line-clamp-5 sm:line-clamp-none max-w-3xl tracking-[-0.1px]"
|
||||
>
|
||||
<slot name="description">{{ description }}</slot>
|
||||
</p>
|
||||
@@ -74,7 +84,7 @@ const openInNewTab = url => {
|
||||
:href="helpURL"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="sm:inline-flex hidden tracking-[-0.6%] gap-1 w-fit items-center text-woot-500 dark:text-woot-500 text-sm font-medium tracking=[-0.6%] hover:underline"
|
||||
class="sm:inline-flex hidden gap-1 w-fit items-center text-woot-500 dark:text-woot-500 text-sm font-medium hover:underline"
|
||||
>
|
||||
{{ linkText }}
|
||||
<fluent-icon
|
||||
@@ -86,14 +96,16 @@ const openInNewTab = url => {
|
||||
</a>
|
||||
</CustomBrandPolicyWrapper>
|
||||
</div>
|
||||
<div class="flex items-start justify-start w-full gap-3 sm:hidden">
|
||||
<div
|
||||
class="flex items-start justify-start w-full gap-3 sm:hidden flex-wrap"
|
||||
>
|
||||
<slot name="actions" />
|
||||
<CustomBrandPolicyWrapper :show-on-custom-branded-instance="false">
|
||||
<woot-button
|
||||
v-if="helpURL && linkText"
|
||||
color-scheme="secondary"
|
||||
icon="arrow-outwards"
|
||||
class="flex-row-reverse rounded-xl min-w-0 !bg-slate-50 !text-slate-900 dark:!text-white dark:!bg-slate-800"
|
||||
class="flex-row-reverse rounded-md min-w-0 !bg-slate-50 !text-slate-900 dark:!text-white dark:!bg-slate-800"
|
||||
@click="openInNewTab(helpURL)"
|
||||
>
|
||||
{{ linkText }}
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
app: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits(['edit', 'delete']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tr>
|
||||
<td class="w-40 max-w-[10rem] truncate" :title="app.title">
|
||||
<tr class="py-1 max-w-full">
|
||||
<td
|
||||
class="py-4 pr-4 text-sm w-40 max-w-[10rem] truncate"
|
||||
:title="app.title"
|
||||
>
|
||||
{{ app.title }}
|
||||
</td>
|
||||
<td class="max-w-xs truncate" :title="app.content[0].url">
|
||||
<td class="py-4 pr-4 text-sm max-w-lg truncate" :title="app.content[0].url">
|
||||
{{ app.content[0].url }}
|
||||
</td>
|
||||
<td class="flex justify-end gap-1">
|
||||
<td class="py-4 pr-4 text-sm flex gap-2 sm:pr-0">
|
||||
<woot-button
|
||||
v-tooltip.top="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.EDIT_TOOLTIP')
|
||||
@@ -32,30 +46,3 @@
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
props: {
|
||||
app: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard-app-label-url {
|
||||
@apply relative w-full;
|
||||
&:before {
|
||||
@apply invisible content-[' '];
|
||||
}
|
||||
span {
|
||||
@apply absolute left-0 right-0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,95 +1,14 @@
|
||||
<template>
|
||||
<div class="flex-1 p-4 overflow-auto">
|
||||
<woot-button
|
||||
color-scheme="success"
|
||||
class-names="button--fixed-top"
|
||||
icon="add-circle"
|
||||
@click="openCreatePopup"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.HEADER_BTN_TXT') }}
|
||||
</woot-button>
|
||||
<div class="flex flex-row gap-4">
|
||||
<div class="w-full lg:w-3/5">
|
||||
<p
|
||||
v-if="!uiFlags.isFetching && !records.length"
|
||||
class="flex flex-col items-center justify-center h-full"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.404') }}
|
||||
</p>
|
||||
<woot-loading-state
|
||||
v-if="uiFlags.isFetching"
|
||||
:message="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.LOADING')"
|
||||
/>
|
||||
<table v-if="!uiFlags.isFetching && records.length" class="woot-table">
|
||||
<thead>
|
||||
<th
|
||||
v-for="thHeader in $t(
|
||||
'INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.TABLE_HEADER'
|
||||
)"
|
||||
:key="thHeader"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<dashboard-apps-row
|
||||
v-for="(dashboardAppItem, index) in records"
|
||||
:key="dashboardAppItem.id"
|
||||
:index="index"
|
||||
:app="dashboardAppItem"
|
||||
@edit="editApp"
|
||||
@delete="openDeletePopup"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="hidden w-1/3 lg:block">
|
||||
<span
|
||||
v-dompurify-html="
|
||||
useInstallationName(
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.SIDEBAR_TXT'),
|
||||
globalConfig.installationName
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dashboard-app-modal
|
||||
v-if="showDashboardAppPopup"
|
||||
:show="showDashboardAppPopup"
|
||||
:mode="mode"
|
||||
:selected-app-data="selectedApp"
|
||||
@close="toggleDashboardAppPopup"
|
||||
/>
|
||||
|
||||
<woot-delete-modal
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.TITLE')"
|
||||
:message="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.MESSAGE', {
|
||||
appName: selectedApp.title,
|
||||
})
|
||||
"
|
||||
:confirm-text="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_YES')
|
||||
"
|
||||
:reject-text="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_NO')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import DashboardAppModal from './DashboardAppModal.vue';
|
||||
import DashboardAppsRow from './DashboardAppsRow.vue';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import BaseSettingsHeader from '../../components/BaseSettingsHeader.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseSettingsHeader,
|
||||
DashboardAppModal,
|
||||
DashboardAppsRow,
|
||||
},
|
||||
@@ -156,3 +75,87 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 overflow-auto flex gap-8 flex-col">
|
||||
<BaseSettingsHeader
|
||||
:title="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.TITLE')"
|
||||
:description="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DESCRIPTION')"
|
||||
:link-text="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LEARN_MORE')"
|
||||
feature-name="dashboard_apps"
|
||||
:back-button-label="$t('INTEGRATION_SETTINGS.HEADER')"
|
||||
>
|
||||
<template #actions>
|
||||
<woot-button
|
||||
class="button nice rounded-md"
|
||||
icon="add-circle"
|
||||
@click="openCreatePopup"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.HEADER_BTN_TXT') }}
|
||||
</woot-button>
|
||||
</template>
|
||||
</BaseSettingsHeader>
|
||||
<div class="w-full text-slate-700 dark:text-slate-200 overflow-x-auto">
|
||||
<p
|
||||
v-if="!uiFlags.isFetching && !records.length"
|
||||
class="flex flex-col items-center justify-center h-full"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.404') }}
|
||||
</p>
|
||||
<woot-loading-state
|
||||
v-if="uiFlags.isFetching"
|
||||
:message="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.LOADING')"
|
||||
/>
|
||||
<table
|
||||
v-if="!uiFlags.isFetching && records.length"
|
||||
class="min-w-full divide-y divide-slate-75 dark:divide-slate-700"
|
||||
>
|
||||
<thead>
|
||||
<th
|
||||
v-for="thHeader in $t(
|
||||
'INTEGRATION_SETTINGS.DASHBOARD_APPS.LIST.TABLE_HEADER'
|
||||
)"
|
||||
:key="thHeader"
|
||||
class="py-4 pr-4 text-left font-semibold text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-50 dark:divide-slate-800">
|
||||
<dashboard-apps-row
|
||||
v-for="(dashboardAppItem, index) in records"
|
||||
:key="dashboardAppItem.id"
|
||||
:index="index"
|
||||
:app="dashboardAppItem"
|
||||
@edit="editApp"
|
||||
@delete="openDeletePopup"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<dashboard-app-modal
|
||||
v-if="showDashboardAppPopup"
|
||||
:show="showDashboardAppPopup"
|
||||
:mode="mode"
|
||||
:selected-app-data="selectedApp"
|
||||
@close="toggleDashboardAppPopup"
|
||||
/>
|
||||
|
||||
<woot-delete-modal
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.TITLE')"
|
||||
:message="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.MESSAGE', {
|
||||
appName: selectedApp.title,
|
||||
})
|
||||
"
|
||||
:confirm-text="
|
||||
$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_YES')
|
||||
"
|
||||
:reject-text="$t('INTEGRATION_SETTINGS.DASHBOARD_APPS.DELETE.CONFIRM_NO')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,7 +33,7 @@ onMounted(() => {
|
||||
/>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="flex-grow flex-shrink overflow-auto font-inter">
|
||||
<div class="flex-grow flex-shrink overflow-auto">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<integration-item
|
||||
v-for="item in integrationList"
|
||||
|
||||
@@ -22,9 +22,16 @@ export default {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'dashboard_apps',
|
||||
component: DashboardApps,
|
||||
name: 'settings_integrations_dashboard_apps',
|
||||
meta: {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/integrations'),
|
||||
component: SettingsContent,
|
||||
@@ -50,14 +57,6 @@ export default {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'dashboard_apps',
|
||||
component: DashboardApps,
|
||||
name: 'settings_integrations_dashboard_apps',
|
||||
meta: {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'slack',
|
||||
name: 'settings_integrations_slack',
|
||||
|
||||
Reference in New Issue
Block a user