|
|
|
|
@@ -1,12 +1,12 @@
|
|
|
|
|
<script>
|
|
|
|
|
import { mapGetters } from 'vuex';
|
|
|
|
|
<script setup>
|
|
|
|
|
import { computed, ref } from 'vue';
|
|
|
|
|
import { useRouter } from 'vue-router';
|
|
|
|
|
import { useMapGetter } from 'dashboard/composables/store';
|
|
|
|
|
import { getLastMessage } from 'dashboard/helper/conversationHelper';
|
|
|
|
|
import { frontendURL, conversationUrl } from 'dashboard/helper/URLHelper';
|
|
|
|
|
import Thumbnail from '../Thumbnail.vue';
|
|
|
|
|
import MessagePreview from './MessagePreview.vue';
|
|
|
|
|
import router from '../../../routes';
|
|
|
|
|
import { frontendURL, conversationUrl } from '../../../helper/URLHelper';
|
|
|
|
|
import InboxName from '../InboxName.vue';
|
|
|
|
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
|
|
|
|
import ConversationContextMenu from './contextMenu/Index.vue';
|
|
|
|
|
import TimeAgo from 'dashboard/components/ui/TimeAgo.vue';
|
|
|
|
|
import CardLabels from './conversationCardComponents/CardLabels.vue';
|
|
|
|
|
@@ -14,257 +14,224 @@ import PriorityMark from './PriorityMark.vue';
|
|
|
|
|
import SLACardLabel from './components/SLACardLabel.vue';
|
|
|
|
|
import ContextMenu from 'dashboard/components/ui/ContextMenu.vue';
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
components: {
|
|
|
|
|
CardLabels,
|
|
|
|
|
InboxName,
|
|
|
|
|
Thumbnail,
|
|
|
|
|
ConversationContextMenu,
|
|
|
|
|
TimeAgo,
|
|
|
|
|
MessagePreview,
|
|
|
|
|
PriorityMark,
|
|
|
|
|
SLACardLabel,
|
|
|
|
|
ContextMenu,
|
|
|
|
|
},
|
|
|
|
|
mixins: [inboxMixin],
|
|
|
|
|
props: {
|
|
|
|
|
activeLabel: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '',
|
|
|
|
|
},
|
|
|
|
|
chat: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => {},
|
|
|
|
|
},
|
|
|
|
|
hideInboxName: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
hideThumbnail: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
teamId: {
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
default: 0,
|
|
|
|
|
},
|
|
|
|
|
foldersId: {
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
default: 0,
|
|
|
|
|
},
|
|
|
|
|
showAssignee: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
conversationType: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '',
|
|
|
|
|
},
|
|
|
|
|
selected: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
enableContextMenu: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
allowedContextMenuOptions: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => [],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
emits: [
|
|
|
|
|
'contextMenuToggle',
|
|
|
|
|
'assignAgent',
|
|
|
|
|
'assignLabel',
|
|
|
|
|
'assignTeam',
|
|
|
|
|
'markAsUnread',
|
|
|
|
|
'markAsRead',
|
|
|
|
|
'assignPriority',
|
|
|
|
|
'updateConversationStatus',
|
|
|
|
|
'deleteConversation',
|
|
|
|
|
],
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
hovered: false,
|
|
|
|
|
showContextMenu: false,
|
|
|
|
|
contextMenu: {
|
|
|
|
|
x: null,
|
|
|
|
|
y: null,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
...mapGetters({
|
|
|
|
|
currentChat: 'getSelectedChat',
|
|
|
|
|
inboxesList: 'inboxes/getInboxes',
|
|
|
|
|
activeInbox: 'getSelectedInbox',
|
|
|
|
|
accountId: 'getCurrentAccountId',
|
|
|
|
|
}),
|
|
|
|
|
chatMetadata() {
|
|
|
|
|
return this.chat.meta || {};
|
|
|
|
|
},
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
activeLabel: { type: String, default: '' },
|
|
|
|
|
chat: { type: Object, default: () => ({}) },
|
|
|
|
|
hideInboxName: { type: Boolean, default: false },
|
|
|
|
|
hideThumbnail: { type: Boolean, default: false },
|
|
|
|
|
teamId: { type: [String, Number], default: 0 },
|
|
|
|
|
foldersId: { type: [String, Number], default: 0 },
|
|
|
|
|
showAssignee: { type: Boolean, default: false },
|
|
|
|
|
conversationType: { type: String, default: '' },
|
|
|
|
|
selected: { type: Boolean, default: false },
|
|
|
|
|
compact: { type: Boolean, default: false },
|
|
|
|
|
enableContextMenu: { type: Boolean, default: false },
|
|
|
|
|
allowedContextMenuOptions: { type: Array, default: () => [] },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
assignee() {
|
|
|
|
|
return this.chatMetadata.assignee || {};
|
|
|
|
|
},
|
|
|
|
|
const emit = defineEmits([
|
|
|
|
|
'contextMenuToggle',
|
|
|
|
|
'assignAgent',
|
|
|
|
|
'assignLabel',
|
|
|
|
|
'assignTeam',
|
|
|
|
|
'markAsUnread',
|
|
|
|
|
'markAsRead',
|
|
|
|
|
'assignPriority',
|
|
|
|
|
'updateConversationStatus',
|
|
|
|
|
'deleteConversation',
|
|
|
|
|
'selectConversation',
|
|
|
|
|
'deSelectConversation',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
currentContact() {
|
|
|
|
|
return this.$store.getters['contacts/getContact'](
|
|
|
|
|
this.chatMetadata.sender.id
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
isActiveChat() {
|
|
|
|
|
return this.currentChat.id === this.chat.id;
|
|
|
|
|
},
|
|
|
|
|
const hovered = ref(false);
|
|
|
|
|
const showContextMenu = ref(false);
|
|
|
|
|
const contextMenu = ref({
|
|
|
|
|
x: null,
|
|
|
|
|
y: null,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
unreadCount() {
|
|
|
|
|
return this.chat.unread_count;
|
|
|
|
|
},
|
|
|
|
|
const currentChat = useMapGetter('getSelectedChat');
|
|
|
|
|
const inboxesList = useMapGetter('inboxes/getInboxes');
|
|
|
|
|
const activeInbox = useMapGetter('getSelectedInbox');
|
|
|
|
|
const accountId = useMapGetter('getCurrentAccountId');
|
|
|
|
|
const contactById = useMapGetter('contacts/getContact');
|
|
|
|
|
const inboxById = useMapGetter('inboxes/getInbox');
|
|
|
|
|
|
|
|
|
|
hasUnread() {
|
|
|
|
|
return this.unreadCount > 0;
|
|
|
|
|
},
|
|
|
|
|
const chatMetadata = computed(() => props.chat.meta || {});
|
|
|
|
|
|
|
|
|
|
isInboxNameVisible() {
|
|
|
|
|
return !this.activeInbox;
|
|
|
|
|
},
|
|
|
|
|
const assignee = computed(() => chatMetadata.value.assignee || {});
|
|
|
|
|
|
|
|
|
|
lastMessageInChat() {
|
|
|
|
|
return getLastMessage(this.chat);
|
|
|
|
|
},
|
|
|
|
|
const currentContact = computed(() => {
|
|
|
|
|
return contactById.value(chatMetadata.value.sender.id);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
inbox() {
|
|
|
|
|
const { inbox_id: inboxId } = this.chat;
|
|
|
|
|
const stateInbox = this.$store.getters['inboxes/getInbox'](inboxId);
|
|
|
|
|
return stateInbox;
|
|
|
|
|
},
|
|
|
|
|
const isActiveChat = computed(() => {
|
|
|
|
|
return currentChat.value.id === props.chat.id;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
showInboxName() {
|
|
|
|
|
return (
|
|
|
|
|
!this.hideInboxName &&
|
|
|
|
|
this.isInboxNameVisible &&
|
|
|
|
|
this.inboxesList.length > 1
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
inboxName() {
|
|
|
|
|
const stateInbox = this.inbox;
|
|
|
|
|
return stateInbox.name || '';
|
|
|
|
|
},
|
|
|
|
|
hasSlaPolicyId() {
|
|
|
|
|
return this.chat?.sla_policy_id;
|
|
|
|
|
},
|
|
|
|
|
conversationPath() {
|
|
|
|
|
const { activeInbox, chat } = this;
|
|
|
|
|
return frontendURL(
|
|
|
|
|
conversationUrl({
|
|
|
|
|
accountId: this.accountId,
|
|
|
|
|
activeInbox,
|
|
|
|
|
id: chat.id,
|
|
|
|
|
label: this.activeLabel,
|
|
|
|
|
teamId: this.teamId,
|
|
|
|
|
foldersId: this.foldersId,
|
|
|
|
|
conversationType: this.conversationType,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
onCardClick(e) {
|
|
|
|
|
const path = this.conversationPath;
|
|
|
|
|
if (!path) return;
|
|
|
|
|
const unreadCount = computed(() => props.chat.unread_count);
|
|
|
|
|
|
|
|
|
|
// Handle Ctrl/Cmd + Click for new tab
|
|
|
|
|
if (e.metaKey || e.ctrlKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
window.open(
|
|
|
|
|
`${window.chatwootConfig.hostURL}${path}`,
|
|
|
|
|
'_blank',
|
|
|
|
|
'noopener,noreferrer'
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const hasUnread = computed(() => unreadCount.value > 0);
|
|
|
|
|
|
|
|
|
|
// Skip if already active
|
|
|
|
|
if (this.isActiveChat) return;
|
|
|
|
|
const isInboxNameVisible = computed(() => !activeInbox.value);
|
|
|
|
|
|
|
|
|
|
router.push({ path });
|
|
|
|
|
},
|
|
|
|
|
onThumbnailHover() {
|
|
|
|
|
this.hovered = !this.hideThumbnail;
|
|
|
|
|
},
|
|
|
|
|
onThumbnailLeave() {
|
|
|
|
|
this.hovered = false;
|
|
|
|
|
},
|
|
|
|
|
onSelectConversation(checked) {
|
|
|
|
|
const action = checked ? 'selectConversation' : 'deSelectConversation';
|
|
|
|
|
this.$emit(action, this.chat.id, this.inbox.id);
|
|
|
|
|
},
|
|
|
|
|
openContextMenu(e) {
|
|
|
|
|
if (!this.enableContextMenu) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
this.$emit('contextMenuToggle', true);
|
|
|
|
|
this.contextMenu.x = e.pageX || e.clientX;
|
|
|
|
|
this.contextMenu.y = e.pageY || e.clientY;
|
|
|
|
|
this.showContextMenu = true;
|
|
|
|
|
},
|
|
|
|
|
closeContextMenu() {
|
|
|
|
|
this.$emit('contextMenuToggle', false);
|
|
|
|
|
this.showContextMenu = false;
|
|
|
|
|
this.contextMenu.x = null;
|
|
|
|
|
this.contextMenu.y = null;
|
|
|
|
|
},
|
|
|
|
|
onUpdateConversation(status, snoozedUntil) {
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
this.$emit(
|
|
|
|
|
'updateConversationStatus',
|
|
|
|
|
this.chat.id,
|
|
|
|
|
status,
|
|
|
|
|
snoozedUntil
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
async onAssignAgent(agent) {
|
|
|
|
|
this.$emit('assignAgent', agent, [this.chat.id]);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async onAssignLabel(label) {
|
|
|
|
|
this.$emit('assignLabel', [label.title], [this.chat.id]);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async onAssignTeam(team) {
|
|
|
|
|
this.$emit('assignTeam', team, this.chat.id);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async markAsUnread() {
|
|
|
|
|
this.$emit('markAsUnread', this.chat.id);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async markAsRead() {
|
|
|
|
|
this.$emit('markAsRead', this.chat.id);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async assignPriority(priority) {
|
|
|
|
|
this.$emit('assignPriority', priority, this.chat.id);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
async deleteConversation() {
|
|
|
|
|
this.$emit('deleteConversation', this.chat.id);
|
|
|
|
|
this.closeContextMenu();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
const lastMessageInChat = computed(() => getLastMessage(props.chat));
|
|
|
|
|
|
|
|
|
|
const inbox = computed(() => {
|
|
|
|
|
const { inbox_id: inboxId } = props.chat;
|
|
|
|
|
const stateInbox = inboxById.value(inboxId);
|
|
|
|
|
return stateInbox;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const showInboxName = computed(() => {
|
|
|
|
|
return (
|
|
|
|
|
!props.hideInboxName &&
|
|
|
|
|
isInboxNameVisible.value &&
|
|
|
|
|
inboxesList.value.length > 1
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const showMetaSection = computed(() => {
|
|
|
|
|
return (
|
|
|
|
|
showInboxName.value ||
|
|
|
|
|
(props.showAssignee && assignee.value.name) ||
|
|
|
|
|
props.chat.priority
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id);
|
|
|
|
|
|
|
|
|
|
const showLabelsSection = computed(() => {
|
|
|
|
|
return props.chat.labels?.length > 0 || hasSlaPolicyId.value;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const messagePreviewClass = computed(() => {
|
|
|
|
|
return [
|
|
|
|
|
hasUnread.value ? 'font-medium text-n-slate-12' : 'text-n-slate-11',
|
|
|
|
|
!props.compact && hasUnread.value ? 'ltr:pr-4 rtl:pl-4' : '',
|
|
|
|
|
props.compact && hasUnread.value ? 'ltr:pr-6 rtl:pl-6' : '',
|
|
|
|
|
];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const conversationPath = computed(() => {
|
|
|
|
|
return frontendURL(
|
|
|
|
|
conversationUrl({
|
|
|
|
|
accountId: accountId.value,
|
|
|
|
|
activeInbox: activeInbox.value,
|
|
|
|
|
id: props.chat.id,
|
|
|
|
|
label: props.activeLabel,
|
|
|
|
|
teamId: props.teamId,
|
|
|
|
|
conversationType: props.conversationType,
|
|
|
|
|
foldersId: props.foldersId,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const onCardClick = e => {
|
|
|
|
|
const path = conversationPath.value;
|
|
|
|
|
if (!path) return;
|
|
|
|
|
|
|
|
|
|
// Handle Ctrl/Cmd + Click for new tab
|
|
|
|
|
if (e.metaKey || e.ctrlKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
window.open(
|
|
|
|
|
`${window.chatwootConfig.hostURL}${path}`,
|
|
|
|
|
'_blank',
|
|
|
|
|
'noopener,noreferrer'
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip if already active
|
|
|
|
|
if (isActiveChat.value) return;
|
|
|
|
|
|
|
|
|
|
router.push({ path });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onThumbnailHover = () => {
|
|
|
|
|
hovered.value = !props.hideThumbnail;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onThumbnailLeave = () => {
|
|
|
|
|
hovered.value = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onSelectConversation = checked => {
|
|
|
|
|
if (checked) {
|
|
|
|
|
emit('selectConversation', props.chat.id, inbox.value.id);
|
|
|
|
|
} else {
|
|
|
|
|
emit('deSelectConversation', props.chat.id, inbox.value.id);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const openContextMenu = e => {
|
|
|
|
|
if (!props.enableContextMenu) return;
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
emit('contextMenuToggle', true);
|
|
|
|
|
contextMenu.value.x = e.pageX || e.clientX;
|
|
|
|
|
contextMenu.value.y = e.pageY || e.clientY;
|
|
|
|
|
showContextMenu.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const closeContextMenu = () => {
|
|
|
|
|
emit('contextMenuToggle', false);
|
|
|
|
|
showContextMenu.value = false;
|
|
|
|
|
contextMenu.value.x = null;
|
|
|
|
|
contextMenu.value.y = null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onUpdateConversation = (status, snoozedUntil) => {
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
emit('updateConversationStatus', props.chat.id, status, snoozedUntil);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onAssignAgent = agent => {
|
|
|
|
|
emit('assignAgent', agent, [props.chat.id]);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onAssignLabel = label => {
|
|
|
|
|
emit('assignLabel', [label.title], [props.chat.id]);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onAssignTeam = team => {
|
|
|
|
|
emit('assignTeam', team, props.chat.id);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const markAsUnread = () => {
|
|
|
|
|
emit('markAsUnread', props.chat.id);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const markAsRead = () => {
|
|
|
|
|
emit('markAsRead', props.chat.id);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const assignPriority = priority => {
|
|
|
|
|
emit('assignPriority', priority, props.chat.id);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const deleteConversation = () => {
|
|
|
|
|
emit('deleteConversation', props.chat.id);
|
|
|
|
|
closeContextMenu();
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div
|
|
|
|
|
class="relative flex items-start flex-grow-0 flex-shrink-0 w-auto max-w-full px-3 py-0 border-t-0 border-b-0 border-l-2 border-r-0 border-transparent border-solid cursor-pointer conversation hover:bg-n-alpha-1 dark:hover:bg-n-alpha-3 group"
|
|
|
|
|
class="relative flex items-start flex-grow-0 flex-shrink-0 w-auto max-w-full py-0 border-t-0 border-b-0 border-l-0 border-r-0 border-transparent border-solid cursor-pointer conversation hover:bg-n-alpha-1 dark:hover:bg-n-alpha-3 group"
|
|
|
|
|
:class="{
|
|
|
|
|
'active animate-card-select bg-n-alpha-1 dark:bg-n-alpha-3 border-n-weak':
|
|
|
|
|
isActiveChat,
|
|
|
|
|
'unread-chat': hasUnread,
|
|
|
|
|
'has-inbox-name': showInboxName,
|
|
|
|
|
'conversation-selected': selected,
|
|
|
|
|
'bg-n-slate-2 dark:bg-n-slate-3': selected,
|
|
|
|
|
'px-0': compact,
|
|
|
|
|
'px-3': !compact,
|
|
|
|
|
}"
|
|
|
|
|
@click="onCardClick"
|
|
|
|
|
@contextmenu="openContextMenu($event)"
|
|
|
|
|
@@ -276,13 +243,14 @@ export default {
|
|
|
|
|
>
|
|
|
|
|
<label
|
|
|
|
|
v-if="hovered || selected"
|
|
|
|
|
class="checkbox-wrapper absolute inset-0 z-20 backdrop-blur-[2px]"
|
|
|
|
|
class="flex items-center justify-center rounded-full cursor-pointer absolute inset-0 z-20 backdrop-blur-[2px]"
|
|
|
|
|
:class="!showInboxName ? 'mt-4' : 'mt-8'"
|
|
|
|
|
@click.stop
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
:value="selected"
|
|
|
|
|
:checked="selected"
|
|
|
|
|
class="checkbox"
|
|
|
|
|
class="!m-0 cursor-pointer"
|
|
|
|
|
type="checkbox"
|
|
|
|
|
@change="onSelectConversation($event.target.checked)"
|
|
|
|
|
/>
|
|
|
|
|
@@ -290,22 +258,30 @@ export default {
|
|
|
|
|
<Thumbnail
|
|
|
|
|
v-if="!hideThumbnail"
|
|
|
|
|
:src="currentContact.thumbnail"
|
|
|
|
|
:badge="inboxBadge"
|
|
|
|
|
:username="currentContact.name"
|
|
|
|
|
:status="currentContact.availability_status"
|
|
|
|
|
size="32px"
|
|
|
|
|
:class="!showInboxName ? 'mt-4' : 'mt-8'"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="px-0 py-3 border-b group-hover:border-transparent flex-1 border-n-slate-3 w-[calc(100%-40px)]"
|
|
|
|
|
class="px-0 py-3 border-b group-hover:border-transparent flex-1 border-n-slate-3 min-w-0"
|
|
|
|
|
>
|
|
|
|
|
<div class="flex items-center conversation-card--meta min-w-0">
|
|
|
|
|
<InboxName
|
|
|
|
|
v-if="showInboxName"
|
|
|
|
|
:inbox="inbox"
|
|
|
|
|
class="flex-1 min-w-0 mx-2"
|
|
|
|
|
/>
|
|
|
|
|
<div class="flex items-center gap-2 flex-shrink-0">
|
|
|
|
|
<div
|
|
|
|
|
v-if="showMetaSection"
|
|
|
|
|
class="flex items-center min-w-0 gap-1"
|
|
|
|
|
:class="{
|
|
|
|
|
'ltr:ml-2 rtl:mr-2': !compact,
|
|
|
|
|
'mx-2': compact,
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<InboxName v-if="showInboxName" :inbox="inbox" class="flex-1 min-w-0" />
|
|
|
|
|
<div
|
|
|
|
|
class="flex items-center gap-2 flex-shrink-0"
|
|
|
|
|
:class="{
|
|
|
|
|
'flex-1 justify-between': !showInboxName,
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
v-if="showAssignee && assignee.name"
|
|
|
|
|
class="text-n-slate-11 text-xs font-medium leading-3 py-0.5 px-0 inline-flex items-center truncate"
|
|
|
|
|
@@ -317,7 +293,7 @@ export default {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<h4
|
|
|
|
|
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis overflow-hidden whitespace-nowrap w-[calc(100%-70px)] text-n-slate-12"
|
|
|
|
|
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis overflow-hidden whitespace-nowrap flex-1 min-w-0 ltr:pr-16 rtl:pl-16 text-n-slate-12"
|
|
|
|
|
:class="hasUnread ? 'font-semibold' : 'font-medium'"
|
|
|
|
|
>
|
|
|
|
|
{{ currentContact.name }}
|
|
|
|
|
@@ -325,24 +301,27 @@ export default {
|
|
|
|
|
<MessagePreview
|
|
|
|
|
v-if="lastMessageInChat"
|
|
|
|
|
:message="lastMessageInChat"
|
|
|
|
|
class="conversation--message my-0 mx-2 leading-6 h-6 max-w-[96%] w-[16.875rem] text-sm"
|
|
|
|
|
:class="hasUnread ? 'font-medium text-n-slate-12' : 'text-n-slate-11'"
|
|
|
|
|
class="my-0 mx-2 leading-6 h-6 flex-1 min-w-0 text-sm"
|
|
|
|
|
:class="messagePreviewClass"
|
|
|
|
|
/>
|
|
|
|
|
<p
|
|
|
|
|
v-else
|
|
|
|
|
class="conversation--message text-n-slate-11 text-sm my-0 mx-2 leading-6 h-6 max-w-[96%] w-[16.875rem] overflow-hidden text-ellipsis whitespace-nowrap"
|
|
|
|
|
:class="hasUnread ? 'font-medium text-n-slate-12' : 'text-n-slate-11'"
|
|
|
|
|
class="text-n-slate-11 text-sm my-0 mx-2 leading-6 h-6 flex-1 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap"
|
|
|
|
|
:class="messagePreviewClass"
|
|
|
|
|
>
|
|
|
|
|
<fluent-icon
|
|
|
|
|
size="16"
|
|
|
|
|
class="-mt-0.5 align-middle inline-block text-n-slate-10"
|
|
|
|
|
icon="info"
|
|
|
|
|
/>
|
|
|
|
|
<span>
|
|
|
|
|
<span class="mx-0.5">
|
|
|
|
|
{{ $t(`CHAT_LIST.NO_MESSAGES`) }}
|
|
|
|
|
</span>
|
|
|
|
|
</p>
|
|
|
|
|
<div class="absolute flex flex-col mt-4 ltr:right-4 rtl:left-4 top-4">
|
|
|
|
|
<div
|
|
|
|
|
class="absolute flex flex-col ltr:right-3 rtl:left-3"
|
|
|
|
|
:class="showMetaSection ? 'top-8' : 'top-4'"
|
|
|
|
|
>
|
|
|
|
|
<span class="ml-auto font-normal leading-4 text-xxs">
|
|
|
|
|
<TimeAgo
|
|
|
|
|
:last-activity-timestamp="chat.timestamp"
|
|
|
|
|
@@ -350,12 +329,17 @@ export default {
|
|
|
|
|
/>
|
|
|
|
|
</span>
|
|
|
|
|
<span
|
|
|
|
|
class="unread shadow-lg rounded-full hidden text-xxs font-semibold h-4 leading-4 ltr:ml-auto rtl:mr-auto mt-1 min-w-[1rem] px-1 py-0 text-center text-white bg-n-teal-9"
|
|
|
|
|
class="shadow-lg rounded-full text-xxs font-semibold h-4 leading-4 ltr:ml-auto rtl:mr-auto mt-1 min-w-[1rem] px-1 py-0 text-center text-white bg-n-teal-9"
|
|
|
|
|
:class="hasUnread ? 'block' : 'hidden'"
|
|
|
|
|
>
|
|
|
|
|
{{ unreadCount > 9 ? '9+' : unreadCount }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<CardLabels :conversation-labels="chat.labels" class="mt-0.5 mx-2 mb-0">
|
|
|
|
|
<CardLabels
|
|
|
|
|
v-if="showLabelsSection"
|
|
|
|
|
:conversation-labels="chat.labels"
|
|
|
|
|
class="mt-0.5 mx-2 mb-0"
|
|
|
|
|
>
|
|
|
|
|
<template v-if="hasSlaPolicyId" #before>
|
|
|
|
|
<SLACardLabel :chat="chat" class="ltr:mr-1 rtl:ml-1" />
|
|
|
|
|
</template>
|
|
|
|
|
@@ -388,55 +372,3 @@ export default {
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.conversation {
|
|
|
|
|
&.unread-chat {
|
|
|
|
|
.unread {
|
|
|
|
|
@apply block;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.compact {
|
|
|
|
|
@apply pl-0;
|
|
|
|
|
|
|
|
|
|
.conversation-card--meta {
|
|
|
|
|
@apply ltr:pr-4 rtl:pl-4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation--details {
|
|
|
|
|
@apply rounded-sm ml-0 pl-5 pr-2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&::v-deep .user-thumbnail-box {
|
|
|
|
|
@apply mt-4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.conversation-selected {
|
|
|
|
|
@apply bg-n-slate-2 dark:bg-n-slate-3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.has-inbox-name {
|
|
|
|
|
&::v-deep .user-thumbnail-box {
|
|
|
|
|
@apply mt-8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.checkbox-wrapper {
|
|
|
|
|
@apply mt-8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation--meta {
|
|
|
|
|
@apply mt-4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.checkbox-wrapper {
|
|
|
|
|
@apply flex items-center justify-center rounded-full cursor-pointer mt-4;
|
|
|
|
|
|
|
|
|
|
input[type='checkbox'] {
|
|
|
|
|
@apply m-0 cursor-pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|