Add Email Account and Email Message Management Components
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
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.
This commit is contained in:
parent
098fec3b2e
commit
483682ce44
7
assets/icons/emailaccounticon.svg
Normal file
7
assets/icons/emailaccounticon.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.881711,0,0,0.881711,3,-0.216913)">
|
||||
<path d="M0,36.625C0,57 14.062,69.844 34.156,69.844C39.234,69.844 43.125,69.328 45.578,68.453C47.531,67.75 48.266,66.672 48.266,65.156C48.266,63.531 47.203,62.391 45.266,62.391C44.688,62.391 44.109,62.453 43.438,62.547C40.844,63.094 38.5,63.484 35.219,63.484C18.266,63.484 7.078,53.359 7.078,36.797C7.078,20.641 17.812,9.578 33.641,9.578C48.109,9.578 58.953,18.625 58.953,33.953C58.953,41.922 56.359,47.25 52.203,47.25C49.438,47.25 47.891,45.422 47.891,42.266L47.891,24.203C47.891,21.688 46.516,20.25 44.062,20.25C41.656,20.25 40.266,21.688 40.266,24.203L40.266,26.188L39.703,26.188C38.125,22.453 34.5,20.25 30,20.25C22.062,20.25 16.625,26.891 16.625,36.703C16.625,46.75 22.188,53.609 30.469,53.609C35.172,53.609 38.516,51.266 40.234,47.062L40.766,47.062C41.719,51.156 45.438,53.578 50.5,53.578C59.984,53.578 65.781,45.391 65.781,33.203C65.781,15.172 52.547,3.234 33.672,3.234C13.828,3.234 0,16.672 0,36.625ZM32.375,46.75C27.922,46.75 25.156,43 25.156,36.891C25.156,30.828 27.953,27.047 32.391,27.047C36.906,27.047 39.734,30.781 39.734,36.828C39.734,42.938 36.875,46.75 32.375,46.75Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
13
assets/icons/mailtemplateicon.svg
Normal file
13
assets/icons/mailtemplateicon.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.550669,0,0,0.550669,3.539221,19.893693)">
|
||||
<path d="M12.25,59.219L69.375,59.219C77,59.219 81.531,54.688 81.531,46.969L81.531,12.281C81.531,4.562 77,0.031 69.25,0.031L12.156,0.031C4.531,0.031 0,4.562 0,12.281L0,46.969C0,54.688 4.531,59.219 12.25,59.219ZM13.031,49.094C11.156,49.094 10.125,48.062 10.125,46.156L10.125,13.094C10.125,11.188 11.156,10.156 13.031,10.156L68.469,10.156C70.344,10.156 71.406,11.188 71.406,13.094L71.406,46.156C71.406,48.062 70.344,49.094 68.469,49.094L13.031,49.094ZM40.75,39.594C43.531,39.594 45.562,38.719 48.031,36.5L76.344,11L70.406,5.031L42.75,30C42.094,30.562 41.469,30.906 40.75,30.906C40.062,30.906 39.438,30.562 38.781,30L11.125,5.031L5.156,11L33.5,36.5C35.938,38.719 38,39.594 40.75,39.594ZM11.875,52.531L33.656,30.75L27.531,24.906L5.906,46.531L11.875,52.531ZM69.656,52.531L75.625,46.531L54,24.906L47.875,30.75L69.656,52.531Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.712671,0,0,0.701139,14.732018,4.477477)">
|
||||
<path d="M60.461,23.664L63.969,20.107L63.969,20.931C63.969,22.899 62.397,24.496 60.461,24.496C58.525,24.496 56.953,22.899 56.953,20.931L56.953,20.107L60.461,23.664ZM60.461,23.664L56.953,20.099C56.953,19.993 56.948,19.889 56.938,19.787C56.744,17.829 58.15,16.079 60.077,15.882C62.003,15.685 63.724,17.115 63.918,19.073C63.952,19.41 63.969,19.752 63.969,20.099L60.461,23.664ZM60.461,49.46L56.953,45.886L56.953,45.063C56.953,43.095 58.525,41.497 60.461,41.497C62.397,41.497 63.969,43.095 63.969,45.063L63.969,45.886L60.461,49.46ZM60.461,49.46L63.969,45.895C63.969,46.241 63.952,46.583 63.918,46.921C63.724,48.879 62.003,50.308 60.077,50.111C58.15,49.914 56.744,48.164 56.938,46.206C56.948,46.104 56.953,46 56.953,45.895L60.461,49.46ZM53.959,56.069L53.043,56.069C51.107,56.069 49.535,54.472 49.535,52.504C49.535,50.536 51.107,48.938 53.043,48.938L53.949,48.938L53.959,56.069ZM53.959,56.069L53.959,48.938C54.063,48.938 54.165,48.933 54.266,48.922C56.192,48.725 57.913,50.155 58.107,52.113C58.301,54.071 56.895,55.821 54.968,56.018C54.636,56.052 54.3,56.069 53.959,56.069ZM4.873,23.664L1.365,20.099C1.365,19.752 1.382,19.41 1.415,19.073C1.609,17.115 3.33,15.685 5.257,15.882C7.183,16.079 8.59,17.829 8.396,19.787C8.386,19.889 8.38,19.993 8.38,20.099L4.873,23.664ZM4.873,23.664L8.38,20.107L8.38,20.931C8.38,22.899 6.809,24.496 4.873,24.496C2.936,24.496 1.365,22.899 1.365,20.931L1.365,20.107L4.873,23.664ZM11.374,17.055C11.271,17.055 11.169,17.061 11.068,17.071C9.142,17.268 7.42,15.838 7.226,13.88C7.032,11.922 8.439,10.173 10.365,9.976C10.697,9.942 11.034,9.924 11.374,9.924L11.374,17.055ZM11.374,17.055L11.384,9.924L12.29,9.924C14.226,9.924 15.798,11.522 15.798,13.49C15.798,15.458 14.226,17.055 12.29,17.055L11.374,17.055ZM53.959,17.055L53.043,17.055C51.107,17.055 49.535,15.458 49.535,13.49C49.535,11.522 51.107,9.924 53.043,9.924L53.949,9.924L53.959,17.055ZM53.959,17.055L53.959,9.924C54.3,9.924 54.636,9.942 54.968,9.976C56.895,10.173 58.301,11.922 58.107,13.88C57.913,15.838 56.192,17.268 54.266,17.071C54.165,17.061 54.063,17.055 53.959,17.055ZM56.953,32.165C56.953,30.197 58.525,28.599 60.461,28.599C62.397,28.599 63.969,30.197 63.969,32.165L63.969,33.829C63.969,35.797 62.397,37.394 60.461,37.394C58.525,37.394 56.953,35.797 56.953,33.829L56.953,32.165ZM24.654,17.055C22.717,17.055 21.146,15.458 21.146,13.49C21.146,11.522 22.717,9.924 24.654,9.924L26.485,9.924C28.421,9.924 29.993,11.522 29.993,13.49C29.993,15.458 28.421,17.055 26.485,17.055L24.654,17.055ZM38.848,17.055C36.912,17.055 35.341,15.458 35.341,13.49C35.341,11.522 36.912,9.924 38.848,9.924L40.68,9.924C42.616,9.924 44.188,11.522 44.188,13.49C44.188,15.458 42.616,17.055 40.68,17.055L38.848,17.055Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.643125,0.667237,-0.65644,0.632719,30.88979,-37.900014)">
|
||||
<path d="M57.763,28.143C57.763,26.629 58.972,25.401 60.461,25.401C61.95,25.401 63.159,26.629 63.159,28.143L63.159,29.252C63.159,30.765 61.95,31.994 60.461,31.994C58.972,31.994 57.763,30.765 57.763,29.252L57.763,28.143Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@ -24,7 +24,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -131,7 +131,7 @@ const InvoiceInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='invoice'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelProperty,
|
||||
@ -121,7 +121,7 @@ const PaymentPolicyInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='paymentPolicy'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -121,7 +121,7 @@ const PaymentInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='payment'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -112,7 +112,7 @@ const TaxRecordInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='taxRecord'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import NoteIcon from '../../../Icons/NoteIcon'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||
import GraphIcon from '../../../Icons/GraphIcon'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle'
|
||||
import ScrollBox from '../../common/ScrollBox'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -116,7 +116,7 @@ const FilamentStockInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='filamentStock'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,6 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
@ -115,6 +116,11 @@ const OrderItemInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<InfoActionButtons
|
||||
type='orderItem'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<ActivityIndicator
|
||||
activities={objectFormState.activities}
|
||||
|
||||
@ -22,7 +22,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
||||
@ -123,7 +123,7 @@ const PartStockInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='partStock'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -30,7 +30,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import HistoryDisplay from '../../common/HistoryDisplay.jsx'
|
||||
@ -140,7 +140,7 @@ const ProductStockInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='productStock'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
@ -143,7 +143,7 @@ const PurchaseOrderInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='purchaseOrder'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
import NewOrderItem from '../OrderItems/NewOrderItem.jsx'
|
||||
@ -146,7 +146,7 @@ const PurchaseOrderInfo = () => {
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='purchaseOrder'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -124,7 +124,7 @@ const ShipmentInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='shipment'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -24,7 +24,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -122,7 +122,7 @@ const StockAuditInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='stockAudit'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -25,7 +25,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -130,7 +130,7 @@ const StockLocationInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='stockLocation'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -24,7 +24,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
@ -128,7 +128,7 @@ const StockTransferInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='stockTransfer'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -18,7 +18,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -113,7 +113,7 @@ const AppPasswordInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='appPassword'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -118,7 +118,7 @@ const CourierServiceInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='courierService'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -110,7 +110,7 @@ const CourierInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='courier'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -114,7 +114,7 @@ const DocumentJobInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='documentJob'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -122,7 +122,7 @@ const DocumentPrinterInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='documentPrinter'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -114,7 +114,7 @@ const DocumentSizeInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='documentSize'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -119,7 +119,7 @@ const DocumentTemplateInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='documentTemplate'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
83
src/components/Dashboard/Management/EmailAccounts.jsx
Normal file
83
src/components/Dashboard/Management/EmailAccounts.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { useRef } from 'react'
|
||||
import { Flex, Space } from 'antd'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import ObjectActions from '../common/ObjectActions'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||
import SortSidebarButton from '../common/SortSidebarButton'
|
||||
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import ListViewTabs from '../common/ListViewTabs'
|
||||
import ListViewEditButtons from '../common/ListViewEditButtons'
|
||||
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
|
||||
|
||||
const EmailAccounts = () => {
|
||||
const tableRef = useRef()
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('emailAccount')
|
||||
|
||||
const [showFilterSidebar, setShowFilterSidebar] =
|
||||
useFilterSidebarVisibility('EmailAccounts')
|
||||
|
||||
const [showSortSidebar, setShowSortSidebar] =
|
||||
useSortSidebarVisibility('EmailAccounts')
|
||||
|
||||
return (
|
||||
<ObjectListViewProvider objectType='emailAccount' tableRef={tableRef}>
|
||||
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailAccount'
|
||||
pageName='list'
|
||||
onReload={() => tableRef.current?.reload()}
|
||||
/>
|
||||
<ColumnViewButton
|
||||
type='emailAccount'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='emailAccount' />
|
||||
</Space>
|
||||
<ListViewTabs />
|
||||
<Space>
|
||||
<SortSidebarButton
|
||||
active={showSortSidebar}
|
||||
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||
/>
|
||||
<FilterSidebarButton
|
||||
active={showFilterSidebar}
|
||||
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||
/>
|
||||
<ObjectTableViewButton objectType='emailAccount' />
|
||||
<ListViewEditButtons showStartingDivider={true} />
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='emailAccount'
|
||||
showFilterSidebar={showFilterSidebar}
|
||||
showSortSidebar={showSortSidebar}
|
||||
expandHeight={true}
|
||||
saveFilterInSession={true}
|
||||
saveFilterInUrl={true}
|
||||
useFilterInSession={true}
|
||||
useFilterInUrl={true}
|
||||
saveSortInSession={true}
|
||||
saveSortInUrl={true}
|
||||
useSortInSession={true}
|
||||
useSortInUrl={true}
|
||||
/>
|
||||
</Flex>
|
||||
</ObjectListViewProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailAccounts
|
||||
@ -0,0 +1,238 @@
|
||||
import { useRef, useState, useMemo } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
import useCollapseState from '../../hooks/useCollapseState.jsx'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
const log = loglevel.getLogger('EmailAccountInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const EmailAccountInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const emailAccountId = new URLSearchParams(location.search).get(
|
||||
'emailAccountId'
|
||||
)
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'EmailAccountInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: false
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
activities: [],
|
||||
loading: false,
|
||||
editDisabled: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const currentObjectTools = useMemo(
|
||||
() => (
|
||||
<ObjectTableNavigationButtons
|
||||
disabled={objectFormState.loading || objectFormState.isEditing}
|
||||
_id={emailAccountId}
|
||||
objectType='emailAccount'
|
||||
showEndingDivider={false}
|
||||
/>
|
||||
),
|
||||
[objectFormState.loading, objectFormState.isEditing, emailAccountId]
|
||||
)
|
||||
useDashboardObjectTools(currentObjectTools)
|
||||
|
||||
const actions = {
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailAccount'
|
||||
pageName='info'
|
||||
id={emailAccountId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Email Account Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<UserNotifierToggle
|
||||
type='emailAccount'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<InfoActionButtons
|
||||
type='emailAccount'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<ActivityIndicator
|
||||
activities={objectFormState.activities}
|
||||
showStartingDivider={true}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
cancelEditing={() => {
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}}
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={
|
||||
objectFormState.beingEditedByOther ||
|
||||
objectFormState.loading ||
|
||||
objectFormState.editDisabled
|
||||
}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
title='Email Account Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectForm
|
||||
id={emailAccountId}
|
||||
type='emailAccount'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
setCurrentObject={true}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
type='emailAccount'
|
||||
objectData={objectData}
|
||||
labelWidth='190px'
|
||||
/>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Spin
|
||||
spinning={objectFormState.loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={emailAccountId} type='emailAccount' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{
|
||||
parent:
|
||||
getModelByName('emailAccount').prefix +
|
||||
':' +
|
||||
emailAccountId
|
||||
}}
|
||||
visibleColumns={{ _id: false, parent: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailAccountInfo
|
||||
@ -0,0 +1,95 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewEmailAccount = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'emailAccount'}
|
||||
defaultValues={{
|
||||
active: true,
|
||||
port: 587,
|
||||
secure: false,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailAccount'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
labelWidth={130}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailAccount'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
labelWidth={130}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailAccount'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
_reference: false,
|
||||
createdAt: false,
|
||||
updatedAt: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Email Account'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewEmailAccount.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewEmailAccount
|
||||
83
src/components/Dashboard/Management/EmailMessages.jsx
Normal file
83
src/components/Dashboard/Management/EmailMessages.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { useRef } from 'react'
|
||||
import { Flex, Space } from 'antd'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import ObjectActions from '../common/ObjectActions'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||
import SortSidebarButton from '../common/SortSidebarButton'
|
||||
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import ListViewTabs from '../common/ListViewTabs'
|
||||
import ListViewEditButtons from '../common/ListViewEditButtons'
|
||||
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
|
||||
|
||||
const EmailMessages = () => {
|
||||
const tableRef = useRef()
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('emailMessage')
|
||||
|
||||
const [showFilterSidebar, setShowFilterSidebar] =
|
||||
useFilterSidebarVisibility('EmailMessages')
|
||||
|
||||
const [showSortSidebar, setShowSortSidebar] =
|
||||
useSortSidebarVisibility('EmailMessages')
|
||||
|
||||
return (
|
||||
<ObjectListViewProvider objectType='emailMessage' tableRef={tableRef}>
|
||||
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailMessage'
|
||||
pageName='list'
|
||||
onReload={() => tableRef.current?.reload()}
|
||||
/>
|
||||
<ColumnViewButton
|
||||
type='emailMessage'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='emailMessage' />
|
||||
</Space>
|
||||
<ListViewTabs />
|
||||
<Space>
|
||||
<SortSidebarButton
|
||||
active={showSortSidebar}
|
||||
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||
/>
|
||||
<FilterSidebarButton
|
||||
active={showFilterSidebar}
|
||||
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||
/>
|
||||
<ObjectTableViewButton objectType='emailMessage' />
|
||||
<ListViewEditButtons showStartingDivider={true} />
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='emailMessage'
|
||||
showFilterSidebar={showFilterSidebar}
|
||||
showSortSidebar={showSortSidebar}
|
||||
expandHeight={true}
|
||||
saveFilterInSession={true}
|
||||
saveFilterInUrl={true}
|
||||
useFilterInSession={true}
|
||||
useFilterInUrl={true}
|
||||
saveSortInSession={true}
|
||||
saveSortInUrl={true}
|
||||
useSortInSession={true}
|
||||
useSortInUrl={true}
|
||||
/>
|
||||
</Flex>
|
||||
</ObjectListViewProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailMessages
|
||||
@ -0,0 +1,227 @@
|
||||
import { useRef, useState, useMemo } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
import useCollapseState from '../../hooks/useCollapseState.jsx'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import TemplatePreview from '../../common/TemplatePreview.jsx'
|
||||
|
||||
const log = loglevel.getLogger('EmailMessageInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const EmailMessageInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const emailMessageId = new URLSearchParams(location.search).get(
|
||||
'emailMessageId'
|
||||
)
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'EmailMessageInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: false
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
activities: [],
|
||||
loading: false,
|
||||
editDisabled: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const currentObjectTools = useMemo(
|
||||
() => (
|
||||
<ObjectTableNavigationButtons
|
||||
disabled={objectFormState.loading}
|
||||
_id={emailMessageId}
|
||||
objectType='emailMessage'
|
||||
showEndingDivider={false}
|
||||
/>
|
||||
),
|
||||
[objectFormState.loading, emailMessageId]
|
||||
)
|
||||
useDashboardObjectTools(currentObjectTools)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailMessage'
|
||||
pageName='info'
|
||||
id={emailMessageId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Email Message Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<UserNotifierToggle
|
||||
type='emailMessage'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<InfoActionButtons
|
||||
type='emailMessage'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<ActivityIndicator
|
||||
activities={objectFormState.activities}
|
||||
showStartingDivider={true}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ObjectForm
|
||||
id={emailMessageId}
|
||||
type='emailMessage'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
setCurrentObject={true}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Email Message Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
type='emailMessage'
|
||||
objectData={objectData}
|
||||
labelWidth='190px'
|
||||
visibleProperties={{
|
||||
content: false
|
||||
}}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Email Message Content'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.content}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('content', expanded)
|
||||
}
|
||||
collapseKey='content'
|
||||
>
|
||||
<Spin
|
||||
spinning={objectFormState.loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<TemplatePreview
|
||||
template={objectData?.template}
|
||||
content={objectData?.content}
|
||||
templateType='emailMessage'
|
||||
capabilities={{
|
||||
htmlOnly: true,
|
||||
widthControl: true,
|
||||
defaultWidth: 600
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Spin
|
||||
spinning={objectFormState.loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={emailMessageId} type='emailMessage' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{
|
||||
parent:
|
||||
getModelByName('emailMessage').prefix +
|
||||
':' +
|
||||
emailMessageId
|
||||
}}
|
||||
visibleColumns={{ _id: false, parent: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailMessageInfo
|
||||
@ -0,0 +1,319 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
import { Modal } from 'antd'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import TemplatePreview from '../../common/TemplatePreview'
|
||||
import ProgressDisplay from '../../common/ProgressDisplay'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { resolveEmailRecipients } from '../../utils/emailRecipients'
|
||||
|
||||
const progressByState = {
|
||||
queued: 10,
|
||||
rendering: 35,
|
||||
sending: 70,
|
||||
sent: 100,
|
||||
error: 100
|
||||
}
|
||||
const EMPTY_RECIPIENT_CANDIDATES = []
|
||||
const EMPTY_DEFAULT_VALUES = {}
|
||||
|
||||
const EmailWizardContent = ({
|
||||
accounts,
|
||||
defaultCandidates,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
objectData,
|
||||
setObjectData,
|
||||
form,
|
||||
formValid,
|
||||
onCreated
|
||||
}) => {
|
||||
const { fetchObject } = useContext(ApiServerContext)
|
||||
const [candidates, setCandidates] = useState(defaultCandidates)
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultCandidates.length) {
|
||||
setCandidates(defaultCandidates)
|
||||
return
|
||||
}
|
||||
let cancelled = false
|
||||
resolveEmailRecipients({
|
||||
type: objectData?.objectType,
|
||||
objectData: objectData?.object,
|
||||
fetchObject
|
||||
}).then((result) => {
|
||||
if (!cancelled) setCandidates(result)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [
|
||||
defaultCandidates,
|
||||
fetchObject,
|
||||
objectData?.object,
|
||||
objectData?.objectType
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
setObjectData((previous) => ({
|
||||
...previous,
|
||||
_recipientCandidates: candidates
|
||||
}))
|
||||
}, [candidates, setObjectData])
|
||||
|
||||
useEffect(() => {
|
||||
const currentAccount = objectData?.emailAccount
|
||||
const account =
|
||||
accounts.find(
|
||||
(entry) => entry._id === (currentAccount?._id || currentAccount)
|
||||
) || accounts[0]
|
||||
if (!account || objectData?.emailAccount) return
|
||||
form.setFieldsValue({ emailAccount: account, fromEmail: account.fromEmail })
|
||||
setObjectData((previous) => ({
|
||||
...previous,
|
||||
emailAccount: account,
|
||||
fromEmail: account.fromEmail
|
||||
}))
|
||||
}, [accounts, form, objectData?.emailAccount, setObjectData])
|
||||
|
||||
useEffect(() => {
|
||||
if (!candidates.length || objectData?.recipientEmail) return
|
||||
const candidate = candidates[0]
|
||||
const values = {
|
||||
recipientEmail: candidate.email,
|
||||
recipientType: candidate.recipientType,
|
||||
recipient: candidate.recipient
|
||||
}
|
||||
form.setFieldsValue(values)
|
||||
setObjectData((previous) => ({ ...previous, ...values }))
|
||||
}, [candidates, form, objectData?.recipientEmail, setObjectData])
|
||||
|
||||
return (
|
||||
<WizardView
|
||||
steps={[
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailMessage'
|
||||
column={1}
|
||||
visibleProperties={{
|
||||
name: true,
|
||||
objectType: true,
|
||||
object: true,
|
||||
emailTemplate: true,
|
||||
emailAccount: true,
|
||||
recipientEmail: true,
|
||||
recipientType: true,
|
||||
recipient: true,
|
||||
fromEmail: true
|
||||
}}
|
||||
bordered={false}
|
||||
labelWidth={115}
|
||||
isEditing
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]}
|
||||
submitText='Send'
|
||||
title='Send Email'
|
||||
formValid={
|
||||
formValid && Boolean(objectData?.recipientEmail) && accounts.length > 0
|
||||
}
|
||||
loading={submitLoading}
|
||||
sideBarGrow
|
||||
sideBar={
|
||||
<div
|
||||
style={{
|
||||
height: 'calc(100vh - 240px)',
|
||||
flexGrow: 1,
|
||||
minWidth: 0
|
||||
}}
|
||||
>
|
||||
<TemplatePreview
|
||||
objectData={objectData?.object}
|
||||
template={objectData?.emailTemplate}
|
||||
templateType='emailTemplate'
|
||||
capabilities={{
|
||||
htmlOnly: true,
|
||||
widthControl: true,
|
||||
defaultWidth: 600
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result?._id) onCreated(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
EmailWizardContent.propTypes = {
|
||||
accounts: PropTypes.array.isRequired,
|
||||
defaultCandidates: PropTypes.array.isRequired,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
submitLoading: PropTypes.bool,
|
||||
objectData: PropTypes.object,
|
||||
setObjectData: PropTypes.func.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
formValid: PropTypes.bool,
|
||||
onCreated: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
const NewEmailMessage = ({
|
||||
onOk,
|
||||
defaultValues = EMPTY_DEFAULT_VALUES,
|
||||
recipientCandidates = EMPTY_RECIPIENT_CANDIDATES
|
||||
}) => {
|
||||
const {
|
||||
fetchObject,
|
||||
fetchObjects,
|
||||
connected,
|
||||
subscribeToObjectUpdates,
|
||||
subscribeToObjectTypeUpdates
|
||||
} = useContext(ApiServerContext)
|
||||
const [accounts, setAccounts] = useState([])
|
||||
const [sendingMessage, setSendingMessage] = useState(null)
|
||||
const fetchObjectRef = useRef(fetchObject)
|
||||
const fetchObjectsRef = useRef(fetchObjects)
|
||||
const fetchedMessageIdRef = useRef(null)
|
||||
fetchObjectRef.current = fetchObject
|
||||
fetchObjectsRef.current = fetchObjects
|
||||
const initialValues = useMemo(
|
||||
() => ({ read: false, ...defaultValues }),
|
||||
[defaultValues]
|
||||
)
|
||||
|
||||
const loadAccounts = useCallback(async () => {
|
||||
const result = await fetchObjectsRef.current('emailAccount', {
|
||||
filter: { active: true },
|
||||
limit: 100
|
||||
})
|
||||
setAccounts(Array.isArray(result?.data) ? result.data : [])
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
loadAccounts()
|
||||
}, [loadAccounts])
|
||||
|
||||
useEffect(() => {
|
||||
if (!connected) return undefined
|
||||
return subscribeToObjectTypeUpdates(
|
||||
'emailAccount',
|
||||
{ active: true },
|
||||
loadAccounts
|
||||
)
|
||||
}, [connected, loadAccounts, subscribeToObjectTypeUpdates])
|
||||
|
||||
useEffect(() => {
|
||||
if (!connected) return undefined
|
||||
const unsubscribes = accounts
|
||||
.filter((account) => account?._id)
|
||||
.map((account) =>
|
||||
subscribeToObjectUpdates(
|
||||
account._id.toLowerCase(),
|
||||
'emailAccount',
|
||||
(update) => {
|
||||
const merged = { ...account, ...update }
|
||||
setAccounts((previous) =>
|
||||
merged.active === true
|
||||
? previous.map((entry) =>
|
||||
entry._id === account._id ? merged : entry
|
||||
)
|
||||
: previous.filter((entry) => entry._id !== account._id)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
return () => unsubscribes.forEach((unsubscribe) => unsubscribe?.())
|
||||
}, [accounts, connected, subscribeToObjectUpdates])
|
||||
|
||||
useEffect(() => {
|
||||
const id = sendingMessage?._id
|
||||
if (!id) return undefined
|
||||
|
||||
let cancelled = false
|
||||
if (fetchedMessageIdRef.current !== id) {
|
||||
fetchedMessageIdRef.current = id
|
||||
fetchObjectRef.current(id, 'emailMessage').then((current) => {
|
||||
if (!cancelled && current?._id) {
|
||||
setSendingMessage((previous) => ({ ...previous, ...current }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unsubscribe =
|
||||
connected === true
|
||||
? subscribeToObjectUpdates(id.toLowerCase(), 'emailMessage', (update) =>
|
||||
setSendingMessage((previous) => ({ ...previous, ...update }))
|
||||
)
|
||||
: undefined
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
unsubscribe?.()
|
||||
}
|
||||
}, [connected, sendingMessage?._id, subscribeToObjectUpdates])
|
||||
|
||||
useEffect(() => {
|
||||
if (sendingMessage?.state?.type === 'sent') onOk?.(sendingMessage)
|
||||
}, [onOk, sendingMessage])
|
||||
|
||||
return (
|
||||
<>
|
||||
<NewObjectForm type='emailMessage' defaultValues={initialValues}>
|
||||
{(formProps) => (
|
||||
<EmailWizardContent
|
||||
{...formProps}
|
||||
accounts={accounts}
|
||||
defaultCandidates={recipientCandidates}
|
||||
onCreated={setSendingMessage}
|
||||
/>
|
||||
)}
|
||||
</NewObjectForm>
|
||||
<Modal
|
||||
open={Boolean(sendingMessage)}
|
||||
footer={null}
|
||||
closable={sendingMessage?.state?.type === 'error'}
|
||||
onCancel={() => setSendingMessage(null)}
|
||||
maskClosable={false}
|
||||
centered
|
||||
destroyOnHidden
|
||||
zIndex={2000}
|
||||
width={360}
|
||||
getContainer={() => document.body}
|
||||
>
|
||||
<ProgressDisplay
|
||||
percent={progressByState[sendingMessage?.state?.type] || 0}
|
||||
status={
|
||||
sendingMessage?.state?.type === 'error' ? 'exception' : 'active'
|
||||
}
|
||||
>
|
||||
{sendingMessage?.state?.message ||
|
||||
`Email ${sendingMessage?.state?.type || 'queued'}...`}
|
||||
</ProgressDisplay>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
NewEmailMessage.propTypes = {
|
||||
onOk: PropTypes.func,
|
||||
defaultValues: PropTypes.object,
|
||||
recipientCandidates: PropTypes.array
|
||||
}
|
||||
|
||||
export default NewEmailMessage
|
||||
83
src/components/Dashboard/Management/EmailTemplates.jsx
Normal file
83
src/components/Dashboard/Management/EmailTemplates.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { useRef } from 'react'
|
||||
import { Flex, Space } from 'antd'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import ObjectActions from '../common/ObjectActions'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||
import SortSidebarButton from '../common/SortSidebarButton'
|
||||
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import ListViewTabs from '../common/ListViewTabs'
|
||||
import ListViewEditButtons from '../common/ListViewEditButtons'
|
||||
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
|
||||
|
||||
const EmailTemplates = () => {
|
||||
const tableRef = useRef()
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('emailTemplate')
|
||||
|
||||
const [showFilterSidebar, setShowFilterSidebar] =
|
||||
useFilterSidebarVisibility('EmailTemplates')
|
||||
|
||||
const [showSortSidebar, setShowSortSidebar] =
|
||||
useSortSidebarVisibility('EmailTemplates')
|
||||
|
||||
return (
|
||||
<ObjectListViewProvider objectType='emailTemplate' tableRef={tableRef}>
|
||||
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailTemplate'
|
||||
pageName='list'
|
||||
onReload={() => tableRef.current?.reload()}
|
||||
/>
|
||||
<ColumnViewButton
|
||||
type='emailTemplate'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='emailTemplate' />
|
||||
</Space>
|
||||
<ListViewTabs />
|
||||
<Space>
|
||||
<SortSidebarButton
|
||||
active={showSortSidebar}
|
||||
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||
/>
|
||||
<FilterSidebarButton
|
||||
active={showFilterSidebar}
|
||||
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||
/>
|
||||
<ObjectTableViewButton objectType='emailTemplate' />
|
||||
<ListViewEditButtons showStartingDivider={true} />
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='emailTemplate'
|
||||
showFilterSidebar={showFilterSidebar}
|
||||
showSortSidebar={showSortSidebar}
|
||||
expandHeight={true}
|
||||
saveFilterInSession={true}
|
||||
saveFilterInUrl={true}
|
||||
useFilterInSession={true}
|
||||
useFilterInUrl={true}
|
||||
saveSortInSession={true}
|
||||
saveSortInUrl={true}
|
||||
useSortInSession={true}
|
||||
useSortInUrl={true}
|
||||
/>
|
||||
</Flex>
|
||||
</ObjectListViewProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailTemplates
|
||||
@ -0,0 +1,159 @@
|
||||
import { useMemo, useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import useCollapseState from '../../hooks/useCollapseState'
|
||||
import NotesPanel from '../../common/NotesPanel'
|
||||
import InfoCollapse from '../../common/InfoCollapse'
|
||||
import ViewButton from '../../common/ViewButton'
|
||||
import NoteIcon from '../../../Icons/NoteIcon'
|
||||
import ObjectForm from '../../common/ObjectForm'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons'
|
||||
import ActivityIndicator from '../../common/ActivityIndicator'
|
||||
import ActionHandler from '../../common/ActionHandler'
|
||||
import ObjectActions from '../../common/ObjectActions'
|
||||
import TemplateEditor from '../../common/TemplateEditor'
|
||||
import ScrollBox from '../../common/ScrollBox'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
|
||||
const EmailTemplateDesign = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const emailTemplateId = new URLSearchParams(location.search).get(
|
||||
'emailTemplateId'
|
||||
)
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'EmailTemplateDesign',
|
||||
{ preview: true, editor: true, notes: true }
|
||||
)
|
||||
const [formState, setFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
activities: [],
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
useDashboardObjectTools(
|
||||
useMemo(
|
||||
() => (
|
||||
<ObjectTableNavigationButtons
|
||||
disabled={formState.loading || formState.isEditing}
|
||||
_id={emailTemplateId}
|
||||
objectType='emailTemplate'
|
||||
showEndingDivider={false}
|
||||
/>
|
||||
),
|
||||
[emailTemplateId, formState.isEditing, formState.loading]
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<Flex gap='large' vertical style={{ maxHeight: '100%', minHeight: 0 }}>
|
||||
<Flex justify='space-between'>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailTemplate'
|
||||
pageName='info'
|
||||
id={emailTemplateId}
|
||||
disabled={formState.loading}
|
||||
visibleActions={{ edit: false }}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={formState.loading}
|
||||
items={[
|
||||
{ key: 'preview', label: 'Preview' },
|
||||
{ key: 'editor', label: 'Editor' },
|
||||
{ key: 'notes', label: 'Notes' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<ActivityIndicator activities={formState.activities} />
|
||||
</Space>
|
||||
<EditButtons
|
||||
isEditing={formState.isEditing}
|
||||
handleUpdate={() => actionHandlerRef.current.callAction('finishEdit')}
|
||||
cancelEditing={() =>
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}
|
||||
startEditing={() => actionHandlerRef.current.callAction('edit')}
|
||||
editLoading={formState.editLoading}
|
||||
formValid={formState.formValid}
|
||||
disabled={formState.beingEditedByOther || formState.loading}
|
||||
loading={formState.editLoading}
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollBox>
|
||||
<Flex vertical gap='large'>
|
||||
<ActionHandler
|
||||
actions={{
|
||||
edit: () => {
|
||||
objectFormRef.current?.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef.current?.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef.current?.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}}
|
||||
loading={formState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={emailTemplateId}
|
||||
type='emailTemplate'
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) =>
|
||||
setFormState((previous) => ({ ...previous, ...state }))
|
||||
}
|
||||
>
|
||||
{({ loading, isEditing, objectData, form, setObjectData }) => (
|
||||
<TemplateEditor
|
||||
objectData={objectData}
|
||||
templateType='emailTemplate'
|
||||
subjectProperty='subject'
|
||||
previewCapabilities={{
|
||||
htmlOnly: true,
|
||||
widthControl: true,
|
||||
defaultWidth: 600
|
||||
}}
|
||||
loading={loading}
|
||||
style={{ height: '72vh' }}
|
||||
collapseState={collapseState}
|
||||
isEditing={isEditing}
|
||||
form={form}
|
||||
setObjectData={setObjectData}
|
||||
/>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Spin spinning={formState.loading} indicator={<LoadingOutlined />}>
|
||||
<Card>
|
||||
<NotesPanel _id={emailTemplateId} type='emailTemplate' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailTemplateDesign
|
||||
@ -0,0 +1,269 @@
|
||||
import { useRef, useState, useMemo } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
import useCollapseState from '../../hooks/useCollapseState.jsx'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import FileIcon from '../../../Icons/FileIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName, getModelProperty } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
const log = loglevel.getLogger('EmailTemplateInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const EmailTemplateInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const emailTemplateId = new URLSearchParams(location.search).get(
|
||||
'emailTemplateId'
|
||||
)
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'EmailTemplateInfo',
|
||||
{
|
||||
info: true,
|
||||
attachments: true,
|
||||
notes: true,
|
||||
auditLogs: false
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
activities: [],
|
||||
loading: false,
|
||||
editDisabled: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const currentObjectTools = useMemo(
|
||||
() => (
|
||||
<ObjectTableNavigationButtons
|
||||
disabled={objectFormState.loading || objectFormState.isEditing}
|
||||
_id={emailTemplateId}
|
||||
objectType='emailTemplate'
|
||||
showEndingDivider={false}
|
||||
/>
|
||||
),
|
||||
[objectFormState.loading, objectFormState.isEditing, emailTemplateId]
|
||||
)
|
||||
useDashboardObjectTools(currentObjectTools)
|
||||
|
||||
const actions = {
|
||||
edit: () => {
|
||||
objectFormRef?.current.startEditing()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current.cancelEditing()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current.handleUpdate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='emailTemplate'
|
||||
pageName='info'
|
||||
id={emailTemplateId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Email Template Information' },
|
||||
{ key: 'attachments', label: 'Attachments' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<UserNotifierToggle
|
||||
type='emailTemplate'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<InfoActionButtons
|
||||
type='emailTemplate'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<ActivityIndicator
|
||||
activities={objectFormState.activities}
|
||||
showStartingDivider={true}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
cancelEditing={() => {
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}}
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={
|
||||
objectFormState.beingEditedByOther ||
|
||||
objectFormState.loading ||
|
||||
objectFormState.editDisabled
|
||||
}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={emailTemplateId}
|
||||
type='emailTemplate'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
setCurrentObject={true}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Email Template Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='emailTemplate'
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
content: false,
|
||||
testObject: false,
|
||||
attachments: false
|
||||
}}
|
||||
labelWidth='210px'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Attachments'
|
||||
icon={<FileIcon />}
|
||||
active={collapseState.attachments}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('attachments', expanded)
|
||||
}
|
||||
collapseKey='attachments'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('emailTemplate', 'attachments')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
size='medium'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Spin
|
||||
spinning={objectFormState.loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={emailTemplateId} type='emailTemplate' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{
|
||||
parent:
|
||||
getModelByName('emailTemplate').prefix +
|
||||
':' +
|
||||
emailTemplateId
|
||||
}}
|
||||
visibleColumns={{ _id: false, parent: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmailTemplateInfo
|
||||
@ -0,0 +1,97 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewEmailTemplate = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'emailTemplate'}
|
||||
defaultValues={{ active: true, global: false, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailTemplate'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
labelWidth={110}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailTemplate'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
visibleProperties={{
|
||||
content: false,
|
||||
testObject: false,
|
||||
referencedTemplates: false,
|
||||
attachments: false
|
||||
}}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='emailTemplate'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
_reference: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
referencedTemplates: false,
|
||||
attachments: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Email Template'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewEmailTemplate.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewEmailTemplate
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -109,7 +109,7 @@ const FilamentSkuInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='filamentSku'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -24,7 +24,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import FilamentIcon from '../../../Icons/FilamentIcon.jsx'
|
||||
import FilamentSkuIcon from '../../../Icons/FilamentSkuIcon.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
@ -131,7 +131,7 @@ const FilamentInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='filament'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import FileIcon from '../../../Icons/FileIcon.jsx'
|
||||
import FilePreview from '../../common/FilePreview.jsx'
|
||||
@ -124,7 +124,7 @@ const FileInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='file'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
||||
import DocumentPrinterIcon from '../../../Icons/DocumentPrinterIcon.jsx'
|
||||
@ -126,7 +126,7 @@ const HostInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='host'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -112,7 +112,7 @@ const MaterialInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='material'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -17,7 +17,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -103,7 +103,7 @@ const NoteTypeInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='noteType'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -113,7 +113,7 @@ const NoteInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='note'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -104,7 +104,7 @@ const PartSkuInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='partSku'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -20,7 +20,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -116,7 +116,7 @@ const PartInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='part'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelByName,
|
||||
@ -115,7 +115,7 @@ const PermissionSettingInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='permissionSetting'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -113,7 +113,7 @@ const ProductCategoryInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='productCategory'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
@ -111,7 +111,7 @@ const ProductSkuInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='productSku'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -20,7 +20,7 @@ import { useActions } from '../../context/ActionsContext'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -116,7 +116,7 @@ const ProductInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='product'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -20,7 +20,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -110,7 +110,7 @@ const StockAuditLevelInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='stockAuditLevel'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -113,7 +113,7 @@ const TaxRateInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='taxRate'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
@ -115,7 +115,7 @@ const UserGroupInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='userGroup'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -20,7 +20,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
@ -114,7 +114,7 @@ const UserInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='user'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -109,7 +109,7 @@ const VendorInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='vendor'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -4,7 +4,7 @@ import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import ActionHandler from '../../common/ActionHandler'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
import FilamentIcon from '../../../Icons/FilamentIcon'
|
||||
@ -158,7 +158,7 @@ const FilamentProfileInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='filamentProfile'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -120,7 +120,7 @@ const GCodeFileInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='gcodeFile'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import JobIcon from '../../../Icons/JobIcon.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -126,7 +126,7 @@ const JobInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='job'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -4,7 +4,7 @@ import { Card, Flex, Space, Spin } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import ActionHandler from '../../common/ActionHandler'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
import HotEndIcon from '../../../Icons/HotEndIcon'
|
||||
@ -130,7 +130,7 @@ const PrinterProfileInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='printerProfile'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import PrinterProfileIcon from '../../../Icons/PrinterProfileIcon.jsx'
|
||||
@ -127,7 +127,7 @@ const PrinterInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='printer'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -19,7 +19,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
@ -104,7 +104,7 @@ const SubJobInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='subJob'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -21,7 +21,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
@ -109,7 +109,7 @@ const ClientInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='client'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelProperty,
|
||||
@ -121,7 +121,7 @@ const FulfillmentPolicyInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='fulfillmentPolicy'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -25,7 +25,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import {
|
||||
@ -132,7 +132,7 @@ const ListingVarientInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='listingVarient'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -26,7 +26,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelByName,
|
||||
@ -131,7 +131,7 @@ const ListingInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='listing'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
||||
@ -189,7 +189,7 @@ const MarketplaceInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='marketplace'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -23,7 +23,7 @@ import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelProperty,
|
||||
@ -121,7 +121,7 @@ const ReturnPolicyInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='returnPolicy'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
@ -22,7 +22,7 @@ import { useDashboardObjectTools } from '../../context/DashboardObjectToolsConte
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
@ -141,7 +141,7 @@ const SalesOrderInfo = () => {
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
<InfoActionButtons
|
||||
type='salesOrder'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
|
||||
215
src/components/Dashboard/common/EmailSendButton.jsx
Normal file
215
src/components/Dashboard/common/EmailSendButton.jsx
Normal file
@ -0,0 +1,215 @@
|
||||
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
|
||||
19
src/components/Dashboard/common/InfoActionButtons.jsx
Normal file
19
src/components/Dashboard/common/InfoActionButtons.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Flex } from 'antd'
|
||||
import PropTypes from 'prop-types'
|
||||
import DocumentPrintButton from './DocumentPrintButton'
|
||||
import EmailSendButton from './EmailSendButton'
|
||||
|
||||
const InfoActionButtons = (props) => (
|
||||
<Flex gap='small'>
|
||||
<DocumentPrintButton {...props} />
|
||||
<EmailSendButton {...props} />
|
||||
</Flex>
|
||||
)
|
||||
|
||||
InfoActionButtons.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
objectData: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default InfoActionButtons
|
||||
@ -967,6 +967,17 @@ const ObjectProperty = ({
|
||||
)
|
||||
case 'text':
|
||||
return <Input placeholder={label} {...inputProps} />
|
||||
case 'email':
|
||||
if (Array.isArray(options) && options.length) {
|
||||
return (
|
||||
<CustomSelect
|
||||
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
||||
options={options}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return <Input type='email' placeholder={label} {...inputProps} />
|
||||
case 'codeBlock':
|
||||
return (
|
||||
<CodeBlockEditor
|
||||
|
||||
@ -96,6 +96,11 @@ const parseEjsErrorLines = (message) => {
|
||||
|
||||
const TemplateEditor = ({
|
||||
objectData,
|
||||
template,
|
||||
templateType = 'documentTemplate',
|
||||
editorLanguage = 'fcTemplateLang',
|
||||
subjectProperty,
|
||||
previewCapabilities,
|
||||
loading,
|
||||
collapseState,
|
||||
isEditing,
|
||||
@ -104,6 +109,7 @@ const TemplateEditor = ({
|
||||
setObjectData
|
||||
}) => {
|
||||
const { fetchTemplateFormat } = useContext(ApiServerContext)
|
||||
const currentTemplate = template || objectData
|
||||
const [testObjectOpen, setTestObjectOpen] = useState(false)
|
||||
const [testObjectViewMode, setTestObjectViewMode] = useState('Tree')
|
||||
const [previewMessage, setPreviewMessage] = useState('No issues found.')
|
||||
@ -150,14 +156,14 @@ const TemplateEditor = ({
|
||||
}, [])
|
||||
|
||||
const handleFormatCode = useCallback(async () => {
|
||||
const content = form?.getFieldValue?.('content') ?? objectData?.content
|
||||
const content = form?.getFieldValue?.('content') ?? currentTemplate?.content
|
||||
if (content == null || typeof content !== 'string') {
|
||||
return
|
||||
}
|
||||
|
||||
setFormatLoading(true)
|
||||
try {
|
||||
const result = await fetchTemplateFormat(content)
|
||||
const result = await fetchTemplateFormat(content, null, templateType)
|
||||
if (result?.error) {
|
||||
handlePreviewMessage(result.error, true)
|
||||
return
|
||||
@ -177,10 +183,11 @@ const TemplateEditor = ({
|
||||
}
|
||||
}, [
|
||||
form,
|
||||
objectData?.content,
|
||||
currentTemplate?.content,
|
||||
fetchTemplateFormat,
|
||||
handlePreviewMessage,
|
||||
setObjectData
|
||||
setObjectData,
|
||||
templateType
|
||||
])
|
||||
|
||||
return (
|
||||
@ -195,8 +202,10 @@ const TemplateEditor = ({
|
||||
styles={{ body: { height: '100%', padding: '22px' } }}
|
||||
>
|
||||
<TemplatePreview
|
||||
objectData={objectData?.testObject}
|
||||
documentTemplate={objectData}
|
||||
objectData={currentTemplate?.testObject}
|
||||
template={currentTemplate}
|
||||
templateType={templateType}
|
||||
capabilities={previewCapabilities}
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
style={style}
|
||||
@ -244,6 +253,24 @@ const TemplateEditor = ({
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{subjectProperty ? (
|
||||
<ObjectProperty
|
||||
useFormItem={false}
|
||||
name={subjectProperty}
|
||||
type='text'
|
||||
objectData={currentTemplate}
|
||||
value={currentTemplate?.[subjectProperty]}
|
||||
isEditing={isEditing}
|
||||
onChange={(value) => {
|
||||
form?.setFieldValue?.(subjectProperty, value)
|
||||
setObjectData?.((previous) => ({
|
||||
...previous,
|
||||
[subjectProperty]: value
|
||||
}))
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -264,8 +291,8 @@ const TemplateEditor = ({
|
||||
name={'content'}
|
||||
height='100%'
|
||||
type='codeBlock'
|
||||
language={'fcTemplateLang'}
|
||||
objectData={objectData}
|
||||
language={editorLanguage}
|
||||
objectData={currentTemplate}
|
||||
onChange={(value) => {
|
||||
form?.setFieldValue?.('content', value)
|
||||
setObjectData?.((prev) => ({
|
||||
@ -274,7 +301,7 @@ const TemplateEditor = ({
|
||||
}))
|
||||
}}
|
||||
isEditing={isEditing}
|
||||
autoCompleteObject={objectData?.testObject}
|
||||
autoCompleteObject={currentTemplate?.testObject}
|
||||
errorLines={errorLines}
|
||||
/>
|
||||
</div>
|
||||
@ -318,13 +345,13 @@ const TemplateEditor = ({
|
||||
type={'codeBlock'}
|
||||
name='testObject'
|
||||
language='json'
|
||||
objectData={objectData}
|
||||
objectData={currentTemplate}
|
||||
isEditing={true}
|
||||
/>
|
||||
)}
|
||||
{testObjectViewMode == 'Tree' && (
|
||||
<DataTree
|
||||
data={objectData?.testObject}
|
||||
data={currentTemplate?.testObject}
|
||||
defaultExpandAll={true}
|
||||
showValueCopy={false}
|
||||
showKeyCopy={true}
|
||||
@ -340,6 +367,11 @@ const TemplateEditor = ({
|
||||
TemplateEditor.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
objectData: PropTypes.object,
|
||||
template: PropTypes.object,
|
||||
templateType: PropTypes.string,
|
||||
editorLanguage: PropTypes.string,
|
||||
subjectProperty: PropTypes.string,
|
||||
previewCapabilities: PropTypes.object,
|
||||
collapseState: PropTypes.object,
|
||||
isEditing: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
useCallback
|
||||
} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Flex, Button, Input, Select, Card, Popover } from 'antd'
|
||||
import { Flex, Button, Input, InputNumber, Select, Card, Popover } from 'antd'
|
||||
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||
@ -24,6 +24,10 @@ const noop = () => {}
|
||||
const TemplatePreview = ({
|
||||
objectData,
|
||||
documentTemplate,
|
||||
content = null,
|
||||
template,
|
||||
templateType = 'documentTemplate',
|
||||
capabilities = {},
|
||||
loading,
|
||||
isEditing,
|
||||
onTestObjectOpen = noop,
|
||||
@ -31,6 +35,10 @@ const TemplatePreview = ({
|
||||
showTestObject = false,
|
||||
showPreviewSwitch = true
|
||||
}) => {
|
||||
const currentTemplate = template || documentTemplate
|
||||
const htmlOnly = capabilities.htmlOnly === true
|
||||
const showWidthControl = capabilities.widthControl === true
|
||||
const defaultPreviewWidth = capabilities.defaultWidth || 600
|
||||
const iframeRef = useRef(null)
|
||||
const scrollElementRef = useRef(null)
|
||||
const savedScrollRef = useRef({ top: 0, left: 0 })
|
||||
@ -48,6 +56,7 @@ const TemplatePreview = ({
|
||||
const [downloadProgress, setDownloadProgress] = useState(null)
|
||||
const [previewScale, setPreviewScale] = useState(1)
|
||||
const [previewType, setPreviewType] = useState('HTML')
|
||||
const [previewWidth, setPreviewWidth] = useState(defaultPreviewWidth)
|
||||
const [panMode, setPanMode] = useState(false)
|
||||
const [isPanning, setIsPanning] = useState(false)
|
||||
const panStartRef = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 })
|
||||
@ -121,8 +130,12 @@ const TemplatePreview = ({
|
||||
parseFloat(containerStyle.paddingBottom)
|
||||
}
|
||||
|
||||
const contentWidth = Math.ceil(visual.width + padX)
|
||||
const contentHeight = Math.ceil(visual.height + padY)
|
||||
const contentWidth = showWidthControl
|
||||
? previewWidth
|
||||
: Math.ceil(visual.width + padX)
|
||||
const contentHeight = Math.ceil(
|
||||
Math.max(visual.height + padY, doc.documentElement.scrollHeight)
|
||||
)
|
||||
|
||||
if (!doc.head?.querySelector('style[data-preview-outer-scroll]')) {
|
||||
const styleEl = doc.createElement('style')
|
||||
@ -140,7 +153,7 @@ const TemplatePreview = ({
|
||||
}
|
||||
return { width, height }
|
||||
})
|
||||
}, [])
|
||||
}, [previewWidth, showWidthControl])
|
||||
|
||||
const restoreScrollPosition = useCallback(() => {
|
||||
const scrollEl = scrollElementRef.current
|
||||
@ -200,8 +213,11 @@ const TemplatePreview = ({
|
||||
}
|
||||
|
||||
setPreviewContentHTML('')
|
||||
setIframeSize({ width: '100%', height: 'auto' })
|
||||
}, [])
|
||||
setIframeSize({
|
||||
width: showWidthControl ? `${previewWidth}px` : '100%',
|
||||
height: 'auto'
|
||||
})
|
||||
}, [previewWidth, showWidthControl])
|
||||
|
||||
const stopRenderProgressListener = useCallback(() => {
|
||||
if (typeof renderUnsubscribeRef.current === 'function') {
|
||||
@ -234,7 +250,7 @@ const TemplatePreview = ({
|
||||
|
||||
const reloadPreviewPDF = useCallback(
|
||||
(content, testObject = {}) => {
|
||||
const templateId = documentTemplate?._id
|
||||
const templateId = currentTemplate?._id
|
||||
if (!templateId) {
|
||||
return
|
||||
}
|
||||
@ -346,7 +362,7 @@ const TemplatePreview = ({
|
||||
},
|
||||
[
|
||||
applyPdfResult,
|
||||
documentTemplate?._id,
|
||||
currentTemplate?._id,
|
||||
fetchTemplateDownload,
|
||||
stopRenderProgressListener,
|
||||
subscribeToObjectEvent
|
||||
@ -355,39 +371,61 @@ const TemplatePreview = ({
|
||||
|
||||
const reloadPreview = useCallback(
|
||||
(content, testObject = {}, scale = 1) => {
|
||||
const templateId = documentTemplate?._id
|
||||
const templateId = currentTemplate?._id
|
||||
if (!templateId) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = ++previewRequestIdRef.current
|
||||
setReloadLoading(true)
|
||||
fetchTemplatePreview(templateId, content, testObject, scale, (result) => {
|
||||
if (requestId !== previewRequestIdRef.current) {
|
||||
return
|
||||
}
|
||||
setReloadLoading(false)
|
||||
if (result?.error) {
|
||||
onPreviewMessage(result.error, true)
|
||||
} else {
|
||||
updatePreviewContentHTML(result.html)
|
||||
onPreviewMessage('No issues found.', false)
|
||||
}
|
||||
})
|
||||
fetchTemplatePreview(
|
||||
templateId,
|
||||
content,
|
||||
testObject,
|
||||
scale,
|
||||
(result) => {
|
||||
if (requestId !== previewRequestIdRef.current) {
|
||||
return
|
||||
}
|
||||
setReloadLoading(false)
|
||||
if (result?.error) {
|
||||
onPreviewMessage(result.error, true)
|
||||
} else {
|
||||
updatePreviewContentHTML(result.html)
|
||||
onPreviewMessage('No issues found.', false)
|
||||
}
|
||||
},
|
||||
templateType,
|
||||
showWidthControl ? { width: previewWidth } : {}
|
||||
)
|
||||
},
|
||||
[fetchTemplatePreview, onPreviewMessage, documentTemplate?._id]
|
||||
[
|
||||
currentTemplate?._id,
|
||||
fetchTemplatePreview,
|
||||
onPreviewMessage,
|
||||
previewWidth,
|
||||
showWidthControl,
|
||||
templateType
|
||||
]
|
||||
)
|
||||
|
||||
const templateContent = documentTemplate?.content
|
||||
const templateId = documentTemplate?._id
|
||||
const templateContent = currentTemplate?.content
|
||||
const templateId = currentTemplate?._id
|
||||
|
||||
useEffect(() => {
|
||||
if (content != null) {
|
||||
updatePreviewContentHTML(content)
|
||||
return
|
||||
}
|
||||
|
||||
if (!templateId || !templateContent || previewType != 'HTML') {
|
||||
return
|
||||
}
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
reloadPreview(templateContent, objectData, previewScale)
|
||||
if (content == null) {
|
||||
reloadPreview(templateContent, objectData, previewScale)
|
||||
}
|
||||
}, 300)
|
||||
|
||||
return () => {
|
||||
@ -398,8 +436,10 @@ const TemplatePreview = ({
|
||||
templateContent,
|
||||
templateId,
|
||||
previewScale,
|
||||
previewWidth,
|
||||
previewType,
|
||||
reloadPreview
|
||||
reloadPreview,
|
||||
content
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
@ -534,7 +574,20 @@ const TemplatePreview = ({
|
||||
}}
|
||||
disabled={loading || reloadLoading}
|
||||
/>
|
||||
{showPreviewSwitch == true ? (
|
||||
{showWidthControl ? (
|
||||
<InputNumber
|
||||
min={240}
|
||||
max={1600}
|
||||
step={10}
|
||||
addonAfter='px'
|
||||
value={previewWidth}
|
||||
disabled={loading || reloadLoading}
|
||||
onChange={(value) => setPreviewWidth(value || defaultPreviewWidth)}
|
||||
style={{ width: 130 }}
|
||||
aria-label='Email preview width'
|
||||
/>
|
||||
) : null}
|
||||
{showPreviewSwitch == true && !htmlOnly ? (
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'HTML', label: 'HTML' },
|
||||
@ -561,13 +614,13 @@ const TemplatePreview = ({
|
||||
<Flex gap={'small'} ref={toolbarRef}>
|
||||
{showTestObject == true ? (
|
||||
<>
|
||||
{documentTemplate?.objectType ? (
|
||||
{currentTemplate?.objectType ? (
|
||||
<ObjectProperty
|
||||
objectType={documentTemplate?.objectType}
|
||||
objectType={currentTemplate?.objectType}
|
||||
name={'testObject'}
|
||||
isEditing={true}
|
||||
objectData={documentTemplate}
|
||||
disabled={!isEditing || documentTemplate?.global}
|
||||
objectData={currentTemplate}
|
||||
disabled={!isEditing || currentTemplate?.global}
|
||||
type={'object'}
|
||||
style={{ minWidth: 0 }}
|
||||
/>
|
||||
@ -578,7 +631,7 @@ const TemplatePreview = ({
|
||||
)}
|
||||
<Button
|
||||
icon={<InfoCircleIcon />}
|
||||
disabled={documentTemplate?.global}
|
||||
disabled={currentTemplate?.global}
|
||||
onClick={() => {
|
||||
onTestObjectOpen()
|
||||
}}
|
||||
@ -707,12 +760,16 @@ TemplatePreview.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
objectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
documentTemplate: PropTypes.object,
|
||||
template: PropTypes.object,
|
||||
templateType: PropTypes.string,
|
||||
capabilities: PropTypes.object,
|
||||
isEditing: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
showTestObject: PropTypes.bool,
|
||||
onTestObjectOpen: PropTypes.func,
|
||||
onPreviewMessage: PropTypes.func,
|
||||
showPreviewSwitch: PropTypes.bool
|
||||
showPreviewSwitch: PropTypes.bool,
|
||||
content: PropTypes.string
|
||||
}
|
||||
|
||||
export default TemplatePreview
|
||||
|
||||
@ -2105,11 +2105,15 @@ const ApiServerProvider = ({ children }) => {
|
||||
URL.revokeObjectURL(fileUrl)
|
||||
}
|
||||
|
||||
const fetchTemplateFormat = async (content, callback) => {
|
||||
const fetchTemplateFormat = async (
|
||||
content,
|
||||
callback,
|
||||
templateType = 'documentTemplate'
|
||||
) => {
|
||||
logger.debug('Formatting template content...')
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${config.backendUrl}/documenttemplates/format`,
|
||||
`${config.backendUrl}/${getObjectEndpoint(templateType)}/format`,
|
||||
{ content },
|
||||
{
|
||||
headers: {
|
||||
@ -2137,17 +2141,20 @@ const ApiServerProvider = ({ children }) => {
|
||||
content,
|
||||
testObject,
|
||||
scale,
|
||||
callback
|
||||
callback,
|
||||
templateType = 'documentTemplate',
|
||||
previewOptions = {}
|
||||
) => {
|
||||
logger.debug('Fetching preview...')
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${config.backendUrl}/documenttemplates/${id}/preview`,
|
||||
`${config.backendUrl}/${getObjectEndpoint(templateType)}/${id}/preview`,
|
||||
{
|
||||
content,
|
||||
testObject,
|
||||
scale,
|
||||
padding: true
|
||||
...(templateType === 'documentTemplate' ? { padding: true } : {}),
|
||||
...previewOptions
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
|
||||
@ -289,6 +289,18 @@ export function getStateTagInfo(state) {
|
||||
status = 'error'
|
||||
text = 'Overdue'
|
||||
break
|
||||
case 'rendering':
|
||||
status = 'volcano'
|
||||
text = 'Rendering'
|
||||
break
|
||||
case 'sending':
|
||||
status = 'processing'
|
||||
text = 'Sending'
|
||||
break
|
||||
case 'read':
|
||||
status = 'success'
|
||||
text = 'Read'
|
||||
break
|
||||
default:
|
||||
status = 'default'
|
||||
text = state || 'Unknown'
|
||||
|
||||
105
src/components/Dashboard/utils/emailRecipients.js
Normal file
105
src/components/Dashboard/utils/emailRecipients.js
Normal file
@ -0,0 +1,105 @@
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
|
||||
const normalizeType = (type) =>
|
||||
typeof type === 'string'
|
||||
? `${type.charAt(0).toLowerCase()}${type.slice(1)}`
|
||||
: type
|
||||
|
||||
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) || ''
|
||||
}`
|
||||
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
|
||||
})
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return candidates
|
||||
}
|
||||
|
||||
/** Prefer a client/vendor ref; otherwise use a plain email candidate. */
|
||||
export const getDefaultEmailRecipient = (candidates = []) => {
|
||||
if (!Array.isArray(candidates) || !candidates.length) return null
|
||||
|
||||
const objectCandidate = candidates.find(
|
||||
(candidate) =>
|
||||
(candidate.recipientType === 'client' ||
|
||||
candidate.recipientType === 'vendor') &&
|
||||
candidate.recipient &&
|
||||
candidate.email
|
||||
)
|
||||
if (objectCandidate) {
|
||||
return {
|
||||
recipientEmail: objectCandidate.email,
|
||||
recipientType: objectCandidate.recipientType,
|
||||
recipient: objectCandidate.recipient
|
||||
}
|
||||
}
|
||||
|
||||
const emailCandidate = candidates.find((candidate) => candidate.email)
|
||||
if (!emailCandidate) return null
|
||||
|
||||
return { recipientEmail: emailCandidate.email }
|
||||
}
|
||||
8
src/components/Icons/EmailAccountIcon.jsx
Normal file
8
src/components/Icons/EmailAccountIcon.jsx
Normal file
@ -0,0 +1,8 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/emailaccounticon.svg?react'
|
||||
|
||||
const EmailAccountIcon = (props) => (
|
||||
<Icon component={CustomIconSvg} {...props} />
|
||||
)
|
||||
|
||||
export default EmailAccountIcon
|
||||
1
src/components/Icons/EmailMessageIcon.jsx
Normal file
1
src/components/Icons/EmailMessageIcon.jsx
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './MailIcon.jsx'
|
||||
8
src/components/Icons/EmailTemplateIcon.jsx
Normal file
8
src/components/Icons/EmailTemplateIcon.jsx
Normal file
@ -0,0 +1,8 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/mailtemplateicon.svg?react'
|
||||
|
||||
const EmailTemplateIcon = (props) => (
|
||||
<Icon component={CustomIconSvg} {...props} />
|
||||
)
|
||||
|
||||
export default EmailTemplateIcon
|
||||
@ -58,6 +58,9 @@ import ReturnPolicyIcon from './ReturnPolicyIcon'
|
||||
import PaymentPolicyIcon from './PaymentPolicyIcon'
|
||||
import AppPasswordIcon from './AppPasswordIcon'
|
||||
import InfoCircleIcon from './InfoCircleIcon'
|
||||
import MailIcon from './MailIcon'
|
||||
import EmailAccountIcon from './EmailAccountIcon'
|
||||
import EmailTemplateIcon from './EmailTemplateIcon'
|
||||
|
||||
const toEmoji = (emoji) => <span aria-hidden>{emoji}</span>
|
||||
|
||||
@ -121,6 +124,10 @@ const sidebarIconMap = {
|
||||
taxRate: <TaxRateIcon />,
|
||||
taxRecord: <TaxRecordIcon />,
|
||||
appPassword: <AppPasswordIcon />,
|
||||
email: <MailIcon />,
|
||||
emailAccount: <EmailAccountIcon />,
|
||||
emailTemplate: <EmailTemplateIcon />,
|
||||
emailMessage: <MailIcon />,
|
||||
infoCircle: <InfoCircleIcon />,
|
||||
sessionStorage: toEmoji('🗃️'),
|
||||
authDebug: toEmoji('🔐'),
|
||||
|
||||
@ -41,6 +41,9 @@ import { DocumentSize } from './models/DocumentSize.js'
|
||||
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
||||
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
||||
import { DocumentJob } from './models/DocumentJob.js'
|
||||
import { EmailAccount } from './models/EmailAccount.js'
|
||||
import { EmailTemplate } from './models/EmailTemplate.js'
|
||||
import { EmailMessage } from './models/EmailMessage.js'
|
||||
import { TaxRate } from './models/TaxRate.js'
|
||||
import { TaxRecord } from './models/TaxRecord.js'
|
||||
import { Invoice } from './models/Invoice.js'
|
||||
@ -99,6 +102,9 @@ export const objectModels = [
|
||||
DocumentTemplate,
|
||||
DocumentPrinter,
|
||||
DocumentJob,
|
||||
EmailAccount,
|
||||
EmailTemplate,
|
||||
EmailMessage,
|
||||
TaxRate,
|
||||
TaxRecord,
|
||||
Invoice,
|
||||
@ -158,6 +164,9 @@ export {
|
||||
DocumentTemplate,
|
||||
DocumentPrinter,
|
||||
DocumentJob,
|
||||
EmailAccount,
|
||||
EmailTemplate,
|
||||
EmailMessage,
|
||||
TaxRate,
|
||||
TaxRecord,
|
||||
Invoice,
|
||||
|
||||
@ -28,6 +28,7 @@ export const AuditLog = {
|
||||
filters: [
|
||||
'parent',
|
||||
'owner',
|
||||
'system',
|
||||
'operation',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
@ -74,16 +75,23 @@ export const AuditLog = {
|
||||
{
|
||||
name: 'owner',
|
||||
label: 'Owner',
|
||||
type: 'object',
|
||||
type: (objectData) => (objectData.system ? 'text' : 'object'),
|
||||
objectType: (objectData) => {
|
||||
return objectData.ownerType
|
||||
},
|
||||
columnFixed: 'left',
|
||||
value: null,
|
||||
value: (objectData) => (objectData.system ? 'System' : objectData.owner),
|
||||
showCopy: true,
|
||||
showHyperlink: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'system',
|
||||
label: 'System',
|
||||
type: 'bool',
|
||||
readOnly: true,
|
||||
columnWidth: 100
|
||||
},
|
||||
{
|
||||
name: 'parent',
|
||||
label: 'Parent',
|
||||
|
||||
233
src/database/models/EmailAccount.js
Normal file
233
src/database/models/EmailAccount.js
Normal file
@ -0,0 +1,233 @@
|
||||
import { createElement, lazy } from 'react'
|
||||
|
||||
const EmailAccountInfo = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailAccounts/EmailAccountInfo')
|
||||
)
|
||||
const NewEmailAccount = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailAccounts/NewEmailAccount')
|
||||
)
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import ListIcon from '../../components/Icons/ListIcon'
|
||||
import EmailAccountIcon from '../../components/Icons/EmailAccountIcon'
|
||||
|
||||
export const EmailAccount = {
|
||||
name: 'emailAccount',
|
||||
label: 'Email Account',
|
||||
labelPlural: 'Email Accounts',
|
||||
endpoint: 'emailaccounts',
|
||||
url: '/dashboard/management/emailaccounts',
|
||||
prefix: 'EAC',
|
||||
icon: EmailAccountIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'new',
|
||||
type: 'modal',
|
||||
pageName: 'list',
|
||||
modalWidth: 700,
|
||||
label: 'New Email Account',
|
||||
icon: PlusIcon,
|
||||
content: (objectData, { onOk } = {}) => {
|
||||
return createElement(NewEmailAccount, {
|
||||
defaultValues: objectData,
|
||||
onOk,
|
||||
reset: true
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
type: 'page',
|
||||
pageName: 'list',
|
||||
label: 'List',
|
||||
icon: ListIcon
|
||||
},
|
||||
{
|
||||
name: 'info',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
label: 'Edit',
|
||||
row: true,
|
||||
icon: EditIcon,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edits',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
icon: XMarkIcon,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Save Edits',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
icon: CheckIcon,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
}
|
||||
],
|
||||
pages: [
|
||||
{
|
||||
name: 'info',
|
||||
content: () => createElement(EmailAccountInfo)
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'name',
|
||||
'active',
|
||||
'host',
|
||||
'port',
|
||||
'secure',
|
||||
'username',
|
||||
'fromName',
|
||||
'fromEmail',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: [
|
||||
'name',
|
||||
'active',
|
||||
'host',
|
||||
'secure',
|
||||
'username',
|
||||
'fromEmail',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_reference'
|
||||
],
|
||||
group: ['active', 'secure'],
|
||||
sorters: [
|
||||
'name',
|
||||
'active',
|
||||
'host',
|
||||
'port',
|
||||
'secure',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
objectType: 'emailAccount',
|
||||
showCopy: true,
|
||||
columnWidth: 140
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
objectType: 'emailAccount',
|
||||
showCopy: true,
|
||||
readOnly: true,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
label: 'Active',
|
||||
type: 'bool',
|
||||
required: true,
|
||||
columnWidth: 125
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
label: 'SMTP Host',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnWidth: 220
|
||||
},
|
||||
{
|
||||
name: 'port',
|
||||
label: 'SMTP Port',
|
||||
type: 'number',
|
||||
required: true,
|
||||
min: 1,
|
||||
max: 65535,
|
||||
step: 1,
|
||||
columnWidth: 125
|
||||
},
|
||||
{
|
||||
name: 'secure',
|
||||
label: 'Secure',
|
||||
type: 'bool',
|
||||
required: true,
|
||||
columnWidth: 125
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
label: 'Username',
|
||||
type: 'text',
|
||||
required: false,
|
||||
columnWidth: 220
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
label: 'Password',
|
||||
type: 'secret',
|
||||
required: false,
|
||||
value: (data) => (data?._id ? undefined : data?.password),
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'fromName',
|
||||
label: 'From Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'fromEmail',
|
||||
label: 'From Email',
|
||||
type: 'email',
|
||||
required: true,
|
||||
columnWidth: 260
|
||||
}
|
||||
]
|
||||
}
|
||||
331
src/database/models/EmailMessage.js
Normal file
331
src/database/models/EmailMessage.js
Normal file
@ -0,0 +1,331 @@
|
||||
import { createElement, lazy } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
import ListIcon from '../../components/Icons/ListIcon'
|
||||
import EmailMessageIcon from '../../components/Icons/EmailMessageIcon'
|
||||
|
||||
const EmailMessageInfo = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailMessages/EmailMessageInfo')
|
||||
)
|
||||
const NewEmailMessage = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailMessages/NewEmailMessage')
|
||||
)
|
||||
|
||||
const readOnlyAfterCreate = (data) => data?._id != null
|
||||
|
||||
export const EmailMessage = {
|
||||
name: 'emailMessage',
|
||||
label: 'Email Message',
|
||||
labelPlural: 'Email Messages',
|
||||
endpoint: 'emailmessages',
|
||||
url: '/dashboard/management/emailmessages',
|
||||
prefix: 'EMS',
|
||||
icon: EmailMessageIcon,
|
||||
readOnly: true,
|
||||
actions: [
|
||||
{
|
||||
name: 'new',
|
||||
type: 'modal',
|
||||
pageName: 'list',
|
||||
modalWidth: 1100,
|
||||
label: 'New Email Message',
|
||||
icon: PlusIcon,
|
||||
content: (objectData, { onOk } = {}) =>
|
||||
createElement(NewEmailMessage, { defaultValues: objectData, onOk })
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
type: 'page',
|
||||
pageName: 'list',
|
||||
label: 'List',
|
||||
icon: ListIcon
|
||||
},
|
||||
{
|
||||
name: 'info',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon
|
||||
}
|
||||
],
|
||||
pages: [
|
||||
{
|
||||
name: 'info',
|
||||
content: () => createElement(EmailMessageInfo)
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'name',
|
||||
'state',
|
||||
'recipientEmail',
|
||||
'fromEmail',
|
||||
'messageId',
|
||||
'emailTemplate',
|
||||
'emailAccount',
|
||||
'objectType',
|
||||
'sentAt',
|
||||
'readAt',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: [
|
||||
'name',
|
||||
'state',
|
||||
'recipientEmail',
|
||||
'fromEmail',
|
||||
'messageId',
|
||||
'emailTemplate',
|
||||
'emailAccount',
|
||||
'objectType',
|
||||
'recipientType',
|
||||
'read',
|
||||
'sentAt',
|
||||
'readAt',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_reference'
|
||||
],
|
||||
group: [
|
||||
'state',
|
||||
'emailTemplate',
|
||||
'emailAccount',
|
||||
'objectType',
|
||||
'recipientType'
|
||||
],
|
||||
sorters: [
|
||||
'name',
|
||||
'state',
|
||||
'recipientEmail',
|
||||
'fromEmail',
|
||||
'messageId',
|
||||
'sentAt',
|
||||
'readAt',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
objectType: 'emailMessage',
|
||||
showCopy: true,
|
||||
columnWidth: 140
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
objectType: 'emailMessage',
|
||||
showCopy: true,
|
||||
readOnly: true,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
value: (data) =>
|
||||
data?.createdAt == null
|
||||
? `${data?.emailTemplate?.name || 'Email'} ${dayjs().format('YYYY-MM-DD HH:mm:ss')}`
|
||||
: undefined,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 240
|
||||
},
|
||||
{
|
||||
name: 'sentAt',
|
||||
label: 'Sent At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'state',
|
||||
label: 'Status',
|
||||
type: 'state',
|
||||
showName: false,
|
||||
readOnly: true,
|
||||
columnWidth: 240
|
||||
},
|
||||
{
|
||||
name: 'readAt',
|
||||
label: 'Read At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'read',
|
||||
label: 'Read',
|
||||
type: 'bool',
|
||||
readOnly: true,
|
||||
columnWidth: 100
|
||||
},
|
||||
{
|
||||
name: 'emailTemplate',
|
||||
label: 'Template',
|
||||
type: 'object',
|
||||
objectType: 'emailTemplate',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
showHyperlink: true,
|
||||
masterFilter: (data) => ({
|
||||
active: true,
|
||||
global: false,
|
||||
objectType: data?.objectType
|
||||
}),
|
||||
columnWidth: 200
|
||||
},
|
||||
|
||||
{
|
||||
name: 'objectType',
|
||||
label: 'Object Type',
|
||||
type: 'objectType',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'object',
|
||||
label: 'Object',
|
||||
type: 'object',
|
||||
objectType: (data) => data?.objectType,
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
showHyperlink: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'emailAccount',
|
||||
label: 'Email Account',
|
||||
type: 'object',
|
||||
objectType: 'emailAccount',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
showHyperlink: true,
|
||||
masterFilter: { active: true },
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'recipientEmail',
|
||||
label: 'Recipient',
|
||||
type: 'email',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
options: (data) =>
|
||||
Array.isArray(data?._recipientCandidates) &&
|
||||
data._recipientCandidates.length
|
||||
? data._recipientCandidates.map((candidate) => ({
|
||||
value: candidate.email,
|
||||
label: candidate.label
|
||||
}))
|
||||
: undefined,
|
||||
columnWidth: 260
|
||||
},
|
||||
{
|
||||
name: 'recipientType',
|
||||
label: 'Recipient Type',
|
||||
type: 'objectType',
|
||||
readOnly: readOnlyAfterCreate,
|
||||
value: (data) => {
|
||||
if (
|
||||
!Array.isArray(data?._recipientCandidates) ||
|
||||
!data._recipientCandidates.length
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
const match = data._recipientCandidates.find(
|
||||
(candidate) => candidate.email === data?.recipientEmail
|
||||
)
|
||||
if (!match) return undefined
|
||||
return match.recipientType ?? null
|
||||
},
|
||||
columnWidth: 160
|
||||
},
|
||||
{
|
||||
name: 'recipient',
|
||||
label: 'Recipient Object',
|
||||
type: 'object',
|
||||
objectType: (data) => data?.recipientType,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
showHyperlink: true,
|
||||
value: (data) => {
|
||||
if (
|
||||
!Array.isArray(data?._recipientCandidates) ||
|
||||
!data._recipientCandidates.length
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
const match = data._recipientCandidates.find(
|
||||
(candidate) => candidate.email === data?.recipientEmail
|
||||
)
|
||||
if (!match) return undefined
|
||||
return match.recipient ?? null
|
||||
},
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'fromEmail',
|
||||
label: 'From Email',
|
||||
type: 'email',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: (data) => data?.emailAccount?.fromEmail,
|
||||
columnWidth: 260
|
||||
},
|
||||
{
|
||||
name: 'messageId',
|
||||
label: 'Message ID',
|
||||
type: 'text',
|
||||
readOnly: true,
|
||||
showCopy: true,
|
||||
columnWidth: 280
|
||||
},
|
||||
{
|
||||
name: 'subject',
|
||||
label: 'Rendered Subject',
|
||||
type: 'text',
|
||||
readOnly: true,
|
||||
columnWidth: 300
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Rendered Body',
|
||||
type: 'codeBlock',
|
||||
language: 'html',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
label: 'Attachments',
|
||||
type: 'objectList',
|
||||
objectType: 'file',
|
||||
readOnly: true,
|
||||
showHyperlink: true,
|
||||
columnWidth: 230
|
||||
}
|
||||
]
|
||||
}
|
||||
313
src/database/models/EmailTemplate.js
Normal file
313
src/database/models/EmailTemplate.js
Normal file
@ -0,0 +1,313 @@
|
||||
import { createElement, lazy } from 'react'
|
||||
|
||||
const EmailTemplateInfo = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailTemplates/EmailTemplateInfo')
|
||||
)
|
||||
const NewEmailTemplate = lazy(
|
||||
() =>
|
||||
import('../../components/Dashboard/Management/EmailTemplates/NewEmailTemplate')
|
||||
)
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import DesignIcon from '../../components/Icons/DesignIcon'
|
||||
import ListIcon from '../../components/Icons/ListIcon'
|
||||
import EmailTemplateIcon from '../../components/Icons/EmailTemplateIcon'
|
||||
|
||||
export const EmailTemplate = {
|
||||
name: 'emailTemplate',
|
||||
label: 'Email Template',
|
||||
labelPlural: 'Email Templates',
|
||||
endpoint: 'emailtemplates',
|
||||
url: '/dashboard/management/emailtemplates',
|
||||
prefix: 'ETP',
|
||||
icon: EmailTemplateIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'new',
|
||||
type: 'modal',
|
||||
pageName: 'list',
|
||||
modalWidth: 700,
|
||||
label: 'New Email Template',
|
||||
icon: PlusIcon,
|
||||
content: (objectData, { onOk } = {}) => {
|
||||
return createElement(NewEmailTemplate, {
|
||||
defaultValues: objectData,
|
||||
onOk,
|
||||
reset: true
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
type: 'page',
|
||||
pageName: 'list',
|
||||
label: 'List',
|
||||
icon: ListIcon
|
||||
},
|
||||
{
|
||||
name: 'design',
|
||||
label: 'Design',
|
||||
row: true,
|
||||
icon: DesignIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/emailtemplates/design?emailTemplateId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'info',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
label: 'Edit',
|
||||
row: true,
|
||||
icon: EditIcon,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edits',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
icon: XMarkIcon,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Save Edits',
|
||||
type: 'page',
|
||||
pageName: 'info',
|
||||
icon: CheckIcon,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
}
|
||||
],
|
||||
pages: [
|
||||
{
|
||||
name: 'info',
|
||||
content: () => createElement(EmailTemplateInfo)
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'name',
|
||||
'active',
|
||||
'global',
|
||||
'objectType',
|
||||
'tags',
|
||||
'parent',
|
||||
'referencedTemplates',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: [
|
||||
'name',
|
||||
'tags',
|
||||
'active',
|
||||
'global',
|
||||
'parent',
|
||||
'referencedTemplates',
|
||||
'objectType',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_reference'
|
||||
],
|
||||
group: ['tags', 'objectType'],
|
||||
sorters: [
|
||||
'name',
|
||||
'parent',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'active',
|
||||
'global',
|
||||
'objectType'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
objectType: 'emailTemplate',
|
||||
showCopy: true,
|
||||
columnWidth: 140
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
objectType: 'emailTemplate',
|
||||
showCopy: true,
|
||||
readOnly: true,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnFixed: 'left',
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'objectType',
|
||||
label: 'Object Type',
|
||||
type: 'objectType',
|
||||
required: true,
|
||||
empty: (data) => data?.global,
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
label: 'Active',
|
||||
type: 'bool',
|
||||
required: true,
|
||||
columnWidth: 125
|
||||
},
|
||||
{ name: 'tags', label: 'Tags', type: 'tags', columnWidth: 200 },
|
||||
{
|
||||
name: 'global',
|
||||
label: 'Global',
|
||||
type: 'bool',
|
||||
required: true,
|
||||
columnWidth: 125
|
||||
},
|
||||
{
|
||||
name: 'parent',
|
||||
label: 'Parent',
|
||||
type: 'object',
|
||||
objectType: 'emailTemplate',
|
||||
masterFilter: { global: true, active: true },
|
||||
showHyperlink: true,
|
||||
empty: (data) => data?.global,
|
||||
columnWidth: 230
|
||||
},
|
||||
{
|
||||
name: 'referencedTemplates',
|
||||
label: 'Referenced Templates',
|
||||
type: 'objectList',
|
||||
objectType: 'emailTemplate',
|
||||
readOnly: true,
|
||||
showHyperlink: true,
|
||||
columnWidth: 230
|
||||
},
|
||||
{
|
||||
name: 'subject',
|
||||
label: 'Subject',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnWidth: 300
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Content',
|
||||
type: 'codeBlock',
|
||||
language: 'fcTemplateLang'
|
||||
},
|
||||
{
|
||||
name: 'testObject',
|
||||
label: 'Test Object',
|
||||
type: 'object',
|
||||
objectType: (data) => data?.objectType,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
label: 'Attachments',
|
||||
type: 'objectChildren',
|
||||
required: false,
|
||||
canAddRemove: true,
|
||||
size: 'medium',
|
||||
columns: ['type', 'file', 'fileName', 'fileType'],
|
||||
properties: [
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
type: 'objectType',
|
||||
required: true,
|
||||
columnWidth: 180,
|
||||
masterFilter: ['file', 'documentTemplate']
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
label: 'File',
|
||||
type: 'object',
|
||||
objectType: (row) => row?.type,
|
||||
required: true,
|
||||
showHyperlink: true,
|
||||
columnWidth: 230,
|
||||
masterFilter: (row, parentData) => {
|
||||
if (row?.type === 'documentTemplate') {
|
||||
return {
|
||||
active: true,
|
||||
global: false,
|
||||
objectType: parentData?.objectType
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fileName',
|
||||
label: 'File Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
columnWidth: 200,
|
||||
value: (row) => {
|
||||
if (row?.fileName) return row.fileName
|
||||
return row?.file?.name
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fileType',
|
||||
label: 'File Type',
|
||||
type: 'select',
|
||||
columnWidth: 140,
|
||||
disabled: (row) => row?.type !== 'documentTemplate',
|
||||
options: [
|
||||
{ value: 'pdf', label: 'PDF' },
|
||||
{ value: 'jpeg', label: 'JPEG' },
|
||||
{ value: 'png', label: 'PNG' },
|
||||
{ value: 'svg', label: 'SVG' }
|
||||
],
|
||||
value: (row) => {
|
||||
if (row?.type === 'documentTemplate') {
|
||||
return row?.fileType || 'pdf'
|
||||
}
|
||||
return row?.fileType
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -117,6 +117,31 @@ const managementSidebarItems = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
iconKey: 'email',
|
||||
label: 'Email',
|
||||
children: [
|
||||
{
|
||||
key: 'emailAccounts',
|
||||
iconKey: 'emailAccount',
|
||||
label: 'Email Accounts',
|
||||
path: '/dashboard/management/emailaccounts'
|
||||
},
|
||||
{
|
||||
key: 'emailMessages',
|
||||
iconKey: 'emailMessage',
|
||||
label: 'Email Messages',
|
||||
path: '/dashboard/management/emailmessages'
|
||||
},
|
||||
{
|
||||
key: 'emailTemplates',
|
||||
iconKey: 'emailTemplate',
|
||||
label: 'Email Templates',
|
||||
path: '/dashboard/management/emailtemplates'
|
||||
}
|
||||
]
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'hosts',
|
||||
|
||||
@ -69,6 +69,19 @@ const DocumentTemplateDesign = lazy(
|
||||
() =>
|
||||
import('../components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx')
|
||||
)
|
||||
const EmailAccounts = lazy(
|
||||
() => import('../components/Dashboard/Management/EmailAccounts.jsx')
|
||||
)
|
||||
const EmailTemplates = lazy(
|
||||
() => import('../components/Dashboard/Management/EmailTemplates.jsx')
|
||||
)
|
||||
const EmailMessages = lazy(
|
||||
() => import('../components/Dashboard/Management/EmailMessages.jsx')
|
||||
)
|
||||
const EmailTemplateDesign = lazy(
|
||||
() =>
|
||||
import('../components/Dashboard/Management/EmailTemplates/EmailTemplateDesign.jsx')
|
||||
)
|
||||
const Files = lazy(() => import('../components/Dashboard/Management/Files.jsx'))
|
||||
const TaxRates = lazy(
|
||||
() => import('../components/Dashboard/Management/TaxRates.jsx')
|
||||
@ -146,6 +159,26 @@ const ManagementRoutes = [
|
||||
path='management/documenttemplates/design'
|
||||
element={<DocumentTemplateDesign />}
|
||||
/>,
|
||||
<Route
|
||||
key='emailaccounts'
|
||||
path='management/emailaccounts'
|
||||
element={<EmailAccounts />}
|
||||
/>,
|
||||
<Route
|
||||
key='emailtemplates'
|
||||
path='management/emailtemplates'
|
||||
element={<EmailTemplates />}
|
||||
/>,
|
||||
<Route
|
||||
key='emailmessages'
|
||||
path='management/emailmessages'
|
||||
element={<EmailMessages />}
|
||||
/>,
|
||||
<Route
|
||||
key='emailtemplates-design'
|
||||
path='management/emailtemplates/design'
|
||||
element={<EmailTemplateDesign />}
|
||||
/>,
|
||||
<Route
|
||||
key='apppasswords'
|
||||
path='management/apppasswords'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user