Files
chatwoot/app/javascript/dashboard/components-next/message/bubbles/Contact.vue
Shivam Mishra 19ff5bdd5e feat: Add new message bubbles (#10481)
---------

Co-authored-by: Pranav <pranavrajs@gmail.com>
2024-12-12 17:42:22 -08:00

138 lines
3.7 KiB
Vue

<script setup>
import { computed } from 'vue';
import { useAlert } from 'dashboard/composables';
import { useStore } from 'dashboard/composables/store';
import { useI18n } from 'vue-i18n';
import BaseAttachmentBubble from './BaseAttachment.vue';
import {
DuplicateContactException,
ExceptionWithMessage,
} from 'shared/helpers/CustomErrors';
/**
* @typedef {Object} Attachment
* @property {number} id - Unique identifier for the attachment
* @property {number} messageId - ID of the associated message
* @property {'image'|'audio'|'video'|'file'|'location'|'fallback'|'share'|'story_mention'|'contact'|'ig_reel'} fileType - Type of the attachment (file or image)
* @property {number} accountId - ID of the associated account
* @property {string|null} extension - File extension
* @property {string} dataUrl - URL to access the full attachment data
* @property {string} thumbUrl - URL to access the thumbnail version
* @property {number} fileSize - Size of the file in bytes
* @property {number|null} width - Width of the image if applicable
* @property {number|null} height - Height of the image if applicable
*/
/**
* @typedef {Object} Props
* @property {Attachment[]} [attachments=[]] - The attachments associated with the message
*/
const props = defineProps({
content: {
type: String,
required: true,
},
attachments: {
type: Array,
required: true,
},
sender: {
type: Object,
default: () => ({}),
},
});
const $store = useStore();
const { t } = useI18n();
const attachment = computed(() => {
return props.attachments[0];
});
const phoneNumber = computed(() => {
return attachment.value.fallbackTitle;
});
const formattedPhoneNumber = computed(() => {
return phoneNumber.value.replace(/\s|-|[A-Za-z]/g, '');
});
const rawPhoneNumber = computed(() => {
return phoneNumber.value.replace(/\D/g, '');
});
const name = computed(() => {
return props.content;
});
function getContactObject() {
const contactItem = {
name: name.value,
phone_number: `+${rawPhoneNumber.value}`,
};
return contactItem;
}
async function filterContactByNumber(searchCandidate) {
const query = {
attribute_key: 'phone_number',
filter_operator: 'equal_to',
values: [searchCandidate],
attribute_model: 'standard',
custom_attribute_type: '',
};
const queryPayload = { payload: [query] };
const contacts = await $store.dispatch('contacts/filter', {
queryPayload,
resetState: false,
});
return contacts.shift();
}
function openContactNewTab(contactId) {
const accountId = window.location.pathname.split('/')[3];
const url = `/app/accounts/${accountId}/contacts/${contactId}`;
window.open(url, '_blank');
}
async function addContact() {
try {
let contact = await filterContactByNumber(rawPhoneNumber);
if (!contact) {
contact = await $store.dispatch('contacts/create', getContactObject());
useAlert(t('CONTACT_FORM.SUCCESS_MESSAGE'));
}
openContactNewTab(contact.id);
} catch (error) {
if (error instanceof DuplicateContactException) {
if (error.data.includes('phone_number')) {
useAlert(t('CONTACT_FORM.FORM.PHONE_NUMBER.DUPLICATE'));
}
} else if (error instanceof ExceptionWithMessage) {
useAlert(error.data);
} else {
useAlert(t('CONTACT_FORM.ERROR_MESSAGE'));
}
}
}
const action = computed(() => ({
label: t('CONVERSATION.SAVE_CONTACT'),
onClick: addContact,
}));
</script>
<template>
<BaseAttachmentBubble
icon="i-teenyicons-user-circle-solid"
icon-bg-color="bg-[#D6409F]"
:sender="sender"
sender-translation-key="CONVERSATION.SHARED_ATTACHMENT.CONTACT"
:content="phoneNumber"
:action="formattedPhoneNumber ? action : null"
/>
</template>