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:
Pranav
2024-07-25 16:26:00 -07:00
committed by GitHub
parent 0331815cc5
commit 6694db093f
11 changed files with 188 additions and 161 deletions

View File

@@ -10,6 +10,7 @@
v-if="showBackButton"
:button-label="backButtonLabel"
:back-url="backUrl"
class="ml-2 mr-4"
/>
<fluent-icon
v-if="icon"

View File

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

View File

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

View File

@@ -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-['&nbsp'];
}
span {
@apply absolute left-0 right-0;
}
}
</style>

View File

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

View File

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

View File

@@ -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',