mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 13:07:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			411 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			411 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="shouldShowTeams"
 | 
						|
          :key="teamSection.toState"
 | 
						|
          :menu-item="teamSection"
 | 
						|
        />
 | 
						|
        <sidebar-item
 | 
						|
          v-if="shouldShowSidebarItem"
 | 
						|
          :key="inboxSection.toState"
 | 
						|
          :menu-item="inboxSection"
 | 
						|
        />
 | 
						|
        <sidebar-item
 | 
						|
          v-if="shouldShowSidebarItem"
 | 
						|
          :key="labelSection.toState"
 | 
						|
          :menu-item="labelSection"
 | 
						|
          @add-label="showAddLabelPopup"
 | 
						|
        />
 | 
						|
        <sidebar-item
 | 
						|
          v-if="showShowContactSideMenu"
 | 
						|
          :key="contactLabelSection.key"
 | 
						|
          :menu-item="contactLabelSection"
 | 
						|
          @add-label="showAddLabelPopup"
 | 
						|
        />
 | 
						|
      </transition-group>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <div class="bottom-nav">
 | 
						|
      <availability-status />
 | 
						|
    </div>
 | 
						|
 | 
						|
    <div class="bottom-nav app-context-menu" @click="toggleOptions">
 | 
						|
      <agent-details @show-options="toggleOptions" />
 | 
						|
      <notification-bell />
 | 
						|
      <span class="current-user--options icon ion-android-more-vertical" />
 | 
						|
      <options-menu
 | 
						|
        :show="showOptionsMenu"
 | 
						|
        @toggle-accounts="toggleAccountModal"
 | 
						|
        @show-support-chat-window="toggleSupportChatWindow"
 | 
						|
        @key-shortcut-modal="toggleKeyShortcutModal"
 | 
						|
        @close="toggleOptions"
 | 
						|
      />
 | 
						|
    </div>
 | 
						|
 | 
						|
    <woot-key-shortcut-modal
 | 
						|
      v-if="showShortcutModal"
 | 
						|
      @close="closeKeyShortcutModal"
 | 
						|
      @clickaway="closeKeyShortcutModal"
 | 
						|
    />
 | 
						|
 | 
						|
    <account-selector
 | 
						|
      :show-account-modal="showAccountModal"
 | 
						|
      @close-account-modal="toggleAccountModal"
 | 
						|
      @show-create-account-modal="openCreateAccountModal"
 | 
						|
    />
 | 
						|
 | 
						|
    <add-account-modal
 | 
						|
      :show="showCreateAccountModal"
 | 
						|
      @close-account-create-modal="closeCreateAccountModal"
 | 
						|
    />
 | 
						|
 | 
						|
    <woot-modal :show.sync="showAddLabelModal" :on-close="hideAddLabelPopup">
 | 
						|
      <add-label-modal @close="hideAddLabelPopup" />
 | 
						|
    </woot-modal>
 | 
						|
  </aside>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
import { mapGetters } from 'vuex';
 | 
						|
 | 
						|
import adminMixin from '../../mixins/isAdmin';
 | 
						|
import SidebarItem from './SidebarItem';
 | 
						|
import AvailabilityStatus from './AvailabilityStatus';
 | 
						|
import { frontendURL } from '../../helper/URLHelper';
 | 
						|
import { getSidebarItems } from '../../i18n/default-sidebar';
 | 
						|
import alertMixin from 'shared/mixins/alertMixin';
 | 
						|
import NotificationBell from './sidebarComponents/NotificationBell';
 | 
						|
import AgentDetails from './sidebarComponents/AgentDetails.vue';
 | 
						|
import OptionsMenu from './sidebarComponents/OptionsMenu.vue';
 | 
						|
import AccountSelector from './sidebarComponents/AccountSelector.vue';
 | 
						|
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
 | 
						|
import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel';
 | 
						|
import WootKeyShortcutModal from 'components/widgets/modal/WootKeyShortcutModal';
 | 
						|
import {
 | 
						|
  hasPressedAltAndCKey,
 | 
						|
  hasPressedAltAndRKey,
 | 
						|
  hasPressedAltAndSKey,
 | 
						|
  hasPressedAltAndVKey,
 | 
						|
  hasPressedCommandAndForwardSlash,
 | 
						|
  isEscape,
 | 
						|
} from 'shared/helpers/KeyboardHelpers';
 | 
						|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
 | 
						|
import router from '../../routes';
 | 
						|
 | 
						|
