Enhancement: Updates sidebar to a new design (#2733)
* feat: Changes primary navbar to new design (#2598) * feat: updates design for secondary navbar (#2612) * Changes primary nvbar to new design * Updates design for contexual sidebar * Fixes issues with JSON * Remove duplication of notificatons in Navigation * Fixes broken tests * Fixes broken tests * Update app/javascript/dashboard/components/layout/AvailabilityStatus.vue * Update app/javascript/dashboard/components/layout/AvailabilityStatus.vue * Update app/javascript/dashboard/components/layout/SidebarItem.vue Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> * Update app/javascript/dashboard/components/layout/SidebarItem.vue Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> * Update app/javascript/dashboard/modules/sidebar/components/Secondary.vue Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
parent
e834b545ef
commit
030df7afe1
27 changed files with 716 additions and 364 deletions
|
@ -49,7 +49,12 @@ code {
|
|||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// remove when grid gutters are fixed
|
||||
.columns.with-right-space {
|
||||
padding-right: var(--space-normal);
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: var(--border-radius-normal);
|
||||
}
|
||||
|
|
|
@ -219,9 +219,9 @@ $badge-background: $primary-color;
|
|||
$badge-color: $white;
|
||||
$badge-color-alt: $black;
|
||||
$badge-palette: $foundation-palette;
|
||||
$badge-padding: 0.3em;
|
||||
$badge-padding: var(--space-smaller);
|
||||
$badge-minwidth: 2.1em;
|
||||
$badge-font-size: 0.6rem;
|
||||
$badge-font-size: var(--font-size-nano);
|
||||
|
||||
// 10. Breadcrumbs
|
||||
// ---------------
|
||||
|
@ -400,7 +400,7 @@ $mediaobject-image-width-stacked: 100%;
|
|||
|
||||
$menu-margin: 0;
|
||||
$menu-margin-nested: $space-medium;
|
||||
$menu-item-padding: $space-one;
|
||||
$menu-item-padding: $space-slab;
|
||||
$menu-item-color-active: $white;
|
||||
$menu-item-background-active: $color-background;
|
||||
$menu-icon-spacing: 0.25rem;
|
||||
|
|
|
@ -44,11 +44,14 @@ $woot-logo-padding: $space-large $space-two;
|
|||
$color-woot: #1f93ff;
|
||||
$color-gray: #6e6f73;
|
||||
$color-light-gray: #999a9b;
|
||||
$color-border: #e0e6ed;
|
||||
$color-border-light: #f0f4f5;
|
||||
$color-border-dark: #cad0d4;
|
||||
$color-background: #f4f6fb;
|
||||
$color-background-light: #f9fafc;
|
||||
|
||||
$color-border: var(--s-75);
|
||||
$color-border-light: var(--s-50);
|
||||
$color-border-dark: var(--s-100);
|
||||
|
||||
$color-background: var(--s-50);
|
||||
$color-background-light: var(--s-25);
|
||||
|
||||
$color-white: #fff;
|
||||
$color-body: #3c4858;
|
||||
$color-heading: #1f2d3d;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
@include background-white;
|
||||
@include flex;
|
||||
@include flex-align($x: justify, $y: middle);
|
||||
@include border-normal-bottom;
|
||||
border-bottom: 1px solid var(--s-50);
|
||||
height: $header-height;
|
||||
min-height: $header-height;
|
||||
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
}
|
||||
|
||||
.sidebar {
|
||||
@include border-normal-right;
|
||||
@include background-white;
|
||||
@include full-height;
|
||||
@include margin(0);
|
||||
@include space-between-column;
|
||||
width: $nav-bar-width;
|
||||
z-index: 1024 - 1;
|
||||
|
||||
//logo
|
||||
|
@ -22,26 +16,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
a {
|
||||
border-radius: $space-smaller;
|
||||
color: $color-gray;
|
||||
font-size: $font-size-default;
|
||||
font-weight: $font-weight-medium;
|
||||
|
||||
.wrap,
|
||||
.child-icon {
|
||||
&:hover {
|
||||
color: $color-woot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active a .wrap {
|
||||
color: $color-woot;
|
||||
}
|
||||
}
|
||||
|
||||
.nested {
|
||||
a {
|
||||
font-size: $font-size-small;
|
||||
|
@ -83,34 +57,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
@include flex-weight(1);
|
||||
@include scroll-on-hover;
|
||||
padding: 0 $space-medium - $space-one;
|
||||
|
||||
a {
|
||||
&::before {
|
||||
margin-right: $space-slab;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
color: $color-gray;
|
||||
font-size: $font-size-medium;
|
||||
margin-top: $space-medium;
|
||||
|
||||
>span {
|
||||
margin-left: $space-one;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-title+ul>li>a {
|
||||
@include padding($space-micro null);
|
||||
color: $medium-gray;
|
||||
line-height: $global-lineheight;
|
||||
}
|
||||
|
||||
.hamburger--menu {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
|
|
|
@ -1,51 +1,23 @@
|
|||
<template>
|
||||
<div class="status">
|
||||
<div class="status-view">
|
||||
<availability-status-badge :status="currentUserAvailabilityStatus" />
|
||||
<div class="status-view--title">
|
||||
{{ availabilityDisplayLabel }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-change">
|
||||
<transition name="menu-slide">
|
||||
<div
|
||||
v-if="isStatusMenuOpened"
|
||||
v-on-clickaway="closeStatusMenu"
|
||||
class="dropdown-pane dropdowm--top"
|
||||
>
|
||||
<woot-dropdown-menu>
|
||||
<woot-dropdown-item
|
||||
v-for="status in availabilityStatuses"
|
||||
:key="status.value"
|
||||
class="status-items"
|
||||
>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
size="small"
|
||||
color-scheme="secondary"
|
||||
class-names="status-change--dropdown-button"
|
||||
:is-disabled="status.disabled"
|
||||
@click="changeAvailabilityStatus(status.value)"
|
||||
>
|
||||
<availability-status-badge :status="status.value" />
|
||||
{{ status.label }}
|
||||
</woot-button>
|
||||
</woot-dropdown-item>
|
||||
</woot-dropdown-menu>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<woot-dropdown-menu>
|
||||
<woot-dropdown-item
|
||||
v-for="status in availabilityStatuses"
|
||||
:key="status.value"
|
||||
class="status-items"
|
||||
>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
size="small"
|
||||
color-scheme="secondary"
|
||||
class-names="status-change--change-button link"
|
||||
@click="openStatusMenu"
|
||||
class-names="status-change--dropdown-button"
|
||||
:is-disabled="status.disabled"
|
||||
@click="changeAvailabilityStatus(status.value)"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.CHANGE_AVAILABILITY_STATUS') }}
|
||||
<availability-status-badge :status="status.value" />
|
||||
{{ status.label }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</woot-dropdown-item>
|
||||
</woot-dropdown-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
<template>
|
||||
<aside class="sidebar animated shrink columns">
|
||||
<div class="logo">
|
||||
<router-link :to="dashboardPath" replace>
|
||||
<img :src="globalConfig.logo" :alt="globalConfig.installationName" />
|
||||
</router-link>
|
||||
</div>
|
||||
<aside class="woot-sidebar" :class="{ 'only-primary': !showSecondaryMenu }">
|
||||
<primary-sidebar
|
||||
:logo-source="globalConfig.logo"
|
||||
:installation-name="globalConfig.installationName"
|
||||
:account-id="accountId"
|
||||
:menu-items="primaryMenuItems"
|
||||
@toggle-accounts="toggleAccountModal"
|
||||
/>
|
||||
|
||||
<div class="main-nav">
|
||||
<div v-if="showSecondaryMenu" class="main-nav secondary-menu">
|
||||
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
||||
<sidebar-item
|
||||
v-for="item in accessibleMenuItems"
|
||||
:key="item.toState"
|
||||
:menu-item="item"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowTeams"
|
||||
:key="teamSection.toState"
|
||||
|
@ -35,25 +32,19 @@
|
|||
:menu-item="contactLabelSection"
|
||||
@add-label="showAddLabelPopup"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowSettingsSideMenu"
|
||||
:key="settingsSubMenu.key"
|
||||
:menu-item="settingsSubMenu"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowNotificationsSideMenu"
|
||||
:key="notificationsSubMenu.key"
|
||||
:menu-item="notificationsSubMenu"
|
||||
/>
|
||||
</transition-group>
|
||||
</div>
|
||||
|
||||
<div class="bottom-nav">
|
||||
<availability-status />
|
||||
</div>
|
||||
|
||||
<div class="bottom-nav app-context-menu" @click="toggleOptions">
|
||||
<agent-details @show-options="toggleOptions" />
|
||||
<notification-bell />
|
||||
<span class="current-user--options icon ion-android-more-vertical" />
|
||||
<options-menu
|
||||
:show="showOptionsMenu"
|
||||
@toggle-accounts="toggleAccountModal"
|
||||
@show-support-chat-window="toggleSupportChatWindow"
|
||||
@close="toggleOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<account-selector
|
||||
:show-account-modal="showAccountModal"
|
||||
@close-account-modal="toggleAccountModal"
|
||||
|
@ -76,27 +67,22 @@ import { mapGetters } from 'vuex';
|
|||
|
||||
import adminMixin from '../../mixins/isAdmin';
|
||||
import SidebarItem from './SidebarItem';
|
||||
import AvailabilityStatus from './AvailabilityStatus';
|
||||
import { frontendURL } from '../../helper/URLHelper';
|
||||
import { getSidebarItems } from '../../i18n/default-sidebar';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import NotificationBell from './sidebarComponents/NotificationBell';
|
||||
import AgentDetails from './sidebarComponents/AgentDetails.vue';
|
||||
import OptionsMenu from './sidebarComponents/OptionsMenu.vue';
|
||||
|
||||
import AccountSelector from './sidebarComponents/AccountSelector.vue';
|
||||
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
|
||||
import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel';
|
||||
import PrimarySidebar from 'dashboard/modules/sidebar/components/Primary';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AgentDetails,
|
||||
SidebarItem,
|
||||
AvailabilityStatus,
|
||||
NotificationBell,
|
||||
OptionsMenu,
|
||||
AccountSelector,
|
||||
AddAccountModal,
|
||||
AddLabelModal,
|
||||
PrimarySidebar,
|
||||
},
|
||||
mixins: [adminMixin, alertMixin],
|
||||
data() {
|
||||
|
@ -122,6 +108,13 @@ export default {
|
|||
sidemenuItems() {
|
||||
return getSidebarItems(this.accountId);
|
||||
},
|
||||
primaryMenuItems() {
|
||||
const menuItems = Object.values(
|
||||
getSidebarItems(this.accountId).common.menuItems
|
||||
);
|
||||
|
||||
return menuItems;
|
||||
},
|
||||
accessibleMenuItems() {
|
||||
// get all keys in menuGroup
|
||||
const groupKey = Object.keys(this.sidemenuItems);
|
||||
|
@ -148,6 +141,17 @@ export default {
|
|||
showShowContactSideMenu() {
|
||||
return this.sidemenuItems.contacts.routes.includes(this.currentRoute);
|
||||
},
|
||||
shouldShowSettingsSideMenu() {
|
||||
return this.sidemenuItems.settings.routes.includes(this.currentRoute);
|
||||
},
|
||||
shouldShowReportsSideMenu() {
|
||||
return this.sidemenuItems.reports.routes.includes(this.currentRoute);
|
||||
},
|
||||
shouldShowNotificationsSideMenu() {
|
||||
return this.sidemenuItems.notifications.routes.includes(
|
||||
this.currentRoute
|
||||
);
|
||||
},
|
||||
shouldShowTeams() {
|
||||
return this.shouldShowSidebarItem && this.teams.length;
|
||||
},
|
||||
|
@ -157,6 +161,7 @@ export default {
|
|||
label: 'INBOXES',
|
||||
hasSubMenu: true,
|
||||
newLink: true,
|
||||
newLinkTag: 'NEW_INBOX',
|
||||
key: 'inbox',
|
||||
cssClass: 'menu-title align-justify',
|
||||
toState: frontendURL(`accounts/${this.accountId}/settings/inboxes`),
|
||||
|
@ -177,6 +182,7 @@ export default {
|
|||
label: 'LABELS',
|
||||
hasSubMenu: true,
|
||||
newLink: true,
|
||||
newLinkTag: 'NEW_LABEL',
|
||||
key: 'label',
|
||||
cssClass: 'menu-title align-justify',
|
||||
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
|
||||
|
@ -201,6 +207,7 @@ export default {
|
|||
hasSubMenu: true,
|
||||
key: 'label',
|
||||
newLink: false,
|
||||
newLinkTag: 'NEW_LABEL',
|
||||
cssClass: 'menu-title align-justify',
|
||||
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
|
||||
toStateName: 'labels_list',
|
||||
|
@ -223,6 +230,7 @@ export default {
|
|||
label: 'TEAMS',
|
||||
hasSubMenu: true,
|
||||
newLink: true,
|
||||
newLinkTag: 'NEW_TEAM',
|
||||
key: 'team',
|
||||
cssClass: 'menu-title align-justify teams-sidebar-menu',
|
||||
toState: frontendURL(`accounts/${this.accountId}/settings/teams`),
|
||||
|
@ -236,9 +244,49 @@ export default {
|
|||
})),
|
||||
};
|
||||
},
|
||||
settingsSubMenu() {
|
||||
const menuItems = Object.values(this.sidemenuItems.settings.menuItems);
|
||||
return {
|
||||
icon: 'ion-settings',
|
||||
label: 'SETTINGS',
|
||||
hasSubMenu: true,
|
||||
key: 'settings',
|
||||
cssClass: 'menu-title align-justify',
|
||||
toState: frontendURL(`accounts/${this.accountId}/settings`),
|
||||
children: menuItems.map(item => ({
|
||||
...item,
|
||||
label: this.$t(`SIDEBAR.${item.label}`),
|
||||
})),
|
||||
};
|
||||
},
|
||||
reportsSubMenu() {
|
||||
return {
|
||||
icon: 'ion-ion-arrow-graph-up-right',
|
||||
cssClass: 'menu-title align-justify',
|
||||
label: 'REPORTS',
|
||||
hasSubMenu: false,
|
||||
key: 'reports',
|
||||
children: [],
|
||||
};
|
||||
},
|
||||
notificationsSubMenu() {
|
||||
return {
|
||||
icon: 'ion-ios-bell',
|
||||
label: 'NOTIFICATIONS',
|
||||
hasSubMenu: false,
|
||||
cssClass: 'menu-title align-justify',
|
||||
key: 'notifications',
|
||||
children: [],
|
||||
};
|
||||
},
|
||||
dashboardPath() {
|
||||
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
||||
},
|
||||
showSecondaryMenu() {
|
||||
if (this.shouldShowReportsSideMenu) return false;
|
||||
if (this.shouldShowNotificationsSideMenu) return false;
|
||||
return true;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentUser(newUserInfo, oldUserInfo) {
|
||||
|
@ -280,9 +328,7 @@ export default {
|
|||
) > -1
|
||||
);
|
||||
},
|
||||
toggleOptions() {
|
||||
this.showOptionsMenu = !this.showOptionsMenu;
|
||||
},
|
||||
|
||||
toggleAccountModal() {
|
||||
this.showAccountModal = !this.showAccountModal;
|
||||
},
|
||||
|
@ -303,6 +349,26 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.woot-sidebar {
|
||||
display: flex;
|
||||
|
||||
&.only-primary {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-menu {
|
||||
background: var(--white);
|
||||
border-right: 1px solid var(--s-50);
|
||||
height: 100vh;
|
||||
width: 216px;
|
||||
flex-shrink: 0;
|
||||
overflow: auto;
|
||||
padding: var(--space-small);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~dashboard/assets/scss/variables';
|
||||
|
||||
|
@ -367,7 +433,7 @@ export default {
|
|||
margin-top: auto;
|
||||
}
|
||||
|
||||
.teams-sidebar-menu + .nested.vertical.menu {
|
||||
padding-left: calc(var(--space-medium) - var(--space-one));
|
||||
.secondary-menu .nested.vertical.menu {
|
||||
margin-left: var(--space-small);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
<template>
|
||||
<router-link
|
||||
:to="menuItem.toState"
|
||||
tag="li"
|
||||
active-class="active"
|
||||
:class="computedClass"
|
||||
>
|
||||
<li :class="computedClass" class="sidebar-item">
|
||||
<a
|
||||
class="sub-menu-title"
|
||||
:class="getMenuItemClass"
|
||||
|
@ -12,63 +7,57 @@
|
|||
aria-haspopup="true"
|
||||
:title="menuItem.toolTip"
|
||||
>
|
||||
<div class="wrap">
|
||||
<i :class="menuItem.icon" />
|
||||
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
||||
</div>
|
||||
<span
|
||||
v-if="showItem(menuItem)"
|
||||
class="child-icon ion-android-add-circle"
|
||||
@click.prevent="newLinkClick(menuItem)"
|
||||
/>
|
||||
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
||||
</a>
|
||||
<ul v-if="menuItem.hasSubMenu" class="nested vertical menu">
|
||||
<router-link
|
||||
<secondary-nav-item
|
||||
v-for="child in menuItem.children"
|
||||
:key="child.id"
|
||||
active-class="active flex-container"
|
||||
tag="li"
|
||||
:to="child.toState"
|
||||
:label="child.label"
|
||||
:label-color="child.color"
|
||||
:should-truncate="child.truncateLabel"
|
||||
:icon="computedInboxClass(child)"
|
||||
/>
|
||||
<router-link
|
||||
v-if="menuItem.newLink"
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="menuItem.toState"
|
||||
custom
|
||||
>
|
||||
<a href="#" :class="computedChildClass(child)">
|
||||
<div class="wrap">
|
||||
<i
|
||||
v-if="computedInboxClass(child)"
|
||||
class="inbox-icon"
|
||||
:class="computedInboxClass(child)"
|
||||
/>
|
||||
<span
|
||||
v-if="child.color"
|
||||
class="label-color--display"
|
||||
:style="{ backgroundColor: child.color }"
|
||||
/>
|
||||
<div
|
||||
:title="computedChildTitle(child)"
|
||||
:class="computedChildClass(child)"
|
||||
>
|
||||
{{ child.label }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<li>
|
||||
<a
|
||||
:href="href"
|
||||
class="button small clear menu-item--new secondary"
|
||||
:class="{ 'is-active': isActive }"
|
||||
@click="e => newLinkClick(e, navigate)"
|
||||
>
|
||||
<i class="icon ion-plus-round" />
|
||||
<span class="button__content">
|
||||
{{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</router-link>
|
||||
</ul>
|
||||
</router-link>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import router from '../../routes';
|
||||
import adminMixin from '../../mixins/isAdmin';
|
||||
import { getInboxClassByType } from 'dashboard/helper/inbox';
|
||||
|
||||
import SecondaryNavItem from 'dashboard/modules/sidebar/components/SecondaryNavItem';
|
||||
|
||||
export default {
|
||||
components: { SecondaryNavItem },
|
||||
mixins: [adminMixin],
|
||||
props: {
|
||||
menuItem: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
@ -108,11 +97,11 @@ export default {
|
|||
if (!child.truncateLabel) return false;
|
||||
return child.label;
|
||||
},
|
||||
newLinkClick(item) {
|
||||
if (item.newLinkRouteName) {
|
||||
router.push({ name: item.newLinkRouteName, params: { page: 'new' } });
|
||||
} else if (item.showModalForNewItem) {
|
||||
if (item.modalName === 'AddLabel') {
|
||||
newLinkClick(e, navigate) {
|
||||
if (this.menuItem.newLinkRouteName) {
|
||||
navigate(e);
|
||||
} else if (this.menuItem.showModalForNewItem) {
|
||||
if (this.menuItem.modalName === 'AddLabel') {
|
||||
this.$emit('add-label');
|
||||
}
|
||||
}
|
||||
|
@ -124,11 +113,22 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '~dashboard/assets/scss/variables';
|
||||
|
||||
.sidebar-item {
|
||||
margin: var(--space-small) 0;
|
||||
}
|
||||
.sub-menu-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 var(--space-small);
|
||||
margin-bottom: var(--space-smaller);
|
||||
color: var(--s-600);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--space-two);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sub-menu-link {
|
||||
color: var(--s-600);
|
||||
}
|
||||
|
||||
.wrap {
|
||||
|
@ -137,11 +137,11 @@ export default {
|
|||
}
|
||||
|
||||
.label-color--display {
|
||||
border-radius: $space-smaller;
|
||||
height: $space-normal;
|
||||
margin-right: $space-small;
|
||||
min-width: $space-normal;
|
||||
width: $space-normal;
|
||||
border-radius: var(--space-smaller);
|
||||
height: var(--space-normal);
|
||||
margin-right: var(--space-small);
|
||||
min-width: var(--space-normal);
|
||||
width: var(--space-normal);
|
||||
}
|
||||
|
||||
.inbox-icon {
|
||||
|
@ -151,4 +151,12 @@ export default {
|
|||
font-size: var(--font-size-medium);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-item .button.menu-item--new {
|
||||
display: inline-flex;
|
||||
height: var(--space-medium);
|
||||
margin: var(--space-smaller) 0;
|
||||
padding: var(--space-smaller);
|
||||
color: var(--s-500);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
<template>
|
||||
<div class="current-user--row">
|
||||
<woot-button variant="link" class="current-user" @click="handleClick">
|
||||
<thumbnail
|
||||
:src="currentUser.avatar_url"
|
||||
:username="currentUserAvailableName"
|
||||
:username="currentUser.name"
|
||||
:status="currentUser.availability_status"
|
||||
size="32px"
|
||||
/>
|
||||
<div class="current-user--data">
|
||||
<h3 class="current-user--name text-truncate">
|
||||
{{ currentUserAvailableName }}
|
||||
</h3>
|
||||
<h5 v-if="currentRole" class="current-user--role">
|
||||
{{ $t(`AGENT_MGMT.AGENT_TYPES.${currentRole.toUpperCase()}`) }}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</woot-button>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
@ -27,37 +21,20 @@ export default {
|
|||
currentUser: 'getCurrentUser',
|
||||
currentRole: 'getCurrentRole',
|
||||
}),
|
||||
currentUserAvailableName() {
|
||||
return this.currentUser.name;
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('toggle-menu');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.current-user--row {
|
||||
.current-user {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.current-user--data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.current-user--name {
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin-bottom: var(--space-micro);
|
||||
margin-left: var(--space-one);
|
||||
max-width: 12rem;
|
||||
}
|
||||
|
||||
.current-user--role {
|
||||
color: var(--color-gray);
|
||||
font-size: var(--font-size-mini);
|
||||
margin-bottom: var(--zero);
|
||||
margin-left: var(--space-one);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--white);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
<template>
|
||||
<span class="notifications icon ion-ios-bell" @click.stop="showNotification">
|
||||
<span v-if="unreadCount" class="unread-badge">{{ unreadCount }}</span>
|
||||
</span>
|
||||
<div class="notifications-link">
|
||||
<primary-nav-item
|
||||
icon="ion-ios-bell"
|
||||
:to="`/app/accounts/${accountId}/notifications`"
|
||||
:count="unreadCount"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import PrimaryNavItem from 'dashboard/modules/sidebar/components/PrimaryNavItem';
|
||||
|
||||
export default {
|
||||
components: { PrimaryNavItem },
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountId: 'getCurrentAccountId',
|
||||
|
@ -13,40 +20,20 @@ export default {
|
|||
}),
|
||||
unreadCount() {
|
||||
if (!this.notificationMetadata.unreadCount) {
|
||||
return 0;
|
||||
return '0';
|
||||
}
|
||||
|
||||
return this.notificationMetadata.unreadCount < 100
|
||||
? this.notificationMetadata.unreadCount
|
||||
? `${this.notificationMetadata.unreadCount}`
|
||||
: '99+';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showNotification() {
|
||||
this.$router.push(`/app/accounts/${this.accountId}/notifications`);
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.notifications {
|
||||
font-size: var(--font-size-big);
|
||||
margin-bottom: auto;
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
position: relative;
|
||||
|
||||
.unread-badge {
|
||||
background: var(--r-300);
|
||||
border-radius: var(--space-small);
|
||||
color: var(--white);
|
||||
font-size: var(--font-size-micro);
|
||||
font-weight: var(--font-weight-black);
|
||||
left: var(--space-slab);
|
||||
padding: 0 var(--space-smaller);
|
||||
position: absolute;
|
||||
top: var(--space-smaller);
|
||||
}
|
||||
.notifications-link {
|
||||
margin-bottom: var(--space-small);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
<transition name="menu-slide">
|
||||
<div
|
||||
v-if="show"
|
||||
v-on-clickaway="() => $emit('close')"
|
||||
class="dropdown-pane dropdowm--top"
|
||||
v-on-clickaway="onClickAway"
|
||||
class="dropdown-pane"
|
||||
:class="{ 'dropdown-pane--open': show }"
|
||||
>
|
||||
<availability-status />
|
||||
<woot-dropdown-menu>
|
||||
<woot-dropdown-item v-if="showChangeAccountOption">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="ion-arrow-swap"
|
||||
class=" change-accounts--button"
|
||||
@click="$emit('toggle-accounts')"
|
||||
>
|
||||
|
@ -19,7 +23,9 @@
|
|||
<woot-dropdown-item v-if="globalConfig.chatwootInboxToken">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="ion-help-buoy"
|
||||
class=" change-accounts--button"
|
||||
@click="$emit('show-support-chat-window')"
|
||||
>
|
||||
|
@ -29,15 +35,20 @@
|
|||
<woot-dropdown-item>
|
||||
<router-link
|
||||
:to="`/app/accounts/${accountId}/profile/settings`"
|
||||
class="button clear small change-accounts--button"
|
||||
class="button clear small secondary change-accounts--button"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||
<i class="icon ion-person" />
|
||||
<span class="button__content">
|
||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||
</span>
|
||||
</router-link>
|
||||
</woot-dropdown-item>
|
||||
<woot-dropdown-item>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="ion-log-out"
|
||||
class=" change-accounts--button"
|
||||
@click="logout"
|
||||
>
|
||||
|
@ -53,13 +64,15 @@
|
|||
import { mixin as clickaway } from 'vue-clickaway';
|
||||
import { mapGetters } from 'vuex';
|
||||
import Auth from '../../../api/auth';
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
||||
import AvailabilityStatus from 'dashboard/components/layout/AvailabilityStatus';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WootDropdownMenu,
|
||||
WootDropdownItem,
|
||||
AvailabilityStatus,
|
||||
},
|
||||
mixins: [clickaway],
|
||||
props: {
|
||||
|
@ -78,7 +91,9 @@ export default {
|
|||
if (this.globalConfig.createNewAccountFromDashboard) {
|
||||
return true;
|
||||
}
|
||||
return this.currentUser.accounts.length > 1;
|
||||
|
||||
const { accounts = [] } = this.currentUser;
|
||||
return accounts.length > 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -89,11 +104,15 @@ export default {
|
|||
window.$chatwoot.reset();
|
||||
}
|
||||
},
|
||||
onClickAway() {
|
||||
if (this.show) this.$emit('close');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dropdown-pane {
|
||||
right: 0;
|
||||
left: var(--space-slab);
|
||||
bottom: var(--space-larger);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,10 +5,12 @@ import VueI18n from 'vue-i18n';
|
|||
|
||||
import i18n from 'dashboard/i18n';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail';
|
||||
import WootButton from 'dashboard/components/ui/WootButton';
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
localVue.use(VueI18n);
|
||||
localVue.component('thumbnail', Thumbnail);
|
||||
localVue.component('woot-button', WootButton);
|
||||
|
||||
const i18nConfig = new VueI18n({
|
||||
locale: 'en',
|
||||
|
@ -16,7 +18,11 @@ const i18nConfig = new VueI18n({
|
|||
});
|
||||
|
||||
describe('agentDetails', () => {
|
||||
const currentUser = { name: 'Neymar Junior', avatar_url: '' };
|
||||
const currentUser = {
|
||||
name: 'Neymar Junior',
|
||||
avatar_url: '',
|
||||
availability_status: 'online',
|
||||
};
|
||||
const currentRole = 'agent';
|
||||
let store = null;
|
||||
let actions = null;
|
||||
|
@ -47,14 +53,8 @@ describe('agentDetails', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('shows the agent name', () => {
|
||||
const agentTitle = agentDetails.find('.current-user--name');
|
||||
expect(agentTitle.text()).toBe('Neymar Junior');
|
||||
});
|
||||
|
||||
it('shows the agent role', () => {
|
||||
const agentTitle = agentDetails.find('.current-user--role');
|
||||
expect(agentTitle.text()).toBe('Agent');
|
||||
it(' the agent status', () => {
|
||||
expect(agentDetails.find('thumbnail-stub').vm.status).toBe('online');
|
||||
});
|
||||
|
||||
it('agent thumbnail exists', () => {
|
||||
|
|
|
@ -50,8 +50,9 @@ describe('notificationBell', () => {
|
|||
localVue,
|
||||
i18n: i18nConfig,
|
||||
});
|
||||
const statusViewTitle = notificationBell.find('.unread-badge');
|
||||
expect(statusViewTitle.text()).toBe('19');
|
||||
|
||||
const statusViewTitle = notificationBell.find('primary-nav-item-stub');
|
||||
expect(statusViewTitle.vm.count).toBe('19');
|
||||
});
|
||||
|
||||
it('it should return unread count 99+ ', async () => {
|
||||
|
@ -61,7 +62,7 @@ describe('notificationBell', () => {
|
|||
localVue,
|
||||
i18n: i18nConfig,
|
||||
});
|
||||
const statusViewTitle = notificationBell.find('.unread-badge');
|
||||
expect(statusViewTitle.text()).toBe('99+');
|
||||
const statusViewTitle = notificationBell.find('primary-nav-item-stub');
|
||||
expect(statusViewTitle.vm.count).toBe('99+');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ const i18nConfig = new VueI18n({
|
|||
});
|
||||
|
||||
describe('AvailabilityStatus', () => {
|
||||
const currentUser = { availability_status: 'online' };
|
||||
const currentUser = { availability_status: 'offline' };
|
||||
let store = null;
|
||||
let actions = null;
|
||||
let modules = null;
|
||||
|
@ -50,34 +50,12 @@ describe('AvailabilityStatus', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('shows current user status', () => {
|
||||
const statusViewTitle = availabilityStatus.find('.status-view--title');
|
||||
|
||||
expect(statusViewTitle.text()).toBe('Online');
|
||||
});
|
||||
|
||||
it('opens the menu when user clicks "change"', async () => {
|
||||
expect(availabilityStatus.find('.dropdown-pane').exists()).toBe(false);
|
||||
|
||||
await availabilityStatus
|
||||
.find('.status-change--change-button')
|
||||
.trigger('click');
|
||||
|
||||
expect(availabilityStatus.find('.dropdown-pane').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('dispatches an action when user changes status', async () => {
|
||||
await availabilityStatus
|
||||
.find('.status-change--change-button')
|
||||
.trigger('click');
|
||||
|
||||
await availabilityStatus
|
||||
.find('.status-change li:last-child button')
|
||||
.trigger('click');
|
||||
await availabilityStatus.find('button:first-child').trigger('click');
|
||||
|
||||
expect(actions.updateAvailability).toBeCalledWith(
|
||||
expect.any(Object),
|
||||
{ availability: 'offline' },
|
||||
{ availability: 'online' },
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
@toggle-user-mention="toggleUserMention"
|
||||
@toggle-canned-menu="toggleCannedMenu"
|
||||
/>
|
||||
<h1>{{ message }}</h1>
|
||||
</div>
|
||||
<div v-if="hasAttachments" class="attachment-preview-box">
|
||||
<attachment-preview
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { frontendURL } from '../helper/URLHelper';
|
||||
|
||||
// TODO - find hasSubMenu usage - July/2021
|
||||
|
||||
export const getSidebarItems = accountId => ({
|
||||
common: {
|
||||
routes: [
|
||||
|
@ -33,13 +35,6 @@ export const getSidebarItems = accountId => ({
|
|||
toState: frontendURL(`accounts/${accountId}/contacts`),
|
||||
toStateName: 'contacts_dashboard',
|
||||
},
|
||||
notifications: {
|
||||
icon: 'ion-ios-bell',
|
||||
label: 'NOTIFICATIONS',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL(`accounts/${accountId}/notifications`),
|
||||
toStateName: 'notifications_dashboard',
|
||||
},
|
||||
report: {
|
||||
icon: 'ion-arrow-graph-up-right',
|
||||
label: 'REPORTS',
|
||||
|
@ -105,6 +100,10 @@ export const getSidebarItems = accountId => ({
|
|||
},
|
||||
},
|
||||
},
|
||||
notifications: {
|
||||
routes: ['notifications_index'],
|
||||
menuItems: {},
|
||||
},
|
||||
settings: {
|
||||
routes: [
|
||||
'agent_list',
|
||||
|
@ -134,13 +133,6 @@ export const getSidebarItems = accountId => ({
|
|||
'settings_teams_edit_finish',
|
||||
],
|
||||
menuItems: {
|
||||
back: {
|
||||
icon: 'ion-ios-arrow-back',
|
||||
label: 'HOME',
|
||||
hasSubMenu: false,
|
||||
toStateName: 'home',
|
||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
||||
},
|
||||
agents: {
|
||||
icon: 'ion-person-stalker',
|
||||
label: 'AGENTS',
|
||||
|
|
|
@ -140,6 +140,9 @@
|
|||
"TEAMS": "Teams",
|
||||
"ALL_CONTACTS": "All Contacts",
|
||||
"TAGGED_WITH": "Tagged with",
|
||||
"NEW_LABEL": "New label",
|
||||
"NEW_TEAM": "New team",
|
||||
"NEW_INBOX": "New inbox",
|
||||
"REPORTS_OVERVIEW": "Overview",
|
||||
"CSAT": "CSAT"
|
||||
},
|
||||
|
|
44
app/javascript/dashboard/modules/sidebar/components/Logo.vue
Normal file
44
app/javascript/dashboard/modules/sidebar/components/Logo.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="logo">
|
||||
<router-link :to="dashboardPath" replace>
|
||||
<img :src="source" :alt="name" />
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
source: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
accountId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dashboardPath() {
|
||||
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
$logo-size: 40px;
|
||||
|
||||
.logo {
|
||||
padding: var(--space-normal);
|
||||
|
||||
img {
|
||||
width: $logo-size;
|
||||
height: $logo-size;
|
||||
}
|
||||
}
|
||||
</style>
|
106
app/javascript/dashboard/modules/sidebar/components/Primary.vue
Normal file
106
app/javascript/dashboard/modules/sidebar/components/Primary.vue
Normal file
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<div class="primary--sidebar">
|
||||
<logo
|
||||
:source="logoSource"
|
||||
:name="installationName"
|
||||
:account-id="accountId"
|
||||
/>
|
||||
<nav class="menu vertical">
|
||||
<primary-nav-item
|
||||
v-for="menuItem in menuItems"
|
||||
:key="menuItem.toState"
|
||||
:icon="menuItem.icon"
|
||||
:name="menuItem.label"
|
||||
:to="menuItem.toState"
|
||||
/>
|
||||
</nav>
|
||||
<div class="menu vertical user-menu">
|
||||
<notification-bell />
|
||||
<agent-details @toggle-menu="toggleOptions" />
|
||||
<options-menu
|
||||
:show="showOptionsMenu"
|
||||
@toggle-accounts="toggleAccountModal"
|
||||
@show-support-chat-window="toggleSupportChatWindow"
|
||||
@close="toggleOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Logo from './Logo';
|
||||
import PrimaryNavItem from './PrimaryNavItem';
|
||||
import OptionsMenu from 'dashboard/components/layout/sidebarComponents/OptionsMenu';
|
||||
import AgentDetails from 'dashboard/components/layout/sidebarComponents/AgentDetails';
|
||||
import NotificationBell from 'dashboard/components/layout/sidebarComponents/NotificationBell';
|
||||
|
||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Logo,
|
||||
PrimaryNavItem,
|
||||
OptionsMenu,
|
||||
AgentDetails,
|
||||
NotificationBell,
|
||||
},
|
||||
props: {
|
||||
logoSource: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
installationName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
accountId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
menuItems: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showOptionsMenu: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
frontendURL,
|
||||
toggleOptions() {
|
||||
this.showOptionsMenu = !this.showOptionsMenu;
|
||||
},
|
||||
toggleAccountModal() {
|
||||
this.$emit('toggle-accounts');
|
||||
},
|
||||
toggleSupportChatWindow() {
|
||||
window.$chatwoot.toggle();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.primary--sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: var(--space-jumbo);
|
||||
border-right: 1px solid var(--s-50);
|
||||
box-sizing: content-box;
|
||||
height: 100vh;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.menu {
|
||||
align-items: center;
|
||||
margin-top: var(--space-medium);
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: var(--space-normal);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<router-link v-slot="{ href, isActive, navigate }" :to="to" custom>
|
||||
<a
|
||||
:href="href"
|
||||
class="button clear button--only-icon menu-item"
|
||||
:class="{ 'is-active': isActive }"
|
||||
@click="navigate"
|
||||
>
|
||||
<i class="icon" :class="icon" />
|
||||
<span class="show-for-sr">{{ name }}</span>
|
||||
<span v-if="count" class="badge warning">{{ count }}</span>
|
||||
</a>
|
||||
</router-link>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
count: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.button {
|
||||
margin: var(--space-small) 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
border-radius: var(--border-radius-large);
|
||||
border: 1px solid transparent;
|
||||
color: var(--s-600);
|
||||
|
||||
&:hover {
|
||||
background: var(--w-25);
|
||||
color: var(--s-600);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--w-500);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: var(--w-50);
|
||||
color: var(--w-500);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: var(--font-size-default);
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
right: var(--space-minus-smaller);
|
||||
top: var(--space-minus-smaller);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<div class="secondary--sidebar">
|
||||
<div class="main-nav">
|
||||
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
||||
<sidebar-item
|
||||
v-for="item in accessibleMenuItems"
|
||||
:key="item.toState"
|
||||
:menu-item="item"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowTeams"
|
||||
:key="teamSection.toState"
|
||||
:menu-item="teamSection"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowSidebarItem"
|
||||
:key="inboxSection.toState"
|
||||
:menu-item="inboxSection"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="shouldShowSidebarItem"
|
||||
:key="labelSection.toState"
|
||||
:menu-item="labelSection"
|
||||
/>
|
||||
<sidebar-item
|
||||
v-if="showShowContactSideMenu"
|
||||
:key="contactLabelSection.key"
|
||||
:menu-item="contactLabelSection"
|
||||
@add-label="showAddLabelPopup"
|
||||
/>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<router-link
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="to"
|
||||
custom
|
||||
active-class="active"
|
||||
>
|
||||
<li :class="{ active: isActive }">
|
||||
<a
|
||||
:href="href"
|
||||
class="button clear menu-item"
|
||||
:class="{ 'is-active': isActive, 'text-truncate': shouldTruncate }"
|
||||
@click="navigate"
|
||||
>
|
||||
<span v-if="icon" class="badge--icon">
|
||||
<i class="icon inbox-icon" :class="icon" />
|
||||
</span>
|
||||
<span
|
||||
v-if="labelColor"
|
||||
class="badge--label"
|
||||
:style="{ backgroundColor: labelColor }"
|
||||
/>
|
||||
<span
|
||||
:title="menuTitle"
|
||||
class="menu-label button__content"
|
||||
:class="{ 'text-truncate': shouldTruncate }"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
<span v-if="count" class="badge" :class="{ secondary: !isActive }">
|
||||
{{ count }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</router-link>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
labelColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
shouldTruncate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
count: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showIcon() {
|
||||
return { 'text-truncate': this.shouldTruncate };
|
||||
},
|
||||
menuTitle() {
|
||||
return this.shouldTruncate ? this.label : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
$badge-size: var(--space-slab);
|
||||
|
||||
.button {
|
||||
margin: var(--space-small) 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: inline-flex;
|
||||
color: var(--s-600);
|
||||
font-weight: var(--font-weight-medium);
|
||||
width: 100%;
|
||||
height: var(--space-medium);
|
||||
padding: var(--space-smaller) var(--space-smaller);
|
||||
margin: var(--space-smaller) 0;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background: var(--s-25);
|
||||
color: var(--s-600);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--w-300);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: var(--w-25);
|
||||
color: var(--w-500);
|
||||
border-color: var(--w-25);
|
||||
}
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
flex-grow: 1;
|
||||
line-height: var(--space-two);
|
||||
}
|
||||
|
||||
.inbox-icon {
|
||||
font-size: var(--font-size-nano);
|
||||
}
|
||||
|
||||
.badge--label,
|
||||
.badge--icon {
|
||||
display: inline-flex;
|
||||
min-width: $badge-size;
|
||||
height: $badge-size;
|
||||
border-radius: var(--border-radius-small);
|
||||
margin-right: var(--space-smaller);
|
||||
background: var(--s-100);
|
||||
}
|
||||
|
||||
.badge--icon {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge.secondary {
|
||||
min-width: unset;
|
||||
background: var(--s-75);
|
||||
color: var(--s-600);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
</style>
|
|
@ -2,16 +2,17 @@
|
|||
--white: #fff;
|
||||
--white-transparent: rgba(255, 255, 255, 0.9);
|
||||
|
||||
--w-50: #E3F2FF;
|
||||
--w-100: #BBDDFF;
|
||||
--w-200: #8FC9FF;
|
||||
--w-300: #61B3FF;
|
||||
--w-400: #3FA3FF;
|
||||
--w-25: #F5FAFF;
|
||||
--w-50: #EBF5FF;
|
||||
--w-100: #C2E1FF;
|
||||
--w-200: #99CEFF;
|
||||
--w-300: ##70BAFF;
|
||||
--w-400: #47A6FF;
|
||||
--w-500: #1F93FF;
|
||||
--w-600: #2284F0;
|
||||
--w-700: #2272DC;
|
||||
--w-800: #2161CA;
|
||||
--w-900: #1F41AB;
|
||||
--w-600: #1976CC;
|
||||
--w-700: #135899;
|
||||
--w-800: #0C3B66;
|
||||
--w-900: #061D33;
|
||||
|
||||
--g-50: #E6F8E6;
|
||||
--g-100: #C4EEC2;
|
||||
|
@ -35,16 +36,18 @@
|
|||
--y-800: #FDAD2A;
|
||||
--y-900: #F9841B;
|
||||
|
||||
--s-50: #E7EEFB;
|
||||
--s-100: #C8D6E6;
|
||||
--s-200: #ABBACE;
|
||||
--s-300: #8C9EB6;
|
||||
--s-400: #7489A4;
|
||||
--s-500: #5D7592;
|
||||
--s-600: #506781;
|
||||
--s-700: #40546B;
|
||||
--s-800: #314155;
|
||||
--s-900: #1F2D3D;
|
||||
--s-25: #F8FAFC;
|
||||
--s-50: #F1F5F8;
|
||||
--s-75: #EBF0F5;
|
||||
--s-100: #E4EBF1;
|
||||
--s-200: #C9D7E3;
|
||||
--s-300: #AEC3D5;
|
||||
--s-400: #93AFC8;
|
||||
--s-500: #779BBB;
|
||||
--s-600: #446888;
|
||||
--s-700: #37546D;
|
||||
--s-800: #293F51;
|
||||
--s-900: #1B2836;
|
||||
|
||||
--b-50: #F8F9FE;
|
||||
--b-100: #F2F3F7;
|
||||
|
@ -85,12 +88,12 @@
|
|||
--color-heading: #1f2d3d;
|
||||
--color-body: #3c4858;
|
||||
|
||||
--color-border: #e0e6ed;
|
||||
--color-border-light: #f0f4f5;
|
||||
--color-border-dark: #cad0d4;
|
||||
--color-border: var(--s-75);
|
||||
--color-border-light: var(--s-50);
|
||||
--color-border-dark: var(--s-100);
|
||||
|
||||
--color-background: #f4f6fb;
|
||||
--color-background-light: #f9fafc;
|
||||
--color-background: var(--s-50);
|
||||
--color-background-light: var(--s-25);
|
||||
|
||||
// Social and inboxes brand colors
|
||||
--color-facebook-brand: #3b5998;
|
||||
|
|
|
@ -24,7 +24,6 @@ export const actions = {
|
|||
refreshActionCableConnector(pubsubToken);
|
||||
dispatch('conversationAttributes/getAttributes', {}, { root: true });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// Ignore error
|
||||
} finally {
|
||||
commit('setConversationUIFlag', { isCreating: false });
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
"vue-i18n": "8.24.3",
|
||||
"vue-loader": "15.9.6",
|
||||
"vue-multiselect": "~2.1.6",
|
||||
"vue-router": "~2.2.0",
|
||||
"vue-router": "3.5.2",
|
||||
"vue-template-compiler": "2.6.12",
|
||||
"vue-upload-component": "2.8.22",
|
||||
"vue2-datepicker": "^3.9.1",
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 938 B |
|
@ -15045,10 +15045,10 @@ vue-resize@^1.0.1:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
vue-router@~2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-2.2.1.tgz#b027f9fac2cf13462725e843d6dc631b6aa077f6"
|
||||
integrity sha1-sCf5+sLPE0YnJehD1txjG2qgd/Y=
|
||||
vue-router@3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.2.tgz#5f55e3f251970e36c3e8d88a7cd2d67a350ade5c"
|
||||
integrity sha512-807gn82hTnjCYGrnF3eNmIw/dk7/GE4B5h69BlyCK9KHASwSloD1Sjcn06zg9fVG4fYH2DrsNBZkpLtb25WtaQ==
|
||||
|
||||
vue-style-loader@^4.1.0:
|
||||
version "4.1.3"
|
||||
|
|
Loading…
Reference in a new issue