mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	 41d6f9a200
			
		
	
	41d6f9a200
	
	
	
		
			
			- Add dynamic importing for routes. - Added caching for `campaign`, `articles` and `inbox_members` API end points. --------- Co-authored-by: Pranav <pranavrajs@gmail.com>
		
			
				
	
	
		
			237 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { INBOX_TYPES } from 'dashboard/helper/inbox';
 | |
| import { getInboxIconByType } from 'dashboard/helper/inbox';
 | |
| import camelcaseKeys from 'camelcase-keys';
 | |
| import ContactAPI from 'dashboard/api/contacts';
 | |
| 
 | |
| const CHANNEL_PRIORITY = {
 | |
|   'Channel::Email': 1,
 | |
|   'Channel::Whatsapp': 2,
 | |
|   'Channel::Sms': 3,
 | |
|   'Channel::TwilioSms': 4,
 | |
|   'Channel::WebWidget': 5,
 | |
|   'Channel::Api': 6,
 | |
| };
 | |
| 
 | |
| export const generateLabelForContactableInboxesList = ({
 | |
|   name,
 | |
|   email,
 | |
|   channelType,
 | |
|   phoneNumber,
 | |
| }) => {
 | |
|   if (channelType === INBOX_TYPES.EMAIL) {
 | |
|     return `${name} (${email})`;
 | |
|   }
 | |
|   if (
 | |
|     channelType === INBOX_TYPES.TWILIO ||
 | |
|     channelType === INBOX_TYPES.WHATSAPP
 | |
|   ) {
 | |
|     return `${name} (${phoneNumber})`;
 | |
|   }
 | |
|   return name;
 | |
| };
 | |
| 
 | |
| const transformInbox = ({
 | |
|   name,
 | |
|   id,
 | |
|   email,
 | |
|   channelType,
 | |
|   phoneNumber,
 | |
|   ...rest
 | |
| }) => ({
 | |
|   id,
 | |
|   icon: getInboxIconByType(channelType, phoneNumber, 'line'),
 | |
|   label: generateLabelForContactableInboxesList({
 | |
|     name,
 | |
|     email,
 | |
|     channelType,
 | |
|     phoneNumber,
 | |
|   }),
 | |
|   action: 'inbox',
 | |
|   value: id,
 | |
|   name,
 | |
|   email,
 | |
|   phoneNumber,
 | |
|   channelType,
 | |
|   ...rest,
 | |
| });
 | |
| 
 | |
| export const compareInboxes = (a, b) => {
 | |
|   // Channels that have no priority defined should come at the end.
 | |
|   const priorityA = CHANNEL_PRIORITY[a.channelType] || 999;
 | |
|   const priorityB = CHANNEL_PRIORITY[b.channelType] || 999;
 | |
| 
 | |
|   if (priorityA !== priorityB) {
 | |
|     return priorityA - priorityB;
 | |
|   }
 | |
| 
 | |
|   const nameA = a.name || '';
 | |
|   const nameB = b.name || '';
 | |
|   return nameA.localeCompare(nameB);
 | |
| };
 | |
| 
 | |
| export const buildContactableInboxesList = contactInboxes => {
 | |
|   if (!contactInboxes) return [];
 | |
| 
 | |
|   return contactInboxes.map(transformInbox).sort(compareInboxes);
 | |
| };
 | |
| 
 | |
| export const getCapitalizedNameFromEmail = email => {
 | |
|   const name = email.match(/^([^@]*)@/)?.[1] || email.split('@')[0];
 | |
|   return name.charAt(0).toUpperCase() + name.slice(1);
 | |
| };
 | |
| 
 | |
| export const processContactableInboxes = inboxes => {
 | |
|   return inboxes.map(inbox => ({
 | |
|     ...inbox.inbox,
 | |
|     sourceId: inbox.sourceId,
 | |
|   }));
 | |
| };
 | |
| 
 | |
| export const mergeInboxDetails = (inboxesData, inboxesList = []) => {
 | |
|   if (!inboxesData || !inboxesData.length) {
 | |
|     return [];
 | |
|   }
 | |
| 
 | |
|   return inboxesData.map(inboxData => {
 | |
|     const matchingInbox =
 | |
|       inboxesList.find(inbox => inbox.id === inboxData.id) || {};
 | |
|     return {
 | |
|       ...camelcaseKeys(matchingInbox, { deep: true }),
 | |
|       ...inboxData,
 | |
|     };
 | |
|   });
 | |
| };
 | |
| 
 | |
