5c3de5e095
* feat: Sort by name * feat: Fetch labels from sidebar * Remove unused language file * Add beta tag to contacts * Add timeMixin, reduce font-size * Remove unused methods * Remove unused prop * Disabled footer if no contacts or invalid page * Add keyup for input * Fix conversation not loading if there are no active conversations * return last_seen_at as unix time * Fix contact edit modal * Add loader for edit contact button * Fix review comments
412 lines
11 KiB
Vue
412 lines
11 KiB
Vue
<template>
|
|
<aside class="sidebar animated shrink columns">
|
|
<div class="logo">
|
|
<router-link :to="dashboardPath" replace>
|
|
<img :src="globalConfig.logo" :alt="globalConfig.installationName" />
|
|
</router-link>
|
|
</div>
|
|
|
|
<div class="main-nav">
|
|
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
|
<sidebar-item
|
|
v-for="item in accessibleMenuItems"
|
|
:key="item.toState"
|
|
:menu-item="item"
|
|
/>
|
|
<sidebar-item
|
|
v-if="shouldShowInboxes"
|
|
:key="inboxSection.toState"
|
|
:menu-item="inboxSection"
|
|
/>
|
|
<sidebar-item
|
|
v-if="shouldShowInboxes"
|
|
:key="labelSection.toState"
|
|
:menu-item="labelSection"
|
|
/>
|
|
</transition-group>
|
|
</div>
|
|
|
|
<div class="bottom-nav">
|
|
<availability-status />
|
|
</div>
|
|
|
|
<div class="bottom-nav app-context-menu">
|
|
<transition name="menu-slide">
|
|
<div
|
|
v-if="showOptionsMenu"
|
|
v-on-clickaway="showOptions"
|
|
class="dropdown-pane top"
|
|
>
|
|
<ul class="vertical dropdown menu">
|
|
<li v-if="showChangeAccountOption">
|
|
<button
|
|
class="button clear change-accounts--button"
|
|
@click="changeAccount"
|
|
>
|
|
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<router-link :to="`/app/accounts/${accountId}/profile/settings`">
|
|
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
|
</router-link>
|
|
</li>
|
|
<li>
|
|
<a href="#" @click.prevent="logout()">
|
|
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</transition>
|
|
|
|
<div class="current-user" @click.prevent="showOptions()">
|
|
<thumbnail
|
|
:src="currentUser.avatar_url"
|
|
:username="currentUserAvailableName"
|
|
/>
|
|
<div class="current-user--data">
|
|
<h3 class="current-user--name">
|
|
{{ currentUserAvailableName }}
|
|
</h3>
|
|
<h5 v-if="currentRole" class="current-user--role">
|
|
{{ $t(`AGENT_MGMT.AGENT_TYPES.${currentRole.toUpperCase()}`) }}
|
|
</h5>
|
|
</div>
|
|
<span class="current-user--options icon ion-android-more-vertical" />
|
|
</div>
|
|
</div>
|
|
<woot-modal
|
|
:show="showAccountModal"
|
|
:on-close="onClose"
|
|
class="account-selector--modal"
|
|
>
|
|
<woot-modal-header
|
|
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
|
|
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
|
|
/>
|
|
<div
|
|
v-for="account in currentUser.accounts"
|
|
:key="account.id"
|
|
class="account-selector"
|
|
>
|
|
<a :href="`/app/accounts/${account.id}/dashboard`">
|
|
<i v-if="account.id === accountId" class="ion ion-ios-checkmark" />
|
|
<label :for="account.name" class="account--details">
|
|
<div class="account--name">{{ account.name }}</div>
|
|
<div class="account--role">{{ account.role }}</div>
|
|
</label>
|
|
</a>
|
|
</div>
|
|
<div
|
|
v-if="globalConfig.createNewAccountFromDashboard"
|
|
class="modal-footer delete-item"
|
|
>
|
|
<button
|
|
class="button success large expanded nice"
|
|
@click="createAccount"
|
|
>
|
|
{{ $t('CREATE_ACCOUNT.NEW_ACCOUNT') }}
|
|
</button>
|
|
</div>
|
|
</woot-modal>
|
|
|
|
<woot-modal
|
|
:show="showCreateAccountModal"
|
|
:on-close="onCloseCreate"
|
|
class="account-selector--modal"
|
|
>
|
|
<div class="column content-box">
|
|
<woot-modal-header
|
|
:header-title="$t('CREATE_ACCOUNT.NEW_ACCOUNT')"
|
|
:header-content="$t('CREATE_ACCOUNT.SELECTOR_SUBTITLE')"
|
|
/>
|
|
|
|
<form class="row" @submit.prevent="addAccount()">
|
|
<div class="medium-12 columns">
|
|
<label :class="{ error: $v.accountName.$error }">
|
|
{{ $t('CREATE_ACCOUNT.FORM.NAME.LABEL') }}
|
|
<input
|
|
v-model.trim="accountName"
|
|
type="text"
|
|
:placeholder="$t('CREATE_ACCOUNT.FORM.NAME.PLACEHOLDER')"
|
|
@input="$v.accountName.$touch"
|
|
/>
|
|
</label>
|
|
</div>
|
|
<div class="modal-footer medium-12 columns">
|
|
<div class="medium-12 columns">
|
|
<woot-submit-button
|
|
:disabled="
|
|
$v.accountName.$invalid ||
|
|
$v.accountName.$invalid ||
|
|
uiFlags.isCreating
|
|
"
|
|
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
|
:loading="uiFlags.isCreating"
|
|
button-class="large expanded"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</woot-modal>
|
|
</aside>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import { mixin as clickaway } from 'vue-clickaway';
|
|
|
|
import adminMixin from '../../mixins/isAdmin';
|
|
import Auth from '../../api/auth';
|
|
import SidebarItem from './SidebarItem';
|
|
import AvailabilityStatus from './AvailabilityStatus';
|
|
import { frontendURL } from '../../helper/URLHelper';
|
|
import Thumbnail from '../widgets/Thumbnail';
|
|
import { getSidebarItems } from '../../i18n/default-sidebar';
|
|
import { required, minLength } from 'vuelidate/lib/validators';
|
|
import alertMixin from 'shared/mixins/alertMixin';
|
|
|
|
export default {
|
|
components: {
|
|
SidebarItem,
|
|
Thumbnail,
|
|
AvailabilityStatus,
|
|
},
|
|
mixins: [clickaway, adminMixin, alertMixin],
|
|
props: {
|
|
route: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
showOptionsMenu: false,
|
|
showAccountModal: false,
|
|
showCreateAccountModal: false,
|
|
accountName: '',
|
|
vertical: 'bottom',
|
|
horizontal: 'center',
|
|
};
|
|
},
|
|
validations: {
|
|
accountName: {
|
|
required,
|
|
minLength: minLength(1),
|
|
},
|
|
},
|
|
computed: {
|
|
...mapGetters({
|
|
currentUser: 'getCurrentUser',
|
|
globalConfig: 'globalConfig/get',
|
|
inboxes: 'inboxes/getInboxes',
|
|
accountId: 'getCurrentAccountId',
|
|
currentRole: 'getCurrentRole',
|
|
uiFlags: 'agents/getUIFlags',
|
|
accountLabels: 'labels/getLabelsOnSidebar',
|
|
}),
|
|
currentUserAvailableName() {
|
|
return this.currentUser.name;
|
|
},
|
|
showChangeAccountOption() {
|
|
if (this.globalConfig.createNewAccountFromDashboard) {
|
|
return true;
|
|
}
|
|
return this.currentUser.accounts.length > 1;
|
|
},
|
|
sidemenuItems() {
|
|
return getSidebarItems(this.accountId);
|
|
},
|
|
accessibleMenuItems() {
|
|
// get all keys in menuGroup
|
|
const groupKey = Object.keys(this.sidemenuItems);
|
|
|
|
let menuItems = [];
|
|
// Iterate over menuGroup to find the correct group
|
|
for (let i = 0; i < groupKey.length; i += 1) {
|
|
const groupItem = this.sidemenuItems[groupKey[i]];
|
|
// Check if current route is included
|
|
const isRouteIncluded = groupItem.routes.includes(this.currentRoute);
|
|
if (isRouteIncluded) {
|
|
menuItems = Object.values(groupItem.menuItems);
|
|
}
|
|
}
|
|
|
|
return this.filterMenuItemsByRole(menuItems);
|
|
},
|
|
currentRoute() {
|
|
return this.$store.state.route.name;
|
|
},
|
|
shouldShowInboxes() {
|
|
return this.sidemenuItems.common.routes.includes(this.currentRoute);
|
|
},
|
|
inboxSection() {
|
|
return {
|
|
icon: 'ion-folder',
|
|
label: 'INBOXES',
|
|
hasSubMenu: true,
|
|
newLink: true,
|
|
key: 'inbox',
|
|
cssClass: 'menu-title align-justify',
|
|
toState: frontendURL(`accounts/${this.accountId}/settings/inboxes`),
|
|
toStateName: 'settings_inbox_list',
|
|
children: this.inboxes.map(inbox => ({
|
|
id: inbox.id,
|
|
label: inbox.name,
|
|
toState: frontendURL(`accounts/${this.accountId}/inbox/${inbox.id}`),
|
|
type: inbox.channel_type,
|
|
phoneNumber: inbox.phone_number,
|
|
})),
|
|
};
|
|
},
|
|
labelSection() {
|
|
return {
|
|
icon: 'ion-pound',
|
|
label: 'LABELS',
|
|
hasSubMenu: true,
|
|
key: 'label',
|
|
cssClass: 'menu-title align-justify',
|
|
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
|
|
toStateName: 'labels_list',
|
|
children: this.accountLabels.map(label => ({
|
|
id: label.id,
|
|
label: label.title,
|
|
color: label.color,
|
|
truncateLabel: true,
|
|
toState: frontendURL(
|
|
`accounts/${this.accountId}/label/${label.title}`
|
|
),
|
|
})),
|
|
};
|
|
},
|
|
dashboardPath() {
|
|
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
|
},
|
|
},
|
|
mounted() {
|
|
this.$store.dispatch('labels/get');
|
|
this.$store.dispatch('inboxes/get');
|
|
},
|
|
methods: {
|
|
filterMenuItemsByRole(menuItems) {
|
|
if (!this.currentRole) {
|
|
return [];
|
|
}
|
|
return menuItems.filter(
|
|
menuItem =>
|
|
window.roleWiseRoutes[this.currentRole].indexOf(
|
|
menuItem.toStateName
|
|
) > -1
|
|
);
|
|
},
|
|
logout() {
|
|
Auth.logout();
|
|
},
|
|
showOptions() {
|
|
this.showOptionsMenu = !this.showOptionsMenu;
|
|
},
|
|
changeAccount() {
|
|
this.showAccountModal = true;
|
|
},
|
|
onClose() {
|
|
this.showAccountModal = false;
|
|
},
|
|
createAccount() {
|
|
this.showAccountModal = false;
|
|
this.showCreateAccountModal = true;
|
|
},
|
|
onCloseCreate() {
|
|
this.showCreateAccountModal = false;
|
|
},
|
|
async addAccount() {
|
|
try {
|
|
const account_id = await this.$store.dispatch('accounts/create', {
|
|
account_name: this.accountName,
|
|
});
|
|
this.onClose();
|
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.SUCCESS_MESSAGE'));
|
|
window.location = `/app/accounts/${account_id}/dashboard`;
|
|
} catch (error) {
|
|
if (error.response.status === 422) {
|
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.EXIST_MESSAGE'));
|
|
} else {
|
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.ERROR_MESSAGE'));
|
|
}
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import '~dashboard/assets/scss/variables';
|
|
|
|
.account-selector--modal {
|
|
.modal-container {
|
|
width: 40rem;
|
|
}
|
|
|
|
.page-top-bar {
|
|
padding-bottom: $space-two;
|
|
}
|
|
}
|
|
|
|
.change-accounts--button.button {
|
|
font-weight: $font-weight-normal;
|
|
font-size: $font-size-small;
|
|
padding: $space-small $space-one;
|
|
}
|
|
|
|
.dropdown-pane {
|
|
li {
|
|
a {
|
|
padding: $space-small $space-one !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
.account-selector {
|
|
cursor: pointer;
|
|
padding: $space-small $space-large;
|
|
|
|
.ion-ios-checkmark {
|
|
font-size: $font-size-big;
|
|
|
|
& + .account--details {
|
|
padding-left: $space-normal;
|
|
}
|
|
}
|
|
|
|
.account--details {
|
|
padding-left: $space-large + $space-smaller;
|
|
}
|
|
|
|
&:last-child {
|
|
margin-bottom: $space-large;
|
|
}
|
|
|
|
a {
|
|
align-items: center;
|
|
cursor: pointer;
|
|
display: flex;
|
|
|
|
.account--name {
|
|
cursor: pointer;
|
|
font-size: $font-size-medium;
|
|
font-weight: $font-weight-medium;
|
|
line-height: 1;
|
|
}
|
|
|
|
.account--role {
|
|
cursor: pointer;
|
|
font-size: $font-size-mini;
|
|
text-transform: capitalize;
|
|
}
|
|
}
|
|
}
|
|
.app-context-menu {
|
|
height: 6rem;
|
|
}
|
|
</style>
|