export default {
 | 
						|
  components: {
 | 
						|
    AgentDetails,
 | 
						|
    SidebarItem,
 | 
						|
    AvailabilityStatus,
 | 
						|
    NotificationBell,
 | 
						|
    OptionsMenu,
 | 
						|
    AccountSelector,
 | 
						|
    AddAccountModal,
 | 
						|
    AddLabelModal,
 | 
						|
    WootKeyShortcutModal,
 | 
						|
  },
 | 
						|
  mixins: [adminMixin, alertMixin, eventListenerMixins],
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      showOptionsMenu: false,
 | 
						|
      showAccountModal: false,
 | 
						|
      showCreateAccountModal: false,
 | 
						|
      showAddLabelModal: false,
 | 
						|
      showShortcutModal: false,
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  computed: {
 | 
						|
    ...mapGetters({
 | 
						|
      currentUser: 'getCurrentUser',
 | 
						|
      globalConfig: 'globalConfig/get',
 | 
						|
      inboxes: 'inboxes/getInboxes',
 | 
						|
      accountId: 'getCurrentAccountId',
 | 
						|
      currentRole: 'getCurrentRole',
 | 
						|
      accountLabels: 'labels/getLabelsOnSidebar',
 | 
						|
      teams: 'teams/getMyTeams',
 | 
						|
    }),
 | 
						|
 | 
						|
    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;
 | 
						|
    },
 | 
						|
    shouldShowSidebarItem() {
 | 
						|
      return this.sidemenuItems.common.routes.includes(this.currentRoute);
 | 
						|
    },
 | 
						|
    showShowContactSideMenu() {
 | 
						|
      return this.sidemenuItems.contacts.routes.includes(this.currentRoute);
 | 
						|
    },
 | 
						|
    shouldShowTeams() {
 | 
						|
      return this.shouldShowSidebarItem && this.teams.length;
 | 
						|
    },
 | 
						|
    inboxSection() {
 | 
						|
      return {
 | 
						|
        icon: 'ion-folder',
 | 
						|
        label: 'INBOXES',
 | 
						|
        hasSubMenu: true,
 | 
						|
        newLink: true,
 | 
						|
        key: 'inbox',
 | 
						|
        cssClass: 'menu-title align-justify',
 | 
						|
        toState: frontendURL(`accounts/${this.accountId}/settings/inboxes`),
 | 
						|
        toStateName: 'settings_inbox_list',
 | 
						|
        newLinkRouteName: 'settings_inbox_new',
 | 
						|
        children: this.inboxes.map(inbox => ({
 | 
						|
          id: inbox.id,
 | 
						|
          label: inbox.name,
 | 
						|
          toState: frontendURL(`accounts/${this.accountId}/inbox/${inbox.id}`),
 | 
						|
          type: inbox.channel_type,
 | 
						|
          phoneNumber: inbox.phone_number,
 | 
						|
        })),
 | 
						|
      };
 | 
						|
    },
 | 
						|
    labelSection() {
 | 
						|
      return {
 | 
						|
        icon: 'ion-pound',
 | 
						|
        label: 'LABELS',
 | 
						|
        hasSubMenu: true,
 | 
						|
        newLink: true,
 | 
						|
        key: 'label',
 | 
						|
        cssClass: 'menu-title align-justify',
 | 
						|
        toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
 | 
						|
        toStateName: 'labels_list',
 | 
						|
        showModalForNewItem: true,
 | 
						|
        modalName: 'AddLabel',
 | 
						|
        children: this.accountLabels.map(label => ({
 | 
						|
          id: label.id,
 | 
						|
          label: label.title,
 | 
						|
          color: label.color,
 | 
						|
          truncateLabel: true,
 | 
						|
          toState: frontendURL(
 | 
						|
            `accounts/${this.accountId}/label/${label.title}`
 | 
						|
          ),
 | 
						|
        })),
 | 
						|
      };
 | 
						|
    },
 | 
						|
    contactLabelSection() {
 | 
						|
      return {
 | 
						|
        icon: 'ion-pound',
 | 
						|
        label: 'TAGGED_WITH',
 | 
						|
        hasSubMenu: true,
 | 
						|
        key: 'label',
 | 
						|
        newLink: false,
 | 
						|
        cssClass: 'menu-title align-justify',
 | 
						|
        toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
 | 
						|
        toStateName: 'labels_list',
 | 
						|
        showModalForNewItem: true,
 | 
						|
        modalName: 'AddLabel',
 | 
						|
        children: this.accountLabels.map(label => ({
 | 
						|
          id: label.id,
 | 
						|
          label: label.title,
 | 
						|
          color: label.color,
 | 
						|
          truncateLabel: true,
 | 
						|
          toState: frontendURL(
 | 
						|
            `accounts/${this.accountId}/labels/${label.title}/contacts`
 | 
						|
          ),
 | 
						|
        })),
 | 
						|
      };
 | 
						|
    },
 | 
						|
    teamSection() {
 | 
						|
      return {
 | 
						|
        icon: 'ion-ios-people',
 | 
						|
        label: 'TEAMS',
 | 
						|
        hasSubMenu: true,
 | 
						|
        newLink: true,
 | 
						|
        key: 'team',
 | 
						|
        cssClass: 'menu-title align-justify teams-sidebar-menu',
 | 
						|
        toState: frontendURL(`accounts/${this.accountId}/settings/teams`),
 | 
						|
        toStateName: 'teams_list',
 | 
						|
        newLinkRouteName: 'settings_teams_new',
 | 
						|
        children: this.teams.map(team => ({
 | 
						|
          id: team.id,
 | 
						|
          label: team.name,
 | 
						|
          truncateLabel: true,
 | 
						|
          toState: frontendURL(`accounts/${this.accountId}/team/${team.id}`),
 | 
						|
        })),
 | 
						|
      };
 | 
						|
    },
 | 
						|
    dashboardPath() {
 | 
						|
      return frontendURL(`accounts/${this.accountId}/dashboard`);
 | 
						|
    },
 | 
						|
  },
 | 
						|
  mounted() {
 | 
						|
    this.$store.dispatch('labels/get');
 | 
						|
    this.$store.dispatch('inboxes/get');
 | 
						|
    this.$store.dispatch('notifications/unReadCount');
 | 
						|
    this.$store.dispatch('teams/get');
 | 
						|
  },
 | 
						|
 | 
						|
  methods: {
 | 
						|
    toggleKeyShortcutModal() {
 | 
						|
      this.showShortcutModal = true;
 | 
						|
    },
 | 
						|
    closeKeyShortcutModal() {
 | 
						|
      this.showShortcutModal = false;
 | 
						|
    },
 | 
						|
    handleKeyEvents(e) {
 | 
						|
      if (hasPressedCommandAndForwardSlash(e)) {
 | 
						|
        this.toggleKeyShortcutModal();
 | 
						|
      }
 | 
						|
      if (isEscape(e)) {
 | 
						|
        this.closeKeyShortcutModal();
 | 
						|
      }
 | 
						|
 | 
						|
      if (hasPressedAltAndCKey(e)) {
 | 
						|
        if (!this.isCurrentRouteSameAsNavigation('home')) {
 | 
						|
          router.push({ name: 'home' });
 | 
						|
        }
 | 
						|
      } else if (hasPressedAltAndVKey(e)) {
 | 
						|
        if (!this.isCurrentRouteSameAsNavigation('contacts_dashboard')) {
 | 
						|
          router.push({ name: 'contacts_dashboard' });
 | 
						|
        }
 | 
						|
      } else if (hasPressedAltAndRKey(e)) {
 | 
						|
        if (!this.isCurrentRouteSameAsNavigation('settings_account_reports')) {
 | 
						|
          router.push({ name: 'settings_account_reports' });
 | 
						|
        }
 | 
						|
      } else if (hasPressedAltAndSKey(e)) {
 | 
						|
        if (!this.isCurrentRouteSameAsNavigation('agent_list')) {
 | 
						|
          router.push({ name: 'agent_list' });
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    isCurrentRouteSameAsNavigation(routeName) {
 | 
						|
      return router.currentRoute && router.currentRoute.name === routeName;
 | 
						|
    },
 | 
						|
    toggleSupportChatWindow() {
 | 
						|
      window.$chatwoot.toggle();
 | 
						|
    },
 | 
						|
    filterMenuItemsByRole(menuItems) {
 | 
						|
      if (!this.currentRole) {
 | 
						|
        return [];
 | 
						|
      }
 | 
						|
      return menuItems.filter(
 | 
						|
        menuItem =>
 | 
						|
          window.roleWiseRoutes[this.currentRole].indexOf(
 | 
						|
            menuItem.toStateName
 | 
						|
          ) > -1
 | 
						|
      );
 | 
						|
    },
 | 
						|
    toggleOptions() {
 | 
						|
      this.showOptionsMenu = !this.showOptionsMenu;
 | 
						|
    },
 | 
						|
    toggleAccountModal() {
 | 
						|
      this.showAccountModal = !this.showAccountModal;
 | 
						|
    },
 | 
						|
    openCreateAccountModal() {
 | 
						|
      this.showAccountModal = false;
 | 
						|
      this.showCreateAccountModal = true;
 | 
						|
    },
 | 
						|
    closeCreateAccountModal() {
 | 
						|
      this.showCreateAccountModal = false;
 | 
						|
    },
 | 
						|
    showAddLabelPopup() {
 | 
						|
      this.showAddLabelModal = true;
 | 
						|
    },
 | 
						|
    hideAddLabelPopup() {
 | 
						|
      this.showAddLabelModal = false;
 | 
						|
    },
 | 
						|
  },
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss">
 | 
						|
@import '~dashboard/assets/scss/variables';
 | 
						|
 | 
						|
.account-selector--modal {
 | 
						|
  .modal-container {
 | 
						|
    width: 40rem;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.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 {
 | 
						|
  align-items: center;
 | 
						|
  cursor: pointer;
 | 
						|
  display: flex;
 | 
						|
  flex-direction: row;
 | 
						|
  height: 6rem;
 | 
						|
}
 | 
						|
 | 
						|
.current-user--options {
 | 
						|
  font-size: $font-size-big;
 | 
						|
  margin-bottom: auto;
 | 
						|
  margin-left: auto;
 | 
						|
  margin-top: auto;
 | 
						|
}
 | 
						|
 | 
						|
.teams-sidebar-menu + .nested.vertical.menu {
 | 
						|
  padding-left: calc(var(--space-medium) - var(--space-one));
 | 
						|
}
 | 
						|
</style>
 |