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:
@@ -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