Files
chatwoot/app/javascript/dashboard/modules/conversations/components/MessageContextMenu.vue
Sivin Varghese cc4d54becf chore: Update buttons in dashboard (#11145)
# Pull Request Template

## Changes

* Remove unused component `MaskedText.vue`
* Remove unused component `ContactIntro.vue`
* Remove unused `AddCustomViews.vue` component
* Update buttons in help center upgrade page
<img width="741" alt="image"
src="https://github.com/user-attachments/assets/f46b8ffa-ef74-4b83-95c1-83842844c4b7"
/>

* Update SLA view details button in reports page
<img width="419" alt="image"
src="https://github.com/user-attachments/assets/7db6bff6-9ba5-4c13-850f-b9d4dbb55489"
/>

* Update assign to me conversation action button
<img width="152" alt="image"
src="https://github.com/user-attachments/assets/587ee1fe-93c9-48e0-910a-390dd4eac1f2"
/>

* Update button in participants action
<img width="365" alt="image"
src="https://github.com/user-attachments/assets/50eb386f-882b-4277-ad61-d557893ac490"
/>
<img width="365" alt="image"
src="https://github.com/user-attachments/assets/3c64ed42-0203-43c6-9a26-8ad9465aa394"
/>

* Update the show more attributes button
<img width="365" alt="image"
src="https://github.com/user-attachments/assets/f2105a2f-8441-4d8b-b355-dfe97ce2e362"
/>

* Update SLA empty state
<img width="376" alt="image"
src="https://github.com/user-attachments/assets/55fd4376-6183-45c9-acb2-6ad5a6a58730"
/>

* Update create new label button from dropdown
<img width="376" alt="image"
src="https://github.com/user-attachments/assets/bb8226d0-795f-431f-8fe4-15d15ee1d695"
/>

* Update add macro button
<img width="382" alt="image"
src="https://github.com/user-attachments/assets/b6375402-5213-4041-841b-09c2157ed56a"
/>

* Update copy button
<img width="382" alt="image"
src="https://github.com/user-attachments/assets/f1929d63-f7ca-4eab-bfa8-716be2aa4be8"
/>

* Update the buttons in banner component
<img width="849" alt="image"
src="https://github.com/user-attachments/assets/bb86b498-ab8e-4a92-8c6a-60edcef0fd7b"
/>
<img width="838" alt="image"
src="https://github.com/user-attachments/assets/4bde00de-907e-49ac-9128-f364a90cd3c1"
/>

* Update table pagination buttons
<img width="968" alt="image"
src="https://github.com/user-attachments/assets/8d439af6-8126-4fad-a3fe-89b26a56f880"
/>
<img width="968" alt="image"
src="https://github.com/user-attachments/assets/a178b3d7-3385-4a2b-a0d7-35be9a25fd93"
/>

* Update filter chip buttons and dropdown colors
<img width="541" alt="image"
src="https://github.com/user-attachments/assets/7be1f418-82df-4db6-b265-4c185fc79f4b"
/>
<img width="541" alt="image"
src="https://github.com/user-attachments/assets/69110d75-85e9-4974-8c2a-e195b23a335c"
/>
<img width="173" alt="image"
src="https://github.com/user-attachments/assets/c8f5bc82-15b4-4f25-b7d4-35a9280ec7c6"
/>
<img width="565" alt="image"
src="https://github.com/user-attachments/assets/7dad1d00-784b-46eb-b823-e6e55d5f9dc8"
/>
<img width="565" alt="image"
src="https://github.com/user-attachments/assets/384a86ed-f45f-488c-b9aa-1f02b16c813d"
/>

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-03-31 17:21:32 -07:00

285 lines
7.5 KiB
Vue

<script>
import { useAlert } from 'dashboard/composables';
import { mapGetters } from 'vuex';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import ContextMenu from 'dashboard/components/ui/ContextMenu.vue';
import AddCannedModal from 'dashboard/routes/dashboard/settings/canned/AddCanned.vue';
import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import { conversationUrl, frontendURL } from '../../../helper/URLHelper';
import {
ACCOUNT_EVENTS,
CONVERSATION_EVENTS,
} from '../../../helper/AnalyticsHelper/events';
import MenuItem from '../../../components/widgets/conversation/contextMenu/menuItem.vue';
import { useTrack } from 'dashboard/composables';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
AddCannedModal,
MenuItem,
ContextMenu,
NextButton,
},
props: {
message: {
type: Object,
required: true,
},
isOpen: {
type: Boolean,
default: false,
},
enabledOptions: {
type: Object,
default: () => ({}),
},
contextMenuPosition: {
type: Object,
default: () => ({}),
},
hideButton: {
type: Boolean,
default: false,
},
},
emits: ['open', 'close', 'replyTo'],
setup() {
const { getPlainText } = useMessageFormatter();
return {
getPlainText,
};
},
data() {
return {
isCannedResponseModalOpen: false,
showDeleteModal: false,
};
},
computed: {
...mapGetters({
getAccount: 'accounts/getAccount',
currentAccountId: 'getCurrentAccountId',
}),
plainTextContent() {
return this.getPlainText(this.messageContent);
},
conversationId() {
return this.message.conversation_id ?? this.message.conversationId;
},
messageId() {
return this.message.id;
},
messageContent() {
return this.message.content;
},
contentAttributes() {
return useSnakeCase(
this.message.content_attributes ?? this.message.contentAttributes
);
},
},
methods: {
async copyLinkToMessage() {
const fullConversationURL =
window.chatwootConfig.hostURL +
frontendURL(
conversationUrl({
id: this.conversationId,
accountId: this.currentAccountId,
})
);
await copyTextToClipboard(
`${fullConversationURL}?messageId=${this.messageId}`
);
useAlert(this.$t('CONVERSATION.CONTEXT_MENU.LINK_COPIED'));
this.handleClose();
},
async handleCopy() {
await copyTextToClipboard(this.plainTextContent);
useAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL'));
this.handleClose();
},
showCannedResponseModal() {
useTrack(ACCOUNT_EVENTS.ADDED_TO_CANNED_RESPONSE);
this.isCannedResponseModalOpen = true;
},
hideCannedResponseModal() {
this.isCannedResponseModalOpen = false;
this.handleClose();
},
handleOpen(e) {
this.$emit('open', e);
},
handleClose(e) {
this.$emit('close', e);
},
handleTranslate() {
const { locale } = this.getAccount(this.currentAccountId);
this.$store.dispatch('translateMessage', {
conversationId: this.conversationId,
messageId: this.messageId,
targetLanguage: locale || 'en',
});
useTrack(CONVERSATION_EVENTS.TRANSLATE_A_MESSAGE);
this.handleClose();
},
handleReplyTo() {
this.$emit('replyTo', this.message);
this.handleClose();
},
openDeleteModal() {
this.handleClose();
this.showDeleteModal = true;
},
async confirmDeletion() {
try {
await this.$store.dispatch('deleteMessage', {
conversationId: this.conversationId,
messageId: this.messageId,
});
useAlert(this.$t('CONVERSATION.SUCCESS_DELETE_MESSAGE'));
this.handleClose();
} catch (error) {
useAlert(this.$t('CONVERSATION.FAIL_DELETE_MESSSAGE'));
}
},
closeDeleteModal() {
this.showDeleteModal = false;
},
},
};
</script>
<template>
<div class="context-menu">
<!-- Add To Canned Responses -->
<woot-modal
v-if="isCannedResponseModalOpen && enabledOptions['cannedResponse']"
v-model:show="isCannedResponseModalOpen"
:on-close="hideCannedResponseModal"
>
<AddCannedModal
:response-content="plainTextContent"
:on-close="hideCannedResponseModal"
/>
</woot-modal>
<!-- Confirm Deletion -->
<woot-delete-modal
v-if="showDeleteModal"
v-model:show="showDeleteModal"
class="context-menu--delete-modal"
:on-close="closeDeleteModal"
:on-confirm="confirmDeletion"
:title="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.TITLE')"
:message="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.MESSAGE')"
:confirm-text="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.DELETE')"
:reject-text="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.CANCEL')"
/>
<NextButton
v-if="!hideButton"
ghost
slate
sm
icon="i-lucide-ellipsis-vertical"
class="invisible group-hover/context-menu:visible"
@click="handleOpen"
/>
<ContextMenu
v-if="isOpen && !isCannedResponseModalOpen"
:x="contextMenuPosition.x"
:y="contextMenuPosition.y"
@close="handleClose"
>
<div class="menu-container">
<MenuItem
v-if="enabledOptions['replyTo']"
:option="{
icon: 'arrow-reply',
label: $t('CONVERSATION.CONTEXT_MENU.REPLY_TO'),
}"
variant="icon"
@click.stop="handleReplyTo"
/>
<MenuItem
v-if="enabledOptions['copy']"
:option="{
icon: 'clipboard',
label: $t('CONVERSATION.CONTEXT_MENU.COPY'),
}"
variant="icon"
@click.stop="handleCopy"
/>
<MenuItem
v-if="enabledOptions['copy']"
:option="{
icon: 'translate',
label: $t('CONVERSATION.CONTEXT_MENU.TRANSLATE'),
}"
variant="icon"
@click.stop="handleTranslate"
/>
<hr />
<MenuItem
:option="{
icon: 'link',
label: $t('CONVERSATION.CONTEXT_MENU.COPY_PERMALINK'),
}"
variant="icon"
@click.stop="copyLinkToMessage"
/>
<MenuItem
v-if="enabledOptions['cannedResponse']"
:option="{
icon: 'comment-add',
label: $t('CONVERSATION.CONTEXT_MENU.CREATE_A_CANNED_RESPONSE'),
}"
variant="icon"
@click.stop="showCannedResponseModal"
/>
<hr v-if="enabledOptions['delete']" />
<MenuItem
v-if="enabledOptions['delete']"
:option="{
icon: 'delete',
label: $t('CONVERSATION.CONTEXT_MENU.DELETE'),
}"
variant="icon"
@click.stop="openDeleteModal"
/>
</div>
</ContextMenu>
</div>
</template>
<style lang="scss" scoped>
.menu-container {
@apply p-1 bg-white dark:bg-slate-900 shadow-xl rounded-md;
hr:first-child {
@apply hidden;
}
hr {
@apply m-1 border-b border-solid border-n-strong;
}
}
.context-menu--delete-modal {
::v-deep {
.modal-container {
@apply max-w-[30rem];
h2 {
@apply font-medium text-base;
}
}
.modal-footer {
@apply pt-4 pb-8 px-8;
}
}
}
</style>