Tom Butcher da2881d505 Enhance Email Messaging Components with Recipients Management and UI Improvements
- 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.
2026-09-15 01:56:51 +01:00

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]
}