All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Integrated InfoActionButtons into multiple components across the Finance and Inventory sections, enhancing user interaction and providing contextual actions for invoices, payment policies, payments, tax records, and various inventory items. - Updated the respective components to include the new buttons with appropriate object types and list mode settings, improving the overall functionality and user experience in the dashboard.
292 lines
8.2 KiB
JavaScript
292 lines
8.2 KiB
JavaScript
import PropTypes from 'prop-types'
|
|
import {
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState
|
|
} from 'react'
|
|
import { Button, Dropdown, Modal, message } from 'antd'
|
|
import { ApiServerContext } from '../context/ApiServerContext'
|
|
import { AuthContext } from '../context/AuthContext'
|
|
import { TableStateContext } from '../context/TableStateContext'
|
|
import MailIcon from '../../Icons/MailIcon'
|
|
import EmailTemplateIcon from '../../Icons/EmailTemplateIcon'
|
|
import NewEmailMessage from '../Management/EmailMessages/NewEmailMessage'
|
|
import {
|
|
getDefaultEmailRecipient,
|
|
resolveEmailRecipients,
|
|
resolveEmailRecipientsForObjects
|
|
} from '../utils/emailRecipients'
|
|
import LoadingPleaseWaitModal from './LoadingPleaseWaitModal'
|
|
import { fetchAllObjectPages } from '../utils/fetchAllObjectPages'
|
|
import { resolveObjectsList } from '../utils/templateObjectData'
|
|
|
|
const EmailSendButton = ({
|
|
type,
|
|
objectData,
|
|
objects,
|
|
listMode = false,
|
|
disabled = false,
|
|
...buttonProps
|
|
}) => {
|
|
const {
|
|
fetchObject,
|
|
fetchObjects,
|
|
connected,
|
|
subscribeToObjectUpdates,
|
|
subscribeToObjectTypeUpdates
|
|
} = useContext(ApiServerContext)
|
|
const { token } = useContext(AuthContext)
|
|
const tableState = useContext(TableStateContext)
|
|
const [templates, setTemplates] = useState([])
|
|
const [recipients, setRecipients] = useState([])
|
|
const [selectedTemplate, setSelectedTemplate] = useState(null)
|
|
const [selectedObjects, setSelectedObjects] = useState(() =>
|
|
resolveObjectsList(objects, objectData)
|
|
)
|
|
const [open, setOpen] = useState(false)
|
|
const [loading, setLoading] = useState(false)
|
|
const [listLoading, setListLoading] = useState(false)
|
|
const fetchObjectsRef = useRef(fetchObjects)
|
|
const fetchObjectRef = useRef(fetchObject)
|
|
fetchObjectsRef.current = fetchObjects
|
|
fetchObjectRef.current = fetchObject
|
|
|
|
const resolvedObjects = useMemo(
|
|
() => resolveObjectsList(objects, objectData),
|
|
[objects, objectData]
|
|
)
|
|
|
|
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(() => {
|
|
if (listMode) {
|
|
setRecipients([])
|
|
return undefined
|
|
}
|
|
let cancelled = false
|
|
setRecipients([])
|
|
resolveEmailRecipients({
|
|
type,
|
|
objectData: resolvedObjects[0],
|
|
fetchObject: (...args) => fetchObjectRef.current(...args)
|
|
}).then((result) => {
|
|
if (!cancelled) setRecipients(result)
|
|
})
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [listMode, resolvedObjects, 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 handleTemplateSelect = useCallback(
|
|
async (template) => {
|
|
setSelectedTemplate(template)
|
|
|
|
if (listMode) {
|
|
setListLoading(true)
|
|
try {
|
|
const list = await fetchAllObjectPages({
|
|
fetchObjects: fetchObjectsRef.current,
|
|
type,
|
|
filter: tableState?.getObjectListFilter?.(type) || {},
|
|
sorter: tableState?.getObjectListSorter?.(type) || {}
|
|
})
|
|
if (!list.length) {
|
|
message.warning('No objects in this list to email')
|
|
return
|
|
}
|
|
const nextRecipients = await resolveEmailRecipientsForObjects({
|
|
type,
|
|
objects: list,
|
|
fetchObject: (...args) => fetchObjectRef.current(...args)
|
|
})
|
|
if (!nextRecipients.length) {
|
|
message.warning('No email recipients available for these objects')
|
|
return
|
|
}
|
|
setSelectedObjects(list)
|
|
setRecipients(nextRecipients)
|
|
setOpen(true)
|
|
} catch (error) {
|
|
console.error('Error fetching objects for email:', error)
|
|
message.error('Failed to load objects for email')
|
|
} finally {
|
|
setListLoading(false)
|
|
}
|
|
return
|
|
}
|
|
|
|
setSelectedObjects(resolvedObjects)
|
|
setOpen(true)
|
|
},
|
|
[listMode, resolvedObjects, tableState, type]
|
|
)
|
|
|
|
const menuItems = useMemo(() => {
|
|
const byTag = new Map()
|
|
const untagged = []
|
|
const makeItem = (template) => ({
|
|
key: template._id,
|
|
label: template.name,
|
|
icon: <EmailTemplateIcon />,
|
|
onClick: () => {
|
|
handleTemplateSelect(template)
|
|
}
|
|
})
|
|
|
|
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)
|
|
}))
|
|
]
|
|
}, [handleTemplateSelect, templates])
|
|
|
|
const defaultRecipient = useMemo(
|
|
() => getDefaultEmailRecipient(recipients),
|
|
[recipients]
|
|
)
|
|
|
|
const unavailable =
|
|
disabled ||
|
|
loading ||
|
|
listLoading ||
|
|
!templates.length ||
|
|
(!listMode && (!resolvedObjects.length || !recipients.length))
|
|
const title = listMode
|
|
? !templates.length
|
|
? 'No email templates available for this object type'
|
|
: 'Send email'
|
|
: !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 || listLoading}
|
|
title={title}
|
|
/>
|
|
</Dropdown>
|
|
<LoadingPleaseWaitModal open={listLoading} />
|
|
<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,
|
|
objects: selectedObjects,
|
|
emailTemplate: selectedTemplate,
|
|
...defaultRecipient
|
|
}}
|
|
recipientCandidates={recipients}
|
|
/>
|
|
</Modal>
|
|
</>
|
|
)
|
|
}
|
|
|
|
EmailSendButton.propTypes = {
|
|
type: PropTypes.string.isRequired,
|
|
objectData: PropTypes.object,
|
|
objects: PropTypes.array,
|
|
listMode: PropTypes.bool,
|
|
disabled: PropTypes.bool
|
|
}
|
|
|
|
export default EmailSendButton
|