diff --git a/app/javascript/dashboard/composables/commands/useMacroHotKeys.js b/app/javascript/dashboard/composables/commands/useMacroHotKeys.js new file mode 100644 index 000000000..4288e4686 --- /dev/null +++ b/app/javascript/dashboard/composables/commands/useMacroHotKeys.js @@ -0,0 +1,76 @@ +import { computed, ref, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRoute } from 'vue-router'; +import { useStore, useMapGetter } from 'dashboard/composables/store'; +import { useMacroExecution } from 'dashboard/composables/useMacroExecution'; +import { useOrderedMacros } from 'dashboard/composables/useOrderedMacros'; +import { usePolicy } from 'dashboard/composables/usePolicy'; +import { FEATURE_FLAGS } from 'dashboard/featureFlags'; +import { ICON_TOY_BRICK } from 'dashboard/helper/commandbar/icons'; +import { + isAConversationRoute, + isAInboxViewRoute, +} from 'dashboard/helper/routeHelpers'; + +export function useMacroHotKeys() { + const { t } = useI18n(); + const store = useStore(); + const route = useRoute(); + + const { orderedMacros } = useOrderedMacros(); + const { execute, submitPendingAttributes, dismissPendingAttributes } = + useMacroExecution(); + const { isFeatureFlagEnabled } = usePolicy(); + + const currentChat = useMapGetter('getSelectedChat'); + const pendingAttributes = ref(null); + + const isMacrosAvailable = computed( + () => + isFeatureFlagEnabled(FEATURE_FLAGS.MACROS) && + (isAConversationRoute(route.name) || isAInboxViewRoute(route.name)) + ); + + watch( + isMacrosAvailable, + isActive => { + if (isActive && !orderedMacros.value.length) store.dispatch('macros/get'); + }, + { immediate: true } + ); + + const macroHotKeys = computed(() => { + if (!isMacrosAvailable.value || !orderedMacros.value.length) { + return []; + } + + const options = orderedMacros.value.map(macro => ({ + id: `macro-${macro.id}`, + title: macro.name, + parent: 'execute_a_macro', + section: t('COMMAND_BAR.SECTIONS.EXECUTE_MACRO'), + icon: ICON_TOY_BRICK, + handler: () => { + pendingAttributes.value = execute(macro, currentChat.value.id); + }, + })); + + return [ + { + id: 'execute_a_macro', + title: t('COMMAND_BAR.COMMANDS.EXECUTE_A_MACRO'), + section: t('COMMAND_BAR.SECTIONS.CONVERSATION'), + icon: ICON_TOY_BRICK, + children: options.map(option => option.id), + }, + ...options, + ]; + }); + + return { + macroHotKeys, + pendingAttributes, + submitPendingAttributes, + dismissPendingAttributes, + }; +} diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index 715aa1ae8..80843da1e 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -161,6 +161,7 @@ "CHANGE_PRIORITY": "Change Priority", "CHANGE_TEAM": "Change Team", "SNOOZE_CONVERSATION": "Snooze Conversation", + "EXECUTE_MACRO": "Execute Macro", "ADD_LABEL": "Add label to the conversation", "REMOVE_LABEL": "Remove label from the conversation", "SETTINGS": "Settings", @@ -206,6 +207,7 @@ "AI_ASSIST": "AI Assist", "ASSIGN_PRIORITY": "Assign priority", "ASSIGN_A_TEAM": "Assign a team", + "EXECUTE_A_MACRO": "Execute a macro", "MUTE_CONVERSATION": "Mute conversation", "UNMUTE_CONVERSATION": "Unmute conversation", "REMOVE_LABEL_FROM_CONVERSATION": "Remove label from the conversation", diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue index d3ea838c7..b6a2ae709 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue +++ b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue @@ -1,6 +1,6 @@