| export const prepareAttachmentPayload = (
 | |
|   attachedFiles,
 | |
|   directUploadsEnabled
 | |
| ) => {
 | |
|   const files = [];
 | |
|   attachedFiles.forEach(attachment => {
 | |
|     if (directUploadsEnabled) {
 | |
|       files.push(attachment.blobSignedId);
 | |
|     } else {
 | |
|       files.push(attachment.resource.file);
 | |
|     }
 | |
|   });
 | |
|   return files;
 | |
| };
 | |
| 
 | |
| export const prepareNewMessagePayload = ({
 | |
|   targetInbox,
 | |
|   selectedContact,
 | |
|   message,
 | |
|   subject,
 | |
|   ccEmails,
 | |
|   bccEmails,
 | |
|   currentUser,
 | |
|   attachedFiles = [],
 | |
|   directUploadsEnabled = false,
 | |
| }) => {
 | |
|   const payload = {
 | |
|     inboxId: targetInbox.id,
 | |
|     sourceId: targetInbox.sourceId,
 | |
|     contactId: Number(selectedContact.id),
 | |
|     message: { content: message },
 | |
|     assigneeId: currentUser.id,
 | |
|   };
 | |
| 
 | |
|   if (attachedFiles?.length) {
 | |
|     payload.files = prepareAttachmentPayload(
 | |
|       attachedFiles,
 | |
|       directUploadsEnabled
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   if (subject) {
 | |
|     payload.mailSubject = subject;
 | |
|   }
 | |
| 
 | |
|   if (ccEmails) {
 | |
|     payload.message.cc_emails = ccEmails;
 | |
|   }
 | |
| 
 | |
|   if (bccEmails) {
 | |
|     payload.message.bcc_emails = bccEmails;
 | |
|   }
 | |
| 
 | |
|   return payload;
 | |
| };
 | |
| 
 | |
| export const prepareWhatsAppMessagePayload = ({
 | |
|   targetInbox,
 | |
|   selectedContact,
 | |
|   message,
 | |
|   templateParams,
 | |
|   currentUser,
 | |
| }) => {
 | |
|   return {
 | |
|     inboxId: targetInbox.id,
 | |
|     sourceId: targetInbox.sourceId,
 | |
|     contactId: selectedContact.id,
 | |
|     message: { content: message, template_params: templateParams },
 | |
|     assigneeId: currentUser.id,
 | |
|   };
 | |
| };
 | |
| 
 | |
| export const generateContactQuery = ({ keys = ['email'], query }) => {
 | |
|   return {
 | |
|     payload: keys.map(key => {
 | |
|       const filterPayload = {
 | |
|         attribute_key: key,
 | |
|         filter_operator: 'contains',
 | |
|         values: [query],
 | |
|         attribute_model: 'standard',
 | |
|       };
 | |
|       if (keys.findIndex(k => k === key) !== keys.length - 1) {
 | |
|         filterPayload.query_operator = 'or';
 | |
|       }
 | |
|       return filterPayload;
 | |
|     }),
 | |
|   };
 | |
| };
 | |
| 
 | |
| // API Calls
 | |
| export const searchContacts = async ({ keys, query }) => {
 | |
|   const {
 | |
|     data: { payload },
 | |
|   } = await ContactAPI.filter(
 | |
|     undefined,
 | |
|     'name',
 | |
|     generateContactQuery({ keys, query })
 | |
|   );
 | |
|   const camelCasedPayload = camelcaseKeys(payload, { deep: true });
 | |
|   // Filter contacts that have either phone_number or email
 | |
|   const filteredPayload = camelCasedPayload?.filter(
 | |
|     contact => contact.phoneNumber || contact.email
 | |
|   );
 | |
|   return filteredPayload || [];
 | |
| };
 | |
| 
 | |
| export const createNewContact = async input => {
 | |
|   const payload = {
 | |
|     name: input.startsWith('+')
 | |
|       ? input.slice(1) // Remove the '+' prefix if it exists
 | |
|       : getCapitalizedNameFromEmail(input),
 | |
|     ...(input.startsWith('+') ? { phone_number: input } : { email: input }),
 | |
|   };
 | |
| 
 | |
|   const {
 | |
|     data: {
 | |
|       payload: { contact: newContact },
 | |
|     },
 | |
|   } = await ContactAPI.create(payload);
 | |
| 
 | |
|   return camelcaseKeys(newContact, { deep: true });
 | |
| };
 | |
| 
 | |
| export const fetchContactableInboxes = async contactId => {
 | |
|   const {
 | |
|     data: { payload: inboxes = [] },
 | |
|   } = await ContactAPI.getContactableInboxes(contactId);
 | |
| 
 | |
|   const convertInboxesToCamelKeys = camelcaseKeys(inboxes, { deep: true });
 | |
| 
 | |
|   return processContactableInboxes(convertInboxesToCamelKeys);
 | |
| };
 |