- Introduced ObjectChildCards component to manage recipients in the NewEmailMessage and EmailMessageInfo components, allowing for better organization and display of recipient information. - Updated EmailMessageInfo to include a collapsible section for recipients, enhancing user experience by providing clear visibility of recipient details. - Refactored email recipient handling in NewEmailMessage to utilize candidatesToRecipients for improved recipient management and validation. - Enhanced EmailSendButton to accommodate the new recipients structure, ensuring proper data flow and functionality. - Improved utility functions for email recipient resolution, streamlining the process of fetching and managing recipient data.
150 lines
4.2 KiB
JavaScript
150 lines
4.2 KiB
JavaScript
import { getModelByName } from '../../../database/ObjectModels'
|
|
|
|
const normalizeType = (type) =>
|
|
typeof type === 'string'
|
|
? `${type.charAt(0).toLowerCase()}${type.slice(1)}`
|
|
: type
|
|
|
|
export const getReferenceId = (value) => {
|
|
if (typeof value === 'string') return value
|
|
return value?._id?._id || value?._id || null
|
|
}
|
|
|
|
const addCandidate = (candidates, candidate) => {
|
|
if (!candidate?.email) return
|
|
const key = `${candidate.email.toLowerCase()}:${candidate.recipientType || ''}:${
|
|
getReferenceId(candidate.recipient) || ''
|
|
}:${getReferenceId(candidate.object) || ''}`
|
|
if (!candidates.some((entry) => entry.key === key)) {
|
|
candidates.push({ ...candidate, key })
|
|
}
|
|
}
|
|
|
|
export const resolveEmailRecipients = async ({
|
|
type,
|
|
objectData,
|
|
fetchObject
|
|
}) => {
|
|
if (!type || !objectData) return []
|
|
|
|
const model = getModelByName(type, true)
|
|
if (model?.name === 'unknown') return []
|
|
|
|
const candidates = []
|
|
const properties = Array.isArray(model.properties) ? model.properties : []
|
|
|
|
for (const property of properties) {
|
|
const value = objectData[property.name]
|
|
|
|
if (property.type === 'email' && typeof value === 'string' && value.trim()) {
|
|
addCandidate(candidates, {
|
|
email: value.trim(),
|
|
label: `${property.label || property.name}: ${value.trim()}`,
|
|
property: property.name,
|
|
object: objectData
|
|
})
|
|
continue
|
|
}
|
|
|
|
if (property.type !== 'object' || value == null) continue
|
|
|
|
const resolvedType = normalizeType(
|
|
typeof property.objectType === 'function'
|
|
? property.objectType(objectData)
|
|
: property.objectType
|
|
)
|
|
if (resolvedType !== 'client' && resolvedType !== 'vendor') continue
|
|
|
|
let recipient = value
|
|
const recipientId = getReferenceId(value)
|
|
if (
|
|
recipientId &&
|
|
(typeof recipient !== 'object' || typeof recipient.email !== 'string')
|
|
) {
|
|
recipient = await fetchObject(recipientId, resolvedType)
|
|
}
|
|
|
|
if (typeof recipient?.email === 'string' && recipient.email.trim()) {
|
|
addCandidate(candidates, {
|
|
email: recipient.email.trim(),
|
|
label: `${property.label || property.name}: ${
|
|
recipient.name || recipient._reference || recipient.email
|
|
}`,
|
|
property: property.name,
|
|
recipientType: resolvedType,
|
|
recipient,
|
|
object: objectData
|
|
})
|
|
}
|
|
}
|
|
|
|
return candidates
|
|
}
|
|
|
|
const hydrateObject = async (item, type, fetchObject) => {
|
|
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
return item
|
|
}
|
|
const id = getReferenceId(item)
|
|
if (!id || !type || typeof fetchObject !== 'function') return null
|
|
return fetchObject(id, type)
|
|
}
|
|
|
|
export const resolveEmailRecipientsForObjects = async ({
|
|
type,
|
|
objects,
|
|
fetchObject
|
|
}) => {
|
|
const list = Array.isArray(objects) ? objects.filter(Boolean) : []
|
|
const merged = []
|
|
for (const item of list) {
|
|
const objectData = await hydrateObject(item, type, fetchObject)
|
|
if (!objectData) continue
|
|
const result = await resolveEmailRecipients({
|
|
type,
|
|
objectData,
|
|
fetchObject
|
|
})
|
|
if (result.length) {
|
|
for (const candidate of result) {
|
|
addCandidate(merged, candidate)
|
|
}
|
|
} else {
|
|
merged.push({
|
|
email: '',
|
|
object: objectData,
|
|
key: `:${getReferenceId(objectData) || ''}`
|
|
})
|
|
}
|
|
}
|
|
return merged
|
|
}
|
|
|
|
export const candidatesToRecipients = (candidates = []) => {
|
|
if (!Array.isArray(candidates) || !candidates.length) return []
|
|
|
|
return candidates.map((candidate) => ({
|
|
recipientEmail: candidate.email || '',
|
|
recipientType: candidate.recipientType ?? null,
|
|
recipient: candidate.recipient ?? null,
|
|
object: candidate.object ?? null,
|
|
read: false,
|
|
sent: false
|
|
}))
|
|
}
|
|
|
|
/** Prefer a client/vendor ref; otherwise use a plain email candidate. */
|
|
export const getDefaultEmailRecipient = (candidates = []) => {
|
|
const recipients = candidatesToRecipients(candidates)
|
|
if (!recipients.length) return null
|
|
|
|
const objectRecipient = recipients.find(
|
|
(recipient) =>
|
|
(recipient.recipientType === 'client' ||
|
|
recipient.recipientType === 'vendor') &&
|
|
recipient.recipient &&
|
|
recipient.recipientEmail
|
|
)
|
|
return objectRecipient || recipients[0]
|
|
}
|