feat(v4): Auto-navigate to first menu item on group menu open(#10350)

Ensures users are seamlessly directed to the first available menu item upon opening a group, improving UX by reducing unnecessary clicks. This change enhances navigation flow within groups.

Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
Shivam Mishra
2024-10-26 01:31:29 +05:30
committed by GitHub
parent 73b6e2cf37
commit 80c9434069
28 changed files with 104 additions and 100 deletions

View File

@@ -1,7 +1,7 @@
<script setup>
import { computed, watch, ref } from 'vue';
import { useSidebarContext } from './provider';
import { useRoute } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import Policy from 'dashboard/components/policy.vue';
import SidebarGroupHeader from './SidebarGroupHeader.vue';
import SidebarGroupLeaf from './SidebarGroupLeaf.vue';
@@ -41,7 +41,7 @@ const navigableChildren = computed(() => {
});
const route = useRoute();
const router = useRouter();
const isExpanded = computed(() => expandedItem.value === props.name);
const isExpandable = computed(() => props.children);
const hasChildren = computed(
@@ -53,10 +53,7 @@ const accessibleItems = computed(() => {
return props.children.filter(child => isAllowed(child.to));
});
const hasAccessibleItems = computed(() => {
// default true so that rendering is not blocked
if (!hasChildren.value) return true;
const hasAccessibleChildren = computed(() => {
return accessibleItems.value.length > 0;
});
@@ -93,6 +90,19 @@ const hasActiveChild = computed(() => {
return activeChild.value !== undefined;
});
const toggleTrigger = () => {
if (
hasAccessibleChildren.value &&
!isExpanded.value &&
!hasActiveChild.value
) {
// if not already expanded, navigate to the first child
const firstItem = accessibleItems.value[0];
router.push(firstItem.to);
}
setExpandedItem(props.name);
};
watch(expandedItem, locateLastChild, {
immediate: true,
});
@@ -101,7 +111,7 @@ watch(expandedItem, locateLastChild, {
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<Policy
v-if="hasAccessibleItems"
v-if="!hasChildren || hasAccessibleChildren"
:permissions="resolvePermissions(to)"
:feature-flag="resolveFeatureFlag(to)"
as="li"
@@ -116,7 +126,7 @@ watch(expandedItem, locateLastChild, {
:has-active-child="hasActiveChild"
:expandable="hasChildren"
:is-expanded="isExpanded"
@toggle="setExpandedItem(name)"
@toggle="toggleTrigger"
/>
<ul
v-if="hasChildren"

View File

@@ -5,7 +5,7 @@ import {
CONTACT_PERMISSIONS,
} from 'dashboard/constants/permissions.js';
const SearchView = () => import('./components/SearchView.vue');
import SearchView from './components/SearchView.vue';
export const routes = [
{

View File

@@ -1,7 +1,7 @@
/* eslint arrow-body-style: 0 */
import { frontendURL } from '../../../helper/URLHelper';
const ContactsView = () => import('./components/ContactsView.vue');
const ContactManageView = () => import('./pages/ContactManageView.vue');
import ContactsView from './components/ContactsView.vue';
import ContactManageView from './pages/ContactManageView.vue';
export const routes = [
{

View File

@@ -1,6 +1,6 @@
/* eslint arrow-body-style: 0 */
import { frontendURL } from '../../../helper/URLHelper';
const ConversationView = () => import('./ConversationView.vue');
import ConversationView from './ConversationView.vue';
const CONVERSATION_PERMISSIONS = [
'administrator',

View File

@@ -9,9 +9,9 @@ import helpcenterRoutes from './helpcenter/helpcenter.routes';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
const AppContainer = () => import('./Dashboard.vue');
const Captain = () => import('./Captain.vue');
const Suspended = () => import('./suspended/Index.vue');
import AppContainer from './Dashboard.vue';
import Captain from './Captain.vue';
import Suspended from './suspended/Index.vue';
export default {
routes: [

View File

@@ -2,8 +2,8 @@ import { getPortalRoute } from './helpers/routeHelper';
import HelpCenterPageRouteView from './pages/HelpCenterPageRouteView.vue';
const PortalsIndex = () => import('./pages/PortalsIndexPage.vue');
const PortalsNew = () => import('./pages/PortalsNewPage.vue');
import PortalsIndex from './pages/PortalsIndexPage.vue';
import PortalsNew from './pages/PortalsNewPage.vue';
const PortalsArticlesIndexPage = () =>
import('./pages/PortalsArticlesIndexPage.vue');

View File

@@ -1,7 +1,7 @@
import { frontendURL } from 'dashboard/helper/URLHelper';
const InboxListView = () => import('./InboxList.vue');
const InboxDetailView = () => import('./InboxView.vue');
const InboxEmptyStateView = () => import('./InboxEmptyState.vue');
import InboxListView from './InboxList.vue';
import InboxDetailView from './InboxView.vue';
import InboxEmptyStateView from './InboxEmptyState.vue';
import {
ROLES,
CONVERSATION_PERMISSIONS,

View File

@@ -1,7 +1,7 @@
/* eslint arrow-body-style: 0 */
import { frontendURL } from '../../../helper/URLHelper';
const SettingsWrapper = () => import('../settings/Wrapper.vue');
const NotificationsView = () => import('./components/NotificationsView.vue');
import SettingsWrapper from '../settings/Wrapper.vue';
import NotificationsView from './components/NotificationsView.vue';
export const routes = [
{

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('../Wrapper.vue');
const Index = () => import('./Index.vue');
import SettingsContent from '../Wrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -1,8 +1,8 @@
const Bot = () => import('./Index.vue');
const CsmlEditBot = () => import('./csml/Edit.vue');
const CsmlNewBot = () => import('./csml/New.vue');
import Bot from './Index.vue';
import CsmlEditBot from './csml/Edit.vue';
import CsmlNewBot from './csml/New.vue';
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('../Wrapper.vue');
import SettingsContent from '../Wrapper.vue';
export default {
routes: [

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const AgentHome = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import AgentHome from './Index.vue';
export default {
routes: [

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const AttributesHome = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import AttributesHome from './Index.vue';
export default {
routes: [

View File

@@ -1,7 +1,7 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const AuditLogsHome = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import AuditLogsHome from './Index.vue';
export default {
routes: [

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const Automation = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import Automation from './Index.vue';
export default {
routes: [

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('../Wrapper.vue');
const Index = () => import('./Index.vue');
import SettingsContent from '../Wrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -1,6 +1,6 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('../Wrapper.vue');
const Index = () => import('./Index.vue');
import SettingsContent from '../Wrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -3,8 +3,8 @@ import {
ROLES,
CONVERSATION_PERMISSIONS,
} from 'dashboard/constants/permissions.js';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const CannedHome = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import CannedHome from './Index.vue';
export default {
routes: [

View File

@@ -1,7 +1,7 @@
import { frontendURL } from 'dashboard/helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const CustomRolesHome = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import CustomRolesHome from './Index.vue';
export default {
routes: [

View File

@@ -1,14 +1,14 @@
import { frontendURL } from '../../../../helper/URLHelper';
import ChannelFactory from './ChannelFactory.vue';
const SettingsContent = () => import('../Wrapper.vue');
const SettingWrapper = () => import('../SettingsWrapper.vue');
const InboxHome = () => import('./Index.vue');
const Settings = () => import('./Settings.vue');
const InboxChannel = () => import('./InboxChannels.vue');
const ChannelList = () => import('./ChannelList.vue');
const AddAgents = () => import('./AddAgents.vue');
const FinishSetup = () => import('./FinishSetup.vue');
import SettingsContent from '../Wrapper.vue';
import SettingWrapper from '../SettingsWrapper.vue';
import InboxHome from './Index.vue';
import Settings from './Settings.vue';
import InboxChannel from './InboxChannels.vue';
import ChannelList from './ChannelList.vue';
import AddAgents from './AddAgents.vue';
import FinishSetup from './FinishSetup.vue';
export default {
routes: [

View File

@@ -1,11 +1,11 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const IntegrationHooks = () => import('./IntegrationHooks.vue');
const Index = () => import('./Index.vue');
const Webhook = () => import('./Webhooks/Index.vue');
const DashboardApps = () => import('./DashboardApps/Index.vue');
const Slack = () => import('./Slack.vue');
const SettingsContent = () => import('../Wrapper.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import IntegrationHooks from './IntegrationHooks.vue';
import Index from './Index.vue';
import Webhook from './Webhooks/Index.vue';
import DashboardApps from './DashboardApps/Index.vue';
import Slack from './Slack.vue';
import SettingsContent from '../Wrapper.vue';
export default {
routes: [

View File

@@ -1,7 +1,7 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const Index = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -4,10 +4,10 @@ import {
ROLES,
CONVERSATION_PERMISSIONS,
} from 'dashboard/constants/permissions.js';
const SettingsContent = () => import('../Wrapper.vue');
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const Macros = () => import('./Index.vue');
const MacroEditor = () => import('./MacroEditor.vue');
import SettingsContent from '../Wrapper.vue';
import SettingsWrapper from '../SettingsWrapper.vue';
import Macros from './Index.vue';
import MacroEditor from './MacroEditor.vue';
export default {
routes: [

View File

@@ -1,7 +1,7 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('./Wrapper.vue');
const Index = () => import('./Index.vue');
import SettingsContent from './Wrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -1,16 +1,16 @@
import { frontendURL } from '../../../../helper/URLHelper';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
const SettingsContent = () => import('../Wrapper.vue');
const Index = () => import('./Index.vue');
const AgentReports = () => import('./AgentReports.vue');
const LabelReports = () => import('./LabelReports.vue');
const InboxReports = () => import('./InboxReports.vue');
const TeamReports = () => import('./TeamReports.vue');
const CsatResponses = () => import('./CsatResponses.vue');
const BotReports = () => import('./BotReports.vue');
const LiveReports = () => import('./LiveReports.vue');
const SLAReports = () => import('./SLAReports.vue');
import SettingsContent from '../Wrapper.vue';
import Index from './Index.vue';
import AgentReports from './AgentReports.vue';
import LabelReports from './LabelReports.vue';
import InboxReports from './InboxReports.vue';
import TeamReports from './TeamReports.vue';
import CsatResponses from './CsatResponses.vue';
import BotReports from './BotReports.vue';
import LiveReports from './LiveReports.vue';
import SLAReports from './SLAReports.vue';
export default {
routes: [

View File

@@ -1,7 +1,7 @@
import { frontendURL } from '../../../../helper/URLHelper';
const SettingsWrapper = () => import('../SettingsWrapper.vue');
const Index = () => import('./Index.vue');
import SettingsWrapper from '../SettingsWrapper.vue';
import Index from './Index.vue';
export default {
routes: [

View File

@@ -1,15 +1,15 @@
import { frontendURL } from '../../../../helper/URLHelper';
const TeamsIndex = () => import('./Index.vue');
const CreateStepWrap = () => import('./Create/Index.vue');
const EditStepWrap = () => import('./Edit/Index.vue');
const CreateTeam = () => import('./Create/CreateTeam.vue');
const EditTeam = () => import('./Edit/EditTeam.vue');
const AddAgents = () => import('./Create/AddAgents.vue');
const EditAgents = () => import('./Edit/EditAgents.vue');
const FinishSetup = () => import('./FinishSetup.vue');
const SettingsContent = () => import('../Wrapper.vue');
const SettingsWrapper = () => import('../SettingsWrapper.vue');
import TeamsIndex from './Index.vue';
import CreateStepWrap from './Create/Index.vue';
import EditStepWrap from './Edit/Index.vue';
import CreateTeam from './Create/CreateTeam.vue';
import EditTeam from './Edit/EditTeam.vue';
import AddAgents from './Create/AddAgents.vue';
import EditAgents from './Edit/EditAgents.vue';
import FinishSetup from './FinishSetup.vue';
import SettingsContent from '../Wrapper.vue';
import SettingsWrapper from '../SettingsWrapper.vue';
export default {
routes: [

View File

@@ -11,16 +11,10 @@ import { directive as onClickaway } from 'vue3-click-away';
import { domPurifyConfig } from '../shared/helpers/HTMLSanitizer';
import { plugin, defaultConfig } from '@formkit/vue';
// import { emitter } from 'shared/helpers/mitt';
// https://github.com/wearebraid/vue-formulate/issues/198
// [VITE] [TODO] Re-enable this later
// import VueFormulate from '@braid/vue-formulate';
import {
startsWithPlus,
isPhoneNumberValidWithDialCode,
} from 'shared/helpers/Validators';
// const PhoneInput = () => import('../widget/components/Form/PhoneInput.vue');
const i18n = createI18n({
legacy: false, // https://github.com/intlify/vue-i18n/issues/1902

View File

@@ -1,10 +1,10 @@
import { frontendURL } from 'dashboard/helper/URLHelper';
const Login = () => import('./login/Index.vue');
const Signup = () => import('./auth/signup/Index.vue');
const ResetPassword = () => import('./auth/reset/password/Index.vue');
const Confirmation = () => import('./auth/confirmation/Index.vue');
const PasswordEdit = () => import('./auth/password/Edit.vue');
import Login from './login/Index.vue';
import Signup from './auth/signup/Index.vue';
import ResetPassword from './auth/reset/password/Index.vue';
import Confirmation from './auth/confirmation/Index.vue';
import PasswordEdit from './auth/password/Edit.vue';
export default [
{