Compare commits

...

8 commits

Author SHA1 Message Date
Nithin David
3f1bf8c10a Merge branch 'develop' of https://github.com/chatwoot/chatwoot into release/2.0.0 2021-11-08 13:15:33 +05:30
Nithin David
1e494144ee lint error fixes 2021-10-13 20:58:11 +05:30
Nithin David
ed4547e24f Refactors sidebar routeing logic 2021-10-13 15:43:37 +05:30
Nithin David
49eb2b4393 Fixes menu transitions and refactors code 2021-10-13 01:41:20 +05:30
Nithin David
b54a413821 Merge branch 'develop' of https://github.com/chatwoot/chatwoot into release/2.0.0 2021-10-08 12:21:55 +05:30
Nithin David
b6b6072bf9 Chore: Update design changes to features 2021-10-08 12:16:09 +05:30
Nithin David
29bbfca80d Merge branch 'develop' of https://github.com/chatwoot/chatwoot into release/2.0.0 2021-10-08 12:15:20 +05:30
Nithin David Thomas
030df7afe1
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>
2021-08-11 12:29:31 +05:30
30 changed files with 958 additions and 542 deletions

View file

@ -1,4 +1,3 @@
/* Enter and leave animations can use different */
/* durations and timing functions. */
.slide-fade-enter-active {
@ -9,7 +8,8 @@
transition: all .3s $ease-out-cubic;
}
.slide-fade-enter, .slide-fade-leave-to {
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
transform: translateX(10px);
}
@ -22,22 +22,36 @@
transform: translateX($space-medium);
}
.conversations-list-enter-active, .conversations-list-leave-active {
.conversations-list-enter-active,
.conversations-list-leave-active {
transition: all .25s $ease-out-cubic;
}
.conversations-list-enter, .conversations-list-leave-to /* .conversations-list-leave-active for <2.1.8 */ {
.conversations-list-enter,
.conversations-list-leave-to
/* .conversations-list-leave-active for <2.1.8 */
{
opacity: 0;
transform: translateX($space-medium);
}
.menu-list-enter-active, .menu-list-leave-active {
transition: all .2s $ease-out-cubic;
.menu-list-enter-active,
.menu-list-leave-active {
transition: opacity .3s $ease-out-cubic,
transform .2s $ease-out-cubic;
}
.menu-list-enter, .menu-list-leave-to /* .conversations-list-leave-active for <2.1.8 */ {
.menu-list-leave-to {
opacity: 0;
transform: translateX($space-medium);
position: absolute;
transform: translateX($space-small);
}
.menu-list-enter {
opacity: 0;
transform: translateX(-$space-small);
}
.slide-up-enter-active {
@ -48,8 +62,11 @@
transition: all .3s $ease-out-cubic;
}
.slide-up-enter, .slide-up-leave-to
/* .slide-fade-leave-active for <2.1.8 */ {
.slide-up-enter,
.slide-up-leave-to
/* .slide-fade-leave-active for <2.1.8 */
{
transform: translateY(-$space-medium);
opacity: 0;
}
@ -60,10 +77,13 @@
transition: transform 0.25s $ease-in-cubic, opacity 0.15s $ease-in-cubic;
}
.menu-slide-enter, .menu-slide-leave-to
/* .slide-fade-leave-active for <2.1.8 */ {
transform: translateY($space-small);
.menu-slide-enter,
.menu-slide-leave-to
/* .slide-fade-leave-active for <2.1.8 */
{
opacity: 0;
transform: translateY($space-small);
}
@ -75,10 +95,13 @@
transition: all .1s $ease-out-sine;
}
.toast-fade-enter, .toast-fade-leave-to
/* .toast-fade-leave-active for <2.1.8 */ {
transform: translateY(-$space-small);
.toast-fade-enter,
.toast-fade-leave-to
/* .toast-fade-leave-active for <2.1.8 */
{
opacity: 0;
transform: translateY(-$space-small);
}
.modal-fade-enter-active {
@ -89,8 +112,11 @@
transition: all .1s $ease-out-sine;
}
.modal-fade-enter, .modal-fade-leave-to
/* .slide-fade-leave-active for <2.1.8 */ {
.modal-fade-enter,
.modal-fade-leave-to
/* .slide-fade-leave-active for <2.1.8 */
{
opacity: 0;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,53 +1,23 @@
<template>
<div class="status">
<div class="status-view">
<availability-status-badge :status="currentUserAvailability" />
<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, currentAccountId)
"
>
<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>
@ -100,8 +70,7 @@ export default {
label: statusLabel,
value: AVAILABILITY_STATUS_KEYS[index],
disabled:
this.currentUserAvailability ===
AVAILABILITY_STATUS_KEYS[index],
this.currentUserAvailability === AVAILABILITY_STATUS_KEYS[index],
})
);
},

View file

@ -1,59 +1,22 @@
<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">
<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"
@add-label="showAddLabelPopup"
/>
<sidebar-item
v-if="showShowContactSideMenu"
:key="contactLabelSection.key"
:menu-item="contactLabelSection"
@add-label="showAddLabelPopup"
/>
</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"
@key-shortcut-modal="toggleKeyShortcutModal"
@close="toggleOptions"
/>
</div>
<secondary-sidebar
v-if="showSecondaryMenu"
:account-id="accountId"
:inboxes="inboxes"
:account-labels="accountLabels"
:teams="teams"
:menu-items="primaryMenuItems"
@add-label="showAddLabelPopup"
/>
<woot-key-shortcut-modal
v-if="showShortcutModal"
@ -82,17 +45,14 @@
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';
import SecondarySidebar from 'dashboard/modules/sidebar/components/Secondary';
import WootKeyShortcutModal from 'components/widgets/modal/WootKeyShortcutModal';
import {
hasPressedAltAndCKey,
@ -107,14 +67,11 @@ import router from '../../routes';
export default {
components: {
AgentDetails,
SidebarItem,
AvailabilityStatus,
NotificationBell,
OptionsMenu,
AccountSelector,
AddAccountModal,
AddLabelModal,
PrimarySidebar,
SecondarySidebar,
WootKeyShortcutModal,
},
mixins: [adminMixin, alertMixin, eventListenerMixins],
@ -142,123 +99,26 @@ export default {
sidemenuItems() {
return getSidebarItems(this.accountId);
},
accessibleMenuItems() {
// get all keys in menuGroup
const groupKey = Object.keys(this.sidemenuItems);
primaryMenuItems() {
const menuItems = Object.values(
getSidebarItems(this.accountId).common.menuItems
);
let menuItems = [];
// Iterate over menuGroup to find the correct group
for (let i = 0; i < groupKey.length; i += 1) {
const groupItem = this.sidemenuItems[groupKey[i]];
// Check if current route is included
const isRouteIncluded = groupItem.routes.includes(this.currentRoute);
if (isRouteIncluded) {
menuItems = Object.values(groupItem.menuItems);
}
}
return this.filterMenuItemsByRole(menuItems);
return menuItems;
},
currentRoute() {
return this.$store.state.route.name;
},
shouldShowSidebarItem() {
return this.sidemenuItems.common.routes.includes(this.currentRoute);
shouldShowNotificationsSideMenu() {
return this.sidemenuItems.notifications.routes.includes(
this.currentRoute
);
},
showShowContactSideMenu() {
return this.sidemenuItems.contacts.routes.includes(this.currentRoute);
},
shouldShowTeams() {
return this.shouldShowSidebarItem && this.teams.length;
},
inboxSection() {
return {
icon: 'ion-folder',
label: 'INBOXES',
hasSubMenu: true,
newLink: true,
key: 'inbox',
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/inboxes`),
toStateName: 'settings_inbox_list',
newLinkRouteName: 'settings_inbox_new',
children: this.inboxes.map(inbox => ({
id: inbox.id,
label: inbox.name,
toState: frontendURL(`accounts/${this.accountId}/inbox/${inbox.id}`),
type: inbox.channel_type,
phoneNumber: inbox.phone_number,
})),
};
},
labelSection() {
return {
icon: 'ion-pound',
label: 'LABELS',
hasSubMenu: true,
newLink: true,
key: 'label',
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
toStateName: 'labels_list',
showModalForNewItem: true,
modalName: 'AddLabel',
children: this.accountLabels.map(label => ({
id: label.id,
label: label.title,
color: label.color,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/label/${label.title}`
),
})),
};
},
contactLabelSection() {
return {
icon: 'ion-pound',
label: 'TAGGED_WITH',
hasSubMenu: true,
key: 'label',
newLink: false,
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
toStateName: 'labels_list',
showModalForNewItem: true,
modalName: 'AddLabel',
children: this.accountLabels.map(label => ({
id: label.id,
label: label.title,
color: label.color,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/labels/${label.title}/contacts`
),
})),
};
},
teamSection() {
return {
icon: 'ion-ios-people',
label: 'TEAMS',
hasSubMenu: true,
newLink: true,
key: 'team',
cssClass: 'menu-title align-justify teams-sidebar-menu',
toState: frontendURL(`accounts/${this.accountId}/settings/teams`),
toStateName: 'teams_list',
newLinkRouteName: 'settings_teams_new',
children: this.teams.map(team => ({
id: team.id,
label: team.name,
truncateLabel: true,
toState: frontendURL(`accounts/${this.accountId}/team/${team.id}`),
})),
};
},
dashboardPath() {
return frontendURL(`accounts/${this.accountId}/dashboard`);
showSecondaryMenu() {
if (this.shouldShowNotificationsSideMenu) return false;
return true;
},
activeParentRouteName() {},
},
mounted() {
this.$store.dispatch('labels/get');
@ -317,9 +177,7 @@ export default {
) > -1
);
},
toggleOptions() {
this.showOptionsMenu = !this.showOptionsMenu;
},
toggleAccountModal() {
this.showAccountModal = !this.showAccountModal;
},
@ -340,6 +198,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: 20rem;
flex-shrink: 0;
overflow: auto;
padding: var(--space-small);
}
</style>
<style lang="scss">
@import '~dashboard/assets/scss/variables';
@ -404,7 +282,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>

View file

@ -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="menuItem.key === 'inbox'"
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: {
@ -97,6 +86,7 @@ export default {
methods: {
computedInboxClass(child) {
const { type, phoneNumber } = child;
if (!type) return '';
const classByType = getInboxClassByType(type, phoneNumber);
return classByType;
},
@ -108,11 +98,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 +114,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 +138,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 +152,16 @@ 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);
&:hover {
color: var(--w-500);
}
}
</style>

View file

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

View file

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

View file

@ -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')"
>
@ -39,15 +45,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"
>
@ -63,13 +74,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: {
@ -88,18 +101,24 @@ export default {
if (this.globalConfig.createNewAccountFromDashboard) {
return true;
}
return this.currentUser.accounts.length > 1;
const { accounts = [] } = this.currentUser;
return accounts.length > 1;
},
},
methods: {
logout() {
Auth.logout();
},
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>

View file

@ -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', () => {

View file

@ -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+');
});
});

View file

@ -52,30 +52,8 @@ 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),

View file

@ -51,6 +51,7 @@
@toggle-user-mention="toggleUserMention"
@toggle-canned-menu="toggleCannedMenu"
/>
<h1>{{ message }}</h1>
</div>
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
<attachment-preview

View file

@ -3,6 +3,9 @@ import contacts from './sidebarItems/contacts';
import reports from './sidebarItems/reports';
import campaigns from './sidebarItems/campaigns';
import settings from './sidebarItems/settings';
import notifications from './sidebarItems/notifications';
// TODO - find hasSubMenu usage - July/2021
export const getSidebarItems = accountId => ({
common: common(accountId),
@ -10,4 +13,5 @@ export const getSidebarItems = accountId => ({
reports: reports(accountId),
campaigns: campaigns(accountId),
settings: settings(accountId),
notifications: notifications(accountId),
});

View file

@ -143,6 +143,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",
"CAMPAIGNS": "Campaigns",

View file

@ -2,29 +2,24 @@ import { frontendURL } from '../../helper/URLHelper';
const campaigns = accountId => ({
routes: ['settings_account_campaigns', 'one_off'],
menuItems: {
back: {
icon: 'ion-ios-arrow-back',
label: 'HOME',
hasSubMenu: false,
toStateName: 'home',
toState: frontendURL(`accounts/${accountId}/dashboard`),
},
ongoingCampaigns: {
menuItems: [
{
icon: 'ion-arrow-swap',
label: 'ONGOING',
key: 'ongoingCampaigns',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`),
toStateName: 'settings_account_campaigns',
},
onOffCampaigns: {
{
key: 'onOffCampaigns',
icon: 'ion-radio-waves',
label: 'ONE_OFF',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/campaigns/one_off`),
toStateName: 'one_off',
},
},
],
});
export default campaigns;

View file

@ -19,7 +19,8 @@ const common = accountId => ({
assignedToMe: {
icon: 'ion-chatbox-working',
label: 'CONVERSATIONS',
hasSubMenu: false,
hasSubMenu: true,
meta: { home: 'neber' },
key: '',
toState: frontendURL(`accounts/${accountId}/dashboard`),
toolTip: 'Conversation from all subscribed inboxes',
@ -28,35 +29,31 @@ const common = accountId => ({
contacts: {
icon: 'ion-person',
label: 'CONTACTS',
hasSubMenu: false,
hasSubMenu: true,
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: {
reports: {
key: 'reports',
icon: 'ion-arrow-graph-up-right',
label: 'REPORTS',
hasSubMenu: false,
hasSubMenu: true,
toState: frontendURL(`accounts/${accountId}/reports`),
toStateName: 'settings_account_reports',
},
campaigns: {
icon: 'ion-speakerphone',
label: 'CAMPAIGNS',
hasSubMenu: false,
key: 'campaigns',
hasSubMenu: true,
toState: frontendURL(`accounts/${accountId}/campaigns`),
toStateName: 'settings_account_campaigns',
},
settings: {
key: 'settings',
icon: 'ion-settings',
label: 'SETTINGS',
hasSubMenu: false,
hasSubMenu: true,
toState: frontendURL(`accounts/${accountId}/settings`),
toStateName: 'settings_home',
},

View file

@ -0,0 +1,6 @@
const notifications = () => ({
routes: ['notifications_index'],
menuItems: {},
});
export default notifications;

View file

@ -9,57 +9,50 @@ const reports = accountId => ({
'inbox_reports',
'team_reports',
],
menuItems: {
back: {
icon: 'ion-ios-arrow-back',
label: 'HOME',
hasSubMenu: false,
toStateName: 'home',
toState: frontendURL(`accounts/${accountId}/dashboard`),
},
reportOverview: {
menuItems: [
{
icon: 'ion-arrow-graph-up-right',
label: 'REPORTS_OVERVIEW',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/overview`),
toStateName: 'settings_account_reports',
},
csatReports: {
{
icon: 'ion-happy',
label: 'CSAT',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/csat`),
toStateName: 'csat_reports',
},
agentReports: {
{
icon: 'ion-person-stalker',
label: 'REPORTS_AGENT',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/agent`),
toStateName: 'agent_reports',
},
labelReports: {
{
icon: 'ion-pricetags',
label: 'REPORTS_LABEL',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/label`),
toStateName: 'label_reports',
},
inboxReports: {
{
icon: 'ion-archive',
label: 'REPORTS_INBOX',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/inboxes`),
toStateName: 'inbox_reports',
},
teamReports: {
{
icon: 'ion-ios-people',
label: 'REPORTS_TEAM',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/reports/teams`),
toStateName: 'team_reports',
},
},
],
});
export default reports;

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

View file

@ -0,0 +1,107 @@
<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"
:is-child-menu-active="false"
/>
</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>

View file

@ -0,0 +1,77 @@
<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 || isChildMenuActive }"
@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: '',
},
isChildMenuActive: {
type: Boolean,
default: false,
},
},
};
</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>

View file

@ -0,0 +1,214 @@
<template>
<div class="main-nav secondary-menu">
<transition-group name="menu-list" tag="ul" class="menu vertical">
<sidebar-item
v-if="shouldShowConversationsSideMenu"
:key="inboxSection.toState"
:menu-item="inboxSection"
/>
<sidebar-item
v-if="shouldShowTeamsSideMenu"
:key="teamSection.toState"
:menu-item="teamSection"
/>
<sidebar-item
v-if="shouldShowConversationsSideMenu"
:key="labelSection.toState"
:menu-item="labelSection"
@add-label="showAddLabelPopup"
/>
<sidebar-item
v-if="shouldShowContactSideMenu"
:key="contactLabelSection.key"
:menu-item="contactLabelSection"
@add-label="showAddLabelPopup"
/>
<sidebar-item
v-if="shouldShowCampaignSideMenu"
:key="campaignSubSection.key"
:menu-item="campaignSubSection"
/>
<sidebar-item
v-if="shouldShowReportsSideMenu"
:key="reportsSubSection.key"
:menu-item="reportsSubSection"
/>
<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>
</template>
<script>
import { frontendURL } from '../../../helper/URLHelper';
import SidebarItem from 'dashboard/components/layout/SidebarItem';
import routesMixin from 'dashboard/modules/sidebar/mixins/routes.mixin';
export default {
components: {
SidebarItem,
},
mixins: [routesMixin],
props: {
accountId: {
type: Number,
default: 0,
},
accountLabels: {
type: Array,
default: () => [],
},
inboxes: {
type: Array,
default: () => [],
},
teams: {
type: Array,
default: () => [],
},
menuItems: {
type: Array,
default: () => [],
},
},
computed: {
inboxSection() {
return {
icon: 'ion-folder',
label: 'INBOXES',
hasSubMenu: true,
newLink: true,
newLinkTag: 'NEW_INBOX',
key: 'inbox',
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/inboxes`),
toStateName: 'settings_inbox_list',
newLinkRouteName: 'settings_inbox_new',
children: this.inboxes.map(inbox => ({
id: inbox.id,
label: inbox.name,
toState: frontendURL(`accounts/${this.accountId}/inbox/${inbox.id}`),
type: inbox.channel_type,
phoneNumber: inbox.phone_number,
})),
};
},
labelSection() {
return {
icon: 'ion-pound',
label: 'LABELS',
hasSubMenu: true,
newLink: true,
newLinkTag: 'NEW_LABEL',
key: 'label',
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
toStateName: 'labels_list',
showModalForNewItem: true,
modalName: 'AddLabel',
children: this.accountLabels.map(label => ({
id: label.id,
label: label.title,
color: label.color,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/label/${label.title}`
),
})),
};
},
contactLabelSection() {
return {
icon: 'ion-pound',
label: 'TAGGED_WITH',
hasSubMenu: true,
key: 'label',
newLink: false,
newLinkTag: 'NEW_LABEL',
cssClass: 'menu-title align-justify',
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
toStateName: 'labels_list',
showModalForNewItem: true,
modalName: 'AddLabel',
children: this.accountLabels.map(label => ({
id: label.id,
label: label.title,
color: label.color,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/labels/${label.title}/contacts`
),
})),
};
},
campaignSubSection() {
return this.getSubSectionByKey('campaigns');
},
teamSection() {
return {
icon: 'ion-ios-people',
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`),
toStateName: 'teams_list',
newLinkRouteName: 'settings_teams_new',
children: this.teams.map(team => ({
id: team.id,
label: team.name,
truncateLabel: true,
toState: frontendURL(`accounts/${this.accountId}/team/${team.id}`),
})),
};
},
notificationsSubMenu() {
return {
icon: 'ion-ios-bell',
label: 'NOTIFICATIONS',
hasSubMenu: false,
cssClass: 'menu-title align-justify',
key: 'notifications',
children: [],
};
},
settingsSubMenu() {
return this.getSubSectionByKey('settings');
},
reportsSubSection() {
return this.getSubSectionByKey('reports');
},
},
methods: {
getSubSectionByKey(subSectionKey) {
const menuItems = Object.values(
this.sidemenuItems[subSectionKey].menuItems
);
const campaignItem = this.menuItems.find(
({ key }) => key === subSectionKey
);
return {
...campaignItem,
children: menuItems.map(item => ({
...item,
label: this.$t(`SIDEBAR.${item.label}`),
})),
};
},
showAddLabelPopup() {
this.$emit('add-label');
},
},
};
</script>

View file

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

View file

@ -0,0 +1,35 @@
import { getSidebarItems } from 'dashboard/i18n/default-sidebar';
export default {
computed: {
currentRoute() {
return this.$store.state.route.name;
},
sidemenuItems() {
return getSidebarItems(this.accountId);
},
shouldShowConversationsSideMenu() {
return this.sidemenuItems.common.routes.includes(this.currentRoute);
},
shouldShowContactSideMenu() {
return this.sidemenuItems.contacts.routes.includes(this.currentRoute);
},
shouldShowCampaignSideMenu() {
return this.sidemenuItems.campaigns.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
);
},
shouldShowTeamsSideMenu() {
return this.shouldShowSidebarItem && this.teams.length;
},
},
};

View file

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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 938 B