mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 10:42:38 +00:00
feat: Inbox card context menu component (#8815)
* feat: Inbox item context menu component * chore: Minor fix * chore: Minor height fix * fix: Conflict * minor fix * chore: Fix conflicts * chore: Minor fix --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -12,6 +12,12 @@
|
|||||||
"CONVERSATION_ASSIGNMENT": "A conversation has been assigned to you",
|
"CONVERSATION_ASSIGNMENT": "A conversation has been assigned to you",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "New message in an assigned conversation",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "New message in an assigned conversation",
|
||||||
"PARTICIPATING_CONVERSATION_NEW_MESSAGE": "New message in a conversation you are participating in"
|
"PARTICIPATING_CONVERSATION_NEW_MESSAGE": "New message in a conversation you are participating in"
|
||||||
|
},
|
||||||
|
"MENU_ITEM": {
|
||||||
|
"MARK_AS_READ": "Mark as read",
|
||||||
|
"MARK_AS_UNREAD": "Mark as unread",
|
||||||
|
"SNOOZE": "Snooze",
|
||||||
|
"DELETE": "Delete"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
class="flex flex-col pl-5 pr-3 gap-2.5 py-3 w-full bg-white dark:bg-slate-900 border-b border-slate-50 dark:border-slate-800/50 hover:bg-slate-25 dark:hover:bg-slate-800 cursor-pointer"
|
class="flex flex-col pl-5 pr-3 gap-2.5 py-3 w-full bg-white dark:bg-slate-900 border-b border-slate-50 dark:border-slate-800/50 hover:bg-slate-25 dark:hover:bg-slate-800 cursor-pointer"
|
||||||
|
@contextmenu="openContextMenu($event)"
|
||||||
@click="openConversation(notificationItem)"
|
@click="openConversation(notificationItem)"
|
||||||
>
|
>
|
||||||
<div class="flex relative items-center justify-between w-full">
|
<div class="flex relative items-center justify-between w-full">
|
||||||
@@ -41,17 +42,24 @@
|
|||||||
{{ lastActivityAt }}
|
{{ lastActivityAt }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<inbox-context-menu
|
||||||
|
v-if="isContextMenuOpen"
|
||||||
|
:context-menu-position="contextMenuPosition"
|
||||||
|
@close="closeContextMenu"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import PriorityIcon from './PriorityIcon.vue';
|
import PriorityIcon from './PriorityIcon.vue';
|
||||||
import StatusIcon from './StatusIcon.vue';
|
import StatusIcon from './StatusIcon.vue';
|
||||||
import InboxNameAndId from './InboxNameAndId.vue';
|
import InboxNameAndId from './InboxNameAndId.vue';
|
||||||
|
import InboxContextMenu from './InboxContextMenu.vue';
|
||||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
import timeMixin from 'dashboard/mixins/time';
|
import timeMixin from 'dashboard/mixins/time';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PriorityIcon,
|
PriorityIcon,
|
||||||
|
InboxContextMenu,
|
||||||
StatusIcon,
|
StatusIcon,
|
||||||
InboxNameAndId,
|
InboxNameAndId,
|
||||||
Thumbnail,
|
Thumbnail,
|
||||||
@@ -63,6 +71,12 @@ export default {
|
|||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isContextMenuOpen: false,
|
||||||
|
contextMenuPosition: { x: null, y: null },
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
primaryActor() {
|
primaryActor() {
|
||||||
return this.notificationItem?.primary_actor;
|
return this.notificationItem?.primary_actor;
|
||||||
@@ -93,10 +107,26 @@ export default {
|
|||||||
return this.shortTimestamp(dynamicTime, true);
|
return this.shortTimestamp(dynamicTime, true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.closeContextMenu();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openConversation(notification) {
|
openConversation(notification) {
|
||||||
this.$emit('open-conversation', notification);
|
this.$emit('open-conversation', notification);
|
||||||
},
|
},
|
||||||
|
closeContextMenu() {
|
||||||
|
this.isContextMenuOpen = false;
|
||||||
|
this.contextMenuPosition = { x: null, y: null };
|
||||||
|
},
|
||||||
|
openContextMenu(e) {
|
||||||
|
this.closeContextMenu();
|
||||||
|
e.preventDefault();
|
||||||
|
this.contextMenuPosition = {
|
||||||
|
x: e.pageX || e.clientX,
|
||||||
|
y: e.pageY || e.clientY,
|
||||||
|
};
|
||||||
|
this.isContextMenuOpen = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<woot-context-menu
|
||||||
|
:x="contextMenuPosition.x"
|
||||||
|
:y="contextMenuPosition.y"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-white dark:bg-slate-900 w-40 py-1 border shadow-md border-slate-100 dark:border-slate-500 rounded-xl"
|
||||||
|
>
|
||||||
|
<menu-item
|
||||||
|
v-for="item in menuItems"
|
||||||
|
:key="item.key"
|
||||||
|
:label="item.label"
|
||||||
|
@click="onMenuItemClick(item.key)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</woot-context-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MenuItem from './MenuItem.vue';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
MenuItem,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contextMenuPosition: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menuItems: [
|
||||||
|
{
|
||||||
|
key: 'mark_as_read',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.MARK_AS_READ'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mark_as_unread',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.MARK_AS_UNREAD'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'snooze',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.SNOOZE'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.DELETE'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onMenuItemClick(key) {
|
||||||
|
this.$emit('click', key);
|
||||||
|
this.handleClose();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['click']);
|
||||||
|
|
||||||
|
const onMenuItemClick = () => {
|
||||||
|
emits('click');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
class="py-1 px-2 w-full h-8 font-medium text-xs text-slate-800 dark:text-slate-100 flex items-center whitespace-nowrap text-ellipsis overflow-hidden hover:text-woot-600 dark:hover:text-woot-500 cursor-pointer rounded-md"
|
||||||
|
@click.stop="onMenuItemClick"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user