feat: Updates sidebar to accomodate sub menu (#3416)
* 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> * Chore: Update design changes to features * Fixes menu transitions and refactors code * Refactors sidebar routeing logic * lint error fixes * Fixes dropdown menu styles * Fixes secondary new item links * Fixes lint scss issues * fixes linter issues * Fixes broken test cases * Update AvailabilityStatus.spec.js * Review feedbacks * Fixes add modal for label * Add tooltip for primary menu item * Tooltip for notifications * Adds tooltip for primary menu items * Review fixes * Review fixes * Fix merge issues * fixes logo size for login pages * fixes Merge breaks with styles Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
parent
c792cfc0be
commit
b01d032d0d
38 changed files with 1119 additions and 562 deletions
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/* Enter and leave animations can use different */
|
/* Enter and leave animations can use different */
|
||||||
/* durations and timing functions. */
|
/* durations and timing functions. */
|
||||||
.slide-fade-enter-active {
|
.slide-fade-enter-active {
|
||||||
|
@ -9,7 +8,8 @@
|
||||||
transition: all .3s $ease-out-cubic;
|
transition: all .3s $ease-out-cubic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-fade-enter, .slide-fade-leave-to {
|
.slide-fade-enter,
|
||||||
|
.slide-fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(10px);
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,33 @@
|
||||||
transform: translateX($space-medium);
|
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;
|
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 {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX($space-medium);
|
transform: translateX($space-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-list-enter-active, .menu-list-leave-active {
|
.menu-list-enter-active,
|
||||||
transition: all .2s $ease-out-cubic;
|
.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;
|
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 {
|
.slide-up-enter-active {
|
||||||
|
@ -48,8 +59,8 @@
|
||||||
transition: all .3s $ease-out-cubic;
|
transition: all .3s $ease-out-cubic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-up-enter, .slide-up-leave-to
|
.slide-up-enter,
|
||||||
/* .slide-fade-leave-active for <2.1.8 */ {
|
.slide-up-leave-to {
|
||||||
transform: translateY(-$space-medium);
|
transform: translateY(-$space-medium);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -60,10 +71,10 @@
|
||||||
transition: transform 0.25s $ease-in-cubic, opacity 0.15s $ease-in-cubic;
|
transition: transform 0.25s $ease-in-cubic, opacity 0.15s $ease-in-cubic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-slide-enter, .menu-slide-leave-to
|
.menu-slide-enter,
|
||||||
/* .slide-fade-leave-active for <2.1.8 */ {
|
.menu-slide-leave-to {
|
||||||
transform: translateY($space-small);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transform: translateY($space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,10 +86,10 @@
|
||||||
transition: all .1s $ease-out-sine;
|
transition: all .1s $ease-out-sine;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-fade-enter, .toast-fade-leave-to
|
.toast-fade-enter,
|
||||||
/* .toast-fade-leave-active for <2.1.8 */ {
|
.toast-fade-leave-to {
|
||||||
transform: translateY(-$space-small);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transform: translateY(-$space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-fade-enter-active {
|
.modal-fade-enter-active {
|
||||||
|
@ -89,8 +100,8 @@
|
||||||
transition: all .1s $ease-out-sine;
|
transition: all .1s $ease-out-sine;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-fade-enter, .modal-fade-leave-to
|
.modal-fade-enter,
|
||||||
/* .slide-fade-leave-active for <2.1.8 */ {
|
.modal-fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,10 @@ code {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
border-radius: var(--border-radius-normal);
|
||||||
|
}
|
||||||
|
|
||||||
.padding-right-small {
|
.padding-right-small {
|
||||||
padding-right: var(--space-one);
|
padding-right: var(--space-one);
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,9 +219,9 @@ $badge-background: $primary-color;
|
||||||
$badge-color: $white;
|
$badge-color: $white;
|
||||||
$badge-color-alt: $black;
|
$badge-color-alt: $black;
|
||||||
$badge-palette: $foundation-palette;
|
$badge-palette: $foundation-palette;
|
||||||
$badge-padding: 0.3em;
|
$badge-padding: var(--space-smaller);
|
||||||
$badge-minwidth: 2.1em;
|
$badge-minwidth: 2.1em;
|
||||||
$badge-font-size: 0.6rem;
|
$badge-font-size: var(--font-size-nano);
|
||||||
|
|
||||||
// 10. Breadcrumbs
|
// 10. Breadcrumbs
|
||||||
// ---------------
|
// ---------------
|
||||||
|
@ -400,7 +400,7 @@ $mediaobject-image-width-stacked: 100%;
|
||||||
|
|
||||||
$menu-margin: 0;
|
$menu-margin: 0;
|
||||||
$menu-margin-nested: $space-medium;
|
$menu-margin-nested: $space-medium;
|
||||||
$menu-item-padding: $space-one;
|
$menu-item-padding: $space-slab;
|
||||||
$menu-item-color-active: $white;
|
$menu-item-color-active: $white;
|
||||||
$menu-item-background-active: $color-background;
|
$menu-item-background-active: $color-background;
|
||||||
$menu-icon-spacing: 0.25rem;
|
$menu-icon-spacing: 0.25rem;
|
||||||
|
|
|
@ -44,11 +44,14 @@ $woot-logo-padding: $space-large $space-two;
|
||||||
$color-woot: #1f93ff;
|
$color-woot: #1f93ff;
|
||||||
$color-gray: #6e6f73;
|
$color-gray: #6e6f73;
|
||||||
$color-light-gray: #999a9b;
|
$color-light-gray: #999a9b;
|
||||||
$color-border: #e0e6ed;
|
|
||||||
$color-border-light: #f0f4f5;
|
$color-border: var(--s-75);
|
||||||
$color-border-dark: #cad0d4;
|
$color-border-light: var(--s-50);
|
||||||
$color-background: #f4f6fb;
|
$color-border-dark: var(--s-100);
|
||||||
$color-background-light: #f9fafc;
|
|
||||||
|
$color-background: var(--s-50);
|
||||||
|
$color-background-light: var(--s-25);
|
||||||
|
|
||||||
$color-white: #fff;
|
$color-white: #fff;
|
||||||
$color-body: #3c4858;
|
$color-body: #3c4858;
|
||||||
$color-heading: #1f2d3d;
|
$color-heading: #1f2d3d;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@include elegant-card;
|
@include elegant-card;
|
||||||
@include border-light;
|
@include border-light;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
|
padding: var(--space-small);
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
z-index: var(--z-index-very-high);
|
z-index: var(--z-index-very-high);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
@include background-white;
|
@include background-white;
|
||||||
@include flex;
|
@include flex;
|
||||||
@include flex-align($x: justify, $y: middle);
|
@include flex-align($x: justify, $y: middle);
|
||||||
@include border-normal-bottom;
|
border-bottom: 1px solid var(--s-50);
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
min-height: $header-height;
|
min-height: $header-height;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.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;
|
z-index: 1024 - 1;
|
||||||
|
|
||||||
//logo
|
//logo
|
||||||
|
@ -22,28 +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 {
|
|
||||||
color: $color-gray;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $color-woot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active a .wrap {
|
|
||||||
color: $color-woot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nested {
|
.nested {
|
||||||
a {
|
a {
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
|
@ -64,7 +36,7 @@
|
||||||
.bottom-nav {
|
.bottom-nav {
|
||||||
@include flex;
|
@include flex;
|
||||||
@include space-between-column;
|
@include space-between-column;
|
||||||
@include padding($space-one);
|
@include padding($space-one $space-normal $space-one $space-one);
|
||||||
@include border-normal-top;
|
@include border-normal-top;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -85,32 +57,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-nav {
|
.hamburger--menu {
|
||||||
@include flex-weight(1);
|
cursor: pointer;
|
||||||
@include scroll-on-hover;
|
display: none;
|
||||||
padding: 0 $space-medium - $space-one;
|
margin-right: $space-normal;
|
||||||
|
|
||||||
a {
|
@media screen and (max-width: 1200px) {
|
||||||
&::before {
|
display: block;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header--icon {
|
.header--icon {
|
||||||
|
|
|
@ -1,66 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="status">
|
<woot-dropdown-menu>
|
||||||
<div class="status-view">
|
<woot-dropdown-header :title="$t('SIDEBAR.SET_AVAILABILITY_TITLE')" />
|
||||||
<availability-status-badge :status="currentUserAvailability" />
|
<woot-dropdown-item
|
||||||
<div class="status-view--title">
|
v-for="status in availabilityStatuses"
|
||||||
{{ availabilityDisplayLabel }}
|
:key="status.value"
|
||||||
</div>
|
class="status-items"
|
||||||
</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-button
|
<woot-button
|
||||||
variant="clear"
|
size="small"
|
||||||
color-scheme="secondary"
|
:color-scheme="status.disabled ? '' : 'secondary'"
|
||||||
class-names="status-change--change-button link"
|
:variant="status.disabled ? 'smooth' : 'clear'"
|
||||||
@click="openStatusMenu"
|
class-names="status-change--dropdown-button"
|
||||||
|
@click="changeAvailabilityStatus(status.value)"
|
||||||
>
|
>
|
||||||
{{ $t('SIDEBAR_ITEMS.CHANGE_AVAILABILITY_STATUS') }}
|
<availability-status-badge :status="status.value" />
|
||||||
|
{{ status.label }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</div>
|
</woot-dropdown-item>
|
||||||
</div>
|
<woot-dropdown-divider />
|
||||||
|
</woot-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
||||||
|
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader';
|
||||||
|
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider';
|
||||||
import AvailabilityStatusBadge from '../widgets/conversation/AvailabilityStatusBadge';
|
import AvailabilityStatusBadge from '../widgets/conversation/AvailabilityStatusBadge';
|
||||||
|
|
||||||
const AVAILABILITY_STATUS_KEYS = ['online', 'busy', 'offline'];
|
const AVAILABILITY_STATUS_KEYS = ['online', 'busy', 'offline'];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
WootDropdownHeader,
|
||||||
|
WootDropdownDivider,
|
||||||
WootDropdownMenu,
|
WootDropdownMenu,
|
||||||
WootDropdownItem,
|
WootDropdownItem,
|
||||||
AvailabilityStatusBadge,
|
AvailabilityStatusBadge,
|
||||||
|
@ -100,8 +75,7 @@ export default {
|
||||||
label: statusLabel,
|
label: statusLabel,
|
||||||
value: AVAILABILITY_STATUS_KEYS[index],
|
value: AVAILABILITY_STATUS_KEYS[index],
|
||||||
disabled:
|
disabled:
|
||||||
this.currentUserAvailability ===
|
this.currentUserAvailability === AVAILABILITY_STATUS_KEYS[index],
|
||||||
AVAILABILITY_STATUS_KEYS[index],
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -114,7 +88,8 @@ export default {
|
||||||
closeStatusMenu() {
|
closeStatusMenu() {
|
||||||
this.isStatusMenuOpened = false;
|
this.isStatusMenuOpened = false;
|
||||||
},
|
},
|
||||||
changeAvailabilityStatus(availability, accountId) {
|
changeAvailabilityStatus(availability) {
|
||||||
|
const accountId = this.currentAccountId;
|
||||||
if (this.isUpdating) {
|
if (this.isUpdating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<aside class="sidebar animated shrink columns">
|
<aside class="woot-sidebar" :class="{ 'only-primary': !showSecondaryMenu }">
|
||||||
<div class="logo">
|
<primary-sidebar
|
||||||
<router-link :to="dashboardPath" replace>
|
:logo-source="globalConfig.logo"
|
||||||
<img :src="globalConfig.logo" :alt="globalConfig.installationName" />
|
:installation-name="globalConfig.installationName"
|
||||||
</router-link>
|
:account-id="accountId"
|
||||||
</div>
|
:menu-items="primaryMenuItems"
|
||||||
|
@toggle-accounts="toggleAccountModal"
|
||||||
|
@key-shortcut-modal="toggleKeyShortcutModal"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="main-nav">
|
<secondary-sidebar
|
||||||
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
v-if="showSecondaryMenu"
|
||||||
<sidebar-item
|
:account-id="accountId"
|
||||||
v-for="item in accessibleMenuItems"
|
:inboxes="inboxes"
|
||||||
:key="item.toState"
|
:account-labels="accountLabels"
|
||||||
:menu-item="item"
|
:teams="teams"
|
||||||
/>
|
:menu-items="primaryMenuItems"
|
||||||
<sidebar-item
|
@add-label="showAddLabelPopup"
|
||||||
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 />
|
|
||||||
<fluent-icon class="current-user--options" icon="more-vertical" />
|
|
||||||
<options-menu
|
|
||||||
:show="showOptionsMenu"
|
|
||||||
@toggle-accounts="toggleAccountModal"
|
|
||||||
@show-support-chat-window="toggleSupportChatWindow"
|
|
||||||
@key-shortcut-modal="toggleKeyShortcutModal"
|
|
||||||
@close="toggleOptions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<woot-key-shortcut-modal
|
<woot-key-shortcut-modal
|
||||||
v-if="showShortcutModal"
|
v-if="showShortcutModal"
|
||||||
|
@ -82,17 +46,14 @@
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import adminMixin from '../../mixins/isAdmin';
|
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 { getSidebarItems } from '../../i18n/default-sidebar';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
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 AccountSelector from './sidebarComponents/AccountSelector.vue';
|
||||||
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
|
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
|
||||||
import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel';
|
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 WootKeyShortcutModal from 'components/widgets/modal/WootKeyShortcutModal';
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndCKey,
|
hasPressedAltAndCKey,
|
||||||
|
@ -110,11 +71,8 @@ export default {
|
||||||
AccountSelector,
|
AccountSelector,
|
||||||
AddAccountModal,
|
AddAccountModal,
|
||||||
AddLabelModal,
|
AddLabelModal,
|
||||||
AgentDetails,
|
PrimarySidebar,
|
||||||
AvailabilityStatus,
|
SecondarySidebar,
|
||||||
NotificationBell,
|
|
||||||
OptionsMenu,
|
|
||||||
SidebarItem,
|
|
||||||
WootKeyShortcutModal,
|
WootKeyShortcutModal,
|
||||||
},
|
},
|
||||||
mixins: [adminMixin, alertMixin, eventListenerMixins],
|
mixins: [adminMixin, alertMixin, eventListenerMixins],
|
||||||
|
@ -139,125 +97,34 @@ export default {
|
||||||
teams: 'teams/getMyTeams',
|
teams: 'teams/getMyTeams',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
sidemenuItems() {
|
sideMenuItems() {
|
||||||
return getSidebarItems(this.accountId);
|
return getSidebarItems(this.accountId);
|
||||||
},
|
},
|
||||||
accessibleMenuItems() {
|
primaryMenuItems() {
|
||||||
// get all keys in menuGroup
|
const menuItems = Object.values(
|
||||||
const groupKey = Object.keys(this.sidemenuItems);
|
getSidebarItems(this.accountId).common.menuItems
|
||||||
|
);
|
||||||
|
|
||||||
let menuItems = [];
|
return 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);
|
|
||||||
},
|
},
|
||||||
currentRoute() {
|
currentRoute() {
|
||||||
return this.$store.state.route.name;
|
return this.$store.state.route.name;
|
||||||
},
|
},
|
||||||
shouldShowSidebarItem() {
|
shouldShowNotificationsSideMenu() {
|
||||||
return this.sidemenuItems.common.routes.includes(this.currentRoute);
|
return this.sideMenuItems.notifications.routes.includes(
|
||||||
|
this.currentRoute
|
||||||
|
);
|
||||||
},
|
},
|
||||||
showShowContactSideMenu() {
|
shouldShowProfileSideMenu() {
|
||||||
return this.sidemenuItems.contacts.routes.includes(this.currentRoute);
|
return (
|
||||||
|
this.currentRoute === 'profile_settings_index' ||
|
||||||
|
this.currentRoute === 'profile_settings'
|
||||||
|
);
|
||||||
},
|
},
|
||||||
shouldShowTeams() {
|
showSecondaryMenu() {
|
||||||
return this.shouldShowSidebarItem && this.teams.length;
|
if (this.shouldShowNotificationsSideMenu) return false;
|
||||||
},
|
if (this.shouldShowProfileSideMenu) return false;
|
||||||
inboxSection() {
|
return true;
|
||||||
return {
|
|
||||||
icon: '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: 'number-symbol',
|
|
||||||
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: 'number-symbol',
|
|
||||||
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: 'people-team',
|
|
||||||
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`);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -318,9 +185,7 @@ export default {
|
||||||
) > -1
|
) > -1
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
toggleOptions() {
|
|
||||||
this.showOptionsMenu = !this.showOptionsMenu;
|
|
||||||
},
|
|
||||||
toggleAccountModal() {
|
toggleAccountModal() {
|
||||||
this.showAccountModal = !this.showAccountModal;
|
this.showAccountModal = !this.showAccountModal;
|
||||||
},
|
},
|
||||||
|
@ -341,6 +206,27 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.woot-sidebar {
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&.only-primary {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-menu {
|
||||||
|
background: var(--white);
|
||||||
|
border-right: 1px solid var(--s-50);
|
||||||
|
height: 100vh;
|
||||||
|
width: 19rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: var(--space-small);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '~dashboard/assets/scss/variables';
|
@import '~dashboard/assets/scss/variables';
|
||||||
|
|
||||||
|
@ -405,7 +291,7 @@ export default {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.teams-sidebar-menu + .nested.vertical.menu {
|
.secondary-menu .nested.vertical.menu {
|
||||||
padding-left: calc(var(--space-medium) - var(--space-one));
|
margin-left: var(--space-small);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<li :class="computedClass" class="sidebar-item">
|
||||||
:to="menuItem.toState"
|
|
||||||
tag="li"
|
|
||||||
active-class="active"
|
|
||||||
:class="computedClass"
|
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
class="sub-menu-title"
|
class="sub-menu-title"
|
||||||
:class="getMenuItemClass"
|
:class="getMenuItemClass"
|
||||||
|
@ -12,76 +7,61 @@
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
:title="menuItem.toolTip"
|
:title="menuItem.toolTip"
|
||||||
>
|
>
|
||||||
<div class="wrap">
|
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
||||||
<fluent-icon
|
|
||||||
size="18"
|
|
||||||
:icon="menuItem.icon"
|
|
||||||
class="margin-right-small"
|
|
||||||
/>
|
|
||||||
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
v-if="showItem(menuItem)"
|
|
||||||
class="child-icon"
|
|
||||||
@click.prevent="newLinkClick(menuItem)"
|
|
||||||
>
|
|
||||||
<fluent-icon icon="add-circle" size="16" />
|
|
||||||
</button>
|
|
||||||
</a>
|
</a>
|
||||||
<ul v-if="menuItem.hasSubMenu" class="nested vertical menu">
|
<ul v-if="menuItem.hasSubMenu" class="nested vertical menu">
|
||||||
<router-link
|
<secondary-nav-item
|
||||||
v-for="child in menuItem.children"
|
v-for="child in menuItem.children"
|
||||||
:key="child.id"
|
:key="child.id"
|
||||||
active-class="active flex-container"
|
|
||||||
tag="li"
|
|
||||||
:to="child.toState"
|
: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)">
|
<li>
|
||||||
<div class="wrap">
|
<a
|
||||||
<fluent-icon
|
:href="href"
|
||||||
v-if="menuItem.key === 'inbox'"
|
class="button small clear menu-item--new secondary"
|
||||||
class="inbox-icon"
|
:class="{ 'is-active': isActive }"
|
||||||
size="14"
|
@click="e => newLinkClick(e, navigate)"
|
||||||
:icon="computedInboxClass(child)"
|
>
|
||||||
/>
|
<fluent-icon icon="add" />
|
||||||
<span
|
<span class="button__content">
|
||||||
v-if="child.color"
|
{{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
|
||||||
class="label-color--display"
|
</span>
|
||||||
:style="{ backgroundColor: child.color }"
|
</a>
|
||||||
/>
|
</li>
|
||||||
<div
|
|
||||||
:title="computedChildTitle(child)"
|
|
||||||
:class="computedChildClass(child)"
|
|
||||||
>
|
|
||||||
{{ child.label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</ul>
|
</ul>
|
||||||
</router-link>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import router from '../../routes';
|
|
||||||
import adminMixin from '../../mixins/isAdmin';
|
import adminMixin from '../../mixins/isAdmin';
|
||||||
import { getInboxClassByType } from 'dashboard/helper/inbox';
|
import { getInboxClassByType } from 'dashboard/helper/inbox';
|
||||||
|
|
||||||
|
import SecondaryNavItem from 'dashboard/modules/sidebar/components/SecondaryNavItem';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { SecondaryNavItem },
|
||||||
mixins: [adminMixin],
|
mixins: [adminMixin],
|
||||||
props: {
|
props: {
|
||||||
menuItem: {
|
menuItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default() {
|
default: () => ({}),
|
||||||
return {};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({ activeInbox: 'getSelectedInbox' }),
|
||||||
activeInbox: 'getSelectedInbox',
|
|
||||||
}),
|
|
||||||
getMenuItemClass() {
|
getMenuItemClass() {
|
||||||
return this.menuItem.cssClass
|
return this.menuItem.cssClass
|
||||||
? `side-menu ${this.menuItem.cssClass}`
|
? `side-menu ${this.menuItem.cssClass}`
|
||||||
|
@ -104,6 +84,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
computedInboxClass(child) {
|
computedInboxClass(child) {
|
||||||
const { type, phoneNumber } = child;
|
const { type, phoneNumber } = child;
|
||||||
|
if (!type) return '';
|
||||||
const classByType = getInboxClassByType(type, phoneNumber);
|
const classByType = getInboxClassByType(type, phoneNumber);
|
||||||
return classByType;
|
return classByType;
|
||||||
},
|
},
|
||||||
|
@ -115,11 +96,12 @@ export default {
|
||||||
if (!child.truncateLabel) return false;
|
if (!child.truncateLabel) return false;
|
||||||
return child.label;
|
return child.label;
|
||||||
},
|
},
|
||||||
newLinkClick(item) {
|
newLinkClick(e, navigate) {
|
||||||
if (item.newLinkRouteName) {
|
if (this.menuItem.newLinkRouteName) {
|
||||||
router.push({ name: item.newLinkRouteName, params: { page: 'new' } });
|
navigate(e);
|
||||||
} else if (item.showModalForNewItem) {
|
} else if (this.menuItem.showModalForNewItem) {
|
||||||
if (item.modalName === 'AddLabel') {
|
if (this.menuItem.modalName === 'AddLabel') {
|
||||||
|
e.preventDefault();
|
||||||
this.$emit('add-label');
|
this.$emit('add-label');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,11 +113,22 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~dashboard/assets/scss/variables';
|
.sidebar-item {
|
||||||
|
margin: var(--space-small) 0;
|
||||||
|
}
|
||||||
.sub-menu-title {
|
.sub-menu-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
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 {
|
.wrap {
|
||||||
|
@ -144,10 +137,27 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-color--display {
|
.label-color--display {
|
||||||
border-radius: $space-smaller;
|
border-radius: var(--space-smaller);
|
||||||
height: $space-normal;
|
height: var(--space-normal);
|
||||||
margin-right: $space-small;
|
margin-right: var(--space-small);
|
||||||
min-width: $space-normal;
|
min-width: var(--space-normal);
|
||||||
width: $space-normal;
|
width: var(--space-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox-icon {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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>
|
</style>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="current-user--row">
|
<woot-button
|
||||||
|
v-tooltip.right="$t(`SIDEBAR.PROFILE_SETTINGS`)"
|
||||||
|
variant="link"
|
||||||
|
class="current-user"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
<thumbnail
|
<thumbnail
|
||||||
:src="currentUser.avatar_url"
|
:src="currentUser.avatar_url"
|
||||||
:username="currentUserAvailableName"
|
:username="currentUser.name"
|
||||||
|
:status="statusOfAgent"
|
||||||
|
should-show-status-always
|
||||||
|
size="32px"
|
||||||
/>
|
/>
|
||||||
<div class="current-user--data">
|
</woot-button>
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
@ -25,39 +25,25 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
currentRole: 'getCurrentRole',
|
currentUserAvailability: 'getCurrentUserAvailability',
|
||||||
}),
|
}),
|
||||||
currentUserAvailableName() {
|
statusOfAgent() {
|
||||||
return this.currentUser.name;
|
return this.currentUserAvailability || 'offline';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
this.$emit('toggle-menu');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.current-user--row {
|
.current-user {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--white);
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<span class="notifications" @click.stop="showNotification">
|
<div class="notifications-link">
|
||||||
<fluent-icon icon="alert" />
|
<primary-nav-item
|
||||||
<span v-if="unreadCount" class="unread-badge">{{ unreadCount }}</span>
|
name="NOTIFICATIONS"
|
||||||
</span>
|
icon="alert"
|
||||||
|
:to="`/app/accounts/${accountId}/notifications`"
|
||||||
|
:count="unreadCount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
import PrimaryNavItem from 'dashboard/modules/sidebar/components/PrimaryNavItem';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { PrimaryNavItem },
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
|
@ -15,40 +21,20 @@ export default {
|
||||||
}),
|
}),
|
||||||
unreadCount() {
|
unreadCount() {
|
||||||
if (!this.notificationMetadata.unreadCount) {
|
if (!this.notificationMetadata.unreadCount) {
|
||||||
return 0;
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.notificationMetadata.unreadCount < 100
|
return this.notificationMetadata.unreadCount < 100
|
||||||
? this.notificationMetadata.unreadCount
|
? `${this.notificationMetadata.unreadCount}`
|
||||||
: '99+';
|
: '99+';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {},
|
||||||
showNotification() {
|
|
||||||
this.$router.push(`/app/accounts/${this.accountId}/notifications`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.notifications {
|
.notifications-link {
|
||||||
font-size: var(--font-size-big);
|
margin-bottom: var(--space-small);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,15 +2,19 @@
|
||||||
<transition name="menu-slide">
|
<transition name="menu-slide">
|
||||||
<div
|
<div
|
||||||
v-if="show"
|
v-if="show"
|
||||||
v-on-clickaway="() => $emit('close')"
|
v-on-clickaway="onClickAway"
|
||||||
class="dropdown-pane dropdowm--top"
|
class="dropdown-pane"
|
||||||
|
:class="{ 'dropdown-pane--open': show }"
|
||||||
>
|
>
|
||||||
|
<availability-status />
|
||||||
|
<li class="divider" />
|
||||||
<woot-dropdown-menu>
|
<woot-dropdown-menu>
|
||||||
<woot-dropdown-item v-if="showChangeAccountOption">
|
<woot-dropdown-item v-if="showChangeAccountOption">
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="clear"
|
variant="clear"
|
||||||
|
color-scheme="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
class=" change-accounts--button"
|
icon="arrow-swap"
|
||||||
@click="$emit('toggle-accounts')"
|
@click="$emit('toggle-accounts')"
|
||||||
>
|
>
|
||||||
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
||||||
|
@ -19,36 +23,50 @@
|
||||||
<woot-dropdown-item v-if="globalConfig.chatwootInboxToken">
|
<woot-dropdown-item v-if="globalConfig.chatwootInboxToken">
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="clear"
|
variant="clear"
|
||||||
|
color-scheme="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
class=" change-accounts--button"
|
icon="ion-help-buoy"
|
||||||
@click="$emit('show-support-chat-window')"
|
@click="$emit('show-support-chat-window')"
|
||||||
>
|
>
|
||||||
Contact Support
|
{{ $t('SIDEBAR_ITEMS.CONTACT_SUPPORT') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
<woot-dropdown-item>
|
<woot-dropdown-item>
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="clear"
|
variant="clear"
|
||||||
|
color-scheme="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
class=" change-accounts--button"
|
icon="keyboard"
|
||||||
@click="$emit('key-shortcut-modal')"
|
@click="handleKeyboardHelpClick"
|
||||||
>
|
>
|
||||||
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
|
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
<woot-dropdown-item>
|
<woot-dropdown-item>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-slot="{ href, isActive, navigate }"
|
||||||
:to="`/app/accounts/${accountId}/profile/settings`"
|
:to="`/app/accounts/${accountId}/profile/settings`"
|
||||||
class="button clear small change-accounts--button"
|
custom
|
||||||
>
|
>
|
||||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
<a
|
||||||
|
:href="href"
|
||||||
|
class="button small clear secondary"
|
||||||
|
:class="{ 'is-active': isActive }"
|
||||||
|
@click="e => handleProfileSettingClick(e, navigate)"
|
||||||
|
>
|
||||||
|
<fluent-icon icon="person" class="icon icon--font" />
|
||||||
|
<span class="button__content">
|
||||||
|
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
<woot-dropdown-item>
|
<woot-dropdown-item>
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="clear"
|
variant="clear"
|
||||||
|
color-scheme="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
class=" change-accounts--button"
|
icon="power"
|
||||||
@click="logout"
|
@click="logout"
|
||||||
>
|
>
|
||||||
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
||||||
|
@ -63,13 +81,15 @@
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Auth from '../../../api/auth';
|
import Auth from '../../../api/auth';
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
||||||
|
import AvailabilityStatus from 'dashboard/components/layout/AvailabilityStatus';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
WootDropdownMenu,
|
WootDropdownMenu,
|
||||||
WootDropdownItem,
|
WootDropdownItem,
|
||||||
|
AvailabilityStatus,
|
||||||
},
|
},
|
||||||
mixins: [clickaway],
|
mixins: [clickaway],
|
||||||
props: {
|
props: {
|
||||||
|
@ -88,18 +108,34 @@ export default {
|
||||||
if (this.globalConfig.createNewAccountFromDashboard) {
|
if (this.globalConfig.createNewAccountFromDashboard) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this.currentUser.accounts.length > 1;
|
|
||||||
|
const { accounts = [] } = this.currentUser;
|
||||||
|
return accounts.length > 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleProfileSettingClick(e, navigate) {
|
||||||
|
this.$emit('close');
|
||||||
|
navigate(e);
|
||||||
|
},
|
||||||
|
handleKeyboardHelpClick() {
|
||||||
|
this.$emit('key-shortcut-modal');
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
logout() {
|
logout() {
|
||||||
Auth.logout();
|
Auth.logout();
|
||||||
},
|
},
|
||||||
|
onClickAway() {
|
||||||
|
if (this.show) this.$emit('close');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dropdown-pane {
|
.dropdown-pane {
|
||||||
right: 0;
|
left: var(--space-slab);
|
||||||
|
bottom: var(--space-larger);
|
||||||
|
min-width: 16.8rem;
|
||||||
|
z-index: var(--z-index-much-higher);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,13 +2,20 @@ import AgentDetails from '../AgentDetails';
|
||||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
import VTooltip from 'v-tooltip';
|
||||||
|
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail';
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail';
|
||||||
|
import WootButton from 'dashboard/components/ui/WootButton';
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
localVue.use(VueI18n);
|
localVue.use(VueI18n);
|
||||||
localVue.component('thumbnail', Thumbnail);
|
localVue.component('thumbnail', Thumbnail);
|
||||||
|
localVue.component('woot-button', WootButton);
|
||||||
|
localVue.component('woot-button', WootButton);
|
||||||
|
localVue.use(VTooltip, {
|
||||||
|
defaultHtml: false,
|
||||||
|
});
|
||||||
|
|
||||||
const i18nConfig = new VueI18n({
|
const i18nConfig = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
|
@ -16,7 +23,11 @@ const i18nConfig = new VueI18n({
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('agentDetails', () => {
|
describe('agentDetails', () => {
|
||||||
const currentUser = { name: 'Neymar Junior', avatar_url: '' };
|
const currentUser = {
|
||||||
|
name: 'Neymar Junior',
|
||||||
|
avatar_url: '',
|
||||||
|
availability_status: 'online',
|
||||||
|
};
|
||||||
const currentRole = 'agent';
|
const currentRole = 'agent';
|
||||||
let store = null;
|
let store = null;
|
||||||
let actions = null;
|
let actions = null;
|
||||||
|
@ -31,6 +42,7 @@ describe('agentDetails', () => {
|
||||||
getters: {
|
getters: {
|
||||||
getCurrentUser: () => currentUser,
|
getCurrentUser: () => currentUser,
|
||||||
getCurrentRole: () => currentRole,
|
getCurrentRole: () => currentRole,
|
||||||
|
getCurrentUserAvailability: () => currentUser.availability_status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -47,14 +59,8 @@ describe('agentDetails', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows the agent name', () => {
|
it(' the agent status', () => {
|
||||||
const agentTitle = agentDetails.find('.current-user--name');
|
expect(agentDetails.find('thumbnail-stub').vm.status).toBe('online');
|
||||||
expect(agentTitle.text()).toBe('Neymar Junior');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows the agent role', () => {
|
|
||||||
const agentTitle = agentDetails.find('.current-user--role');
|
|
||||||
expect(agentTitle.text()).toBe('Agent');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('agent thumbnail exists', () => {
|
it('agent thumbnail exists', () => {
|
||||||
|
|
|
@ -50,8 +50,9 @@ describe('notificationBell', () => {
|
||||||
localVue,
|
localVue,
|
||||||
i18n: i18nConfig,
|
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 () => {
|
it('it should return unread count 99+ ', async () => {
|
||||||
|
@ -61,7 +62,7 @@ describe('notificationBell', () => {
|
||||||
localVue,
|
localVue,
|
||||||
i18n: i18nConfig,
|
i18n: i18nConfig,
|
||||||
});
|
});
|
||||||
const statusViewTitle = notificationBell.find('.unread-badge');
|
const statusViewTitle = notificationBell.find('primary-nav-item-stub');
|
||||||
expect(statusViewTitle.text()).toBe('99+');
|
expect(statusViewTitle.vm.count).toBe('99+');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
import AvailabilityStatus from '../AvailabilityStatus';
|
import AvailabilityStatus from '../AvailabilityStatus.vue';
|
||||||
import { createLocalVue, mount } from '@vue/test-utils';
|
import { createLocalVue, mount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
|
||||||
import WootButton from 'dashboard/components/ui/WootButton';
|
import WootButton from 'dashboard/components/ui/WootButton';
|
||||||
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
||||||
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
||||||
|
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader';
|
||||||
|
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider';
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
localVue.use(VueI18n);
|
localVue.use(VueI18n);
|
||||||
localVue.component('woot-button', WootButton);
|
localVue.component('woot-button', WootButton);
|
||||||
|
localVue.component('woot-dropdown-header', WootDropdownHeader);
|
||||||
|
localVue.component('woot-dropdown-menu', WootDropdownMenu);
|
||||||
|
localVue.component('woot-dropdown-divider', WootDropdownDivider);
|
||||||
|
localVue.component('woot-dropdown-item', WootDropdownItem);
|
||||||
|
|
||||||
const i18nConfig = new VueI18n({
|
const i18nConfig = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
|
@ -52,29 +60,11 @@ 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 () => {
|
it('dispatches an action when user changes status', async () => {
|
||||||
await availabilityStatus
|
await availabilityStatus;
|
||||||
.find('.status-change--change-button')
|
availabilityStatus
|
||||||
.trigger('click');
|
.findAll('.status-change--dropdown-button')
|
||||||
|
.at(2)
|
||||||
await availabilityStatus
|
|
||||||
.find('.status-change li:last-child button')
|
|
||||||
.trigger('click');
|
.trigger('click');
|
||||||
|
|
||||||
expect(actions.updateAvailability).toBeCalledWith(
|
expect(actions.updateAvailability).toBeCalledWith(
|
||||||
|
|
|
@ -116,6 +116,10 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
shouldShowStatusAlways: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -124,6 +128,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showStatusIndicator() {
|
showStatusIndicator() {
|
||||||
|
if (this.shouldShowStatusAlways) return true;
|
||||||
return this.status === 'online' || this.status === 'busy';
|
return this.status === 'online' || this.status === 'busy';
|
||||||
},
|
},
|
||||||
avatarSize() {
|
avatarSize() {
|
||||||
|
@ -210,5 +215,9 @@ export default {
|
||||||
.user-online-status--busy {
|
.user-online-status--busy {
|
||||||
background: $warning-color;
|
background: $warning-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-online-status--offline {
|
||||||
|
background: var(--s-500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
background: var(--g-400);
|
background: var(--g-400);
|
||||||
}
|
}
|
||||||
&__offline {
|
&__offline {
|
||||||
background: var(--b-600);
|
background: var(--s-500);
|
||||||
}
|
}
|
||||||
&__busy {
|
&__busy {
|
||||||
background: var(--y-700);
|
background: var(--y-700);
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
@toggle-user-mention="toggleUserMention"
|
@toggle-user-mention="toggleUserMention"
|
||||||
@toggle-canned-menu="toggleCannedMenu"
|
@toggle-canned-menu="toggleCannedMenu"
|
||||||
/>
|
/>
|
||||||
|
<h1>{{ message }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
|
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
|
||||||
<attachment-preview
|
<attachment-preview
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="file message-text__wrap">
|
<div class="file message-text__wrap">
|
||||||
<div class="icon-wrap">
|
<div class="icon-wrap">
|
||||||
<fluent-icon icon="document" class="file--icon" />
|
<fluent-icon icon="document" class="file--icon" size="32" />
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<h5 class="text-block-title">
|
<h5 class="text-block-title">
|
||||||
{{ decodeURI(fileName) }}
|
{{ decodeURI(fileName) }}
|
||||||
</h5>
|
</h5>
|
||||||
<a
|
<a
|
||||||
class="download clear button small"
|
class="download clear link button small"
|
||||||
rel="noreferrer noopener nofollow"
|
rel="noreferrer noopener nofollow"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:href="url"
|
:href="url"
|
||||||
|
|
|
@ -3,6 +3,9 @@ import contacts from './sidebarItems/contacts';
|
||||||
import reports from './sidebarItems/reports';
|
import reports from './sidebarItems/reports';
|
||||||
import campaigns from './sidebarItems/campaigns';
|
import campaigns from './sidebarItems/campaigns';
|
||||||
import settings from './sidebarItems/settings';
|
import settings from './sidebarItems/settings';
|
||||||
|
import notifications from './sidebarItems/notifications';
|
||||||
|
|
||||||
|
// TODO - find hasSubMenu usage - July/2021
|
||||||
|
|
||||||
export const getSidebarItems = accountId => ({
|
export const getSidebarItems = accountId => ({
|
||||||
common: common(accountId),
|
common: common(accountId),
|
||||||
|
@ -10,4 +13,5 @@ export const getSidebarItems = accountId => ({
|
||||||
reports: reports(accountId),
|
reports: reports(accountId),
|
||||||
campaigns: campaigns(accountId),
|
campaigns: campaigns(accountId),
|
||||||
settings: settings(accountId),
|
settings: settings(accountId),
|
||||||
|
notifications: notifications(accountId),
|
||||||
});
|
});
|
||||||
|
|
|
@ -103,6 +103,7 @@
|
||||||
"SIDEBAR_ITEMS": {
|
"SIDEBAR_ITEMS": {
|
||||||
"CHANGE_AVAILABILITY_STATUS": "Change",
|
"CHANGE_AVAILABILITY_STATUS": "Change",
|
||||||
"CHANGE_ACCOUNTS": "Switch Account",
|
"CHANGE_ACCOUNTS": "Switch Account",
|
||||||
|
"CONTACT_SUPPORT": "Contact Support",
|
||||||
"SELECTOR_SUBTITLE": "Select an account from the following list",
|
"SELECTOR_SUBTITLE": "Select an account from the following list",
|
||||||
"PROFILE_SETTINGS": "Profile Settings",
|
"PROFILE_SETTINGS": "Profile Settings",
|
||||||
"KEYBOARD_SHORTCUTS": "Keyboard Shortcuts",
|
"KEYBOARD_SHORTCUTS": "Keyboard Shortcuts",
|
||||||
|
@ -143,6 +144,7 @@
|
||||||
"NOTIFICATIONS": "Notifications",
|
"NOTIFICATIONS": "Notifications",
|
||||||
"CANNED_RESPONSES": "Canned Responses",
|
"CANNED_RESPONSES": "Canned Responses",
|
||||||
"INTEGRATIONS": "Integrations",
|
"INTEGRATIONS": "Integrations",
|
||||||
|
"PROFILE_SETTINGS": "Profile Settings",
|
||||||
"ACCOUNT_SETTINGS": "Account Settings",
|
"ACCOUNT_SETTINGS": "Account Settings",
|
||||||
"APPLICATIONS": "Applications",
|
"APPLICATIONS": "Applications",
|
||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
|
@ -151,6 +153,9 @@
|
||||||
"TEAMS": "Teams",
|
"TEAMS": "Teams",
|
||||||
"ALL_CONTACTS": "All Contacts",
|
"ALL_CONTACTS": "All Contacts",
|
||||||
"TAGGED_WITH": "Tagged with",
|
"TAGGED_WITH": "Tagged with",
|
||||||
|
"NEW_LABEL": "New label",
|
||||||
|
"NEW_TEAM": "New team",
|
||||||
|
"NEW_INBOX": "New inbox",
|
||||||
"REPORTS_OVERVIEW": "Overview",
|
"REPORTS_OVERVIEW": "Overview",
|
||||||
"CSAT": "CSAT",
|
"CSAT": "CSAT",
|
||||||
"CAMPAIGNS": "Campaigns",
|
"CAMPAIGNS": "Campaigns",
|
||||||
|
@ -159,7 +164,8 @@
|
||||||
"REPORTS_AGENT": "Agents",
|
"REPORTS_AGENT": "Agents",
|
||||||
"REPORTS_LABEL": "Labels",
|
"REPORTS_LABEL": "Labels",
|
||||||
"REPORTS_INBOX": "Inbox",
|
"REPORTS_INBOX": "Inbox",
|
||||||
"REPORTS_TEAM": "Team"
|
"REPORTS_TEAM": "Team",
|
||||||
|
"SET_AVAILABILITY_TITLE": "Set yourself as"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
|
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
|
||||||
|
|
|
@ -2,29 +2,24 @@ import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
const campaigns = accountId => ({
|
const campaigns = accountId => ({
|
||||||
routes: ['settings_account_campaigns', 'one_off'],
|
routes: ['settings_account_campaigns', 'one_off'],
|
||||||
menuItems: {
|
menuItems: [
|
||||||
back: {
|
{
|
||||||
icon: 'chevron-left',
|
|
||||||
label: 'HOME',
|
|
||||||
hasSubMenu: false,
|
|
||||||
toStateName: 'home',
|
|
||||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
|
||||||
},
|
|
||||||
ongoingCampaigns: {
|
|
||||||
icon: 'arrow-swap',
|
icon: 'arrow-swap',
|
||||||
label: 'ONGOING',
|
label: 'ONGOING',
|
||||||
|
key: 'ongoingCampaigns',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`),
|
toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`),
|
||||||
toStateName: 'settings_account_campaigns',
|
toStateName: 'settings_account_campaigns',
|
||||||
},
|
},
|
||||||
onOffCampaigns: {
|
{
|
||||||
|
key: 'oneOffCampaigns',
|
||||||
icon: 'sound-source',
|
icon: 'sound-source',
|
||||||
label: 'ONE_OFF',
|
label: 'ONE_OFF',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/campaigns/one_off`),
|
toState: frontendURL(`accounts/${accountId}/campaigns/one_off`),
|
||||||
toStateName: 'one_off',
|
toStateName: 'one_off',
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default campaigns;
|
export default campaigns;
|
||||||
|
|
|
@ -19,44 +19,41 @@ const common = accountId => ({
|
||||||
assignedToMe: {
|
assignedToMe: {
|
||||||
icon: 'chat',
|
icon: 'chat',
|
||||||
label: 'CONVERSATIONS',
|
label: 'CONVERSATIONS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: true,
|
||||||
key: '',
|
key: 'conversations',
|
||||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
||||||
toolTip: 'Conversation from all subscribed inboxes',
|
toolTip: 'Conversation from all subscribed inboxes',
|
||||||
toStateName: 'home',
|
toStateName: 'home',
|
||||||
},
|
},
|
||||||
contacts: {
|
contacts: {
|
||||||
|
key: 'contacts',
|
||||||
icon: 'book-contacts',
|
icon: 'book-contacts',
|
||||||
label: 'CONTACTS',
|
label: 'CONTACTS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: true,
|
||||||
toState: frontendURL(`accounts/${accountId}/contacts`),
|
toState: frontendURL(`accounts/${accountId}/contacts`),
|
||||||
toStateName: 'contacts_dashboard',
|
toStateName: 'contacts_dashboard',
|
||||||
},
|
},
|
||||||
notifications: {
|
reports: {
|
||||||
icon: 'alert',
|
key: 'reports',
|
||||||
label: 'NOTIFICATIONS',
|
|
||||||
hasSubMenu: false,
|
|
||||||
toState: frontendURL(`accounts/${accountId}/notifications`),
|
|
||||||
toStateName: 'notifications_dashboard',
|
|
||||||
},
|
|
||||||
report: {
|
|
||||||
icon: 'arrow-trending-lines',
|
icon: 'arrow-trending-lines',
|
||||||
label: 'REPORTS',
|
label: 'REPORTS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: true,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports`),
|
toState: frontendURL(`accounts/${accountId}/reports`),
|
||||||
toStateName: 'settings_account_reports',
|
toStateName: 'settings_account_reports',
|
||||||
},
|
},
|
||||||
campaigns: {
|
campaigns: {
|
||||||
|
key: 'campaigns',
|
||||||
icon: 'megaphone',
|
icon: 'megaphone',
|
||||||
label: 'CAMPAIGNS',
|
label: 'CAMPAIGNS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: true,
|
||||||
toState: frontendURL(`accounts/${accountId}/campaigns`),
|
toState: frontendURL(`accounts/${accountId}/campaigns`),
|
||||||
toStateName: 'settings_account_campaigns',
|
toStateName: 'settings_account_campaigns',
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
|
key: 'settings',
|
||||||
icon: 'settings',
|
icon: 'settings',
|
||||||
label: 'SETTINGS',
|
label: 'SETTINGS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: true,
|
||||||
toState: frontendURL(`accounts/${accountId}/settings`),
|
toState: frontendURL(`accounts/${accountId}/settings`),
|
||||||
toStateName: 'settings_home',
|
toStateName: 'settings_home',
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const notifications = () => ({
|
||||||
|
routes: ['notifications_index'],
|
||||||
|
menuItems: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default notifications;
|
|
@ -9,57 +9,50 @@ const reports = accountId => ({
|
||||||
'inbox_reports',
|
'inbox_reports',
|
||||||
'team_reports',
|
'team_reports',
|
||||||
],
|
],
|
||||||
menuItems: {
|
menuItems: [
|
||||||
back: {
|
{
|
||||||
icon: 'chevron-left',
|
|
||||||
label: 'HOME',
|
|
||||||
hasSubMenu: false,
|
|
||||||
toStateName: 'home',
|
|
||||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
|
||||||
},
|
|
||||||
reportOverview: {
|
|
||||||
icon: 'arrow-trending-lines',
|
icon: 'arrow-trending-lines',
|
||||||
label: 'REPORTS_OVERVIEW',
|
label: 'REPORTS_OVERVIEW',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/overview`),
|
toState: frontendURL(`accounts/${accountId}/reports/overview`),
|
||||||
toStateName: 'settings_account_reports',
|
toStateName: 'settings_account_reports',
|
||||||
},
|
},
|
||||||
csatReports: {
|
{
|
||||||
icon: 'emoji',
|
icon: 'emoji',
|
||||||
label: 'CSAT',
|
label: 'CSAT',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/csat`),
|
toState: frontendURL(`accounts/${accountId}/reports/csat`),
|
||||||
toStateName: 'csat_reports',
|
toStateName: 'csat_reports',
|
||||||
},
|
},
|
||||||
agentReports: {
|
{
|
||||||
icon: 'people',
|
icon: 'people',
|
||||||
label: 'REPORTS_AGENT',
|
label: 'REPORTS_AGENT',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/agent`),
|
toState: frontendURL(`accounts/${accountId}/reports/agent`),
|
||||||
toStateName: 'agent_reports',
|
toStateName: 'agent_reports',
|
||||||
},
|
},
|
||||||
labelReports: {
|
{
|
||||||
icon: 'tag',
|
icon: 'tag',
|
||||||
label: 'REPORTS_LABEL',
|
label: 'REPORTS_LABEL',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/label`),
|
toState: frontendURL(`accounts/${accountId}/reports/label`),
|
||||||
toStateName: 'label_reports',
|
toStateName: 'label_reports',
|
||||||
},
|
},
|
||||||
inboxReports: {
|
{
|
||||||
icon: 'mail-inbox-all',
|
icon: 'mail-inbox-all',
|
||||||
label: 'REPORTS_INBOX',
|
label: 'REPORTS_INBOX',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/inboxes`),
|
toState: frontendURL(`accounts/${accountId}/reports/inboxes`),
|
||||||
toStateName: 'inbox_reports',
|
toStateName: 'inbox_reports',
|
||||||
},
|
},
|
||||||
teamReports: {
|
{
|
||||||
icon: 'people-team',
|
icon: 'people-team',
|
||||||
label: 'REPORTS_TEAM',
|
label: 'REPORTS_TEAM',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports/teams`),
|
toState: frontendURL(`accounts/${accountId}/reports/teams`),
|
||||||
toStateName: 'team_reports',
|
toStateName: 'team_reports',
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default reports;
|
export default reports;
|
||||||
|
|
46
app/javascript/dashboard/modules/sidebar/components/Logo.vue
Normal file
46
app/javascript/dashboard/modules/sidebar/components/Logo.vue
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<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: 32px;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
padding: var(--space-normal);
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: $logo-size;
|
||||||
|
height: $logo-size;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: left center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
120
app/javascript/dashboard/modules/sidebar/components/Primary.vue
Normal file
120
app/javascript/dashboard/modules/sidebar/components/Primary.vue
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<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="isMenuActive(menuItem, $route.name)"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
@key-shortcut-modal="$emit('key-shortcut-modal')"
|
||||||
|
@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();
|
||||||
|
},
|
||||||
|
isMenuActive(menuItem, currentRouteName) {
|
||||||
|
const { key = '' } = menuItem;
|
||||||
|
|
||||||
|
if (currentRouteName === key) return true;
|
||||||
|
// Conversations route is defaulted as home
|
||||||
|
// TODO: Needs to ewfactor old statenames to follow a structure while key naming.
|
||||||
|
if (currentRouteName.includes('inbox') && key === 'conversations')
|
||||||
|
return true;
|
||||||
|
if (currentRouteName.includes('conversations') && key === 'conversations')
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</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,78 @@
|
||||||
|
<template>
|
||||||
|
<router-link v-slot="{ href, isActive, navigate }" :to="to" custom>
|
||||||
|
<a
|
||||||
|
v-tooltip.right="$t(`SIDEBAR.${name}`)"
|
||||||
|
:href="href"
|
||||||
|
class="button clear button--only-icon menu-item"
|
||||||
|
:class="{ 'is-active': isActive || isChildMenuActive }"
|
||||||
|
@click="navigate"
|
||||||
|
>
|
||||||
|
<fluent-icon :icon="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>
|
|
@ -0,0 +1,215 @@
|
||||||
|
<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: 'folder',
|
||||||
|
label: 'INBOXES',
|
||||||
|
hasSubMenu: true,
|
||||||
|
newLink: true,
|
||||||
|
newLinkTag: 'NEW_INBOX',
|
||||||
|
key: 'inbox',
|
||||||
|
cssClass: 'menu-title align-justify',
|
||||||
|
toState: frontendURL(`accounts/${this.accountId}/settings/inboxes/new`),
|
||||||
|
toStateName: 'settings_inbox_new',
|
||||||
|
newLinkRouteName: 'settings_inbox_new',
|
||||||
|
children: this.inboxes.map(inbox => ({
|
||||||
|
id: inbox.id,
|
||||||
|
label: inbox.name,
|
||||||
|
truncateLabel: true,
|
||||||
|
toState: frontendURL(`accounts/${this.accountId}/inbox/${inbox.id}`),
|
||||||
|
type: inbox.channel_type,
|
||||||
|
phoneNumber: inbox.phone_number,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
labelSection() {
|
||||||
|
return {
|
||||||
|
icon: 'number-symbol',
|
||||||
|
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: 'number-symbol',
|
||||||
|
label: 'TAGGED_WITH',
|
||||||
|
hasSubMenu: true,
|
||||||
|
key: 'label',
|
||||||
|
newLink: true,
|
||||||
|
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: 'people-team',
|
||||||
|
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/new`),
|
||||||
|
toStateName: 'settings_teams_new',
|
||||||
|
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: 'alert',
|
||||||
|
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>
|
|
@ -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 text-truncate"
|
||||||
|
:class="{ 'is-active': isActive, 'text-truncate': shouldTruncate }"
|
||||||
|
@click="navigate"
|
||||||
|
>
|
||||||
|
<span v-if="icon" class="badge--icon">
|
||||||
|
<fluent-icon class="inbox-icon" :icon="icon" size="10" />
|
||||||
|
</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>
|
|
@ -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.shouldShowConversationsSideMenu && this.teams.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -33,9 +33,9 @@ export default {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if (this.isSidebarOpen) {
|
if (this.isSidebarOpen) {
|
||||||
return 'off-canvas is-open ';
|
return 'off-canvas position-left is-transition-push is-open';
|
||||||
}
|
}
|
||||||
return 'off-canvas position-left is-transition-push is-closed';
|
return 'off-canvas is-transition-push is-closed';
|
||||||
},
|
},
|
||||||
contentClassName() {
|
contentClassName() {
|
||||||
if (this.isOnDesktop) {
|
if (this.isOnDesktop) {
|
||||||
|
@ -44,7 +44,7 @@ export default {
|
||||||
if (this.isSidebarOpen) {
|
if (this.isSidebarOpen) {
|
||||||
return 'off-canvas-content is-open-left has-transition-push has-position-left';
|
return 'off-canvas-content is-open-left has-transition-push has-position-left';
|
||||||
}
|
}
|
||||||
return 'off-canvas-content';
|
return 'off-canvas-content has-transition-push';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -71,3 +71,8 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.off-canvas-content.is-open-left {
|
||||||
|
transform: translateX(25.4rem);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
--white: #fff;
|
--white: #fff;
|
||||||
--white-transparent: rgba(255, 255, 255, 0.9);
|
--white-transparent: rgba(255, 255, 255, 0.9);
|
||||||
|
|
||||||
--w-50: #E3F2FF;
|
--w-25: #F5FAFF;
|
||||||
--w-100: #BBDDFF;
|
--w-50: #EBF5FF;
|
||||||
--w-200: #8FC9FF;
|
--w-100: #C2E1FF;
|
||||||
--w-300: #61B3FF;
|
--w-200: #99CEFF;
|
||||||
--w-400: #3FA3FF;
|
--w-300: ##70BAFF;
|
||||||
|
--w-400: #47A6FF;
|
||||||
--w-500: #1F93FF;
|
--w-500: #1F93FF;
|
||||||
--w-600: #2284F0;
|
--w-600: #1976CC;
|
||||||
--w-700: #2272DC;
|
--w-700: #135899;
|
||||||
--w-800: #2161CA;
|
--w-800: #0C3B66;
|
||||||
--w-900: #1F41AB;
|
--w-900: #061D33;
|
||||||
|
|
||||||
--g-50: #E6F8E6;
|
--g-50: #E6F8E6;
|
||||||
--g-100: #C4EEC2;
|
--g-100: #C4EEC2;
|
||||||
|
@ -35,16 +36,18 @@
|
||||||
--y-800: #FDAD2A;
|
--y-800: #FDAD2A;
|
||||||
--y-900: #F9841B;
|
--y-900: #F9841B;
|
||||||
|
|
||||||
--s-50: #E7EEFB;
|
--s-25: #F8FAFC;
|
||||||
--s-100: #C8D6E6;
|
--s-50: #F1F5F8;
|
||||||
--s-200: #ABBACE;
|
--s-75: #EBF0F5;
|
||||||
--s-300: #8C9EB6;
|
--s-100: #E4EBF1;
|
||||||
--s-400: #7489A4;
|
--s-200: #C9D7E3;
|
||||||
--s-500: #5D7592;
|
--s-300: #AEC3D5;
|
||||||
--s-600: #506781;
|
--s-400: #93AFC8;
|
||||||
--s-700: #40546B;
|
--s-500: #779BBB;
|
||||||
--s-800: #314155;
|
--s-600: #446888;
|
||||||
--s-900: #1F2D3D;
|
--s-700: #37546D;
|
||||||
|
--s-800: #293F51;
|
||||||
|
--s-900: #1B2836;
|
||||||
|
|
||||||
--b-50: #F8F9FE;
|
--b-50: #F8F9FE;
|
||||||
--b-100: #F2F3F7;
|
--b-100: #F2F3F7;
|
||||||
|
@ -85,12 +88,12 @@
|
||||||
--color-heading: #1f2d3d;
|
--color-heading: #1f2d3d;
|
||||||
--color-body: #3c4858;
|
--color-body: #3c4858;
|
||||||
|
|
||||||
--color-border: #e0e6ed;
|
--color-border: var(--s-75);
|
||||||
--color-border-light: #f0f4f5;
|
--color-border-light: var(--s-50);
|
||||||
--color-border-dark: #cad0d4;
|
--color-border-dark: var(--s-100);
|
||||||
|
|
||||||
--color-background: #f4f6fb;
|
--color-background: var(--s-50);
|
||||||
--color-background-light: #f9fafc;
|
--color-background-light: var(--s-25);
|
||||||
|
|
||||||
// Social and inboxes brand colors
|
// Social and inboxes brand colors
|
||||||
--color-facebook-brand: #3b5998;
|
--color-facebook-brand: #3b5998;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"headphones-sound-wave-outline": "M3.5 12a8.5 8.5 0 0 1 17 0v2h-2.25a.75.75 0 0 0-.75.75v6.5c0 .414.336.75.75.75H19a3 3 0 0 0 3-3v-7c0-5.523-4.477-10-10-10S2 6.477 2 12v7a3 3 0 0 0 3 3h.75a.75.75 0 0 0 .75-.75v-6.5a.75.75 0 0 0-.75-.75H3.5v-2Zm17 3.5V19a1.5 1.5 0 0 1-1.5 1.5v-5h1.5ZM3.5 19v-3.5H5v5A1.5 1.5 0 0 1 3.5 19Zm9.25-7.25a.75.75 0 0 0-1.5 0v10.5a.75.75 0 0 0 1.5 0v-10.5Zm-4 2.25a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5a.75.75 0 0 1 .75-.75Zm7.25.75a.75.75 0 0 0-1.5 0v4.5a.75.75 0 0 0 1.5 0v-4.5Z",
|
"headphones-sound-wave-outline": "M3.5 12a8.5 8.5 0 0 1 17 0v2h-2.25a.75.75 0 0 0-.75.75v6.5c0 .414.336.75.75.75H19a3 3 0 0 0 3-3v-7c0-5.523-4.477-10-10-10S2 6.477 2 12v7a3 3 0 0 0 3 3h.75a.75.75 0 0 0 .75-.75v-6.5a.75.75 0 0 0-.75-.75H3.5v-2Zm17 3.5V19a1.5 1.5 0 0 1-1.5 1.5v-5h1.5ZM3.5 19v-3.5H5v5A1.5 1.5 0 0 1 3.5 19Zm9.25-7.25a.75.75 0 0 0-1.5 0v10.5a.75.75 0 0 0 1.5 0v-10.5Zm-4 2.25a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5a.75.75 0 0 1 .75-.75Zm7.25.75a.75.75 0 0 0-1.5 0v4.5a.75.75 0 0 0 1.5 0v-4.5Z",
|
||||||
"image-outline": "M17.75 3A3.25 3.25 0 0 1 21 6.25v11.5A3.25 3.25 0 0 1 17.75 21H6.25A3.25 3.25 0 0 1 3 17.75V6.25A3.25 3.25 0 0 1 6.25 3h11.5Zm.58 16.401-5.805-5.686a.75.75 0 0 0-.966-.071l-.084.07-5.807 5.687c.182.064.378.099.582.099h11.5c.203 0 .399-.035.58-.099l-5.805-5.686L18.33 19.4ZM17.75 4.5H6.25A1.75 1.75 0 0 0 4.5 6.25v11.5c0 .208.036.408.103.594l5.823-5.701a2.25 2.25 0 0 1 3.02-.116l.128.116 5.822 5.702c.067-.186.104-.386.104-.595V6.25a1.75 1.75 0 0 0-1.75-1.75Zm-2.498 2a2.252 2.252 0 1 1 0 4.504 2.252 2.252 0 0 1 0-4.504Zm0 1.5a.752.752 0 1 0 0 1.504.752.752 0 0 0 0-1.504Z",
|
"image-outline": "M17.75 3A3.25 3.25 0 0 1 21 6.25v11.5A3.25 3.25 0 0 1 17.75 21H6.25A3.25 3.25 0 0 1 3 17.75V6.25A3.25 3.25 0 0 1 6.25 3h11.5Zm.58 16.401-5.805-5.686a.75.75 0 0 0-.966-.071l-.084.07-5.807 5.687c.182.064.378.099.582.099h11.5c.203 0 .399-.035.58-.099l-5.805-5.686L18.33 19.4ZM17.75 4.5H6.25A1.75 1.75 0 0 0 4.5 6.25v11.5c0 .208.036.408.103.594l5.823-5.701a2.25 2.25 0 0 1 3.02-.116l.128.116 5.822 5.702c.067-.186.104-.386.104-.595V6.25a1.75 1.75 0 0 0-1.75-1.75Zm-2.498 2a2.252 2.252 0 1 1 0 4.504 2.252 2.252 0 0 1 0-4.504Zm0 1.5a.752.752 0 1 0 0 1.504.752.752 0 0 0 0-1.504Z",
|
||||||
"info-outline": "M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999Zm0 1.5a8.502 8.502 0 1 0 0 17.003A8.502 8.502 0 0 0 12 3.5Zm-.004 7a.75.75 0 0 1 .744.648l.007.102.003 5.502a.75.75 0 0 1-1.493.102l-.007-.101-.003-5.502a.75.75 0 0 1 .75-.75ZM12 7.003a.999.999 0 1 1 0 1.997.999.999 0 0 1 0-1.997Z",
|
"info-outline": "M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999Zm0 1.5a8.502 8.502 0 1 0 0 17.003A8.502 8.502 0 0 0 12 3.5Zm-.004 7a.75.75 0 0 1 .744.648l.007.102.003 5.502a.75.75 0 0 1-1.493.102l-.007-.101-.003-5.502a.75.75 0 0 1 .75-.75ZM12 7.003a.999.999 0 1 1 0 1.997.999.999 0 0 1 0-1.997Z",
|
||||||
|
"keyboard-outline": "M19.745 5a2.25 2.25 0 0 1 2.25 2.25v9.505a2.25 2.25 0 0 1-2.25 2.25H4.25A2.25 2.25 0 0 1 2 16.755V7.25A2.25 2.25 0 0 1 4.25 5h15.495Zm0 1.5H4.25a.75.75 0 0 0-.75.75v9.505c0 .414.336.75.75.75h15.495a.75.75 0 0 0 .75-.75V7.25a.75.75 0 0 0-.75-.75Zm-12.995 8h10.5a.75.75 0 0 1 .102 1.493L17.25 16H6.75a.75.75 0 0 1-.102-1.493l.102-.007h10.5-10.5ZM16.5 11a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm-5.995 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm-3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm6 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2ZM6 8a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm2.995 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Zm3 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z",
|
||||||
"link-outline": "M9.25 7a.75.75 0 0 1 .11 1.492l-.11.008H7a3.5 3.5 0 0 0-.206 6.994L7 15.5h2.25a.75.75 0 0 1 .11 1.492L9.25 17H7a5 5 0 0 1-.25-9.994L7 7h2.25ZM17 7a5 5 0 0 1 .25 9.994L17 17h-2.25a.75.75 0 0 1-.11-1.492l.11-.008H17a3.5 3.5 0 0 0 .206-6.994L17 8.5h-2.25a.75.75 0 0 1-.11-1.492L14.75 7H17ZM7 11.25h10a.75.75 0 0 1 .102 1.493L17 12.75H7a.75.75 0 0 1-.102-1.493L7 11.25h10H7Z",
|
"link-outline": "M9.25 7a.75.75 0 0 1 .11 1.492l-.11.008H7a3.5 3.5 0 0 0-.206 6.994L7 15.5h2.25a.75.75 0 0 1 .11 1.492L9.25 17H7a5 5 0 0 1-.25-9.994L7 7h2.25ZM17 7a5 5 0 0 1 .25 9.994L17 17h-2.25a.75.75 0 0 1-.11-1.492l.11-.008H17a3.5 3.5 0 0 0 .206-6.994L17 8.5h-2.25a.75.75 0 0 1-.11-1.492L14.75 7H17ZM7 11.25h10a.75.75 0 0 1 .102 1.493L17 12.75H7a.75.75 0 0 1-.102-1.493L7 11.25h10H7Z",
|
||||||
"list-outline": "M2.75 18h12.5a.75.75 0 0 1 .102 1.493l-.102.007H2.75a.75.75 0 0 1-.102-1.494L2.75 18h12.5-12.5Zm0-6.5h18.5a.75.75 0 0 1 .102 1.493L21.25 13H2.75a.75.75 0 0 1-.102-1.493l.102-.007h18.5-18.5Zm0-6.497h15.5a.75.75 0 0 1 .102 1.493l-.102.007H2.75a.75.75 0 0 1-.102-1.493l.102-.007h15.5-15.5Z",
|
"list-outline": "M2.75 18h12.5a.75.75 0 0 1 .102 1.493l-.102.007H2.75a.75.75 0 0 1-.102-1.494L2.75 18h12.5-12.5Zm0-6.5h18.5a.75.75 0 0 1 .102 1.493L21.25 13H2.75a.75.75 0 0 1-.102-1.493l.102-.007h18.5-18.5Zm0-6.497h15.5a.75.75 0 0 1 .102 1.493l-.102.007H2.75a.75.75 0 0 1-.102-1.493l.102-.007h15.5-15.5Z",
|
||||||
"location-outline": "M5.843 4.568a8.707 8.707 0 1 1 12.314 12.314l-1.187 1.174c-.875.858-2.01 1.962-3.406 3.312a2.25 2.25 0 0 1-3.128 0l-3.491-3.396c-.439-.431-.806-.794-1.102-1.09a8.707 8.707 0 0 1 0-12.314Zm11.253 1.06A7.207 7.207 0 1 0 6.904 15.822L8.39 17.29a753.98 753.98 0 0 0 3.088 3 .75.75 0 0 0 1.043 0l3.394-3.3c.47-.461.863-.85 1.18-1.168a7.207 7.207 0 0 0 0-10.192ZM12 7.999a3.002 3.002 0 1 1 0 6.004 3.002 3.002 0 0 1 0-6.003Zm0 1.5a1.501 1.501 0 1 0 0 3.004 1.501 1.501 0 0 0 0-3.003Z",
|
"location-outline": "M5.843 4.568a8.707 8.707 0 1 1 12.314 12.314l-1.187 1.174c-.875.858-2.01 1.962-3.406 3.312a2.25 2.25 0 0 1-3.128 0l-3.491-3.396c-.439-.431-.806-.794-1.102-1.09a8.707 8.707 0 0 1 0-12.314Zm11.253 1.06A7.207 7.207 0 1 0 6.904 15.822L8.39 17.29a753.98 753.98 0 0 0 3.088 3 .75.75 0 0 0 1.043 0l3.394-3.3c.47-.461.863-.85 1.18-1.168a7.207 7.207 0 0 0 0-10.192ZM12 7.999a3.002 3.002 0 1 1 0 6.004 3.002 3.002 0 0 1 0-6.003Zm0 1.5a1.501 1.501 0 1 0 0 3.004 1.501 1.501 0 0 0 0-3.003Z",
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
"people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z",
|
"people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z",
|
||||||
"people-team-outline": "M14.754 10c.966 0 1.75.784 1.75 1.75v4.749a4.501 4.501 0 0 1-9.002 0V11.75c0-.966.783-1.75 1.75-1.75h5.502Zm0 1.5H9.252a.25.25 0 0 0-.25.25v4.749a3.001 3.001 0 0 0 6.002 0V11.75a.25.25 0 0 0-.25-.25ZM3.75 10h3.381a2.738 2.738 0 0 0-.618 1.5H3.75a.25.25 0 0 0-.25.25v3.249a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V11.75c0-.966.784-1.75 1.75-1.75Zm13.125 0h3.375c.966 0 1.75.784 1.75 1.75V15a4 4 0 0 1-5.03 3.866c.214-.448.369-.929.455-1.433A2.5 2.5 0 0 0 20.5 15v-3.25a.25.25 0 0 0-.25-.25h-2.757a2.738 2.738 0 0 0-.618-1.5ZM12 3a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm6.5 1a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-13 0a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm6.5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm6.5 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-13 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
|
"people-team-outline": "M14.754 10c.966 0 1.75.784 1.75 1.75v4.749a4.501 4.501 0 0 1-9.002 0V11.75c0-.966.783-1.75 1.75-1.75h5.502Zm0 1.5H9.252a.25.25 0 0 0-.25.25v4.749a3.001 3.001 0 0 0 6.002 0V11.75a.25.25 0 0 0-.25-.25ZM3.75 10h3.381a2.738 2.738 0 0 0-.618 1.5H3.75a.25.25 0 0 0-.25.25v3.249a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V11.75c0-.966.784-1.75 1.75-1.75Zm13.125 0h3.375c.966 0 1.75.784 1.75 1.75V15a4 4 0 0 1-5.03 3.866c.214-.448.369-.929.455-1.433A2.5 2.5 0 0 0 20.5 15v-3.25a.25.25 0 0 0-.25-.25h-2.757a2.738 2.738 0 0 0-.618-1.5ZM12 3a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm6.5 1a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-13 0a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm6.5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm6.5 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-13 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
|
||||||
"person-outline": "M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.575c0 .894-.32 1.76-.902 2.438-1.57 1.834-3.957 2.739-7.102 2.739-3.146 0-5.532-.905-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.577a2.249 2.249 0 0 1 2.249-2.25h11.501Zm0 1.5H6.253a.749.749 0 0 0-.75.749v.577c0 .536.192 1.054.54 1.461 1.253 1.468 3.219 2.214 5.957 2.214s4.706-.746 5.962-2.214a2.25 2.25 0 0 0 .541-1.463v-.575a.749.749 0 0 0-.749-.75ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z",
|
"person-outline": "M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.575c0 .894-.32 1.76-.902 2.438-1.57 1.834-3.957 2.739-7.102 2.739-3.146 0-5.532-.905-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.577a2.249 2.249 0 0 1 2.249-2.25h11.501Zm0 1.5H6.253a.749.749 0 0 0-.75.749v.577c0 .536.192 1.054.54 1.461 1.253 1.468 3.219 2.214 5.957 2.214s4.706-.746 5.962-2.214a2.25 2.25 0 0 0 .541-1.463v-.575a.749.749 0 0 0-.749-.75ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z",
|
||||||
|
"power-outline": "M8.204 4.82a.75.75 0 0 1 .634 1.36A7.51 7.51 0 0 0 4.5 12.991c0 4.148 3.358 7.51 7.499 7.51s7.499-3.362 7.499-7.51a7.51 7.51 0 0 0-4.323-6.804.75.75 0 1 1 .637-1.358 9.01 9.01 0 0 1 5.186 8.162c0 4.976-4.029 9.01-9 9.01C7.029 22 3 17.966 3 12.99a9.01 9.01 0 0 1 5.204-8.17ZM12 2.496a.75.75 0 0 1 .743.648l.007.102v7.5a.75.75 0 0 1-1.493.102l-.007-.102v-7.5a.75.75 0 0 1 .75-.75Z",
|
||||||
"quote-outline": "M7.5 6a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.555-1.24 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.64-1.737 2.66-3.674 3.077-5.859A2.5 2.5 0 1 1 7.5 6Zm9 0a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.56-1.238 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.643-1.738 2.662-3.672 3.078-5.859A2.5 2.5 0 1 1 16.5 6Zm-9 1.5a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Zm9 0a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Z",
|
"quote-outline": "M7.5 6a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.555-1.24 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.64-1.737 2.66-3.674 3.077-5.859A2.5 2.5 0 1 1 7.5 6Zm9 0a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.56-1.238 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.643-1.738 2.662-3.672 3.078-5.859A2.5 2.5 0 1 1 16.5 6Zm-9 1.5a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Zm9 0a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Z",
|
||||||
"resize-large-outline": "M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5A3.25 3.25 0 0 1 6.25 3h1.5a.75.75 0 0 1 0 1.5h-1.5ZM19.5 6.25a1.75 1.75 0 0 0-1.75-1.75h-1.5a.75.75 0 0 1 0-1.5h1.5A3.25 3.25 0 0 1 21 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5ZM19.5 17.75a1.75 1.75 0 0 1-1.75 1.75h-1.5a.75.75 0 0 0 0 1.5h1.5A3.25 3.25 0 0 0 21 17.75v-1.5a.75.75 0 0 0-1.5 0v1.5ZM4.5 17.75c0 .966.784 1.75 1.75 1.75h1.5a.75.75 0 0 1 0 1.5h-1.5A3.25 3.25 0 0 1 3 17.75v-1.5a.75.75 0 0 1 1.5 0v1.5ZM8.25 6A2.25 2.25 0 0 0 6 8.25v7.5A2.25 2.25 0 0 0 8.25 18h7.5A2.25 2.25 0 0 0 18 15.75v-7.5A2.25 2.25 0 0 0 15.75 6h-7.5ZM7.5 8.25a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75h-7.5a.75.75 0 0 1-.75-.75v-7.5Z",
|
"resize-large-outline": "M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5A3.25 3.25 0 0 1 6.25 3h1.5a.75.75 0 0 1 0 1.5h-1.5ZM19.5 6.25a1.75 1.75 0 0 0-1.75-1.75h-1.5a.75.75 0 0 1 0-1.5h1.5A3.25 3.25 0 0 1 21 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5ZM19.5 17.75a1.75 1.75 0 0 1-1.75 1.75h-1.5a.75.75 0 0 0 0 1.5h1.5A3.25 3.25 0 0 0 21 17.75v-1.5a.75.75 0 0 0-1.5 0v1.5ZM4.5 17.75c0 .966.784 1.75 1.75 1.75h1.5a.75.75 0 0 1 0 1.5h-1.5A3.25 3.25 0 0 1 3 17.75v-1.5a.75.75 0 0 1 1.5 0v1.5ZM8.25 6A2.25 2.25 0 0 0 6 8.25v7.5A2.25 2.25 0 0 0 8.25 18h7.5A2.25 2.25 0 0 0 18 15.75v-7.5A2.25 2.25 0 0 0 15.75 6h-7.5ZM7.5 8.25a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75h-7.5a.75.75 0 0 1-.75-.75v-7.5Z",
|
||||||
"search-outline": "M10 2.75a7.25 7.25 0 0 1 5.63 11.819l4.9 4.9a.75.75 0 0 1-.976 1.134l-.084-.073-4.901-4.9A7.25 7.25 0 1 1 10 2.75Zm0 1.5a5.75 5.75 0 1 0 0 11.5 5.75 5.75 0 0 0 0-11.5Z",
|
"search-outline": "M10 2.75a7.25 7.25 0 0 1 5.63 11.819l4.9 4.9a.75.75 0 0 1-.976 1.134l-.084-.073-4.901-4.9A7.25 7.25 0 1 1 10 2.75Zm0 1.5a5.75 5.75 0 1 0 0 11.5 5.75 5.75 0 0 0 0-11.5Z",
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<li
|
||||||
|
class="dropdown-menu--divider"
|
||||||
|
:tabindex="null"
|
||||||
|
:aria-disabled="true"
|
||||||
|
></li>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dropdown-menu--divider {
|
||||||
|
list-style: none;
|
||||||
|
margin: var(--space-small) 0;
|
||||||
|
border-bottom: 1px solid var(--s-50);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<li class="dropdown-menu--header" :tabindex="null" :aria-disabled="true">
|
||||||
|
<span class="title">{{ title }}</span>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
componentName: 'WootDropdownMenu',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dropdown-menu--header {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: var(--space-small) var(--space-small);
|
||||||
|
margin-top: var(--space-smaller);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
color: var(--s-600);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
border-radius: var(--border-radius-normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -35,8 +35,12 @@ export default {
|
||||||
.button {
|
.button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
color: var(--s-700);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
padding: var(--space-small);
|
||||||
|
padding-top: var(--space-small);
|
||||||
|
padding-bottom: var(--space-small);
|
||||||
border-radius: var(--border-radius-normal);
|
border-radius: var(--border-radius-normal);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
Loading…
Reference in a new issue