diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue
index 0f3a1a857..ff87835b4 100644
--- a/app/javascript/dashboard/components/layout/Sidebar.vue
+++ b/app/javascript/dashboard/components/layout/Sidebar.vue
@@ -20,7 +20,7 @@
:teams="teams"
:custom-views="customViews"
:menu-config="activeSecondaryMenu"
- :current-role="currentRole"
+ :current-user="currentUser"
:is-on-chatwoot-cloud="isOnChatwootCloud"
@add-label="showAddLabelPopup"
@toggle-accounts="toggleAccountModal"
diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue
index f291d221e..6d00bd4c2 100644
--- a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue
+++ b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue
@@ -60,9 +60,9 @@ export default {
type: Object,
default: () => {},
},
- currentRole: {
- type: String,
- default: '',
+ currentUser: {
+ type: Object,
+ default: () => {},
},
isOnChatwootCloud: {
type: Boolean,
@@ -83,13 +83,16 @@ export default {
if (!this.currentRole) {
return [];
}
- const menuItemsFilteredByRole = this.menuConfig.menuItems.filter(
- menuItem =>
- window.roleWiseRoutes[this.currentRole].indexOf(
- menuItem.toStateName
- ) > -1
+ const menuItemsFilteredByPermissions = this.menuConfig.menuItems.filter(
+ menuItem => {
+ const { meta: { menuPermissions = [] } = {} } = menuItem;
+ const { permissions: userPermissions = [] } = this.currentUser;
+ return menuPermissions.some(permission =>
+ userPermissions.includes(permission)
+ );
+ }
);
- return menuItemsFilteredByRole.filter(item => {
+ return menuItemsFilteredByPermissions.filter(item => {
if (item.showOnlyOnCloud) {
return this.isOnChatwootCloud;
}
diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
index a7009523a..df4981f83 100644
--- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
+++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
@@ -65,27 +65,29 @@
:show-child-count="showChildCount(child.count)"
:child-item-count="child.count"
/>
-
-
-
- newLinkClick(e, navigate)"
- >
- {{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
-
-
-
-
+
+
+
+
+ newLinkClick(e, navigate)"
+ >
+ {{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
+
+
+
+
+
@@ -105,9 +107,10 @@ import {
isOnMentionsView,
isOnUnattendedView,
} from '../../../store/modules/conversations/helpers/actionHelpers';
+import Policy from '../../policy.vue';
export default {
- components: { SecondaryChildNavItem },
+ components: { SecondaryChildNavItem, Policy },
mixins: [adminMixin, configMixin],
props: {
menuItem: {
diff --git a/app/javascript/dashboard/components/policy.vue b/app/javascript/dashboard/components/policy.vue
new file mode 100644
index 000000000..5520fdcc6
--- /dev/null
+++ b/app/javascript/dashboard/components/policy.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/constants/permissions.js b/app/javascript/dashboard/constants/permissions.js
new file mode 100644
index 000000000..2705a3887
--- /dev/null
+++ b/app/javascript/dashboard/constants/permissions.js
@@ -0,0 +1,10 @@
+export const ACCOUNT_PERMISSIONS = {
+ ACCOUNT_MANAGE: 'account_manage',
+ PORTAL_MANAGE: 'portal_manage',
+ CONVERSATION_MANAGE: 'conversation_manage',
+ CONVERSATION_UNASSIGNED_MANAGE: 'conversation_unassigned_manage',
+ CONVERSATION_PARTICIPATING_MANAGE: 'conversation_participating_manage',
+ MACROS_MANAGE: 'macros_manage',
+ REPORTS_MANAGE: 'reports_manage',
+ CONTACTS_MANAGE: 'contacts_manage',
+};
diff --git a/app/javascript/dashboard/helper/routeHelpers.js b/app/javascript/dashboard/helper/routeHelpers.js
index 86c286368..a63df4e9d 100644
--- a/app/javascript/dashboard/helper/routeHelpers.js
+++ b/app/javascript/dashboard/helper/routeHelpers.js
@@ -3,17 +3,14 @@ export const getCurrentAccount = ({ accounts } = {}, accountId) => {
return accounts.find(account => account.id === accountId);
};
-// eslint-disable-next-line default-param-last
-export const getUserRole = ({ accounts } = {}, accountId) => {
- const currentAccount = getCurrentAccount({ accounts }, accountId) || {};
- return currentAccount.role || null;
+export const routeIsAccessibleFor = (route, userPermissions = []) => {
+ const { meta: { permissions: routePermissions = [] } = {} } = route;
+ return routePermissions.some(permissions =>
+ userPermissions.includes(permissions)
+ );
};
-export const routeIsAccessibleFor = (route, role, roleWiseRoutes) => {
- return roleWiseRoutes[role].includes(route);
-};
-
-const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
+const validateActiveAccountRoutes = (to, user) => {
// If the current account is active, then check for the route permissions
const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`;
@@ -22,15 +19,13 @@ const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
return accountDashboardURL;
}
- const userRole = getUserRole(user, Number(to.params.accountId));
- const isAccessible = routeIsAccessibleFor(to.name, userRole, roleWiseRoutes);
+ const isAccessible = routeIsAccessibleFor(to, user.permissions);
// If the route is not accessible for the user, return to dashboard screen
return isAccessible ? null : accountDashboardURL;
};
-export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
+export const validateLoggedInRoutes = (to, user) => {
const currentAccount = getCurrentAccount(user, Number(to.params.accountId));
-
// If current account is missing, either user does not have
// access to the account or the account is deleted, return to login screen
if (!currentAccount) {
@@ -40,7 +35,7 @@ export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
const isCurrentAccountActive = currentAccount.status === 'active';
if (isCurrentAccountActive) {
- return validateActiveAccountRoutes(to, user, roleWiseRoutes);
+ return validateActiveAccountRoutes(to, user);
}
// If the current account is not active, then redirect the user to the suspended screen
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js b/app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js
index 14487682e..6d39fb00d 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js
+++ b/app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js
@@ -1,13 +1,20 @@
/* eslint arrow-body-style: 0 */
+import { ACCOUNT_PERMISSIONS } from '../../../constants/permissions';
import { frontendURL } from '../../../helper/URLHelper';
const ConversationView = () => import('./ConversationView');
+const GENERIC_CONVERSATION_PERMISSIONS = [
+ ACCOUNT_PERMISSIONS.CONVERSATION_MANAGE,
+ ACCOUNT_PERMISSIONS.CONVERSATION_PARTICIPATING_MANAGE,
+ ACCOUNT_PERMISSIONS.CONVERSATION_UNASSIGNED_MANAGE,
+];
+
export default {
routes: [
{
path: frontendURL('accounts/:accountId/dashboard'),
name: 'home',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: () => {
return { inboxId: 0 };
@@ -16,7 +23,7 @@ export default {
{
path: frontendURL('accounts/:accountId/conversations/:conversation_id'),
name: 'inbox_conversation',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => {
return { inboxId: 0, conversationId: route.params.conversation_id };
@@ -25,7 +32,7 @@ export default {
{
path: frontendURL('accounts/:accountId/inbox/:inbox_id'),
name: 'inbox_dashboard',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => {
return { inboxId: route.params.inbox_id };
@@ -36,7 +43,7 @@ export default {
'accounts/:accountId/inbox/:inbox_id/conversations/:conversation_id'
),
name: 'conversation_through_inbox',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => {
return {
@@ -48,7 +55,7 @@ export default {
{
path: frontendURL('accounts/:accountId/label/:label'),
name: 'label_conversations',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({ label: route.params.label }),
},
@@ -57,7 +64,7 @@ export default {
'accounts/:accountId/label/:label/conversations/:conversation_id'
),
name: 'conversations_through_label',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({
conversationId: route.params.conversation_id,
@@ -67,7 +74,7 @@ export default {
{
path: frontendURL('accounts/:accountId/team/:teamId'),
name: 'team_conversations',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({ teamId: route.params.teamId }),
},
@@ -76,7 +83,7 @@ export default {
'accounts/:accountId/team/:teamId/conversations/:conversationId'
),
name: 'conversations_through_team',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({
conversationId: route.params.conversationId,
@@ -86,7 +93,7 @@ export default {
{
path: frontendURL('accounts/:accountId/custom_view/:id'),
name: 'folder_conversations',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({ foldersId: route.params.id }),
},
@@ -95,7 +102,7 @@ export default {
'accounts/:accountId/custom_view/:id/conversations/:conversation_id'
),
name: 'conversations_through_folders',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({
conversationId: route.params.conversation_id,
@@ -105,7 +112,7 @@ export default {
{
path: frontendURL('accounts/:accountId/mentions/conversations'),
name: 'conversation_mentions',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: () => ({ conversationType: 'mention' }),
},
@@ -114,7 +121,7 @@ export default {
'accounts/:accountId/mentions/conversations/:conversationId'
),
name: 'conversation_through_mentions',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({
conversationId: route.params.conversationId,
@@ -124,7 +131,7 @@ export default {
{
path: frontendURL('accounts/:accountId/unattended/conversations'),
name: 'conversation_unattended',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: () => ({ conversationType: 'unattended' }),
},
@@ -133,7 +140,7 @@ export default {
'accounts/:accountId/unattended/conversations/:conversationId'
),
name: 'conversation_through_unattended',
- roles: ['administrator', 'agent'],
+ meta: { permissions: GENERIC_CONVERSATION_PERMISSIONS },
component: ConversationView,
props: route => ({
conversationId: route.params.conversationId,
@@ -143,8 +150,13 @@ export default {
{
path: frontendURL('accounts/:accountId/participating/conversations'),
name: 'conversation_participating',
- roles: ['administrator', 'agent'],
component: ConversationView,
+ meta: {
+ permissions: [
+ ACCOUNT_PERMISSIONS.CONVERSATION_MANAGE,
+ ACCOUNT_PERMISSIONS.CONVERSATION_PARTICIPATING_MANAGE,
+ ],
+ },
props: () => ({ conversationType: 'participating' }),
},
{
@@ -152,8 +164,13 @@ export default {
'accounts/:accountId/participating/conversations/:conversationId'
),
name: 'conversation_through_participating',
- roles: ['administrator', 'agent'],
component: ConversationView,
+ meta: {
+ permissions: [
+ ACCOUNT_PERMISSIONS.CONVERSATION_MANAGE,
+ ACCOUNT_PERMISSIONS.CONVERSATION_PARTICIPATING_MANAGE,
+ ],
+ },
props: route => ({
conversationId: route.params.conversationId,
conversationType: 'participating',
diff --git a/app/javascript/dashboard/routes/index.js b/app/javascript/dashboard/routes/index.js
index e2245e20f..dc6528d93 100644
--- a/app/javascript/dashboard/routes/index.js
+++ b/app/javascript/dashboard/routes/index.js
@@ -8,29 +8,6 @@ import AnalyticsHelper from '../helper/AnalyticsHelper';
const routes = [...dashboard.routes];
-window.roleWiseRoutes = {
- agent: [],
- administrator: [],
-};
-
-// generateRoleWiseRoute - updates window object with agent/admin route
-const generateRoleWiseRoute = route => {
- route.forEach(element => {
- if (element.children) {
- generateRoleWiseRoute(element.children);
- }
- if (element.roles) {
- element.roles.forEach(roleEl => {
- window.roleWiseRoutes[roleEl].push(element.name);
- });
- }
- });
-};
-// Create a object of routes
-// accessible by each role.
-// returns an object with roles as keys and routeArr as values
-generateRoleWiseRoute(routes);
-
export const router = new VueRouter({ mode: 'history', routes });
export const validateAuthenticateRoutePermission = (to, next, { getters }) => {
@@ -45,11 +22,7 @@ export const validateAuthenticateRoutePermission = (to, next, { getters }) => {
return next(frontendURL(`accounts/${user.account_id}/dashboard`));
}
- const nextRoute = validateLoggedInRoutes(
- to,
- getters.getCurrentUser,
- window.roleWiseRoutes
- );
+ const nextRoute = validateLoggedInRoutes(to, getters.getCurrentUser);
return nextRoute ? next(frontendURL(nextRoute)) : next();
};
diff --git a/app/views/api/v1/models/_user.json.jbuilder b/app/views/api/v1/models/_user.json.jbuilder
index 3d8131f60..0e8c95adb 100644
--- a/app/views/api/v1/models/_user.json.jbuilder
+++ b/app/views/api/v1/models/_user.json.jbuilder
@@ -14,6 +14,7 @@ json.provider resource.provider
json.pubsub_token resource.pubsub_token
json.custom_attributes resource.custom_attributes if resource.custom_attributes.present?
json.role resource.active_account_user&.role
+json.permissions resource.active_account_user&.permissions
json.ui_settings resource.ui_settings
json.uid resource.uid
json.type resource.type