All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Introduced new EmailAccounts and EmailMessages components for managing email accounts and messages. - Implemented ObjectTable for displaying data with filtering and sorting capabilities. - Integrated InfoActionButtons for enhanced user interactions across various management functionalities. - Refactored existing components to replace DocumentPrintButton with InfoActionButtons for consistency in action handling. - Added EmailAccountInfo component for detailed view and management of individual email accounts.
216 lines
5.8 KiB
JavaScript
216 lines
5.8 KiB
JavaScript
import PropTypes from 'prop-types'
|
|
import {
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState
|
|
} from 'react'
|
|
import { Button, Dropdown, Modal } from 'antd'
|
|
import { ApiServerContext } from '../context/ApiServerContext'
|
|
import { AuthContext } from '../context/AuthContext'
|
|
import MailIcon from '../../Icons/MailIcon'
|
|
import EmailTemplateIcon from '../../Icons/EmailTemplateIcon'
|
|
import NewEmailMessage from '../Management/EmailMessages/NewEmailMessage'
|
|
import {
|
|
getDefaultEmailRecipient,
|
|
resolveEmailRecipients
|
|
} from '../utils/emailRecipients'
|
|
|
|
const EmailSendButton = ({ type, objectData, disabled = false, ...buttonProps }) => {
|
|
const {
|
|
fetchObject,
|
|
fetchObjects,
|
|
connected,
|
|
subscribeToObjectUpdates,
|
|
subscribeToObjectTypeUpdates
|
|
} = useContext(ApiServerContext)
|
|
const { token } = useContext(AuthContext)
|
|
const [templates, setTemplates] = useState([])
|
|
const [recipients, setRecipients] = useState([])
|
|
const [selectedTemplate, setSelectedTemplate] = useState(null)
|
|
const [open, setOpen] = useState(false)
|
|
const [loading, setLoading] = useState(false)
|
|
const fetchObjectsRef = useRef(fetchObjects)
|
|
const fetchObjectRef = useRef(fetchObject)
|
|
fetchObjectsRef.current = fetchObjects
|
|
fetchObjectRef.current = fetchObject
|
|
|
|
const filter = useMemo(
|
|
() => ({ objectType: type, global: false, active: true }),
|
|
[type]
|
|
)
|
|
const matches = useCallback(
|
|
(template) =>
|
|
template?.objectType === type &&
|
|
template?.global === false &&
|
|
template?.active === true,
|
|
[type]
|
|
)
|
|
|
|
const loadTemplates = useCallback(
|
|
async (silent = false) => {
|
|
if (!type || token == null) return
|
|
if (!silent) setLoading(true)
|
|
try {
|
|
const result = await fetchObjectsRef.current('emailTemplate', {
|
|
filter,
|
|
limit: 100
|
|
})
|
|
setTemplates(Array.isArray(result?.data) ? result.data : [])
|
|
} finally {
|
|
if (!silent) setLoading(false)
|
|
}
|
|
},
|
|
[filter, token, type]
|
|
)
|
|
|
|
useEffect(() => {
|
|
loadTemplates()
|
|
}, [loadTemplates])
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
setRecipients([])
|
|
resolveEmailRecipients({
|
|
type,
|
|
objectData,
|
|
fetchObject: (...args) => fetchObjectRef.current(...args)
|
|
}).then((result) => {
|
|
if (!cancelled) setRecipients(result)
|
|
})
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [objectData, type])
|
|
|
|
useEffect(() => {
|
|
if (!connected) return undefined
|
|
return subscribeToObjectTypeUpdates('emailTemplate', filter, () =>
|
|
loadTemplates(true)
|
|
)
|
|
}, [
|
|
connected,
|
|
filter,
|
|
loadTemplates,
|
|
subscribeToObjectTypeUpdates
|
|
])
|
|
|
|
useEffect(() => {
|
|
if (!connected) return undefined
|
|
const unsubscribes = templates
|
|
.filter((template) => template?._id)
|
|
.map((template) =>
|
|
subscribeToObjectUpdates(
|
|
template._id.toLowerCase(),
|
|
'emailTemplate',
|
|
(update) => {
|
|
setTemplates((previous) => {
|
|
const merged = { ...template, ...update }
|
|
if (!matches(merged)) {
|
|
return previous.filter((entry) => entry._id !== template._id)
|
|
}
|
|
return previous.map((entry) =>
|
|
entry._id === template._id ? merged : entry
|
|
)
|
|
})
|
|
}
|
|
)
|
|
)
|
|
return () => unsubscribes.forEach((unsubscribe) => unsubscribe?.())
|
|
}, [connected, matches, subscribeToObjectUpdates, templates])
|
|
|
|
const menuItems = useMemo(() => {
|
|
const byTag = new Map()
|
|
const untagged = []
|
|
const makeItem = (template) => ({
|
|
key: template._id,
|
|
label: template.name,
|
|
icon: <EmailTemplateIcon />,
|
|
onClick: () => {
|
|
setSelectedTemplate(template)
|
|
setOpen(true)
|
|
}
|
|
})
|
|
|
|
templates.forEach((template) => {
|
|
const tags = Array.isArray(template.tags) ? template.tags.filter(Boolean) : []
|
|
if (!tags.length) {
|
|
untagged.push(template)
|
|
} else {
|
|
tags.forEach((tag) => byTag.set(tag, [...(byTag.get(tag) || []), template]))
|
|
}
|
|
})
|
|
|
|
return [
|
|
...untagged
|
|
.slice()
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.map(makeItem),
|
|
...[...byTag.entries()]
|
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
.map(([tag, taggedTemplates]) => ({
|
|
key: tag,
|
|
label: tag,
|
|
children: taggedTemplates
|
|
.slice()
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.map(makeItem)
|
|
}))
|
|
]
|
|
}, [templates])
|
|
|
|
const defaultRecipient = useMemo(
|
|
() => getDefaultEmailRecipient(recipients),
|
|
[recipients]
|
|
)
|
|
|
|
const unavailable = disabled || loading || !templates.length || !recipients.length
|
|
const title = !recipients.length
|
|
? 'No email recipients available for this object'
|
|
: !templates.length
|
|
? 'No email templates available for this object type'
|
|
: 'Send email'
|
|
|
|
return (
|
|
<>
|
|
<Dropdown menu={{ items: menuItems }} trigger={['hover']} disabled={unavailable}>
|
|
<Button
|
|
{...buttonProps}
|
|
icon={<MailIcon />}
|
|
disabled={unavailable}
|
|
loading={loading}
|
|
title={title}
|
|
/>
|
|
</Dropdown>
|
|
<Modal
|
|
open={open}
|
|
onCancel={() => setOpen(false)}
|
|
footer={null}
|
|
destroyOnHidden
|
|
width={{ xs: '100%', sm: '100%', md: '100%', lg: '90%', xl: '80%' }}
|
|
>
|
|
<NewEmailMessage
|
|
onOk={() => setOpen(false)}
|
|
defaultValues={{
|
|
objectType: type,
|
|
object: objectData,
|
|
emailTemplate: selectedTemplate,
|
|
...defaultRecipient
|
|
}}
|
|
recipientCandidates={recipients}
|
|
/>
|
|
</Modal>
|
|
</>
|
|
)
|
|
}
|
|
|
|
EmailSendButton.propTypes = {
|
|
type: PropTypes.string.isRequired,
|
|
objectData: PropTypes.object.isRequired,
|
|
disabled: PropTypes.bool
|
|
}
|
|
|
|
export default EmailSendButton
|