-
-
-
-
+
+
+
+
+
diff --git a/frontend/src/constants/navigation.js b/frontend/src/constants/navigation.js
new file mode 100644
index 00000000..5a653133
--- /dev/null
+++ b/frontend/src/constants/navigation.js
@@ -0,0 +1,128 @@
+export const reportsNavItems = [
+ {
+ title: 'Overview',
+ href: '/reports/overview',
+ permission: 'reports:manage'
+ }
+]
+
+export const adminNavItems = [
+ {
+ title: 'Workspace',
+ children: [
+ {
+ title: 'General',
+ href: '/admin/general',
+ permission: 'general_settings:manage'
+ },
+ {
+ title: 'Business Hours',
+ href: '/admin/business-hours',
+ permission: 'business_hours:manage'
+ },
+ {
+ title: 'SLA',
+ href: '/admin/sla',
+ permission: 'sla:manage'
+ }
+ ]
+ },
+ {
+ title: 'Conversations',
+ children: [
+ {
+ title: 'Tags',
+ href: '/admin/conversations/tags',
+ permission: 'tags:manage'
+ },
+ {
+ title: 'Macros',
+ href: '/admin/conversations/macros',
+ permission: 'macros:manage'
+ },
+ {
+ title: 'Statuses',
+ href: '/admin/conversations/statuses',
+ permission: 'status:manage'
+ }
+ ]
+ },
+ {
+ title: 'Inboxes',
+ children: [
+ {
+ title: 'Inboxes',
+ href: '/admin/inboxes',
+ permission: 'inboxes:manage'
+ }
+ ]
+ },
+ {
+ title: 'Teammates',
+ children: [
+ {
+ title: 'Users',
+ href: '/admin/teams/users',
+ permission: 'users:manage'
+ },
+ {
+ title: 'Teams',
+ href: '/admin/teams/teams',
+ permission: 'teams:manage'
+ },
+ {
+ title: 'Roles',
+ href: '/admin/teams/roles',
+ permission: 'roles:manage'
+ }
+ ]
+ },
+ {
+ title: 'Automations',
+ children: [
+ {
+ title: 'Automations',
+ href: '/admin/automations',
+ permission: 'automations:manage'
+ }
+ ]
+ },
+ {
+ title: 'Notifications',
+ children: [
+ {
+ title: 'Email',
+ href: '/admin/notification',
+ permission: 'notification_settings:manage'
+ }
+ ]
+ },
+ {
+ title: 'Templates',
+ children: [
+ {
+ title: 'Templates',
+ href: '/admin/templates',
+ permission: 'templates:manage'
+ }
+ ]
+ },
+ {
+ title: 'Security',
+ children: [
+ {
+ title: 'SSO',
+ href: '/admin/oidc',
+ permission: 'oidc:manage'
+ }
+ ]
+ }
+]
+
+export const accountNavItems = [
+ {
+ title: 'Profile',
+ href: '/account/profile',
+ description: 'Update your profile'
+ }
+]
diff --git a/frontend/src/features/conversation/list/ConversationList.vue b/frontend/src/features/conversation/list/ConversationList.vue
index 0d00fd21..c9b32622 100644
--- a/frontend/src/features/conversation/list/ConversationList.vue
+++ b/frontend/src/features/conversation/list/ConversationList.vue
@@ -38,8 +38,8 @@
- Oldest
- Newest
+ Oldest activity
+ Newest activity
Started first
diff --git a/frontend/src/features/conversation/message/ContactMessageBubble.vue b/frontend/src/features/conversation/message/ContactMessageBubble.vue
index 1c356b40..933832df 100644
--- a/frontend/src/features/conversation/message/ContactMessageBubble.vue
+++ b/frontend/src/features/conversation/message/ContactMessageBubble.vue
@@ -82,7 +82,7 @@ const convStore = useConversationStore()
const showQuotedText = ref(false)
const getAvatar = computed(() => {
- return convStore.current?.avatar_url || ''
+ return convStore.current?.contact.avatar_url || ''
})
const sanitizedMessageContent = computed(() => {
diff --git a/frontend/src/stores/conversation.js b/frontend/src/stores/conversation.js
index 4723fd1e..fb9054d0 100644
--- a/frontend/src/stores/conversation.js
+++ b/frontend/src/stores/conversation.js
@@ -58,8 +58,8 @@ export const useConversationStore = defineStore('conversation', () => {
}
const sortFieldLabels = {
- oldest: 'Oldest',
- newest: 'Newest',
+ oldest: 'Oldest activity',
+ newest: 'Newest activity',
started_first: 'Started first',
started_last: 'Started last',
waiting_longest: 'Waiting longest',
diff --git a/frontend/src/stores/user.js b/frontend/src/stores/user.js
index 97ede280..e03eacdc 100644
--- a/frontend/src/stores/user.js
+++ b/frontend/src/stores/user.js
@@ -3,6 +3,8 @@ import { defineStore } from 'pinia'
import { handleHTTPError } from '@/utils/http'
import { useEmitter } from '@/composables/useEmitter'
import { EMITTER_EVENTS } from '@/constants/emitterEvents'
+import { adminNavItems, reportsNavItems } from '@/constants/navigation'
+import { filterNavItems } from '@/utils/nav-permissions'
import api from '@/api'
export const useUserStore = defineStore('user', () => {
@@ -37,6 +39,18 @@ export const useUserStore = defineStore('user', () => {
return `${firstInitial}${lastInitial}`
})
+ const can = (permission) => {
+ return user.value.permissions.includes(permission)
+ }
+
+ const hasAdminTabPermissions = computed(() => {
+ return filterNavItems(adminNavItems, can).length > 0
+ })
+
+ const hasReportTabPermissions = computed(() => {
+ return filterNavItems(reportsNavItems, can).length > 0
+ })
+
const getCurrentUser = async () => {
try {
const response = await api.getCurrentUser()
@@ -69,10 +83,6 @@ export const useUserStore = defineStore('user', () => {
user.value.avatar_url = ''
}
- const can = (permission) => {
- return user.value.permissions.includes(permission)
- }
-
return {
user,
userID,
@@ -84,6 +94,8 @@ export const useUserStore = defineStore('user', () => {
permissions,
getFullName,
getInitials,
+ hasAdminTabPermissions,
+ hasReportTabPermissions,
getCurrentUser,
clearAvatar,
setAvatar,
diff --git a/frontend/src/utils/nav-permissions.js b/frontend/src/utils/nav-permissions.js
new file mode 100644
index 00000000..83295490
--- /dev/null
+++ b/frontend/src/utils/nav-permissions.js
@@ -0,0 +1,17 @@
+export const filterNavItems = (navItems, can) => {
+ return navItems
+ .map(item => {
+ // Process children first
+ const filteredChildren = item.children
+ ? filterNavItems(item.children, can)
+ : undefined
+ // Check item's permission
+ const hasAccess = item.permission ? can(item.permission) : true
+ // Only keep the item if:
+ // 1. Has required permission (or none required)
+ // 2. Has valid children (if parent item)
+ const keep = hasAccess && (!item.children || filteredChildren.length > 0)
+ return keep ? { ...item, children: filteredChildren } : null
+ })
+ .filter(Boolean) // Remove null entries
+}
\ No newline at end of file
diff --git a/frontend/src/views/conversation/ConversationDetailView.vue b/frontend/src/views/conversation/ConversationDetailView.vue
index 7014669b..c7c1f5d3 100644
--- a/frontend/src/views/conversation/ConversationDetailView.vue
+++ b/frontend/src/views/conversation/ConversationDetailView.vue
@@ -1,6 +1,6 @@
-
+
diff --git a/frontend/src/views/inbox/InboxView.vue b/frontend/src/views/inbox/InboxView.vue
index 4c8e578b..b7b844b6 100644
--- a/frontend/src/views/inbox/InboxView.vue
+++ b/frontend/src/views/inbox/InboxView.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index e57c3bb4..83c4e96b 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -40,6 +40,7 @@ module.exports = {
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
+ sidebar: "white",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",