+import { mapGetters } from 'vuex';
+import {
+ getSortedAgentsByAvailability,
+ getAgentsByUpdatedPresence,
+} from 'dashboard/helper/agentHelper.js';
import MenuItem from './menuItem.vue';
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
import wootConstants from 'dashboard/constants/globals';
-import agentMixin from 'dashboard/mixins/agentMixin';
-import { mapGetters } from 'vuex';
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
export default {
components: {
@@ -11,7 +14,6 @@ export default {
MenuItemWithSubmenu,
AgentLoadingPlaceholder,
},
- mixins: [agentMixin],
props: {
chatId: {
type: Number,
@@ -112,13 +114,19 @@ export default {
labels: 'labels/getLabels',
teams: 'teams/getTeams',
assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags',
+ currentUser: 'getCurrentUser',
+ currentAccountId: 'getCurrentAccountId',
}),
filteredAgentOnAvailability() {
const agents = this.$store.getters[
'inboxAssignableAgents/getAssignableAgents'
](this.inboxId);
- const agentsByUpdatedPresence = this.getAgentsByUpdatedPresence(agents);
- const filteredAgents = this.sortedAgentsByAvailability(
+ const agentsByUpdatedPresence = getAgentsByUpdatedPresence(
+ agents,
+ this.currentUser,
+ this.currentAccountId
+ );
+ const filteredAgents = getSortedAgentsByAvailability(
agentsByUpdatedPresence
);
return filteredAgents;
diff --git a/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue b/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue
index 889051674..4593f25e2 100644
--- a/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/conversation/LabelSuggestion.vue
@@ -2,7 +2,9 @@
// components
import WootButton from '../../../ui/WootButton.vue';
import Avatar from '../../Avatar.vue';
-import aiMixin from 'dashboard/mixins/aiMixin';
+
+// composables
+import { useAI } from 'dashboard/composables/useAI';
// store & api
import { mapGetters } from 'vuex';
@@ -18,7 +20,6 @@ export default {
Avatar,
WootButton,
},
- mixins: [aiMixin],
props: {
suggestedLabels: {
type: Array,
@@ -30,6 +31,11 @@ export default {
default: () => [],
},
},
+ setup() {
+ const { isAIIntegrationEnabled } = useAI();
+
+ return { isAIIntegrationEnabled };
+ },
data() {
return {
isDismissed: false,
@@ -41,7 +47,11 @@ export default {
...mapGetters({
allLabels: 'labels/getLabels',
currentAccountId: 'getCurrentAccountId',
+ currentChat: 'getSelectedChat',
}),
+ conversationId() {
+ return this.currentChat?.id;
+ },
labelTooltip() {
if (this.preparedLabels.length > 1) {
return this.$t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.MULTIPLE_SUGGESTION');
diff --git a/app/javascript/dashboard/components/widgets/conversation/conversationCardComponents/CardLabels.vue b/app/javascript/dashboard/components/widgets/conversation/conversationCardComponents/CardLabels.vue
index 885b59a78..6c719fc01 100644
--- a/app/javascript/dashboard/components/widgets/conversation/conversationCardComponents/CardLabels.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/conversationCardComponents/CardLabels.vue
@@ -1,23 +1,26 @@
import '@chatwoot/ninja-keys';
+import { useConversationLabels } from 'dashboard/composables/useConversationLabels';
+import { useAI } from 'dashboard/composables/useAI';
+import { useAgentsList } from 'dashboard/composables/useAgentsList';
import wootConstants from 'dashboard/constants/globals';
import conversationHotKeysMixin from './conversationHotKeys';
import bulkActionsHotKeysMixin from './bulkActionsHotKeys';
import inboxHotKeysMixin from './inboxHotKeys';
import goToCommandHotKeys from './goToCommandHotKeys';
import appearanceHotKeys from './appearanceHotKeys';
-import agentMixin from 'dashboard/mixins/agentMixin';
-import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin';
-import conversationTeamMixin from 'dashboard/mixins/conversation/teamMixin';
import { GENERAL_EVENTS } from '../../../helper/AnalyticsHelper/events';
export default {
mixins: [
- agentMixin,
conversationHotKeysMixin,
bulkActionsHotKeysMixin,
inboxHotKeysMixin,
- conversationLabelMixin,
- conversationTeamMixin,
appearanceHotKeys,
goToCommandHotKeys,
],
+ setup() {
+ // used in conversationHotKeysMixin
+ const {
+ activeLabels,
+ inactiveLabels,
+ addLabelToConversation,
+ removeLabelFromConversation,
+ } = useConversationLabels();
+
+ const { isAIIntegrationEnabled } = useAI();
+ const { agentsList, assignableAgents } = useAgentsList();
+
+ return {
+ agentsList,
+ assignableAgents,
+ activeLabels,
+ inactiveLabels,
+ addLabelToConversation,
+ removeLabelFromConversation,
+ isAIIntegrationEnabled,
+ };
+ },
data() {
return {
// Added selectedSnoozeType to track the selected snooze type
diff --git a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js
index 9c4a638b6..652624872 100644
--- a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js
+++ b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js
@@ -4,7 +4,6 @@ import { emitter } from 'shared/helpers/mitt';
import { CMD_AI_ASSIST } from './commandBarBusEvents';
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
-import aiMixin from 'dashboard/mixins/aiMixin';
import {
ICON_ADD_LABEL,
ICON_ASSIGN_AGENT,
@@ -36,7 +35,6 @@ import {
isAInboxViewRoute,
} from '../../../helper/routeHelpers';
export default {
- mixins: [aiMixin],
watch: {
assignableAgents() {
this.setCommandbarData();
@@ -65,6 +63,7 @@ export default {
currentChat: 'getSelectedChat',
replyMode: 'draftMessages/getReplyEditorMode',
contextMenuChatId: 'getContextMenuChatId',
+ teams: 'teams/getTeams',
}),
draftMessage() {
return this.$store.getters['draftMessages/get'](this.draftKey);
@@ -78,7 +77,18 @@ export default {
conversationId() {
return this.currentChat?.id;
},
-
+ hasAnAssignedTeam() {
+ return !!this.currentChat?.meta?.team;
+ },
+ teamsList() {
+ if (this.hasAnAssignedTeam) {
+ return [
+ { id: 0, name: this.$t('TEAMS_SETTINGS.LIST.NONE') },
+ ...this.teams,
+ ];
+ }
+ return this.teams;
+ },
statusActions() {
const isOpen =
this.currentChat?.status === wootConstants.STATUS_TYPE.OPEN;
diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
index 3b0133048..0c9a1ec5b 100644
--- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
+++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
@@ -115,13 +115,11 @@ export default {
diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsTable.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsTable.vue
index 3486f6f96..2c5b4b538 100644
--- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsTable.vue
+++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsTable.vue
@@ -1,4 +1,5 @@
-
-
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationParticipant.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationParticipant.vue
index f59f69b87..265baa485 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/ConversationParticipant.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationParticipant.vue
@@ -2,7 +2,8 @@
import Spinner from 'shared/components/Spinner.vue';
import { useAlert } from 'dashboard/composables';
import { mapGetters } from 'vuex';
-import agentMixin from 'dashboard/mixins/agentMixin';
+import { useAgentsList } from 'dashboard/composables/useAgentsList';
+
import ThumbnailGroup from 'dashboard/components/widgets/ThumbnailGroup.vue';
import MultiselectDropdownItems from 'shared/components/ui/MultiselectDropdownItems.vue';
@@ -12,19 +13,17 @@ export default {
ThumbnailGroup,
MultiselectDropdownItems,
},
- mixins: [agentMixin],
props: {
conversationId: {
type: [Number, String],
required: true,
},
- // inboxId prop is used in /mixins/agentMixin,
- // remove this props when refactoring to composable if not needed
- // eslint-disable-next-line vue/no-unused-properties
- inboxId: {
- type: Number,
- default: undefined,
- },
+ },
+ setup() {
+ const { agentsList } = useAgentsList(false);
+ return {
+ agentsList,
+ };
},
data() {
return {
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue
index 48b7ec89f..6b321c55d 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue
@@ -87,6 +87,17 @@ export default {
this.fetchConversationIfUnavailable();
},
},
+
+ created() {
+ // Clear selected state early if no conversation is selected
+ // This prevents child components from accessing stale data
+ // and resolves timing issues during navigation
+ // with conversation view and other screens
+ if (!this.conversationId) {
+ this.$store.dispatch('clearSelectedState');
+ }
+ },
+
mounted() {
this.$store.dispatch('agents/get');
this.initialize();
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/Macros/List.vue b/app/javascript/dashboard/routes/dashboard/conversation/Macros/List.vue
index c4ca6e3d2..6cda0ff7f 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/Macros/List.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/Macros/List.vue
@@ -1,19 +1,25 @@
+
-
+
-
-
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/labels/LabelBox.vue b/app/javascript/dashboard/routes/dashboard/conversation/labels/LabelBox.vue
index 0e3da2463..a5b2a7468 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/labels/LabelBox.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/labels/LabelBox.vue
@@ -2,11 +2,11 @@
import { ref } from 'vue';
import { mapGetters } from 'vuex';
import { useAdmin } from 'dashboard/composables/useAdmin';
+import { useConversationLabels } from 'dashboard/composables/useConversationLabels';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import Spinner from 'shared/components/Spinner.vue';
import LabelDropdown from 'shared/components/ui/label/LabelDropdown.vue';
import AddLabel from 'shared/components/ui/dropdown/AddLabel.vue';
-import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin';
export default {
components: {
@@ -14,21 +14,17 @@ export default {
LabelDropdown,
AddLabel,
},
-
- mixins: [conversationLabelMixin],
- props: {
- // conversationId prop is used in /conversation/labelMixin,
- // remove this props when refactoring to composable if not needed
- // eslint-disable-next-line vue/no-unused-properties
- conversationId: {
- type: Number,
- required: true,
- },
- },
setup() {
const { isAdmin } = useAdmin();
- const conversationLabelBoxRef = ref(null);
+ const {
+ savedLabels,
+ activeLabels,
+ accountLabels,
+ addLabelToConversation,
+ removeLabelFromConversation,
+ } = useConversationLabels();
+
const showSearchDropdownLabel = ref(false);
const toggleLabels = () => {
@@ -55,10 +51,14 @@ export default {
allowOnFocusedInput: true,
},
};
- useKeyboardEvents(keyboardEvents, conversationLabelBoxRef);
+ useKeyboardEvents(keyboardEvents);
return {
isAdmin,
- conversationLabelBoxRef,
+ savedLabels,
+ activeLabels,
+ accountLabels,
+ addLabelToConversation,
+ removeLabelFromConversation,
showSearchDropdownLabel,
closeDropdownLabel,
toggleLabels,
@@ -79,7 +79,7 @@ export default {
-